summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml3
-rw-r--r--.gitignore4
-rw-r--r--.travis.yml101
-rw-r--r--AUTHORS.md4
-rw-r--r--CODEOWNERS37
-rw-r--r--COPYRIGHT.txt645
-rw-r--r--DONORS.md46
-rw-r--r--SConstruct109
-rw-r--r--compat.py4
-rw-r--r--core/SCsub15
-rw-r--r--core/bind/core_bind.cpp51
-rw-r--r--core/bind/core_bind.h10
-rw-r--r--core/class_db.cpp6
-rw-r--r--core/class_db.h14
-rw-r--r--core/color.cpp26
-rw-r--r--core/color_names.inc2
-rw-r--r--core/command_queue_mt.h24
-rw-r--r--core/dictionary.cpp26
-rw-r--r--core/dictionary.h2
-rw-r--r--core/engine.cpp75
-rw-r--r--core/engine.h5
-rw-r--r--core/error_macros.h16
-rw-r--r--core/global_constants.cpp4
-rw-r--r--core/hashfuncs.h7
-rw-r--r--core/image.cpp62
-rw-r--r--core/image.h2
-rw-r--r--core/io/http_client.cpp133
-rw-r--r--core/io/http_client.h2
-rw-r--r--core/io/logger.cpp9
-rw-r--r--core/io/multiplayer_api.cpp268
-rw-r--r--core/io/multiplayer_api.h57
-rw-r--r--core/io/stream_peer_ssl.cpp17
-rw-r--r--core/io/stream_peer_ssl.h8
-rw-r--r--core/io/translation_loader_po.cpp2
-rw-r--r--core/math/a_star.cpp24
-rw-r--r--core/math/a_star.h2
-rw-r--r--core/math/aabb.h20
-rw-r--r--core/math/delaunay.cpp1
-rw-r--r--core/math/delaunay.h145
-rw-r--r--core/math/math_2d.h9
-rw-r--r--core/math/math_funcs.h18
-rw-r--r--core/math/matrix3.cpp37
-rw-r--r--core/math/matrix3.h6
-rw-r--r--core/math/quat.cpp26
-rw-r--r--core/math/quat.h28
-rw-r--r--core/math/transform.cpp4
-rw-r--r--core/math/triangle_mesh.cpp236
-rw-r--r--core/math/triangle_mesh.h6
-rw-r--r--core/math/vector3.h10
-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.cpp1
-rw-r--r--core/object.h6
-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.cpp1
-rw-r--r--core/os/os.h14
-rw-r--r--core/pool_allocator.cpp2
-rw-r--r--core/project_settings.cpp12
-rw-r--r--core/project_settings.h1
-rw-r--r--core/resource.cpp20
-rw-r--r--core/script_debugger_remote.cpp63
-rw-r--r--core/script_debugger_remote.h5
-rw-r--r--core/script_language.cpp91
-rw-r--r--core/script_language.h46
-rw-r--r--core/string_db.h6
-rw-r--r--core/type_info.h1
-rw-r--r--core/undo_redo.cpp16
-rw-r--r--core/undo_redo.h4
-rw-r--r--core/ustring.cpp40
-rw-r--r--core/ustring.h1
-rw-r--r--core/variant.cpp24
-rw-r--r--core/variant.h2
-rw-r--r--core/variant_call.cpp22
-rw-r--r--core/variant_op.cpp13
-rw-r--r--doc/classes/@GDScript.xml19
-rw-r--r--doc/classes/@GlobalScope.xml3
-rw-r--r--doc/classes/AABB.xml2
-rw-r--r--doc/classes/AcceptDialog.xml2
-rw-r--r--doc/classes/AnimatedSprite.xml2
-rw-r--r--doc/classes/Animation.xml228
-rw-r--r--doc/classes/AnimationPlayer.xml4
-rw-r--r--doc/classes/AnimationTrackEditPlugin.xml (renamed from modules/webm/doc_classes/ResourceImporterWebm.xml)2
-rw-r--r--doc/classes/AnimationTreePlayer.xml8
-rw-r--r--doc/classes/Array.xml15
-rw-r--r--doc/classes/ArrayMesh.xml8
-rw-r--r--doc/classes/AudioEffectEQ10.xml1
-rw-r--r--doc/classes/AudioEffectEQ21.xml1
-rw-r--r--doc/classes/AudioEffectEQ6.xml1
-rw-r--r--doc/classes/AudioServer.xml2
-rw-r--r--doc/classes/AudioStream.xml2
-rw-r--r--doc/classes/AudioStreamPlayer.xml4
-rw-r--r--doc/classes/AudioStreamPlayer2D.xml4
-rw-r--r--doc/classes/AudioStreamPlayer3D.xml4
-rw-r--r--doc/classes/AudioStreamSample.xml3
-rw-r--r--doc/classes/BakedLightmap.xml2
-rw-r--r--doc/classes/Basis.xml20
-rw-r--r--doc/classes/Bone2D.xml39
-rw-r--r--doc/classes/CanvasItem.xml7
-rw-r--r--doc/classes/CanvasItemMaterial.xml3
-rw-r--r--doc/classes/CanvasLayer.xml4
-rw-r--r--doc/classes/ClassDB.xml2
-rw-r--r--doc/classes/CollisionShape.xml2
-rw-r--r--doc/classes/CollisionShape2D.xml2
-rw-r--r--doc/classes/Color.xml4
-rw-r--r--doc/classes/ColorPicker.xml3
-rw-r--r--doc/classes/ColorPickerButton.xml6
-rw-r--r--doc/classes/ConfigFile.xml2
-rw-r--r--doc/classes/Control.xml16
-rw-r--r--doc/classes/Curve2D.xml20
-rw-r--r--doc/classes/Curve3D.xml46
-rw-r--r--doc/classes/CylinderShape.xml25
-rw-r--r--doc/classes/DirectionalLight.xml2
-rw-r--r--doc/classes/Directory.xml2
-rw-r--r--doc/classes/DynamicFont.xml4
-rw-r--r--doc/classes/EditorImportPlugin.xml3
-rw-r--r--doc/classes/EditorInspector.xml37
-rw-r--r--doc/classes/EditorInspectorPlugin.xml95
-rw-r--r--doc/classes/EditorInterface.xml2
-rw-r--r--doc/classes/EditorPlugin.xml45
-rw-r--r--doc/classes/EditorProperty.xml111
-rw-r--r--doc/classes/EditorScenePostImport.xml38
-rw-r--r--doc/classes/EditorSettings.xml24
-rw-r--r--doc/classes/EditorSpatialGizmo.xml2
-rw-r--r--doc/classes/Engine.xml45
-rw-r--r--doc/classes/Environment.xml4
-rw-r--r--doc/classes/File.xml6
-rw-r--r--doc/classes/FileDialog.xml6
-rw-r--r--doc/classes/Font.xml10
-rw-r--r--doc/classes/GIProbe.xml2
-rw-r--r--doc/classes/Geometry.xml30
-rw-r--r--doc/classes/GridContainer.xml10
-rw-r--r--doc/classes/HTTPClient.xml4
-rw-r--r--doc/classes/HTTPRequest.xml2
-rw-r--r--doc/classes/Image.xml28
-rw-r--r--doc/classes/ImageTexture.xml4
-rw-r--r--doc/classes/Input.xml20
-rw-r--r--doc/classes/InputEvent.xml19
-rw-r--r--doc/classes/InputEventAction.xml2
-rw-r--r--doc/classes/InputEventJoypadButton.xml4
-rw-r--r--doc/classes/InputEventJoypadMotion.xml4
-rw-r--r--doc/classes/InputEventKey.xml4
-rw-r--r--doc/classes/InputEventMouse.xml2
-rw-r--r--doc/classes/InputEventMouseButton.xml4
-rw-r--r--doc/classes/InputEventMouseMotion.xml2
-rw-r--r--doc/classes/InputEventScreenDrag.xml2
-rw-r--r--doc/classes/InputEventScreenTouch.xml2
-rw-r--r--doc/classes/InputEventWithModifiers.xml2
-rw-r--r--doc/classes/InputMap.xml25
-rw-r--r--doc/classes/InstancePlaceholder.xml10
-rw-r--r--doc/classes/JavaScript.xml2
-rw-r--r--doc/classes/KinematicBody.xml2
-rw-r--r--doc/classes/Label.xml2
-rw-r--r--doc/classes/Light.xml2
-rw-r--r--doc/classes/Line2D.xml3
-rw-r--r--doc/classes/LineEdit.xml2
-rw-r--r--doc/classes/MultiplayerAPI.xml92
-rw-r--r--doc/classes/NetworkedMultiplayerPeer.xml5
-rw-r--r--doc/classes/Node.xml43
-rw-r--r--doc/classes/Node2D.xml2
-rw-r--r--doc/classes/OS.xml42
-rw-r--r--doc/classes/Object.xml2
-rw-r--r--doc/classes/OmniLight.xml2
-rw-r--r--doc/classes/OptionButton.xml2
-rw-r--r--doc/classes/OrientedPathFollow.xml40
-rw-r--r--doc/classes/PacketPeerUDP.xml4
-rw-r--r--doc/classes/PanoramaSky.xml3
-rw-r--r--doc/classes/Particles.xml9
-rw-r--r--doc/classes/Path.xml6
-rw-r--r--doc/classes/PhysicalBone.xml63
-rw-r--r--doc/classes/Physics2DDirectSpaceState.xml2
-rw-r--r--doc/classes/Physics2DServer.xml26
-rw-r--r--doc/classes/PhysicsBody.xml2
-rw-r--r--doc/classes/PhysicsBody2D.xml2
-rw-r--r--doc/classes/PhysicsDirectSpaceState.xml2
-rw-r--r--doc/classes/PhysicsServer.xml35
-rw-r--r--doc/classes/Plane.xml4
-rw-r--r--doc/classes/Polygon2D.xml70
-rw-r--r--doc/classes/Popup.xml2
-rw-r--r--doc/classes/PopupMenu.xml21
-rw-r--r--doc/classes/PrimitiveMesh.xml4
-rw-r--r--doc/classes/PrismMesh.xml2
-rw-r--r--doc/classes/ProceduralSky.xml19
-rw-r--r--doc/classes/ProjectSettings.xml530
-rw-r--r--doc/classes/Quat.xml60
-rw-r--r--doc/classes/Range.xml4
-rw-r--r--doc/classes/Rect2.xml3
-rw-r--r--doc/classes/ReflectionProbe.xml2
-rw-r--r--doc/classes/RichTextLabel.xml28
-rw-r--r--doc/classes/RigidBody.xml2
-rw-r--r--doc/classes/SceneTree.xml17
-rw-r--r--doc/classes/Script.xml2
-rw-r--r--doc/classes/ScriptCreateDialog.xml45
-rw-r--r--doc/classes/ScrollContainer.xml18
-rw-r--r--doc/classes/Shader.xml7
-rw-r--r--doc/classes/ShaderMaterial.xml5
-rw-r--r--doc/classes/Shape.xml2
-rw-r--r--doc/classes/Shape2D.xml2
-rw-r--r--doc/classes/Skeleton.xml39
-rw-r--r--doc/classes/Skeleton2D.xml35
-rw-r--r--doc/classes/Slider.xml2
-rw-r--r--doc/classes/Spatial.xml4
-rw-r--r--doc/classes/SpatialMaterial.xml8
-rw-r--r--doc/classes/SpotLight.xml2
-rw-r--r--doc/classes/SpriteBase3D.xml6
-rw-r--r--doc/classes/SpriteFrames.xml9
-rw-r--r--doc/classes/StreamPeerSSL.xml2
-rw-r--r--doc/classes/String.xml14
-rw-r--r--doc/classes/TextEdit.xml35
-rw-r--r--doc/classes/Texture.xml1
-rw-r--r--doc/classes/TextureRect.xml8
-rw-r--r--doc/classes/TileMap.xml49
-rw-r--r--doc/classes/TileSet.xml22
-rw-r--r--doc/classes/Timer.xml2
-rw-r--r--doc/classes/ToolButton.xml1
-rw-r--r--doc/classes/Transform.xml4
-rw-r--r--doc/classes/TreeItem.xml2
-rw-r--r--doc/classes/Tween.xml8
-rw-r--r--doc/classes/Vector2.xml74
-rw-r--r--doc/classes/Vector3.xml63
-rw-r--r--doc/classes/VideoPlayer.xml25
-rw-r--r--doc/classes/Viewport.xml12
-rw-r--r--doc/classes/ViewportTexture.xml4
-rw-r--r--doc/classes/VisualServer.xml37
-rw-r--r--doc/classes/WebSocketClient.xml57
-rw-r--r--doc/classes/WebSocketPeer.xml51
-rw-r--r--doc/classes/WebSocketServer.xml69
-rw-r--r--doc/classes/World.xml2
-rw-r--r--doc/classes/World2D.xml2
-rw-r--r--doc/classes/WorldEnvironment.xml2
-rwxr-xr-x[-rw-r--r--]doc/tools/makerst.py77
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.cpp82
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.h3
-rw-r--r--drivers/dummy/rasterizer_dummy.h179
-rw-r--r--drivers/dummy/texture_loader_dummy.cpp87
-rw-r--r--drivers/dummy/texture_loader_dummy.h (renamed from modules/webm/resource_importer_webm.h)31
-rw-r--r--drivers/gles2/rasterizer_canvas_gles2.cpp21
-rw-r--r--drivers/gles2/rasterizer_gles2.cpp49
-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.cpp24
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.h13
-rw-r--r--drivers/gles2/shader_compiler_gles2.cpp2
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp42
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h2
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp48
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp46
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h6
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp153
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h13
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp1
-rw-r--r--drivers/gles3/shaders/canvas.glsl14
-rw-r--r--drivers/gles3/shaders/scene.glsl22
-rw-r--r--drivers/gles3/shaders/tonemap.glsl448
-rw-r--r--drivers/unix/os_unix.cpp2
-rw-r--r--drivers/unix/socket_helpers.h7
-rw-r--r--drivers/unix/stream_peer_tcp_posix.cpp1
-rw-r--r--drivers/windows/stream_peer_tcp_winsock.cpp1
-rw-r--r--editor/SCsub284
-rw-r--r--editor/animation_bezier_editor.cpp1183
-rw-r--r--editor/animation_bezier_editor.h141
-rw-r--r--editor/animation_editor.cpp4145
-rw-r--r--editor/animation_editor.h348
-rw-r--r--editor/animation_track_editor.cpp5076
-rw-r--r--editor/animation_track_editor.h484
-rw-r--r--editor/animation_track_editor_plugins.cpp1317
-rw-r--r--editor/animation_track_editor_plugins.h139
-rw-r--r--editor/audio_stream_preview.cpp211
-rw-r--r--editor/audio_stream_preview.h56
-rw-r--r--editor/code_editor.cpp14
-rw-r--r--editor/code_editor.h2
-rw-r--r--editor/connections_dialog.cpp16
-rw-r--r--editor/create_dialog.cpp64
-rw-r--r--editor/create_dialog.h1
-rw-r--r--editor/dependency_editor.cpp26
-rw-r--r--editor/doc/doc_data.cpp35
-rw-r--r--editor/doc/doc_data.h2
-rw-r--r--editor/editor_about.cpp54
-rw-r--r--editor/editor_about.h2
-rw-r--r--editor/editor_autoload_settings.cpp219
-rw-r--r--editor/editor_autoload_settings.h12
-rw-r--r--editor/editor_data.cpp35
-rw-r--r--editor/editor_data.h7
-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.cpp251
-rw-r--r--editor/editor_help.h48
-rw-r--r--editor/editor_inspector.cpp2050
-rw-r--r--editor/editor_inspector.h336
-rw-r--r--editor/editor_node.cpp1083
-rw-r--r--editor/editor_node.h81
-rw-r--r--editor/editor_plugin.cpp37
-rw-r--r--editor/editor_plugin.h7
-rw-r--r--editor/editor_profiler.cpp23
-rw-r--r--editor/editor_profiler.h2
-rw-r--r--editor/editor_properties.cpp2776
-rw-r--r--editor/editor_properties.h553
-rw-r--r--editor/editor_properties_array_dict.cpp1017
-rw-r--r--editor/editor_properties_array_dict.h117
-rw-r--r--editor/editor_settings.cpp119
-rw-r--r--editor/editor_settings.h4
-rw-r--r--editor/editor_spin_slider.cpp398
-rw-r--r--editor/editor_spin_slider.h95
-rw-r--r--editor/editor_themes.cpp154
-rw-r--r--editor/export_template_manager.cpp8
-rw-r--r--editor/export_template_manager.h2
-rw-r--r--editor/filesystem_dock.cpp106
-rw-r--r--editor/filesystem_dock.h9
-rw-r--r--editor/find_in_files.cpp11
-rw-r--r--editor/groups_editor.cpp4
-rw-r--r--editor/icons/README.md4
-rw-r--r--editor/icons/icon_GUI_slider_grabber.svg85
-rw-r--r--editor/icons/icon_GUI_slider_grabber_hl.svg84
-rw-r--r--editor/icons/icon_animated_texture.svg74
-rw-r--r--editor/icons/icon_animation_filter.svg63
-rw-r--r--editor/icons/icon_animation_track_group.svg63
-rw-r--r--editor/icons/icon_animation_track_list.svg60
-rw-r--r--editor/icons/icon_animation_tree.svg5
-rw-r--r--editor/icons/icon_auto_end.svg68
-rw-r--r--editor/icons/icon_auto_triangle.svg64
-rw-r--r--editor/icons/icon_bezier_handles_balanced.svg98
-rw-r--r--editor/icons/icon_bezier_handles_free.svg98
-rw-r--r--editor/icons/icon_bezier_handles_mirror.svg98
-rw-r--r--editor/icons/icon_c_p_u_particles.svg60
-rw-r--r--editor/icons/icon_color_track_vu.svg115
-rw-r--r--editor/icons/icon_cylinder_shape.svg6
-rw-r--r--editor/icons/icon_edit_bezier.svg73
-rw-r--r--editor/icons/icon_expand_bottom_dock.svg70
-rw-r--r--editor/icons/icon_key_animation.svg65
-rw-r--r--editor/icons/icon_key_audio.svg65
-rw-r--r--editor/icons/icon_key_bezier.svg65
-rw-r--r--editor/icons/icon_key_bezier_handle.svg60
-rw-r--r--editor/icons/icon_key_bezier_point.svg64
-rw-r--r--editor/icons/icon_key_bezier_selected.svg64
-rw-r--r--editor/icons/icon_key_call.svg67
-rw-r--r--editor/icons/icon_key_selected.svg79
-rw-r--r--editor/icons/icon_key_xform.svg67
-rw-r--r--editor/icons/icon_new_root.svg69
-rw-r--r--editor/icons/icon_oriented_path_follow.svg5
-rw-r--r--editor/icons/icon_play_overlay.svg4
-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_time.svg74
-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_tool_triangle.svg82
-rw-r--r--editor/icons/icon_track_capture.svg61
-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/import/editor_import_collada.cpp8
-rw-r--r--editor/import/editor_scene_importer_gltf.cpp17
-rw-r--r--editor/import/resource_importer_bitmask.cpp30
-rw-r--r--editor/import/resource_importer_bitmask.h30
-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/import_dock.cpp1
-rw-r--r--editor/inspector_dock.cpp568
-rw-r--r--editor/inspector_dock.h138
-rw-r--r--editor/multi_node_edit.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.cpp1023
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.h130
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp848
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.h117
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp274
-rw-r--r--editor/plugins/animation_player_editor_plugin.h32
-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/asset_library_editor_plugin.cpp111
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp277
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h12
-rw-r--r--editor/plugins/collision_polygon_2d_editor_plugin.cpp1
-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/cube_grid_theme_editor_plugin.cpp2
-rw-r--r--editor/plugins/curve_editor_plugin.cpp96
-rw-r--r--editor/plugins/curve_editor_plugin.h23
-rw-r--r--editor/plugins/editor_preview_plugins.cpp396
-rw-r--r--editor/plugins/editor_preview_plugins.h35
-rw-r--r--editor/plugins/gradient_editor_plugin.cpp99
-rw-r--r--editor/plugins/gradient_editor_plugin.h35
-rw-r--r--editor/plugins/item_list_editor_plugin.cpp2
-rw-r--r--editor/plugins/line_2d_editor_plugin.cpp1
-rw-r--r--editor/plugins/navigation_polygon_editor_plugin.cpp1
-rw-r--r--editor/plugins/particles_editor_plugin.cpp410
-rw-r--r--editor/plugins/particles_editor_plugin.h34
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp2
-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.cpp147
-rw-r--r--editor/plugins/script_editor_plugin.h23
-rw-r--r--editor/plugins/script_text_editor.cpp62
-rw-r--r--editor/plugins/script_text_editor.h2
-rw-r--r--editor/plugins/shader_editor_plugin.cpp4
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.cpp30
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.h30
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp94
-rw-r--r--editor/plugins/spatial_editor_plugin.h2
-rw-r--r--editor/plugins/sprite_editor_plugin.cpp30
-rw-r--r--editor/plugins/sprite_editor_plugin.h30
-rw-r--r--editor/plugins/style_box_editor_plugin.cpp87
-rw-r--r--editor/plugins/style_box_editor_plugin.h26
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp46
-rw-r--r--editor/plugins/texture_region_editor_plugin.h4
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp386
-rw-r--r--editor/plugins/tile_map_editor_plugin.h20
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp12
-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_manager.cpp25
-rw-r--r--editor/project_settings_editor.cpp172
-rw-r--r--editor/project_settings_editor.h2
-rw-r--r--editor/property_editor.cpp5
-rw-r--r--editor/property_editor.h3
-rw-r--r--editor/property_selector.cpp62
-rw-r--r--editor/property_selector.h4
-rw-r--r--editor/scene_tree_dock.cpp159
-rw-r--r--editor/scene_tree_dock.h23
-rw-r--r--editor/scene_tree_editor.cpp91
-rw-r--r--editor/scene_tree_editor.h6
-rw-r--r--editor/script_create_dialog.cpp3
-rw-r--r--editor/script_editor_debugger.cpp9
-rw-r--r--editor/settings_config_dialog.cpp2
-rw-r--r--editor/spatial_editor_gizmos.cpp260
-rw-r--r--editor/spatial_editor_gizmos.h19
-rw-r--r--editor/translations/af.po122
-rw-r--r--editor/translations/ar.po308
-rw-r--r--editor/translations/bg.po160
-rw-r--r--editor/translations/bn.po295
-rw-r--r--editor/translations/ca.po190
-rw-r--r--editor/translations/cs.po942
-rw-r--r--editor/translations/da.po255
-rw-r--r--editor/translations/de.po294
-rw-r--r--editor/translations/de_CH.po124
-rw-r--r--editor/translations/editor.pot23
-rw-r--r--editor/translations/el.po242
-rw-r--r--editor/translations/es.po905
-rw-r--r--editor/translations/es_AR.po294
-rw-r--r--editor/translations/fa.po144
-rw-r--r--editor/translations/fi.po2261
-rw-r--r--editor/translations/fr.po291
-rw-r--r--editor/translations/he.po132
-rw-r--r--editor/translations/hi.po124
-rw-r--r--editor/translations/hu.po279
-rw-r--r--editor/translations/id.po290
-rw-r--r--editor/translations/is.po210
-rw-r--r--editor/translations/it.po280
-rw-r--r--editor/translations/ja.po420
-rw-r--r--editor/translations/ko.po309
-rw-r--r--editor/translations/lt.po140
-rw-r--r--editor/translations/ms.po7956
-rw-r--r--editor/translations/nb.po303
-rw-r--r--editor/translations/nl.po534
-rw-r--r--editor/translations/pl.po348
-rw-r--r--editor/translations/pr.po122
-rw-r--r--editor/translations/pt_BR.po214
-rw-r--r--editor/translations/pt_PT.po226
-rw-r--r--editor/translations/ro.po1668
-rw-r--r--editor/translations/ru.po295
-rw-r--r--editor/translations/sk.po174
-rw-r--r--editor/translations/sl.po1635
-rw-r--r--editor/translations/sr_Cyrl.po133
-rw-r--r--editor/translations/sr_Latn.po131
-rw-r--r--editor/translations/sv.po283
-rw-r--r--editor/translations/ta.po121
-rw-r--r--editor/translations/th.po259
-rw-r--r--editor/translations/tr.po297
-rw-r--r--editor/translations/uk.po413
-rw-r--r--editor/translations/ur_PK.po121
-rw-r--r--editor/translations/vi.po136
-rw-r--r--editor/translations/zh_CN.po297
-rw-r--r--editor/translations/zh_HK.po167
-rw-r--r--editor/translations/zh_TW.po175
-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.cpp466
-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_main.cpp13
-rw-r--r--main/tests/test_shader_lang.cpp4
-rw-r--r--main/timer_sync.cpp223
-rw-r--r--main/timer_sync.h101
-rw-r--r--methods.py238
-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/html/default.html6
-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/linux/godot.64
-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
-rw-r--r--misc/scripts/make_icons.sh29
-rw-r--r--modules/bmp/config.py4
-rw-r--r--modules/bmp/image_loader_bmp.cpp2
-rw-r--r--modules/bullet/bullet_physics_server.cpp4
-rw-r--r--modules/bullet/config.py2
-rw-r--r--modules/bullet/rigid_body_bullet.cpp1
-rw-r--r--modules/bullet/shape_bullet.cpp37
-rw-r--r--modules/bullet/shape_bullet.h20
-rw-r--r--modules/bullet/space_bullet.cpp82
-rw-r--r--modules/csg/config.py18
-rw-r--r--modules/csg/csg.cpp30
-rw-r--r--modules/csg/csg.h30
-rw-r--r--modules/csg/csg_gizmos.cpp30
-rw-r--r--modules/csg/csg_gizmos.h30
-rw-r--r--modules/csg/csg_shape.cpp65
-rw-r--r--modules/csg/csg_shape.h30
-rw-r--r--modules/csg/doc_classes/CSGBox.xml25
-rw-r--r--modules/csg/doc_classes/CSGCombiner.xml (renamed from modules/theora/doc_classes/ResourceImporterTheora.xml)2
-rw-r--r--modules/csg/doc_classes/CSGCylinder.xml29
-rw-r--r--modules/csg/doc_classes/CSGMesh.xml19
-rw-r--r--modules/csg/doc_classes/CSGPolygon.xml49
-rw-r--r--modules/csg/doc_classes/CSGPrimitive.xml19
-rw-r--r--modules/csg/doc_classes/CSGShape.xml35
-rw-r--r--modules/csg/doc_classes/CSGSphere.xml27
-rw-r--r--modules/csg/doc_classes/CSGTorus.xml29
-rw-r--r--modules/csg/register_types.cpp4
-rw-r--r--modules/dds/config.py2
-rw-r--r--modules/enet/config.py2
-rw-r--r--modules/enet/doc_classes/NetworkedMultiplayerENet.xml64
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp89
-rw-r--r--modules/enet/networked_multiplayer_enet.h13
-rw-r--r--modules/etc/config.py10
-rw-r--r--modules/freetype/SCsub3
-rw-r--r--modules/freetype/config.py2
-rw-r--r--modules/gdnative/SCsub42
-rw-r--r--modules/gdnative/android/android_gdn.cpp73
-rw-r--r--modules/gdnative/arvr/arvr_interface_gdnative.cpp4
-rw-r--r--modules/gdnative/config.py3
-rw-r--r--modules/gdnative/gdnative.cpp4
-rw-r--r--modules/gdnative/gdnative_api.json38
-rw-r--r--modules/gdnative/include/android/godot_android.h54
-rw-r--r--modules/gdnative/include/arvr/godot_arvr.h8
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h3
-rw-r--r--modules/gdnative/include/net/godot_net.h118
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp57
-rw-r--r--modules/gdnative/nativescript/nativescript.h7
-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.h59
-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.cpp77
-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_instance.cpp4
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.h4
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.cpp47
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.h8
-rw-r--r--modules/gdnative/register_types.cpp3
-rw-r--r--modules/gdscript/SCsub3
-rw-r--r--modules/gdscript/config.py2
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml2
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp (renamed from modules/gdscript/gdscript_highlighter.cpp)89
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h (renamed from modules/gdscript/gdscript_highlighter.h)14
-rw-r--r--modules/gdscript/gdscript.cpp67
-rw-r--r--modules/gdscript/gdscript.h17
-rw-r--r--modules/gdscript/gdscript_compiler.cpp64
-rw-r--r--modules/gdscript/gdscript_compiler.h3
-rw-r--r--modules/gdscript/gdscript_editor.cpp40
-rw-r--r--modules/gdscript/gdscript_function.cpp37
-rw-r--r--modules/gdscript/gdscript_function.h24
-rw-r--r--modules/gdscript/gdscript_functions.cpp48
-rw-r--r--modules/gdscript/gdscript_functions.h2
-rw-r--r--modules/gdscript/gdscript_parser.cpp86
-rw-r--r--modules/gdscript/gdscript_parser.h8
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp13
-rw-r--r--modules/gdscript/gdscript_tokenizer.h4
-rw-r--r--modules/gdscript/register_types.cpp2
-rw-r--r--modules/gridmap/config.py2
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml2
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp31
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h2
-rw-r--r--modules/hdr/config.py2
-rw-r--r--modules/jpg/config.py2
-rwxr-xr-xmodules/mbedtls/SCsub2
-rwxr-xr-xmodules/mbedtls/config.py2
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.cpp80
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.h6
-rw-r--r--modules/mobile_vr/config.py2
-rw-r--r--modules/mono/SCsub56
-rw-r--r--modules/mono/config.py139
-rw-r--r--modules/mono/csharp_script.cpp176
-rw-r--r--modules/mono/csharp_script.h9
-rw-r--r--modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs119
-rw-r--r--modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj4
-rw-r--r--modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs6
-rw-r--r--modules/mono/editor/csharp_project.cpp32
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp151
-rw-r--r--modules/mono/editor/godotsharp_builds.h5
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp60
-rw-r--r--modules/mono/editor/mono_bottom_panel.h6
-rw-r--r--modules/mono/editor/mono_build_info.cpp62
-rw-r--r--modules/mono/editor/mono_build_info.h24
-rw-r--r--modules/mono/editor/monodevelop_instance.cpp16
-rw-r--r--modules/mono/glue/builtin_types_glue.h30
-rw-r--r--modules/mono/glue/cs_files/Basis.cs20
-rw-r--r--modules/mono/glue/cs_files/Color.cs26
-rw-r--r--modules/mono/glue/cs_files/GodotTaskScheduler.cs2
-rw-r--r--modules/mono/glue/cs_files/Mathf.cs40
-rw-r--r--modules/mono/glue/cs_files/Plane.cs82
-rw-r--r--modules/mono/glue/cs_files/RPCAttributes.cs9
-rw-r--r--modules/mono/glue/cs_files/StringExtensions.cs2
-rwxr-xr-xmodules/mono/glue/cs_files/VERSION.txt2
-rw-r--r--modules/mono/glue/cs_files/Vector2.cs8
-rw-r--r--modules/mono/glue/cs_files/Vector3.cs6
-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_assembly.cpp9
-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.cpp64
-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.cpp30
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp92
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h36
-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/ogg/config.py2
-rw-r--r--modules/opus/config.py2
-rw-r--r--modules/pvr/config.py2
-rw-r--r--modules/recast/SCsub12
-rw-r--r--modules/recast/config.py4
-rw-r--r--modules/recast/navigation_mesh_editor_plugin.cpp (renamed from editor/plugins/navigation_mesh_editor_plugin.cpp)5
-rw-r--r--modules/recast/navigation_mesh_editor_plugin.h (renamed from editor/plugins/navigation_mesh_editor_plugin.h)3
-rw-r--r--modules/recast/navigation_mesh_generator.cpp (renamed from editor/plugins/navigation_mesh_generator.cpp)4
-rw-r--r--modules/recast/navigation_mesh_generator.h (renamed from editor/plugins/navigation_mesh_generator.h)9
-rw-r--r--modules/recast/register_types.cpp7
-rw-r--r--modules/regex/SCsub3
-rw-r--r--modules/regex/config.py2
-rw-r--r--modules/squish/config.py10
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.cpp6
-rw-r--r--modules/stb_vorbis/config.py2
-rw-r--r--modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml3
-rw-r--r--modules/svg/SCsub19
-rw-r--r--modules/svg/config.py2
-rw-r--r--modules/tga/config.py2
-rw-r--r--modules/tga/image_loader_tga.cpp3
-rw-r--r--modules/thekla_unwrap/config.py8
-rw-r--r--modules/theora/config.py2
-rw-r--r--modules/theora/register_types.cpp16
-rw-r--r--modules/theora/resource_importer_theora.cpp90
-rw-r--r--modules/theora/video_stream_theora.cpp43
-rw-r--r--modules/theora/video_stream_theora.h10
-rw-r--r--modules/tinyexr/config.py10
-rw-r--r--modules/upnp/SCsub32
-rw-r--r--modules/upnp/config.py14
-rw-r--r--modules/upnp/doc_classes/UPNP.xml227
-rw-r--r--modules/upnp/doc_classes/UPNPDevice.xml109
-rw-r--r--modules/upnp/register_types.cpp43
-rw-r--r--modules/upnp/register_types.h32
-rw-r--r--modules/upnp/upnp.cpp401
-rw-r--r--modules/upnp/upnp.h124
-rw-r--r--modules/upnp/upnpdevice.cpp197
-rw-r--r--modules/upnp/upnpdevice.h95
-rw-r--r--modules/visual_script/config.py2
-rw-r--r--modules/visual_script/doc_classes/VisualScript.xml2
-rw-r--r--modules/visual_script/visual_script.cpp14
-rw-r--r--modules/visual_script/visual_script.h6
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp4
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h2
-rw-r--r--modules/visual_script/visual_script_editor.cpp36
-rw-r--r--modules/visual_script/visual_script_editor.h2
-rw-r--r--modules/visual_script/visual_script_flow_control.cpp2
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp102
-rw-r--r--modules/visual_script/visual_script_func_nodes.h2
-rw-r--r--modules/visual_script/visual_script_nodes.cpp221
-rw-r--r--modules/visual_script/visual_script_nodes.h25
-rw-r--r--modules/vorbis/config.py2
-rw-r--r--modules/webm/config.py4
-rw-r--r--modules/webm/libvpx/SCsub5
-rw-r--r--modules/webm/register_types.cpp16
-rw-r--r--modules/webm/resource_importer_webm.cpp96
-rw-r--r--modules/webm/video_stream_webm.cpp43
-rw-r--r--modules/webm/video_stream_webm.h10
-rw-r--r--modules/webp/SCsub2
-rw-r--r--modules/webp/config.py2
-rw-r--r--modules/websocket/SCsub80
-rw-r--r--modules/websocket/config.py15
-rw-r--r--modules/websocket/doc_classes/WebSocketClient.xml70
-rw-r--r--modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml (renamed from doc/classes/WebSocketMultiplayerPeer.xml)4
-rw-r--r--modules/websocket/doc_classes/WebSocketPeer.xml75
-rw-r--r--modules/websocket/doc_classes/WebSocketServer.xml109
-rw-r--r--modules/websocket/lws_client.cpp3
-rw-r--r--modules/websocket/lws_helper.h10
-rw-r--r--modules/websocket/lws_peer.cpp4
-rw-r--r--modules/websocket/websocket_server.cpp6
-rw-r--r--platform/android/AndroidManifest.xml.template167
-rw-r--r--platform/android/SCsub5
-rw-r--r--platform/android/build.gradle.template1
-rw-r--r--platform/android/export/export.cpp152
-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/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java10
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/impl/V14CustomNotification.java8
-rw-r--r--platform/android/java/src/org/godotengine/godot/Godot.java17
-rw-r--r--platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java16
-rw-r--r--platform/android/java_glue.cpp16
-rw-r--r--platform/android/logo.pngbin1742 -> 1461 bytes
-rw-r--r--platform/android/os_android.cpp17
-rw-r--r--platform/android/os_android.h4
-rw-r--r--platform/android/run_icon.pngbin636 -> 324 bytes
-rw-r--r--platform/haiku/detect.py2
-rw-r--r--platform/haiku/logo.pngbin1551 -> 1265 bytes
-rw-r--r--platform/iphone/app_delegate.mm23
-rw-r--r--platform/iphone/logo.pngbin1905 -> 1489 bytes
-rw-r--r--platform/iphone/os_iphone.cpp11
-rw-r--r--platform/iphone/os_iphone.h10
-rw-r--r--platform/iphone/view_controller.mm65
-rw-r--r--platform/javascript/SCsub6
-rw-r--r--platform/javascript/audio_driver_javascript.cpp149
-rw-r--r--platform/javascript/audio_driver_javascript.h11
-rw-r--r--platform/javascript/detect.py17
-rw-r--r--platform/javascript/dom_keys.inc (renamed from platform/javascript/dom_keys.h)17
-rw-r--r--platform/javascript/engine.js21
-rw-r--r--platform/javascript/export/export.cpp2
-rw-r--r--platform/javascript/javascript_main.cpp29
-rw-r--r--platform/javascript/logo.pngbin2316 -> 1236 bytes
-rw-r--r--platform/javascript/os_javascript.cpp1158
-rw-r--r--platform/javascript/os_javascript.h133
-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.py4
-rw-r--r--platform/osx/logo.pngbin2065 -> 1752 bytes
-rw-r--r--platform/osx/os_osx.h9
-rw-r--r--platform/osx/os_osx.mm223
-rw-r--r--platform/server/detect.py18
-rw-r--r--platform/server/logo.pngbin2331 -> 2016 bytes
-rw-r--r--platform/server/os_server.cpp6
-rw-r--r--platform/server/os_server.h3
-rw-r--r--platform/uwp/app.cpp15
-rw-r--r--platform/uwp/detect.py6
-rw-r--r--platform/uwp/export/export.cpp4
-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.cpp43
-rw-r--r--platform/uwp/os_uwp.h7
-rw-r--r--platform/windows/crash_handler_win.cpp3
-rw-r--r--platform/windows/detect.py4
-rw-r--r--platform/windows/logo.pngbin1882 -> 1536 bytes
-rw-r--r--platform/windows/os_windows.cpp618
-rw-r--r--platform/windows/os_windows.h19
-rw-r--r--platform/x11/context_gl_x11.cpp83
-rw-r--r--platform/x11/context_gl_x11.h1
-rw-r--r--platform/x11/crash_handler_x11.cpp4
-rw-r--r--platform/x11/detect.py40
-rw-r--r--platform/x11/logo.pngbin2061 -> 1679 bytes
-rw-r--r--platform/x11/os_x11.cpp127
-rw-r--r--platform/x11/os_x11.h11
-rw-r--r--scene/2d/animated_sprite.cpp70
-rw-r--r--scene/2d/animated_sprite.h5
-rw-r--r--scene/2d/area_2d.cpp6
-rw-r--r--scene/2d/audio_stream_player_2d.cpp72
-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/camera_2d.cpp8
-rw-r--r--scene/2d/canvas_item.cpp24
-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/mesh_instance_2d.cpp30
-rw-r--r--scene/2d/mesh_instance_2d.h30
-rw-r--r--scene/2d/navigation2d.cpp2
-rw-r--r--scene/2d/node_2d.cpp1
-rw-r--r--scene/2d/parallax_layer.cpp1
-rw-r--r--scene/2d/particles_2d.cpp4
-rw-r--r--scene/2d/path_2d.cpp2
-rw-r--r--scene/2d/physics_body_2d.cpp232
-rw-r--r--scene/2d/physics_body_2d.h19
-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/skeleton_2d.cpp76
-rw-r--r--scene/2d/skeleton_2d.h33
-rw-r--r--scene/2d/sprite.cpp11
-rw-r--r--scene/2d/sprite.h1
-rw-r--r--scene/2d/tile_map.cpp78
-rw-r--r--scene/2d/tile_map.h6
-rw-r--r--scene/3d/area.cpp2
-rw-r--r--scene/3d/arvr_nodes.cpp20
-rw-r--r--scene/3d/audio_stream_player_3d.cpp69
-rw-r--r--scene/3d/audio_stream_player_3d.h6
-rw-r--r--scene/3d/camera.cpp4
-rw-r--r--scene/3d/cpu_particles.cpp1409
-rw-r--r--scene/3d/cpu_particles.h258
-rw-r--r--scene/3d/light.cpp12
-rw-r--r--scene/3d/mesh_instance.cpp2
-rw-r--r--scene/3d/particles.cpp26
-rw-r--r--scene/3d/path.cpp218
-rw-r--r--scene/3d/path.h43
-rw-r--r--scene/3d/physics_body.cpp22
-rw-r--r--scene/3d/physics_body.h4
-rw-r--r--scene/3d/physics_joint.cpp8
-rw-r--r--scene/3d/reflection_probe.cpp6
-rw-r--r--scene/3d/remote_transform.cpp2
-rw-r--r--scene/3d/scenario_fx.cpp7
-rw-r--r--scene/3d/skeleton.cpp100
-rw-r--r--scene/3d/skeleton.h16
-rw-r--r--scene/3d/sprite_3d.cpp86
-rw-r--r--scene/3d/sprite_3d.h4
-rw-r--r--scene/3d/vehicle_body.cpp18
-rw-r--r--scene/3d/voxel_light_baker.cpp5
-rw-r--r--scene/animation/animation_blend_space_1d.cpp294
-rw-r--r--scene/animation/animation_blend_space_1d.h71
-rw-r--r--scene/animation/animation_blend_space_2d.cpp567
-rw-r--r--scene/animation/animation_blend_space_2d.h97
-rw-r--r--scene/animation/animation_blend_tree.cpp1170
-rw-r--r--scene/animation/animation_blend_tree.h352
-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.cpp325
-rw-r--r--scene/animation/animation_player.h38
-rw-r--r--scene/animation/animation_tree.cpp1339
-rw-r--r--scene/animation/animation_tree.h281
-rw-r--r--scene/animation/animation_tree_player.cpp9
-rw-r--r--scene/animation/animation_tree_player.h1
-rw-r--r--scene/animation/root_motion_view.cpp178
-rw-r--r--scene/animation/root_motion_view.h47
-rw-r--r--scene/animation/tween.cpp12
-rw-r--r--scene/animation/tween.h1
-rw-r--r--scene/audio/audio_player.cpp45
-rw-r--r--scene/audio/audio_player.h5
-rw-r--r--scene/gui/color_picker.cpp84
-rw-r--r--scene/gui/color_picker.h12
-rw-r--r--scene/gui/container.cpp11
-rw-r--r--scene/gui/control.cpp275
-rw-r--r--scene/gui/control.h30
-rw-r--r--scene/gui/file_dialog.cpp1
-rw-r--r--scene/gui/gradient_edit.cpp1
-rw-r--r--scene/gui/graph_edit.cpp33
-rw-r--r--scene/gui/graph_edit.h8
-rw-r--r--scene/gui/item_list.cpp1
-rw-r--r--scene/gui/label.cpp13
-rw-r--r--scene/gui/line_edit.cpp28
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup.cpp34
-rw-r--r--scene/gui/popup_menu.cpp110
-rw-r--r--scene/gui/popup_menu.h9
-rw-r--r--scene/gui/progress_bar.cpp6
-rw-r--r--scene/gui/range.cpp45
-rw-r--r--scene/gui/range.h8
-rw-r--r--scene/gui/rich_text_label.cpp57
-rw-r--r--scene/gui/rich_text_label.h4
-rw-r--r--scene/gui/scroll_bar.cpp6
-rw-r--r--scene/gui/scroll_container.cpp33
-rw-r--r--scene/gui/scroll_container.h3
-rw-r--r--scene/gui/slider.cpp25
-rw-r--r--scene/gui/slider.h4
-rw-r--r--scene/gui/split_container.cpp7
-rw-r--r--scene/gui/tab_container.cpp44
-rw-r--r--scene/gui/tab_container.h6
-rw-r--r--scene/gui/text_edit.cpp1656
-rw-r--r--scene/gui/text_edit.h65
-rw-r--r--scene/main/http_request.cpp20
-rw-r--r--scene/main/node.cpp170
-rw-r--r--scene/main/node.h33
-rw-r--r--scene/main/scene_tree.cpp88
-rw-r--r--scene/main/scene_tree.h9
-rw-r--r--scene/main/viewport.cpp76
-rw-r--r--scene/main/viewport.h2
-rw-r--r--scene/register_scene_types.cpp70
-rw-r--r--scene/resources/animation.cpp1049
-rw-r--r--scene/resources/animation.h83
-rw-r--r--scene/resources/audio_stream_sample.cpp8
-rw-r--r--scene/resources/curve.cpp135
-rw-r--r--scene/resources/curve.h8
-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.cpp10
-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.cpp9
-rw-r--r--scene/resources/environment.cpp37
-rw-r--r--scene/resources/environment.h4
-rw-r--r--scene/resources/material.cpp20
-rw-r--r--scene/resources/material.h5
-rw-r--r--scene/resources/mesh.cpp4
-rw-r--r--scene/resources/mesh.h2
-rw-r--r--scene/resources/primitive_meshes.cpp68
-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.cpp45
-rw-r--r--scene/resources/style_box.h10
-rw-r--r--scene/resources/texture.cpp216
-rw-r--r--scene/resources/texture.h71
-rw-r--r--scene/resources/theme.h12
-rw-r--r--scene/resources/tile_set.cpp3
-rw-r--r--scene/resources/tile_set.h1
-rw-r--r--scene/resources/visual_shader.cpp1555
-rw-r--r--scene/resources/visual_shader.h284
-rw-r--r--scene/resources/visual_shader_nodes.cpp1879
-rw-r--r--scene/resources/visual_shader_nodes.h861
-rw-r--r--scene/scene_string_names.cpp2
-rw-r--r--scene/scene_string_names.h2
-rw-r--r--servers/audio/audio_stream.cpp1
-rw-r--r--servers/audio/audio_stream.h1
-rw-r--r--servers/audio_server.cpp31
-rw-r--r--servers/audio_server.h6
-rw-r--r--servers/physics/collision_solver_sat.cpp18
-rw-r--r--servers/physics/physics_server_sw.cpp5
-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.cpp24
-rw-r--r--servers/physics_2d/physics_2d_server_sw.h3
-rw-r--r--servers/physics_2d/physics_2d_server_wrap_mt.h10
-rw-r--r--servers/physics_2d/space_2d_sw.cpp168
-rw-r--r--servers/physics_2d/space_2d_sw.h3
-rw-r--r--servers/physics_2d_server.cpp2
-rw-r--r--servers/physics_2d_server.h17
-rw-r--r--servers/physics_server.cpp3
-rw-r--r--servers/physics_server.h1
-rw-r--r--servers/server_wrap_mt_common.h9
-rw-r--r--servers/visual/rasterizer.h9
-rw-r--r--servers/visual/shader_language.cpp34
-rw-r--r--servers/visual/shader_language.h6
-rw-r--r--servers/visual/shader_types.cpp82
-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.cpp130
-rw-r--r--servers/visual/visual_server_scene.h5
-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.cpp10
-rw-r--r--servers/visual_server.h18
-rw-r--r--thirdparty/README.md70
-rw-r--r--thirdparty/freetype/FTL.TXT2
-rw-r--r--thirdparty/freetype/include/freetype/config/ftconfig.h108
-rw-r--r--thirdparty/freetype/include/freetype/config/ftheader.h128
-rw-r--r--thirdparty/freetype/include/freetype/config/ftoption.h93
-rw-r--r--thirdparty/freetype/include/freetype/config/ftstdlib.h2
-rw-r--r--thirdparty/freetype/include/freetype/freetype.h270
-rw-r--r--thirdparty/freetype/include/freetype/ftadvanc.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftbbox.h4
-rw-r--r--thirdparty/freetype/include/freetype/ftbdf.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftbitmap.h4
-rw-r--r--thirdparty/freetype/include/freetype/ftbzip2.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftcache.h17
-rw-r--r--thirdparty/freetype/include/freetype/ftchapters.h3
-rw-r--r--thirdparty/freetype/include/freetype/ftcid.h4
-rw-r--r--thirdparty/freetype/include/freetype/ftdriver.h1225
-rw-r--r--thirdparty/freetype/include/freetype/fterrdef.h2
-rw-r--r--thirdparty/freetype/include/freetype/fterrors.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftfntfmt.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftgasp.h7
-rw-r--r--thirdparty/freetype/include/freetype/ftglyph.h6
-rw-r--r--thirdparty/freetype/include/freetype/ftgxval.h10
-rw-r--r--thirdparty/freetype/include/freetype/ftgzip.h5
-rw-r--r--thirdparty/freetype/include/freetype/ftimage.h36
-rw-r--r--thirdparty/freetype/include/freetype/ftincrem.h15
-rw-r--r--thirdparty/freetype/include/freetype/ftlcdfil.h23
-rw-r--r--thirdparty/freetype/include/freetype/ftlist.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftlzw.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftmac.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftmm.h110
-rw-r--r--thirdparty/freetype/include/freetype/ftmodapi.h23
-rw-r--r--thirdparty/freetype/include/freetype/ftmoderr.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftotval.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftoutln.h11
-rw-r--r--thirdparty/freetype/include/freetype/ftparams.h205
-rw-r--r--thirdparty/freetype/include/freetype/ftpfr.h8
-rw-r--r--thirdparty/freetype/include/freetype/ftrender.h4
-rw-r--r--thirdparty/freetype/include/freetype/ftsizes.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftsnames.h50
-rw-r--r--thirdparty/freetype/include/freetype/ftstroke.h12
-rw-r--r--thirdparty/freetype/include/freetype/ftsynth.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftsystem.h2
-rw-r--r--thirdparty/freetype/include/freetype/fttrigon.h2
-rw-r--r--thirdparty/freetype/include/freetype/fttypes.h4
-rw-r--r--thirdparty/freetype/include/freetype/ftwinfnt.h4
-rw-r--r--thirdparty/freetype/include/freetype/internal/autohint.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/cffotypes.h108
-rw-r--r--thirdparty/freetype/include/freetype/internal/cfftypes.h412
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftcalc.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftdebug.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftdrv.h400
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftgloadr.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftmemory.h4
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftobjs.h85
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftpic.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftpsprop.h48
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftrfork.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftserv.h103
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftstream.h6
-rw-r--r--thirdparty/freetype/include/freetype/internal/fttrace.h9
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftvalid.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/internal.h8
-rw-r--r--thirdparty/freetype/include/freetype/internal/psaux.h501
-rw-r--r--thirdparty/freetype/include/freetype/internal/pshints.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svbdf.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svcfftl.h112
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svcid.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svfntfmt.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svgldict.h6
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svgxval.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svkern.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svmetric.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svmm.h57
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svotval.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svpfr.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svpostnm.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svprop.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svpscmap.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svpsinfo.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svsfnt.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svttcmap.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svtteng.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svttglyf.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svwinfnt.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/sfnt.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/t1types.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/tttypes.h12
-rw-r--r--thirdparty/freetype/include/freetype/t1tables.h11
-rw-r--r--thirdparty/freetype/include/freetype/ttnameid.h4
-rw-r--r--thirdparty/freetype/include/freetype/tttables.h2
-rw-r--r--thirdparty/freetype/include/freetype/tttags.h2
-rw-r--r--thirdparty/freetype/include/ft2build.h2
-rw-r--r--thirdparty/freetype/src/autofit/afangles.c2
-rw-r--r--thirdparty/freetype/src/autofit/afblue.c8
-rw-r--r--thirdparty/freetype/src/autofit/afblue.cin2
-rw-r--r--thirdparty/freetype/src/autofit/afblue.dat10
-rw-r--r--thirdparty/freetype/src/autofit/afblue.h300
-rw-r--r--thirdparty/freetype/src/autofit/afblue.hin2
-rw-r--r--thirdparty/freetype/src/autofit/afcjk.c6
-rw-r--r--thirdparty/freetype/src/autofit/afcjk.h2
-rw-r--r--thirdparty/freetype/src/autofit/afcover.h2
-rw-r--r--thirdparty/freetype/src/autofit/afdummy.c2
-rw-r--r--thirdparty/freetype/src/autofit/afdummy.h2
-rw-r--r--thirdparty/freetype/src/autofit/aferrors.h2
-rw-r--r--thirdparty/freetype/src/autofit/afglobal.c2
-rw-r--r--thirdparty/freetype/src/autofit/afglobal.h2
-rw-r--r--thirdparty/freetype/src/autofit/afhints.c15
-rw-r--r--thirdparty/freetype/src/autofit/afhints.h4
-rw-r--r--thirdparty/freetype/src/autofit/afindic.c2
-rw-r--r--thirdparty/freetype/src/autofit/afindic.h2
-rw-r--r--thirdparty/freetype/src/autofit/aflatin.c2
-rw-r--r--thirdparty/freetype/src/autofit/aflatin.h2
-rw-r--r--thirdparty/freetype/src/autofit/aflatin2.c2
-rw-r--r--thirdparty/freetype/src/autofit/aflatin2.h2
-rw-r--r--thirdparty/freetype/src/autofit/afloader.c2
-rw-r--r--thirdparty/freetype/src/autofit/afloader.h2
-rw-r--r--thirdparty/freetype/src/autofit/afmodule.c4
-rw-r--r--thirdparty/freetype/src/autofit/afmodule.h2
-rw-r--r--thirdparty/freetype/src/autofit/afpic.c2
-rw-r--r--thirdparty/freetype/src/autofit/afpic.h2
-rw-r--r--thirdparty/freetype/src/autofit/afranges.c27
-rw-r--r--thirdparty/freetype/src/autofit/afranges.h2
-rw-r--r--thirdparty/freetype/src/autofit/afscript.h4
-rw-r--r--thirdparty/freetype/src/autofit/afshaper.c33
-rw-r--r--thirdparty/freetype/src/autofit/afshaper.h2
-rw-r--r--thirdparty/freetype/src/autofit/afstyles.h2
-rw-r--r--thirdparty/freetype/src/autofit/aftypes.h8
-rw-r--r--thirdparty/freetype/src/autofit/afwarp.c2
-rw-r--r--thirdparty/freetype/src/autofit/afwarp.h2
-rw-r--r--thirdparty/freetype/src/autofit/afwrtsys.h2
-rw-r--r--thirdparty/freetype/src/autofit/autofit.c2
-rw-r--r--thirdparty/freetype/src/autofit/module.mk2
-rw-r--r--thirdparty/freetype/src/autofit/rules.mk2
-rw-r--r--thirdparty/freetype/src/base/basepic.c2
-rw-r--r--thirdparty/freetype/src/base/basepic.h2
-rw-r--r--thirdparty/freetype/src/base/ftadvanc.c16
-rw-r--r--thirdparty/freetype/src/base/ftapi.c2
-rw-r--r--thirdparty/freetype/src/base/ftbase.c5
-rw-r--r--thirdparty/freetype/src/base/ftbase.h8
-rw-r--r--thirdparty/freetype/src/base/ftbbox.c2
-rw-r--r--thirdparty/freetype/src/base/ftbdf.c2
-rw-r--r--thirdparty/freetype/src/base/ftbitmap.c77
-rw-r--r--thirdparty/freetype/src/base/ftcalc.c2
-rw-r--r--thirdparty/freetype/src/base/ftcid.c2
-rw-r--r--thirdparty/freetype/src/base/ftdbgmem.c10
-rw-r--r--thirdparty/freetype/src/base/ftdebug.c4
-rw-r--r--thirdparty/freetype/src/base/ftfntfmt.c2
-rw-r--r--thirdparty/freetype/src/base/ftfstype.c2
-rw-r--r--thirdparty/freetype/src/base/ftgasp.c2
-rw-r--r--thirdparty/freetype/src/base/ftgloadr.c2
-rw-r--r--thirdparty/freetype/src/base/ftglyph.c2
-rw-r--r--thirdparty/freetype/src/base/ftgxval.c2
-rw-r--r--thirdparty/freetype/src/base/ftinit.c2
-rw-r--r--thirdparty/freetype/src/base/ftlcdfil.c59
-rw-r--r--thirdparty/freetype/src/base/ftmac.c2
-rw-r--r--thirdparty/freetype/src/base/ftmm.c81
-rw-r--r--thirdparty/freetype/src/base/ftobjs.c470
-rw-r--r--thirdparty/freetype/src/base/ftotval.c2
-rw-r--r--thirdparty/freetype/src/base/ftoutln.c13
-rw-r--r--thirdparty/freetype/src/base/ftpatent.c2
-rw-r--r--thirdparty/freetype/src/base/ftpfr.c2
-rw-r--r--thirdparty/freetype/src/base/ftpic.c2
-rw-r--r--thirdparty/freetype/src/base/ftpsprop.c285
-rw-r--r--thirdparty/freetype/src/base/ftrfork.c4
-rw-r--r--thirdparty/freetype/src/base/ftsnames.c2
-rw-r--r--thirdparty/freetype/src/base/ftstream.c2
-rw-r--r--thirdparty/freetype/src/base/ftstroke.c2
-rw-r--r--thirdparty/freetype/src/base/ftsynth.c2
-rw-r--r--thirdparty/freetype/src/base/ftsystem.c2
-rw-r--r--thirdparty/freetype/src/base/fttrigon.c2
-rw-r--r--thirdparty/freetype/src/base/fttype1.c2
-rw-r--r--thirdparty/freetype/src/base/ftutil.c2
-rw-r--r--thirdparty/freetype/src/base/ftver.rc61
-rw-r--r--thirdparty/freetype/src/base/ftwinfnt.c2
-rw-r--r--thirdparty/freetype/src/base/rules.mk5
-rw-r--r--thirdparty/freetype/src/bdf/README2
-rw-r--r--thirdparty/freetype/src/bdf/bdfdrivr.c2
-rw-r--r--thirdparty/freetype/src/bdf/bdflib.c12
-rw-r--r--thirdparty/freetype/src/bzip2/ftbzip2.c2
-rw-r--r--thirdparty/freetype/src/bzip2/rules.mk2
-rw-r--r--thirdparty/freetype/src/cache/ftcache.c2
-rw-r--r--thirdparty/freetype/src/cache/ftcbasic.c2
-rw-r--r--thirdparty/freetype/src/cache/ftccache.c2
-rw-r--r--thirdparty/freetype/src/cache/ftccache.h2
-rw-r--r--thirdparty/freetype/src/cache/ftccback.h2
-rw-r--r--thirdparty/freetype/src/cache/ftccmap.c2
-rw-r--r--thirdparty/freetype/src/cache/ftcerror.h2
-rw-r--r--thirdparty/freetype/src/cache/ftcglyph.c2
-rw-r--r--thirdparty/freetype/src/cache/ftcglyph.h2
-rw-r--r--thirdparty/freetype/src/cache/ftcimage.c2
-rw-r--r--thirdparty/freetype/src/cache/ftcimage.h2
-rw-r--r--thirdparty/freetype/src/cache/ftcmanag.c2
-rw-r--r--thirdparty/freetype/src/cache/ftcmanag.h2
-rw-r--r--thirdparty/freetype/src/cache/ftcmru.c2
-rw-r--r--thirdparty/freetype/src/cache/ftcmru.h2
-rw-r--r--thirdparty/freetype/src/cache/ftcsbits.c2
-rw-r--r--thirdparty/freetype/src/cache/ftcsbits.h2
-rw-r--r--thirdparty/freetype/src/cache/rules.mk2
-rw-r--r--thirdparty/freetype/src/cff/cff.c13
-rw-r--r--thirdparty/freetype/src/cff/cffcmap.c2
-rw-r--r--thirdparty/freetype/src/cff/cffcmap.h4
-rw-r--r--thirdparty/freetype/src/cff/cffdrivr.c381
-rw-r--r--thirdparty/freetype/src/cff/cffdrivr.h2
-rw-r--r--thirdparty/freetype/src/cff/cfferrs.h2
-rw-r--r--thirdparty/freetype/src/cff/cffgload.c2623
-rw-r--r--thirdparty/freetype/src/cff/cffgload.h187
-rw-r--r--thirdparty/freetype/src/cff/cffload.c84
-rw-r--r--thirdparty/freetype/src/cff/cffload.h9
-rw-r--r--thirdparty/freetype/src/cff/cffobjs.c120
-rw-r--r--thirdparty/freetype/src/cff/cffobjs.h106
-rw-r--r--thirdparty/freetype/src/cff/cffparse.c27
-rw-r--r--thirdparty/freetype/src/cff/cffparse.h4
-rw-r--r--thirdparty/freetype/src/cff/cffpic.c2
-rw-r--r--thirdparty/freetype/src/cff/cffpic.h7
-rw-r--r--thirdparty/freetype/src/cff/cfftoken.h2
-rw-r--r--thirdparty/freetype/src/cff/module.mk2
-rw-r--r--thirdparty/freetype/src/cff/rules.mk19
-rw-r--r--thirdparty/freetype/src/cid/ciderrs.h2
-rw-r--r--thirdparty/freetype/src/cid/cidgload.c87
-rw-r--r--thirdparty/freetype/src/cid/cidgload.h2
-rw-r--r--thirdparty/freetype/src/cid/cidload.c2
-rw-r--r--thirdparty/freetype/src/cid/cidload.h2
-rw-r--r--thirdparty/freetype/src/cid/cidobjs.c40
-rw-r--r--thirdparty/freetype/src/cid/cidobjs.h2
-rw-r--r--thirdparty/freetype/src/cid/cidparse.c2
-rw-r--r--thirdparty/freetype/src/cid/cidparse.h2
-rw-r--r--thirdparty/freetype/src/cid/cidriver.c22
-rw-r--r--thirdparty/freetype/src/cid/cidriver.h2
-rw-r--r--thirdparty/freetype/src/cid/cidtoken.h2
-rw-r--r--thirdparty/freetype/src/cid/module.mk2
-rw-r--r--thirdparty/freetype/src/cid/rules.mk2
-rw-r--r--thirdparty/freetype/src/cid/type1cid.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/README12
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvalid.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvalid.h2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvbsln.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvcommn.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvcommn.h2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxverror.h2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvfeat.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvfeat.h2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvfgen.c4
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvjust.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvkern.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvlcar.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmod.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmod.h2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmort.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmort.h2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmort0.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmort1.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmort2.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmort4.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmort5.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmorx.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmorx.h2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmorx0.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmorx1.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmorx2.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmorx4.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvmorx5.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvopbd.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvprop.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvtrak.c2
-rw-r--r--thirdparty/freetype/src/gxvalid/module.mk2
-rw-r--r--thirdparty/freetype/src/gxvalid/rules.mk2
-rw-r--r--thirdparty/freetype/src/gzip/ftgzip.c2
-rw-r--r--thirdparty/freetype/src/gzip/rules.mk2
-rw-r--r--thirdparty/freetype/src/lzw/ftlzw.c2
-rw-r--r--thirdparty/freetype/src/lzw/ftzopen.c2
-rw-r--r--thirdparty/freetype/src/lzw/ftzopen.h2
-rw-r--r--thirdparty/freetype/src/lzw/rules.mk2
-rw-r--r--thirdparty/freetype/src/otvalid/module.mk2
-rw-r--r--thirdparty/freetype/src/otvalid/otvalid.c2
-rw-r--r--thirdparty/freetype/src/otvalid/otvalid.h2
-rw-r--r--thirdparty/freetype/src/otvalid/otvbase.c35
-rw-r--r--thirdparty/freetype/src/otvalid/otvcommn.c30
-rw-r--r--thirdparty/freetype/src/otvalid/otvcommn.h54
-rw-r--r--thirdparty/freetype/src/otvalid/otverror.h2
-rw-r--r--thirdparty/freetype/src/otvalid/otvgdef.c113
-rw-r--r--thirdparty/freetype/src/otvalid/otvgpos.c56
-rw-r--r--thirdparty/freetype/src/otvalid/otvgpos.h2
-rw-r--r--thirdparty/freetype/src/otvalid/otvgsub.c36
-rw-r--r--thirdparty/freetype/src/otvalid/otvjstf.c2
-rw-r--r--thirdparty/freetype/src/otvalid/otvmath.c2
-rw-r--r--thirdparty/freetype/src/otvalid/otvmod.c2
-rw-r--r--thirdparty/freetype/src/otvalid/otvmod.h2
-rw-r--r--thirdparty/freetype/src/otvalid/rules.mk2
-rw-r--r--thirdparty/freetype/src/pcf/pcfdrivr.c2
-rw-r--r--thirdparty/freetype/src/pcf/pcfread.c6
-rw-r--r--thirdparty/freetype/src/pfr/module.mk2
-rw-r--r--thirdparty/freetype/src/pfr/pfr.c2
-rw-r--r--thirdparty/freetype/src/pfr/pfrcmap.c2
-rw-r--r--thirdparty/freetype/src/pfr/pfrcmap.h2
-rw-r--r--thirdparty/freetype/src/pfr/pfrdrivr.c2
-rw-r--r--thirdparty/freetype/src/pfr/pfrdrivr.h2
-rw-r--r--thirdparty/freetype/src/pfr/pfrerror.h2
-rw-r--r--thirdparty/freetype/src/pfr/pfrgload.c12
-rw-r--r--thirdparty/freetype/src/pfr/pfrgload.h2
-rw-r--r--thirdparty/freetype/src/pfr/pfrload.c2
-rw-r--r--thirdparty/freetype/src/pfr/pfrload.h2
-rw-r--r--thirdparty/freetype/src/pfr/pfrobjs.c2
-rw-r--r--thirdparty/freetype/src/pfr/pfrobjs.h2
-rw-r--r--thirdparty/freetype/src/pfr/pfrsbit.c2
-rw-r--r--thirdparty/freetype/src/pfr/pfrsbit.h2
-rw-r--r--thirdparty/freetype/src/pfr/pfrtypes.h2
-rw-r--r--thirdparty/freetype/src/pfr/rules.mk2
-rw-r--r--thirdparty/freetype/src/psaux/afmparse.c2
-rw-r--r--thirdparty/freetype/src/psaux/afmparse.h2
-rw-r--r--thirdparty/freetype/src/psaux/cffdecode.c2370
-rw-r--r--thirdparty/freetype/src/psaux/cffdecode.h64
-rw-r--r--thirdparty/freetype/src/psaux/module.mk2
-rw-r--r--thirdparty/freetype/src/psaux/psarrst.c241
-rw-r--r--thirdparty/freetype/src/psaux/psarrst.h100
-rw-r--r--thirdparty/freetype/src/psaux/psaux.c13
-rw-r--r--thirdparty/freetype/src/psaux/psauxerr.h2
-rw-r--r--thirdparty/freetype/src/psaux/psauxmod.c56
-rw-r--r--thirdparty/freetype/src/psaux/psauxmod.h11
-rw-r--r--thirdparty/freetype/src/psaux/psblues.c582
-rw-r--r--thirdparty/freetype/src/psaux/psblues.h185
-rw-r--r--thirdparty/freetype/src/psaux/psconv.c2
-rw-r--r--thirdparty/freetype/src/psaux/psconv.h2
-rw-r--r--thirdparty/freetype/src/psaux/pserror.c52
-rw-r--r--thirdparty/freetype/src/psaux/pserror.h119
-rw-r--r--thirdparty/freetype/src/psaux/psfixed.h95
-rw-r--r--thirdparty/freetype/src/psaux/psfont.c567
-rw-r--r--thirdparty/freetype/src/psaux/psfont.h134
-rw-r--r--thirdparty/freetype/src/psaux/psft.c890
-rw-r--r--thirdparty/freetype/src/psaux/psft.h167
-rw-r--r--thirdparty/freetype/src/psaux/psglue.h144
-rw-r--r--thirdparty/freetype/src/psaux/pshints.c1939
-rw-r--r--thirdparty/freetype/src/psaux/pshints.h288
-rw-r--r--thirdparty/freetype/src/psaux/psintrp.c3040
-rw-r--r--thirdparty/freetype/src/psaux/psintrp.h83
-rw-r--r--thirdparty/freetype/src/psaux/psobjs.c753
-rw-r--r--thirdparty/freetype/src/psaux/psobjs.h103
-rw-r--r--thirdparty/freetype/src/psaux/psread.c112
-rw-r--r--thirdparty/freetype/src/psaux/psread.h68
-rw-r--r--thirdparty/freetype/src/psaux/psstack.c328
-rw-r--r--thirdparty/freetype/src/psaux/psstack.h121
-rw-r--r--thirdparty/freetype/src/psaux/pstypes.h78
-rw-r--r--thirdparty/freetype/src/psaux/rules.mk19
-rw-r--r--thirdparty/freetype/src/psaux/t1cmap.c2
-rw-r--r--thirdparty/freetype/src/psaux/t1cmap.h2
-rw-r--r--thirdparty/freetype/src/psaux/t1decode.c350
-rw-r--r--thirdparty/freetype/src/psaux/t1decode.h12
-rw-r--r--thirdparty/freetype/src/pshinter/module.mk2
-rw-r--r--thirdparty/freetype/src/pshinter/pshalgo.c4
-rw-r--r--thirdparty/freetype/src/pshinter/pshalgo.h2
-rw-r--r--thirdparty/freetype/src/pshinter/pshglob.c6
-rw-r--r--thirdparty/freetype/src/pshinter/pshglob.h2
-rw-r--r--thirdparty/freetype/src/pshinter/pshinter.c2
-rw-r--r--thirdparty/freetype/src/pshinter/pshmod.c2
-rw-r--r--thirdparty/freetype/src/pshinter/pshmod.h2
-rw-r--r--thirdparty/freetype/src/pshinter/pshnterr.h2
-rw-r--r--thirdparty/freetype/src/pshinter/pshpic.c2
-rw-r--r--thirdparty/freetype/src/pshinter/pshpic.h2
-rw-r--r--thirdparty/freetype/src/pshinter/pshrec.c2
-rw-r--r--thirdparty/freetype/src/pshinter/pshrec.h2
-rw-r--r--thirdparty/freetype/src/pshinter/rules.mk2
-rw-r--r--thirdparty/freetype/src/psnames/module.mk2
-rw-r--r--thirdparty/freetype/src/psnames/psmodule.c2
-rw-r--r--thirdparty/freetype/src/psnames/psmodule.h2
-rw-r--r--thirdparty/freetype/src/psnames/psnamerr.h2
-rw-r--r--thirdparty/freetype/src/psnames/psnames.c2
-rw-r--r--thirdparty/freetype/src/psnames/pspic.c2
-rw-r--r--thirdparty/freetype/src/psnames/pspic.h2
-rw-r--r--thirdparty/freetype/src/psnames/pstables.h2
-rw-r--r--thirdparty/freetype/src/psnames/rules.mk2
-rw-r--r--thirdparty/freetype/src/raster/ftmisc.h2
-rw-r--r--thirdparty/freetype/src/raster/ftraster.c2
-rw-r--r--thirdparty/freetype/src/raster/ftraster.h2
-rw-r--r--thirdparty/freetype/src/raster/ftrend1.c111
-rw-r--r--thirdparty/freetype/src/raster/ftrend1.h2
-rw-r--r--thirdparty/freetype/src/raster/module.mk2
-rw-r--r--thirdparty/freetype/src/raster/raster.c2
-rw-r--r--thirdparty/freetype/src/raster/rasterrs.h2
-rw-r--r--thirdparty/freetype/src/raster/rastpic.c2
-rw-r--r--thirdparty/freetype/src/raster/rastpic.h2
-rw-r--r--thirdparty/freetype/src/raster/rules.mk2
-rw-r--r--thirdparty/freetype/src/sfnt/module.mk2
-rw-r--r--thirdparty/freetype/src/sfnt/pngshim.c61
-rw-r--r--thirdparty/freetype/src/sfnt/pngshim.h2
-rw-r--r--thirdparty/freetype/src/sfnt/rules.mk2
-rw-r--r--thirdparty/freetype/src/sfnt/sfdriver.c9
-rw-r--r--thirdparty/freetype/src/sfnt/sfdriver.h2
-rw-r--r--thirdparty/freetype/src/sfnt/sferrors.h2
-rw-r--r--thirdparty/freetype/src/sfnt/sfnt.c2
-rw-r--r--thirdparty/freetype/src/sfnt/sfntpic.c2
-rw-r--r--thirdparty/freetype/src/sfnt/sfntpic.h2
-rw-r--r--thirdparty/freetype/src/sfnt/sfobjs.c19
-rw-r--r--thirdparty/freetype/src/sfnt/sfobjs.h2
-rw-r--r--thirdparty/freetype/src/sfnt/ttbdf.c2
-rw-r--r--thirdparty/freetype/src/sfnt/ttbdf.h6
-rw-r--r--thirdparty/freetype/src/sfnt/ttcmap.c47
-rw-r--r--thirdparty/freetype/src/sfnt/ttcmap.h2
-rw-r--r--thirdparty/freetype/src/sfnt/ttcmapc.h2
-rw-r--r--thirdparty/freetype/src/sfnt/ttkern.c2
-rw-r--r--thirdparty/freetype/src/sfnt/ttkern.h2
-rw-r--r--thirdparty/freetype/src/sfnt/ttload.c4
-rw-r--r--thirdparty/freetype/src/sfnt/ttload.h2
-rw-r--r--thirdparty/freetype/src/sfnt/ttmtx.c2
-rw-r--r--thirdparty/freetype/src/sfnt/ttmtx.h2
-rw-r--r--thirdparty/freetype/src/sfnt/ttpost.c6
-rw-r--r--thirdparty/freetype/src/sfnt/ttpost.h2
-rw-r--r--thirdparty/freetype/src/sfnt/ttsbit.c9
-rw-r--r--thirdparty/freetype/src/sfnt/ttsbit.h2
-rw-r--r--thirdparty/freetype/src/smooth/ftgrays.c85
-rw-r--r--thirdparty/freetype/src/smooth/ftgrays.h2
-rw-r--r--thirdparty/freetype/src/smooth/ftsmerrs.h2
-rw-r--r--thirdparty/freetype/src/smooth/ftsmooth.c287
-rw-r--r--thirdparty/freetype/src/smooth/ftsmooth.h2
-rw-r--r--thirdparty/freetype/src/smooth/ftspic.c2
-rw-r--r--thirdparty/freetype/src/smooth/ftspic.h2
-rw-r--r--thirdparty/freetype/src/smooth/module.mk2
-rw-r--r--thirdparty/freetype/src/smooth/rules.mk2
-rw-r--r--thirdparty/freetype/src/smooth/smooth.c2
-rw-r--r--thirdparty/freetype/src/truetype/module.mk2
-rw-r--r--thirdparty/freetype/src/truetype/rules.mk2
-rw-r--r--thirdparty/freetype/src/truetype/truetype.c2
-rw-r--r--thirdparty/freetype/src/truetype/ttdriver.c13
-rw-r--r--thirdparty/freetype/src/truetype/ttdriver.h2
-rw-r--r--thirdparty/freetype/src/truetype/tterrors.h2
-rw-r--r--thirdparty/freetype/src/truetype/ttgload.c139
-rw-r--r--thirdparty/freetype/src/truetype/ttgload.h2
-rw-r--r--thirdparty/freetype/src/truetype/ttgxvar.c646
-rw-r--r--thirdparty/freetype/src/truetype/ttgxvar.h10
-rw-r--r--thirdparty/freetype/src/truetype/ttinterp.c115
-rw-r--r--thirdparty/freetype/src/truetype/ttinterp.h2
-rw-r--r--thirdparty/freetype/src/truetype/ttobjs.c101
-rw-r--r--thirdparty/freetype/src/truetype/ttobjs.h2
-rw-r--r--thirdparty/freetype/src/truetype/ttpic.c2
-rw-r--r--thirdparty/freetype/src/truetype/ttpic.h2
-rw-r--r--thirdparty/freetype/src/truetype/ttpload.c2
-rw-r--r--thirdparty/freetype/src/truetype/ttpload.h2
-rw-r--r--thirdparty/freetype/src/truetype/ttsubpix.c28
-rw-r--r--thirdparty/freetype/src/truetype/ttsubpix.h2
-rw-r--r--thirdparty/freetype/src/type1/module.mk2
-rw-r--r--thirdparty/freetype/src/type1/rules.mk2
-rw-r--r--thirdparty/freetype/src/type1/t1afm.c2
-rw-r--r--thirdparty/freetype/src/type1/t1afm.h2
-rw-r--r--thirdparty/freetype/src/type1/t1driver.c21
-rw-r--r--thirdparty/freetype/src/type1/t1driver.h2
-rw-r--r--thirdparty/freetype/src/type1/t1errors.h2
-rw-r--r--thirdparty/freetype/src/type1/t1gload.c118
-rw-r--r--thirdparty/freetype/src/type1/t1gload.h2
-rw-r--r--thirdparty/freetype/src/type1/t1load.c113
-rw-r--r--thirdparty/freetype/src/type1/t1load.h6
-rw-r--r--thirdparty/freetype/src/type1/t1objs.c42
-rw-r--r--thirdparty/freetype/src/type1/t1objs.h8
-rw-r--r--thirdparty/freetype/src/type1/t1parse.c2
-rw-r--r--thirdparty/freetype/src/type1/t1parse.h2
-rw-r--r--thirdparty/freetype/src/type1/t1tokens.h2
-rw-r--r--thirdparty/freetype/src/type1/type1.c2
-rw-r--r--thirdparty/freetype/src/type42/module.mk2
-rw-r--r--thirdparty/freetype/src/type42/rules.mk2
-rw-r--r--thirdparty/freetype/src/type42/t42drivr.c2
-rw-r--r--thirdparty/freetype/src/type42/t42drivr.h2
-rw-r--r--thirdparty/freetype/src/type42/t42error.h2
-rw-r--r--thirdparty/freetype/src/type42/t42objs.c2
-rw-r--r--thirdparty/freetype/src/type42/t42objs.h2
-rw-r--r--thirdparty/freetype/src/type42/t42parse.c2
-rw-r--r--thirdparty/freetype/src/type42/t42parse.h2
-rw-r--r--thirdparty/freetype/src/type42/t42types.h2
-rw-r--r--thirdparty/freetype/src/type42/type42.c2
-rw-r--r--thirdparty/freetype/src/winfonts/fnterrs.h2
-rw-r--r--thirdparty/freetype/src/winfonts/module.mk2
-rw-r--r--thirdparty/freetype/src/winfonts/rules.mk2
-rw-r--r--thirdparty/freetype/src/winfonts/winfnt.c2
-rw-r--r--thirdparty/freetype/src/winfonts/winfnt.h2
-rw-r--r--thirdparty/libvorbis/COPYING2
-rw-r--r--thirdparty/libvorbis/analysis.c1
-rw-r--r--thirdparty/libvorbis/backends.h1
-rw-r--r--thirdparty/libvorbis/barkmel.c1
-rw-r--r--thirdparty/libvorbis/bitrate.c1
-rw-r--r--thirdparty/libvorbis/bitrate.h1
-rw-r--r--thirdparty/libvorbis/block.c1
-rw-r--r--thirdparty/libvorbis/books/coupled/res_books_51.h1
-rw-r--r--thirdparty/libvorbis/books/coupled/res_books_stereo.h1
-rw-r--r--thirdparty/libvorbis/books/floor/floor_books.h1
-rw-r--r--thirdparty/libvorbis/books/uncoupled/res_books_uncoupled.h1
-rw-r--r--thirdparty/libvorbis/codebook.c49
-rw-r--r--thirdparty/libvorbis/codebook.h1
-rw-r--r--thirdparty/libvorbis/codec_internal.h1
-rw-r--r--thirdparty/libvorbis/envelope.c1
-rw-r--r--thirdparty/libvorbis/envelope.h1
-rw-r--r--thirdparty/libvorbis/floor0.c1
-rw-r--r--thirdparty/libvorbis/floor1.c1
-rw-r--r--thirdparty/libvorbis/highlevel.h1
-rw-r--r--thirdparty/libvorbis/info.c34
-rw-r--r--thirdparty/libvorbis/lookup.c1
-rw-r--r--thirdparty/libvorbis/lookup.h1
-rw-r--r--thirdparty/libvorbis/lookup_data.h1
-rw-r--r--thirdparty/libvorbis/lpc.c1
-rw-r--r--thirdparty/libvorbis/lpc.h1
-rw-r--r--thirdparty/libvorbis/lsp.c1
-rw-r--r--thirdparty/libvorbis/lsp.h1
-rw-r--r--thirdparty/libvorbis/mapping0.c2
-rw-r--r--thirdparty/libvorbis/masking.h1
-rw-r--r--thirdparty/libvorbis/mdct.c1
-rw-r--r--thirdparty/libvorbis/mdct.h1
-rw-r--r--thirdparty/libvorbis/misc.h1
-rw-r--r--thirdparty/libvorbis/modes/floor_all.h1
-rw-r--r--thirdparty/libvorbis/modes/psych_11.h1
-rw-r--r--thirdparty/libvorbis/modes/psych_16.h1
-rw-r--r--thirdparty/libvorbis/modes/psych_44.h1
-rw-r--r--thirdparty/libvorbis/modes/psych_8.h1
-rw-r--r--thirdparty/libvorbis/modes/residue_16.h1
-rw-r--r--thirdparty/libvorbis/modes/residue_44.h1
-rw-r--r--thirdparty/libvorbis/modes/residue_44p51.h1
-rw-r--r--thirdparty/libvorbis/modes/residue_44u.h1
-rw-r--r--thirdparty/libvorbis/modes/residue_8.h1
-rw-r--r--thirdparty/libvorbis/modes/setup_11.h1
-rw-r--r--thirdparty/libvorbis/modes/setup_16.h1
-rw-r--r--thirdparty/libvorbis/modes/setup_22.h1
-rw-r--r--thirdparty/libvorbis/modes/setup_32.h1
-rw-r--r--thirdparty/libvorbis/modes/setup_44.h1
-rw-r--r--thirdparty/libvorbis/modes/setup_44p51.h1
-rw-r--r--thirdparty/libvorbis/modes/setup_44u.h1
-rw-r--r--thirdparty/libvorbis/modes/setup_8.h1
-rw-r--r--thirdparty/libvorbis/modes/setup_X.h1
-rw-r--r--thirdparty/libvorbis/os.h3
-rw-r--r--thirdparty/libvorbis/psy.c1
-rw-r--r--thirdparty/libvorbis/psy.h1
-rw-r--r--thirdparty/libvorbis/psytune.c65
-rw-r--r--thirdparty/libvorbis/registry.c1
-rw-r--r--thirdparty/libvorbis/registry.h1
-rw-r--r--thirdparty/libvorbis/res0.c1
-rw-r--r--thirdparty/libvorbis/scales.h1
-rw-r--r--thirdparty/libvorbis/sharedbook.c19
-rw-r--r--thirdparty/libvorbis/smallft.c1
-rw-r--r--thirdparty/libvorbis/smallft.h1
-rw-r--r--thirdparty/libvorbis/synthesis.c3
-rw-r--r--thirdparty/libvorbis/tone.c4
-rw-r--r--thirdparty/libvorbis/vorbis/codec.h1
-rw-r--r--thirdparty/libvorbis/vorbis/vorbisenc.h1
-rw-r--r--thirdparty/libvorbis/vorbis/vorbisfile.h1
-rw-r--r--thirdparty/libvorbis/vorbisenc.c1
-rw-r--r--thirdparty/libvorbis/vorbisfile.c1
-rw-r--r--thirdparty/libvorbis/window.c1
-rw-r--r--thirdparty/libvorbis/window.h1
-rw-r--r--thirdparty/libwebp/AUTHORS15
-rw-r--r--thirdparty/libwebp/src/dec/frame_dec.c12
-rw-r--r--thirdparty/libwebp/src/dec/vp8_dec.c2
-rw-r--r--thirdparty/libwebp/src/dec/vp8i_dec.h6
-rw-r--r--thirdparty/libwebp/src/dec/vp8l_dec.c10
-rw-r--r--thirdparty/libwebp/src/demux/demux.c6
-rw-r--r--thirdparty/libwebp/src/dsp/alpha_processing.c29
-rw-r--r--thirdparty/libwebp/src/dsp/alpha_processing_mips_dsp_r2.c46
-rw-r--r--thirdparty/libwebp/src/dsp/common_sse2.h14
-rw-r--r--thirdparty/libwebp/src/dsp/common_sse41.h132
-rw-r--r--thirdparty/libwebp/src/dsp/cost.c9
-rw-r--r--thirdparty/libwebp/src/dsp/dec.c9
-rw-r--r--thirdparty/libwebp/src/dsp/dsp.h52
-rw-r--r--thirdparty/libwebp/src/dsp/enc.c9
-rw-r--r--thirdparty/libwebp/src/dsp/filters.c9
-rw-r--r--thirdparty/libwebp/src/dsp/lossless.c9
-rw-r--r--thirdparty/libwebp/src/dsp/lossless.h4
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_enc.c9
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_enc_sse2.c27
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_enc_sse41.c94
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_sse2.c19
-rw-r--r--thirdparty/libwebp/src/dsp/rescaler.c7
-rw-r--r--thirdparty/libwebp/src/dsp/rescaler_sse2.c20
-rw-r--r--thirdparty/libwebp/src/dsp/ssim.c9
-rw-r--r--thirdparty/libwebp/src/dsp/upsampling.c30
-rw-r--r--thirdparty/libwebp/src/dsp/upsampling_msa.c6
-rw-r--r--thirdparty/libwebp/src/dsp/upsampling_sse2.c32
-rw-r--r--thirdparty/libwebp/src/dsp/upsampling_sse41.c239
-rw-r--r--thirdparty/libwebp/src/dsp/yuv.c29
-rw-r--r--thirdparty/libwebp/src/dsp/yuv.h13
-rw-r--r--thirdparty/libwebp/src/dsp/yuv_sse2.c4
-rw-r--r--thirdparty/libwebp/src/dsp/yuv_sse41.c613
-rw-r--r--thirdparty/libwebp/src/enc/alpha_enc.c5
-rw-r--r--thirdparty/libwebp/src/enc/analysis_enc.c6
-rw-r--r--thirdparty/libwebp/src/enc/frame_enc.c26
-rw-r--r--thirdparty/libwebp/src/enc/histogram_enc.c11
-rw-r--r--thirdparty/libwebp/src/enc/histogram_enc.h5
-rw-r--r--thirdparty/libwebp/src/enc/iterator_enc.c8
-rw-r--r--thirdparty/libwebp/src/enc/near_lossless_enc.c2
-rw-r--r--thirdparty/libwebp/src/enc/picture_csp_enc.c148
-rw-r--r--thirdparty/libwebp/src/enc/picture_psnr_enc.c15
-rw-r--r--thirdparty/libwebp/src/enc/quant_enc.c87
-rw-r--r--thirdparty/libwebp/src/enc/vp8i_enc.h16
-rw-r--r--thirdparty/libwebp/src/enc/vp8l_enc.c79
-rw-r--r--thirdparty/libwebp/src/enc/webp_enc.c9
-rw-r--r--thirdparty/libwebp/src/mux/muxi.h6
-rw-r--r--thirdparty/libwebp/src/utils/endian_inl_utils.h7
-rw-r--r--thirdparty/libwebsockets/LICENSE (renamed from thirdparty/lws/LICENSE.txt)33
-rw-r--r--thirdparty/libwebsockets/core/alloc.c (renamed from thirdparty/lws/alloc.c)10
-rw-r--r--thirdparty/libwebsockets/core/context.c (renamed from thirdparty/lws/context.c)1061
-rw-r--r--thirdparty/libwebsockets/core/libwebsockets.c (renamed from thirdparty/lws/libwebsockets.c)1849
-rw-r--r--thirdparty/libwebsockets/core/output.c308
-rw-r--r--thirdparty/libwebsockets/core/pollfd.c (renamed from thirdparty/lws/pollfd.c)402
-rw-r--r--thirdparty/libwebsockets/core/private.h1751
-rw-r--r--thirdparty/libwebsockets/core/service.c987
-rw-r--r--thirdparty/libwebsockets/event-libs/poll/poll.c43
-rw-r--r--thirdparty/libwebsockets/event-libs/poll/private.h23
-rw-r--r--thirdparty/libwebsockets/event-libs/private.h74
-rw-r--r--thirdparty/libwebsockets/libwebsockets.h (renamed from thirdparty/lws/libwebsockets.h)2811
-rw-r--r--thirdparty/libwebsockets/lws_config.h (renamed from thirdparty/lws/lws_config.h)50
-rw-r--r--thirdparty/libwebsockets/lws_config_private.h (renamed from thirdparty/lws/lws_config_private.h)2
-rw-r--r--thirdparty/libwebsockets/misc/base64-decode.c (renamed from thirdparty/lws/misc/base64-decode.c)56
-rw-r--r--thirdparty/libwebsockets/misc/getifaddrs.c (renamed from thirdparty/lws/misc/getifaddrs.c)2
-rw-r--r--thirdparty/libwebsockets/misc/getifaddrs.h (renamed from thirdparty/lws/misc/getifaddrs.h)0
-rw-r--r--thirdparty/libwebsockets/misc/lejp.c (renamed from thirdparty/lws/misc/lejp.c)59
-rw-r--r--thirdparty/libwebsockets/misc/sha-1.c (renamed from thirdparty/lws/misc/sha-1.c)2
-rw-r--r--thirdparty/libwebsockets/plat/lws-plat-unix.c (renamed from thirdparty/lws/plat/lws-plat-unix.c)303
-rw-r--r--thirdparty/libwebsockets/plat/lws-plat-win.c (renamed from thirdparty/lws/plat/lws-plat-win.c)210
-rw-r--r--thirdparty/libwebsockets/roles/h1/ops-h1.c687
-rw-r--r--thirdparty/libwebsockets/roles/h1/private.h27
-rw-r--r--thirdparty/libwebsockets/roles/http/client/client-handshake.c (renamed from thirdparty/lws/client/client-handshake.c)601
-rw-r--r--thirdparty/libwebsockets/roles/http/client/client.c1231
-rw-r--r--thirdparty/libwebsockets/roles/http/header.c (renamed from thirdparty/lws/header.c)136
-rw-r--r--thirdparty/libwebsockets/roles/http/lextable-strings.h (renamed from thirdparty/lws/lextable-strings.h)4
-rw-r--r--thirdparty/libwebsockets/roles/http/lextable.h838
-rw-r--r--thirdparty/libwebsockets/roles/http/private.h257
-rw-r--r--thirdparty/libwebsockets/roles/http/server/access-log.c182
-rw-r--r--thirdparty/libwebsockets/roles/http/server/fops-zip.c (renamed from thirdparty/lws/server/fops-zip.c)15
-rw-r--r--thirdparty/libwebsockets/roles/http/server/lejp-conf.c (renamed from thirdparty/lws/server/lejp-conf.c)100
-rw-r--r--thirdparty/libwebsockets/roles/http/server/parsers.c1139
-rw-r--r--thirdparty/libwebsockets/roles/http/server/server.c (renamed from thirdparty/lws/server/server.c)1862
-rw-r--r--thirdparty/libwebsockets/roles/listen/ops-listen.c177
-rw-r--r--thirdparty/libwebsockets/roles/pipe/ops-pipe.c81
-rw-r--r--thirdparty/libwebsockets/roles/private.h282
-rw-r--r--thirdparty/libwebsockets/roles/raw/ops-raw.c223
-rw-r--r--thirdparty/libwebsockets/roles/ws/client-parser-ws.c (renamed from thirdparty/lws/client/client-parser.c)362
-rw-r--r--thirdparty/libwebsockets/roles/ws/client-ws.c629
-rw-r--r--thirdparty/libwebsockets/roles/ws/ops-ws.c1992
-rw-r--r--thirdparty/libwebsockets/roles/ws/private.h164
-rw-r--r--thirdparty/libwebsockets/roles/ws/server-ws.c836
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/lws-genhash.c202
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/lws-genrsa.c329
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/mbedtls-client.c240
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c694
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/ssl.c520
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl3.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/ssl3.h)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_cert.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/ssl_cert.h)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_code.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/ssl_code.h)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_dbg.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/ssl_dbg.h)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_lib.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/ssl_lib.h)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_methods.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/ssl_methods.h)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_pkey.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/ssl_pkey.h)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_stack.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/ssl_stack.h)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h)2
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_x509.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/ssl_x509.h)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/tls1.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/tls1.h)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/x509_vfy.h (renamed from thirdparty/lws/mbedtls_wrapper/include/internal/x509_vfy.h)0
-rwxr-xr-x[-rw-r--r--]thirdparty/libwebsockets/tls/mbedtls/wrapper/include/openssl/ssl.h (renamed from thirdparty/lws/mbedtls_wrapper/include/openssl/ssl.h)17
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/platform/ssl_pm.h (renamed from thirdparty/lws/mbedtls_wrapper/include/platform/ssl_pm.h)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/platform/ssl_port.h (renamed from thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h)12
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_cert.c (renamed from thirdparty/lws/mbedtls_wrapper/library/ssl_cert.c)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_lib.c (renamed from thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c)85
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_methods.c (renamed from thirdparty/lws/mbedtls_wrapper/library/ssl_methods.c)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_pkey.c (renamed from thirdparty/lws/mbedtls_wrapper/library/ssl_pkey.c)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_stack.c (renamed from thirdparty/lws/mbedtls_wrapper/library/ssl_stack.c)0
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_x509.c (renamed from thirdparty/lws/mbedtls_wrapper/library/ssl_x509.c)26
-rwxr-xr-x[-rw-r--r--]thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_pm.c (renamed from thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c)220
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_port.c (renamed from thirdparty/lws/mbedtls_wrapper/platform/ssl_port.c)0
-rw-r--r--thirdparty/libwebsockets/tls/private.h281
-rw-r--r--thirdparty/libwebsockets/tls/tls-client.c150
-rw-r--r--thirdparty/libwebsockets/tls/tls-server.c382
-rw-r--r--thirdparty/libwebsockets/tls/tls.c522
-rw-r--r--thirdparty/libwebsockets/win32helpers/getopt.c (renamed from thirdparty/lws/win32helpers/getopt.c)306
-rw-r--r--thirdparty/libwebsockets/win32helpers/getopt.h (renamed from thirdparty/lws/win32helpers/getopt.h)0
-rw-r--r--thirdparty/libwebsockets/win32helpers/getopt_long.c (renamed from thirdparty/lws/win32helpers/getopt_long.c)477
-rw-r--r--thirdparty/libwebsockets/win32helpers/gettimeofday.c (renamed from thirdparty/lws/win32helpers/gettimeofday.c)70
-rw-r--r--thirdparty/libwebsockets/win32helpers/gettimeofday.h (renamed from thirdparty/lws/win32helpers/gettimeofday.h)0
-rw-r--r--thirdparty/lws/client/client.c1304
-rw-r--r--thirdparty/lws/client/ssl-client.c625
-rw-r--r--thirdparty/lws/ext/extension-permessage-deflate.c473
-rw-r--r--thirdparty/lws/ext/extension-permessage-deflate.h41
-rw-r--r--thirdparty/lws/ext/extension.c344
-rw-r--r--thirdparty/lws/handshake.c280
-rw-r--r--thirdparty/lws/lextable.h805
-rw-r--r--thirdparty/lws/mbedtls_verify.diff74
-rw-r--r--thirdparty/lws/misc/lejp.h232
-rw-r--r--thirdparty/lws/output.c883
-rw-r--r--thirdparty/lws/private-libwebsockets.h2615
-rw-r--r--thirdparty/lws/server/parsers.c1783
-rw-r--r--thirdparty/lws/server/ranges.c214
-rw-r--r--thirdparty/lws/server/server-handshake.c360
-rw-r--r--thirdparty/lws/server/ssl-server.c477
-rw-r--r--thirdparty/lws/service.c1714
-rw-r--r--thirdparty/lws/ssl.c973
-rw-r--r--thirdparty/mbedtls/1453.diff24
-rw-r--r--thirdparty/mbedtls/LICENSE2
-rw-r--r--thirdparty/mbedtls/apache-2.0.txt202
-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/miniupnpc/LICENSE27
-rw-r--r--thirdparty/miniupnpc/codelength.h54
-rw-r--r--thirdparty/miniupnpc/connecthostport.c264
-rw-r--r--thirdparty/miniupnpc/connecthostport.h20
-rw-r--r--thirdparty/miniupnpc/igd_desc_parse.c123
-rw-r--r--thirdparty/miniupnpc/igd_desc_parse.h49
-rw-r--r--thirdparty/miniupnpc/listdevices.c197
-rw-r--r--thirdparty/miniupnpc/minisoap.c124
-rw-r--r--thirdparty/miniupnpc/minisoap.h17
-rw-r--r--thirdparty/miniupnpc/minissdpc.c888
-rw-r--r--thirdparty/miniupnpc/minissdpc.h58
-rw-r--r--thirdparty/miniupnpc/miniupnpc.c727
-rw-r--r--thirdparty/miniupnpc/miniupnpc.def45
-rw-r--r--thirdparty/miniupnpc/miniupnpc.h153
-rw-r--r--thirdparty/miniupnpc/miniupnpc/miniupnpc.h153
-rw-r--r--thirdparty/miniupnpc/miniupnpc/miniwget.h27
-rw-r--r--thirdparty/miniupnpc/miniupnpc/upnpcommands.h348
-rw-r--r--thirdparty/miniupnpc/miniupnpc_declspec.h21
-rw-r--r--thirdparty/miniupnpc/miniupnpc_socketdef.h37
-rw-r--r--thirdparty/miniupnpc/miniupnpcmodule.c703
-rw-r--r--thirdparty/miniupnpc/miniupnpcstrings.h17
-rw-r--r--thirdparty/miniupnpc/miniupnpctypes.h19
-rw-r--r--thirdparty/miniupnpc/miniwget.c662
-rw-r--r--thirdparty/miniupnpc/miniwget.h27
-rw-r--r--thirdparty/miniupnpc/miniwget_private.h15
-rw-r--r--thirdparty/miniupnpc/minixml.c231
-rw-r--r--thirdparty/miniupnpc/minixml.h37
-rw-r--r--thirdparty/miniupnpc/minixmlvalid.c163
-rw-r--r--thirdparty/miniupnpc/portlistingparse.c172
-rw-r--r--thirdparty/miniupnpc/portlistingparse.h65
-rw-r--r--thirdparty/miniupnpc/receivedata.c99
-rw-r--r--thirdparty/miniupnpc/receivedata.h21
-rw-r--r--thirdparty/miniupnpc/upnpc.c861
-rw-r--r--thirdparty/miniupnpc/upnpcommands.c1241
-rw-r--r--thirdparty/miniupnpc/upnpcommands.h348
-rw-r--r--thirdparty/miniupnpc/upnpdev.c23
-rw-r--r--thirdparty/miniupnpc/upnpdev.h36
-rw-r--r--thirdparty/miniupnpc/upnperrors.c107
-rw-r--r--thirdparty/miniupnpc/upnperrors.h26
-rw-r--r--thirdparty/miniupnpc/upnpreplyparse.c196
-rw-r--r--thirdparty/miniupnpc/upnpreplyparse.h63
-rw-r--r--thirdparty/minizip/crypt.h8
-rw-r--r--thirdparty/minizip/godot-zlib-1.2.4-minizip-seek.patch6
-rw-r--r--thirdparty/minizip/ioapi.c22
-rw-r--r--thirdparty/minizip/ioapi.h10
-rw-r--r--thirdparty/minizip/unzip.c26
-rw-r--r--thirdparty/minizip/unzip.h4
-rw-r--r--thirdparty/minizip/zip.c35
-rw-r--r--thirdparty/misc/stb_truetype.h487
-rw-r--r--thirdparty/misc/stb_vorbis.c293
-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
-rw-r--r--thirdparty/zstd/common/bitstream.h2
-rw-r--r--thirdparty/zstd/common/compiler.h25
-rw-r--r--thirdparty/zstd/common/cpu.h216
-rw-r--r--thirdparty/zstd/common/error_private.c1
-rw-r--r--thirdparty/zstd/common/fse.h2
-rw-r--r--thirdparty/zstd/common/fse_decompress.c4
-rw-r--r--thirdparty/zstd/common/huf.h203
-rw-r--r--thirdparty/zstd/common/pool.c63
-rw-r--r--thirdparty/zstd/common/pool.h27
-rw-r--r--thirdparty/zstd/common/threading.h24
-rw-r--r--thirdparty/zstd/common/zstd_errors.h23
-rw-r--r--thirdparty/zstd/common/zstd_internal.h9
-rw-r--r--thirdparty/zstd/compress/fse_compress.c30
-rw-r--r--thirdparty/zstd/compress/huf_compress.c222
-rw-r--r--thirdparty/zstd/compress/zstd_compress.c1617
-rw-r--r--thirdparty/zstd/compress/zstd_compress_internal.h315
-rw-r--r--thirdparty/zstd/compress/zstd_double_fast.c142
-rw-r--r--thirdparty/zstd/compress/zstd_double_fast.h15
-rw-r--r--thirdparty/zstd/compress/zstd_fast.c158
-rw-r--r--thirdparty/zstd/compress/zstd_fast.h16
-rw-r--r--thirdparty/zstd/compress/zstd_lazy.c607
-rw-r--r--thirdparty/zstd/compress/zstd_lazy.h49
-rw-r--r--thirdparty/zstd/compress/zstd_ldm.c664
-rw-r--r--thirdparty/zstd/compress/zstd_ldm.h85
-rw-r--r--thirdparty/zstd/compress/zstd_opt.c250
-rw-r--r--thirdparty/zstd/compress/zstd_opt.h22
-rw-r--r--thirdparty/zstd/compress/zstdmt_compress.c1580
-rw-r--r--thirdparty/zstd/compress/zstdmt_compress.h48
-rw-r--r--thirdparty/zstd/decompress/huf_decompress.c680
-rw-r--r--thirdparty/zstd/decompress/zstd_decompress.c1216
-rw-r--r--thirdparty/zstd/zstd.h508
2020 files changed, 162980 insertions, 68280 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 708961fd92..7691f65d6a 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -17,7 +17,8 @@ cache:
install:
- SET "PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- - pip install --egg scons # it will fail on AppVeyor without --egg flag
+ - pip install -U wheel # needed for pip install scons to work, otherwise a flag is missing
+ - pip install scons
- if defined VS call "%VS%" %ARCH% # if defined - so we can also use mingw
before_build:
diff --git a/.gitignore b/.gitignore
index f8296ef51e..6db75f2324 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,8 @@ platform/android/java/build.gradle
platform/android/java/AndroidManifest.xml
platform/android/java/libs/*
platform/android/java/assets
+platform/android/java/.idea/*
+platform/android/java/*.iml
# General c++ generated files
*.lib
@@ -44,6 +46,7 @@ gmon.out
# QT project files
*.config
*.creator
+*.creator.*
*.files
*.includes
@@ -288,6 +291,7 @@ logo.h
TAGS
!TAGS/
tags
+*.tags
!tags/
gtags.files
GTAGS
diff --git a/.travis.yml b/.travis.yml
index 57580c1c41..8c24291328 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
language: cpp
+# OS config, depends on actual 'os' in build matrix
dist: trusty
-
sudo: false
env:
@@ -9,7 +9,7 @@ env:
- SCONS_CACHE=$HOME/.scons_cache
- SCONS_CACHE_LIMIT=1024
- OPTIONS="verbose=yes progress=no gdnative_wrapper=yes"
- - secure: AnjB84ZZYDxDQdWwsT9PJrD5tEnwcauzvVeSG+us2GxgfyJ2HFArkZ/IjlvbsfYIiiHghJ2Ssy9yPtUT921BtNNHoWuN/8YQziGrlwiSfLGmS/n8GFH22OYwzDSa7UC7ODts5La2I4+JzrdtF933TwE+4QzH4E3GyaKbznh402E=
+ - secure: "QLFRizqry/Y5pnEZvDlQz5S3YydQ+600u4rHEzFgUTd0heYeQaETXAQeMzp0ymuG1BkdRAl5YJoLVJgAzjwI9hrvugvoUlh2//SfpqZCHN/Q1fYbtGgNTn01R3VFEpcfYQL93I2EjrxVm0WTM4PwCvMO+hU0aWTRDvCt1Lty0kMR+RMDQOO/woqunoXh5wvFNxTJJkAmuLe0v962DJYOIwJAnqMLR0aFYjmeQJ20bc/2X5oLt+WuJDuf/lGj6WSlD6z/o/kL3YxHoUyw4A/HAZ2IX0IfNHKuay60ESWzl/NlobnePiPwHAE2pdDVu//q16fanb9VeYnBYRFse49TpFRb86Lo+Qz8nKDJqpQEIY0YKNCFqekrubqTM++Lj6QvGpykQZNxUhybmELcEsRG4PS0UMvCpebdnJD46nNB+DtO2Lgb4xXDLQwpq19z1wizq/XDQ5hz61TIIx8+i8TsgdSQKCTeWovd4HcD4CVjAD5XTLGgyRmI/zC2d+lTnKo6W9diLq/bX/Goq2QPeaTPABqv817IaJka2JyugQ7Qal/+gNTjYRRsimRCL9B2tVh+Uh8rWhTFhQL4QbP5P65HF+p8qojUzqtAhPMbZ8mxUtNukUI3liVgPgiMss96sG0nTVglFgkkAkEjIMFnqMSKnTfG812K4jIhp2jCO2Q3NeI="
cache:
directories:
@@ -20,75 +20,66 @@ matrix:
- env: STATIC_CHECKS=yes
os: linux
compiler: gcc
- - env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools"
+ addons:
+ apt:
+ sources:
+ - llvm-toolchain-trusty-5.0
+ packages:
+ - clang-format-5.0
+
+ coverity_scan:
+ project:
+ name: "godotengine/godot"
+ description: "Godot Engine Coverity scans"
+ notification_email: coverity@godotengine.org
+ build_command_prepend: ""
+ build_command: "scons p=x11 -j2 $OPTIONS"
+ branch_pattern: coverity_scan
+
+ - env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-tools-mono-gcc EXTRA_ARGS="module_mono_enabled=yes mono_glue=no"
os: linux
compiler: gcc
- - env: GODOT_TARGET=x11 TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang"
+ addons:
+ apt:
+ sources:
+ - mono
+ packages:
+ - &linux_deps [libasound2-dev, libfreetype6-dev, libgl1-mesa-dev, libglu1-mesa-dev, libx11-dev, libxcursor-dev, libxi-dev, libxinerama-dev, libxrandr-dev]
+ - &linux_mono_deps [mono-devel, msbuild]
+
+ - env: GODOT_TARGET=x11 TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang
os: linux
compiler: clang
- #- env: GODOT_TARGET=windows TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools
- # os: linux
- # compiler: gcc
- - env: GODOT_TARGET=android TOOLS=no CACHE_NAME=${GODOT_TARGET}-gcc
+ addons:
+ apt:
+ packages:
+ - *linux_deps
+
+ - env: GODOT_TARGET=android TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang
os: linux
- compiler: gcc
- - env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-clang-tools
+ compiler: clang
+
+ - env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-tools-clang
os: osx
osx_image: xcode9.3
compiler: clang
+
- env: GODOT_TARGET=iphone TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang
os: osx
osx_image: xcode9.3
compiler: clang
-addons:
- apt:
- sources:
- - ubuntu-toolchain-r-test
- - llvm-toolchain-trusty-5.0
- packages:
- - build-essential
- - scons
- - pkg-config
- - libx11-dev
- - libxcursor-dev
- - libxi-dev
- - libxinerama-dev
- - libxrandr-dev
- - libgl1-mesa-dev
- - libglu1-mesa-dev
- - libasound2-dev
- - libfreetype6-dev
-
- # For cross-compiling to Windows.
- #- binutils-mingw-w64-i686
- #- binutils-mingw-w64-x86-64
- #- gcc-mingw-w64-i686
- #- gcc-mingw-w64-x86-64
- #- g++-mingw-w64-i686
- #- g++-mingw-w64-x86-64
- #- mingw-w64
-
- # For style checks.
- - clang-format-5.0
-
- coverity_scan:
- project:
- name: "godotengine/godot"
- description: "Godot Engine Coverity scans"
- notification_email: coverity@godotengine.org
- build_command_prepend: ""
- build_command: "scons p=x11 -j2 $OPTIONS"
- branch_pattern: coverity_scan
+ - env: GODOT_TARGET=server TOOLS=yes CACHE_NAME=${GODOT_TARGET}-tools-gcc
+ os: linux
+ compiler: gcc
+ addons:
+ apt:
+ packages:
+ - *linux_deps
before_install:
- if [ "$STATIC_CHECKS" = "yes" ]; then
unset SCONS_CACHE;
- else
- if [ "$TRAVIS_BRANCH" = "coverity_scan" ]; then
- echo "This job runs in the Coverity Scan branch and is not the STATIC_CHECKS job meant for it, so aborting with exit code 0.";
- travis_terminate 0;
- fi;
fi
install:
@@ -112,5 +103,5 @@ script:
- if [ "$STATIC_CHECKS" = "yes" ]; then
sh ./misc/travis/clang-format.sh;
else
- scons -j2 CC=$CC CXX=$CXX platform=$GODOT_TARGET TOOLS=$TOOLS $OPTIONS;
+ scons -j2 CC=$CC CXX=$CXX platform=$GODOT_TARGET TOOLS=$TOOLS $EXTRA_ARGS $OPTIONS;
fi
diff --git a/AUTHORS.md b/AUTHORS.md
index 67563298f2..12494a487d 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -38,6 +38,7 @@ name is available.
Ariel Manzur (punto-)
Bastiaan Olij (BastiaanOlij)
Ben Brookshire (sheepandshepherd)
+ Benjamin (Nallebeorn)
Bernard Liebl (poke1024)
Bojidar Marinov (bojidar-bg)
Błażej Szczygieł (zaps166)
@@ -61,6 +62,7 @@ name is available.
Hubert Jarosz (Marqin)
Hugo Locurcio (Calinou)
Ian Bishop (ianb96)
+ Ibrahn Sahir (ibrahn)
Ignacio Etcheverry (neikeq)
Indah Sylvia (ISylvox)
J08nY
@@ -71,6 +73,7 @@ name is available.
Juan Linietsky (reduz)
Julian Murgia (StraToN)
Justo Delgado (mrcdk)
+ Kelly Thomas (KellyThomas)
Kostadin Damyanov (Max-Might)
Leon Krause (eska014)
Marc Gilleron (Zylann)
@@ -85,6 +88,7 @@ name is available.
Nathan Warden (NathanWarden)
Nuno Donato (nunodonato)
Ovnuniarchos
+ Pascal Richter (ShyRed)
Patrick (firefly2442)
Paul Batty (Paulb23)
Paul Joannon (paulloz)
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 0000000000..30d80990a6
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1,37 @@
+# Lines starting with '#' are comments.
+# Each line is a file pattern followed by one or more owners.
+# Owners can be @users, @org/teams or emails
+
+core/* @reduz
+
+doc/* @godotengine/documentation
+
+drivers/gles2/* @karroffel
+drivers/gles3/* @reduz
+
+editor/icons/* @djrm
+
+main/* @reduz
+
+misc/* @akien-mga
+
+modules/bullet/* @AndreaCatania
+modules/enet/* @godotengine/network
+modules/gnative/* @karroffel
+modules/gdscript/* @reduz @vnen @bojidar-bg
+modules/mbedtls/* @godotengine/network
+modules/mobile_vr/* @BastiaanOlij
+modules/mono/* @neikeq
+modules/regex/* @LeeZH
+modules/upnp/* @godotengine/network
+modules/websocket/* @godotengine/network
+
+platform/javascript/* @eska014
+platform/uwp/* @vnen
+
+scene/main/* @reduz
+
+server/physics* @reduz @AndreaCatania
+server/visual* @reduz @karroffel
+
+thirdparty/* @akien-mga
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 6b6ee4c509..f4340719dc 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -218,11 +218,21 @@ Comment: WebP codec
Copyright: 2010, Google Inc.
License: BSD-3-clause
+Files: ./thirdparty/libwebsockets/
+Comment: libwebsockets
+Copyright: 2010-2017, Andy Green
+License: LGPL-2.1+SLE (libwebsockets)
+
Files: ./thirdparty/mbedtls/
Comment: Mbed TLS
Copyright: 2006-2015, ARM Limited
License: Apache-2.0
+Files: ./thirdparty/miniupnpc/
+Comment: MiniUPnPc
+Copyright: 2005-2016, Thomas Bernard
+License: BSD-3-clause
+
Files: ./thirdparty/minizip/
Comment: MiniZip
Copyright: 1998-2010, Gilles Vollant
@@ -244,6 +254,12 @@ Comment: BASE64 conversion methods
Copyright: Ari Edelkind
License: public-domain
+Files: ./thirdparty/misc/clipper.cpp
+ ./thirdparty/misc/clipper.hpp
+Comment: Clipper
+Copyright: 2010-2017, Angus Johnson
+License: BSL-1.0
+
Files: ./thirdparty/misc/curl_hostcheck.c
./thirdparty/misc/curl_hostcheck.h
Comment: curl
@@ -378,30 +394,6 @@ License: Apache-2.0
See the License for the specific language governing permissions and
limitations under the License.
-License: BSD-2-clause
- 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 author
- 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 HOLDER 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.
-
License: Bitstream Vera Fonts Copyright
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a
trademark of Bitstream, Inc.
@@ -444,6 +436,28 @@ License: Bitstream Vera Fonts Copyright
authorization from the GNOME Foundation or Bitstream Inc., respectively. For
further information, contact: fonts at gnome dot org.
+License: BSD-2-clause
+ 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.
+ .
+ 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 HOLDER 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.
+
License: BSD-3-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -472,6 +486,31 @@ License: BSD-3-clause
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
+License: BSL-1.0
+ Boost Software License - Version 1.0 - August 17th, 2003
+ .
+ Permission is hereby granted, free of charge, to any person or organization
+ obtaining a copy of the software and accompanying documentation covered by
+ this license (the "Software") to use, reproduce, display, distribute,
+ execute, and transmit the Software, and to prepare derivative works of the
+ Software, and to permit third-parties to whom the Software is furnished to
+ do so, all subject to the following:
+ .
+ The copyright notices in the Software and this entire statement, including
+ the above license grant, this restriction and the following disclaimer,
+ must be included in all copies of the Software, in whole or in part, and
+ all derivative works of the Software, unless such copies or derivative
+ works are solely in the form of machine-executable object code generated by
+ a source language processor.
+ .
+ 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
License: CC-BY-3.0
Creative Commons Attribution 3.0 Unported
.
@@ -1022,6 +1061,564 @@ License: ISC
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+License: LGPL-2.1+SLE (libwebsockets)
+ Libwebsockets and included programs are provided under the terms of the GNU
+ Library General Public License (LGPL) 2.1, with the following exceptions:
+ .
+ 1) Any reference, whether in these modifications or in the GNU
+ Library General Public License 2.1, to this License, these terms, the
+ GNU Lesser Public License, GNU Library General Public License, LGPL, or
+ any similar reference shall refer to the GNU Library General Public
+ License 2.1 as modified by these paragraphs 1) through 4).
+ .
+ 2) Static linking of programs with the libwebsockets library does not
+ constitute a derivative work and does not require the author to provide
+ source code for the program, use the shared libwebsockets libraries, or
+ link their program against a user-supplied version of libwebsockets.
+ .
+ If you link the program to a modified version of libwebsockets, then the
+ changes to libwebsockets must be provided under the terms of the LGPL in
+ sections 1, 2, and 4.
+ .
+ 3) You do not have to provide a copy of the libwebsockets license with
+ programs that are linked to the libwebsockets library, nor do you have to
+ identify the libwebsockets license in your program or documentation as
+ required by section 6 of the LGPL.
+ .
+ However, programs must still identify their use of libwebsockets. The
+ following example statement can be included in user documentation to
+ satisfy this requirement:
+ .
+ "[program] is based in part on the work of the libwebsockets project
+ (https://libwebsockets.org)"
+ .
+ 4) Some sources included have their own, more liberal licenses, or options
+ to get original sources with the liberal terms.
+ .
+ Original liberal license retained
+ .
+ - lib/misc/sha-1.c - 3-clause BSD license retained, link to original
+ - win32port/zlib - ZLIB license (see zlib.h)
+ - lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)
+ .
+ Relicensed to libwebsocket license
+ .
+ - lib/misc/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
+ - lib/misc/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE,
+ link to original Public Domain version
+ .
+ Public Domain (CC-zero) to simplify reuse
+ .
+ - test-apps/*.c
+ - test-apps/*.h
+ - minimal-examples/*
+ - lwsws/*
+ .
+ ------ end of exceptions
+ .
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+ .
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+ .
+ [This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+ .
+ Preamble
+ .
+ The licenses for most software are designed to take away your
+ freedom to share and change it. By contrast, the GNU General Public
+ Licenses are intended to guarantee your freedom to share and change
+ free software--to make sure the software is free for all its users.
+ .
+ This license, the Lesser General Public License, applies to some
+ specially designated software packages--typically libraries--of the
+ Free Software Foundation and other authors who decide to use it. You
+ can use it too, but we suggest you first think carefully about whether
+ this license or the ordinary General Public License is the better
+ strategy to use in any particular case, based on the explanations below.
+ .
+ When we speak of free software, we are referring to freedom of use,
+ not price. Our General Public Licenses are designed to make sure that
+ you have the freedom to distribute copies of free software (and charge
+ for this service if you wish); that you receive source code or can get
+ it if you want it; that you can change the software and use pieces of
+ it in new free programs; and that you are informed that you can do
+ these things.
+ .
+ To protect your rights, we need to make restrictions that forbid
+ distributors to deny you these rights or to ask you to surrender these
+ rights. These restrictions translate to certain responsibilities for
+ you if you distribute copies of the library or if you modify it.
+ .
+ For example, if you distribute copies of the library, whether gratis
+ or for a fee, you must give the recipients all the rights that we gave
+ you. You must make sure that they, too, receive or can get the source
+ code. If you link other code with the library, you must provide
+ complete object files to the recipients, so that they can relink them
+ with the library after making changes to the library and recompiling
+ it. And you must show them these terms so they know their rights.
+ .
+ We protect your rights with a two-step method: (1) we copyright the
+ library, and (2) we offer you this license, which gives you legal
+ permission to copy, distribute and/or modify the library.
+ .
+ To protect each distributor, we want to make it very clear that
+ there is no warranty for the free library. Also, if the library is
+ modified by someone else and passed on, the recipients should know
+ that what they have is not the original version, so that the original
+ author's reputation will not be affected by problems that might be
+ introduced by others.
+ .
+ Finally, software patents pose a constant threat to the existence of
+ any free program. We wish to make sure that a company cannot
+ effectively restrict the users of a free program by obtaining a
+ restrictive license from a patent holder. Therefore, we insist that
+ any patent license obtained for a version of the library must be
+ consistent with the full freedom of use specified in this license.
+ .
+ Most GNU software, including some libraries, is covered by the
+ ordinary GNU General Public License. This license, the GNU Lesser
+ General Public License, applies to certain designated libraries, and
+ is quite different from the ordinary General Public License. We use
+ this license for certain libraries in order to permit linking those
+ libraries into non-free programs.
+ .
+ When a program is linked with a library, whether statically or using
+ a shared library, the combination of the two is legally speaking a
+ combined work, a derivative of the original library. The ordinary
+ General Public License therefore permits such linking only if the
+ entire combination fits its criteria of freedom. The Lesser General
+ Public License permits more lax criteria for linking other code with
+ the library.
+ .
+ We call this license the "Lesser" General Public License because it
+ does Less to protect the user's freedom than the ordinary General
+ Public License. It also provides other free software developers Less
+ of an advantage over competing non-free programs. These disadvantages
+ are the reason we use the ordinary General Public License for many
+ libraries. However, the Lesser license provides advantages in certain
+ special circumstances.
+ .
+ For example, on rare occasions, there may be a special need to
+ encourage the widest possible use of a certain library, so that it becomes
+ a de-facto standard. To achieve this, non-free programs must be
+ allowed to use the library. A more frequent case is that a free
+ library does the same job as widely used non-free libraries. In this
+ case, there is little to gain by limiting the free library to free
+ software only, so we use the Lesser General Public License.
+ .
+ In other cases, permission to use a particular library in non-free
+ programs enables a greater number of people to use a large body of
+ free software. For example, permission to use the GNU C Library in
+ non-free programs enables many more people to use the whole GNU
+ operating system, as well as its variant, the GNU/Linux operating
+ system.
+ .
+ Although the Lesser General Public License is Less protective of the
+ users' freedom, it does ensure that the user of a program that is
+ linked with the Library has the freedom and the wherewithal to run
+ that program using a modified version of the Library.
+ .
+ The precise terms and conditions for copying, distribution and
+ modification follow. Pay close attention to the difference between a
+ "work based on the library" and a "work that uses the library". The
+ former contains code derived from the library, whereas the latter must
+ be combined with the library in order to run.
+ .
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+ .
+ 0. This License Agreement applies to any software library or other
+ program which contains a notice placed by the copyright holder or
+ other authorized party saying it may be distributed under the terms of
+ this Lesser General Public License (also called "this License").
+ Each licensee is addressed as "you".
+ .
+ A "library" means a collection of software functions and/or data
+ prepared so as to be conveniently linked with application programs
+ (which use some of those functions and data) to form executables.
+ .
+ The "Library", below, refers to any such software library or work
+ which has been distributed under these terms. A "work based on the
+ Library" means either the Library or any derivative work under
+ copyright law: that is to say, a work containing the Library or a
+ portion of it, either verbatim or with modifications and/or translated
+ straightforwardly into another language. (Hereinafter, translation is
+ included without limitation in the term "modification".)
+ .
+ "Source code" for a work means the preferred form of the work for
+ making modifications to it. For a library, complete source code means
+ all the source code for all modules it contains, plus any associated
+ interface definition files, plus the scripts used to control compilation
+ and installation of the library.
+ .
+ Activities other than copying, distribution and modification are not
+ covered by this License; they are outside its scope. The act of
+ running a program using the Library is not restricted, and output from
+ such a program is covered only if its contents constitute a work based
+ on the Library (independent of the use of the Library in a tool for
+ writing it). Whether that is true depends on what the Library does
+ and what the program that uses the Library does.
+ .
+ 1. You may copy and distribute verbatim copies of the Library's
+ complete source code as you receive it, in any medium, provided that
+ you conspicuously and appropriately publish on each copy an
+ appropriate copyright notice and disclaimer of warranty; keep intact
+ all the notices that refer to this License and to the absence of any
+ warranty; and distribute a copy of this License along with the
+ Library.
+ .
+ You may charge a fee for the physical act of transferring a copy,
+ and you may at your option offer warranty protection in exchange for a
+ fee.
+ .
+ 2. You may modify your copy or copies of the Library or any portion
+ of it, thus forming a work based on the Library, and copy and
+ distribute such modifications or work under the terms of Section 1
+ above, provided that you also meet all of these conditions:
+ .
+ a) The modified work must itself be a software library.
+ .
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+ .
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+ .
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+ .
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+ .
+ These requirements apply to the modified work as a whole. If
+ identifiable sections of that work are not derived from the Library,
+ and can be reasonably considered independent and separate works in
+ themselves, then this License, and its terms, do not apply to those
+ sections when you distribute them as separate works. But when you
+ distribute the same sections as part of a whole which is a work based
+ on the Library, the distribution of the whole must be on the terms of
+ this License, whose permissions for other licensees extend to the
+ entire whole, and thus to each and every part regardless of who wrote
+ it.
+ .
+ Thus, it is not the intent of this section to claim rights or contest
+ your rights to work written entirely by you; rather, the intent is to
+ exercise the right to control the distribution of derivative or
+ collective works based on the Library.
+ .
+ In addition, mere aggregation of another work not based on the Library
+ with the Library (or with a work based on the Library) on a volume of
+ a storage or distribution medium does not bring the other work under
+ the scope of this License.
+ .
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+ License instead of this License to a given copy of the Library. To do
+ this, you must alter all the notices that refer to this License, so
+ that they refer to the ordinary GNU General Public License, version 2,
+ instead of to this License. (If a newer version than version 2 of the
+ ordinary GNU General Public License has appeared, then you can specify
+ that version instead if you wish.) Do not make any other change in
+ these notices.
+ .
+ Once this change is made in a given copy, it is irreversible for
+ that copy, so the ordinary GNU General Public License applies to all
+ subsequent copies and derivative works made from that copy.
+ .
+ This option is useful when you wish to copy part of the code of
+ the Library into a program that is not a library.
+ .
+ 4. You may copy and distribute the Library (or a portion or
+ derivative of it, under Section 2) in object code or executable form
+ under the terms of Sections 1 and 2 above provided that you accompany
+ it with the complete corresponding machine-readable source code, which
+ must be distributed under the terms of Sections 1 and 2 above on a
+ medium customarily used for software interchange.
+ .
+ If distribution of object code is made by offering access to copy
+ from a designated place, then offering equivalent access to copy the
+ source code from the same place satisfies the requirement to
+ distribute the source code, even though third parties are not
+ compelled to copy the source along with the object code.
+ .
+ 5. A program that contains no derivative of any portion of the
+ Library, but is designed to work with the Library by being compiled or
+ linked with it, is called a "work that uses the Library". Such a
+ work, in isolation, is not a derivative work of the Library, and
+ therefore falls outside the scope of this License.
+ .
+ However, linking a "work that uses the Library" with the Library
+ creates an executable that is a derivative of the Library (because it
+ contains portions of the Library), rather than a "work that uses the
+ library". The executable is therefore covered by this License.
+ Section 6 states terms for distribution of such executables.
+ .
+ When a "work that uses the Library" uses material from a header file
+ that is part of the Library, the object code for the work may be a
+ derivative work of the Library even though the source code is not.
+ Whether this is true is especially significant if the work can be
+ linked without the Library, or if the work is itself a library. The
+ threshold for this to be true is not precisely defined by law.
+ .
+ If such an object file uses only numerical parameters, data
+ structure layouts and accessors, and small macros and small inline
+ functions (ten lines or less in length), then the use of the object
+ file is unrestricted, regardless of whether it is legally a derivative
+ work. (Executables containing this object code plus portions of the
+ Library will still fall under Section 6.)
+ .
+ Otherwise, if the work is a derivative of the Library, you may
+ distribute the object code for the work under the terms of Section 6.
+ Any executables containing that work also fall under Section 6,
+ whether or not they are linked directly with the Library itself.
+ .
+ 6. As an exception to the Sections above, you may also combine or
+ link a "work that uses the Library" with the Library to produce a
+ work containing portions of the Library, and distribute that work
+ under terms of your choice, provided that the terms permit
+ modification of the work for the customer's own use and reverse
+ engineering for debugging such modifications.
+ .
+ You must give prominent notice with each copy of the work that the
+ Library is used in it and that the Library and its use are covered by
+ this License. You must supply a copy of this License. If the work
+ during execution displays copyright notices, you must include the
+ copyright notice for the Library among them, as well as a reference
+ directing the user to the copy of this License. Also, you must do one
+ of these things:
+ .
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+ .
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+ .
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+ .
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+ .
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+ .
+ For an executable, the required form of the "work that uses the
+ Library" must include any data and utility programs needed for
+ reproducing the executable from it. However, as a special exception,
+ the materials to be distributed need not include anything that is
+ normally distributed (in either source or binary form) with the major
+ components (compiler, kernel, and so on) of the operating system on
+ which the executable runs, unless that component itself accompanies
+ the executable.
+ .
+ It may happen that this requirement contradicts the license
+ restrictions of other proprietary libraries that do not normally
+ accompany the operating system. Such a contradiction means you cannot
+ use both them and the Library together in an executable that you
+ distribute.
+ .
+ 7. You may place library facilities that are a work based on the
+ Library side-by-side in a single library together with other library
+ facilities not covered by this License, and distribute such a combined
+ library, provided that the separate distribution of the work based on
+ the Library and of the other library facilities is otherwise
+ permitted, and provided that you do these two things:
+ .
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+ .
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+ .
+ 8. You may not copy, modify, sublicense, link with, or distribute
+ the Library except as expressly provided under this License. Any
+ attempt otherwise to copy, modify, sublicense, link with, or
+ distribute the Library is void, and will automatically terminate your
+ rights under this License. However, parties who have received copies,
+ or rights, from you under this License will not have their licenses
+ terminated so long as such parties remain in full compliance.
+ .
+ 9. You are not required to accept this License, since you have not
+ signed it. However, nothing else grants you permission to modify or
+ distribute the Library or its derivative works. These actions are
+ prohibited by law if you do not accept this License. Therefore, by
+ modifying or distributing the Library (or any work based on the
+ Library), you indicate your acceptance of this License to do so, and
+ all its terms and conditions for copying, distributing or modifying
+ the Library or works based on it.
+ .
+ 10. Each time you redistribute the Library (or any work based on the
+ Library), the recipient automatically receives a license from the
+ original licensor to copy, distribute, link with or modify the Library
+ subject to these terms and conditions. You may not impose any further
+ restrictions on the recipients' exercise of the rights granted herein.
+ You are not responsible for enforcing compliance by third parties with
+ this License.
+ .
+ 11. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent issues),
+ conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot
+ distribute so as to satisfy simultaneously your obligations under this
+ License and any other pertinent obligations, then as a consequence you
+ may not distribute the Library at all. For example, if a patent
+ license would not permit royalty-free redistribution of the Library by
+ all those who receive copies directly or indirectly through you, then
+ the only way you could satisfy both it and this License would be to
+ refrain entirely from distribution of the Library.
+ .
+ If any portion of this section is held invalid or unenforceable under any
+ particular circumstance, the balance of the section is intended to apply,
+ and the section as a whole is intended to apply in other circumstances.
+ .
+ It is not the purpose of this section to induce you to infringe any
+ patents or other property right claims or to contest validity of any
+ such claims; this section has the sole purpose of protecting the
+ integrity of the free software distribution system which is
+ implemented by public license practices. Many people have made
+ generous contributions to the wide range of software distributed
+ through that system in reliance on consistent application of that
+ system; it is up to the author/donor to decide if he or she is willing
+ to distribute software through any other system and a licensee cannot
+ impose that choice.
+ .
+ This section is intended to make thoroughly clear what is believed to
+ be a consequence of the rest of this License.
+ .
+ 12. If the distribution and/or use of the Library is restricted in
+ certain countries either by patents or by copyrighted interfaces, the
+ original copyright holder who places the Library under this License may add
+ an explicit geographical distribution limitation excluding those countries,
+ so that distribution is permitted only in or among countries not thus
+ excluded. In such case, this License incorporates the limitation as if
+ written in the body of this License.
+ .
+ 13. The Free Software Foundation may publish revised and/or new
+ versions of the Lesser General Public License from time to time.
+ Such new versions will be similar in spirit to the present version,
+ but may differ in detail to address new problems or concerns.
+ .
+ Each version is given a distinguishing version number. If the Library
+ specifies a version number of this License which applies to it and
+ "any later version", you have the option of following the terms and
+ conditions either of that version or of any later version published by
+ the Free Software Foundation. If the Library does not specify a
+ license version number, you may choose any version ever published by
+ the Free Software Foundation.
+ .
+ 14. If you wish to incorporate parts of the Library into other free
+ programs whose distribution conditions are incompatible with these,
+ write to the author to ask for permission. For software which is
+ copyrighted by the Free Software Foundation, write to the Free
+ Software Foundation; we sometimes make exceptions for this. Our
+ decision will be guided by the two goals of preserving the free status
+ of all derivatives of our free software and of promoting the sharing
+ and reuse of software generally.
+ .
+ NO WARRANTY
+ .
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+ WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+ EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+ OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+ LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+ THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+ .
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+ WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+ AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+ FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+ LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+ RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGES.
+ .
+ END OF TERMS AND CONDITIONS
+ .
+ How to Apply These Terms to Your New Libraries
+ .
+ If you develop a new library, and you want it to be of the greatest
+ possible use to the public, we recommend making it free software that
+ everyone can redistribute and change. You can do so by permitting
+ redistribution under these terms (or, alternatively, under the terms of the
+ ordinary General Public License).
+ .
+ To apply these terms, attach the following notices to the library. It is
+ safest to attach them to the start of each source file to most effectively
+ convey the exclusion of warranty; and each file should have at least the
+ "copyright" line and a pointer to where the full notice is found.
+ .
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+ .
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ .
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+ .
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ .
+ Also add information on how to contact you by electronic and paper mail.
+ .
+ You should also get your employer (if you work as a programmer) or your
+ school, if any, to sign a "copyright disclaimer" for the library, if
+ necessary. Here is a sample; alter the names:
+ .
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+ .
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+ .
+ That's all there is to it!
+
License: MPL-2.0
Mozilla Public License Version 2.0
==================================
diff --git a/DONORS.md b/DONORS.md
index c0ab24390a..da0f5bca75 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -22,7 +22,6 @@ generous deed immortalized in the next stable release of Godot Engine.
## Mini sponsors
- Andreas
Brandon Lamb
Christian Uldall Pedersen
Christopher Igoe
@@ -44,6 +43,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Stephan Lanfermann
Stoney Meyerhoeffer
Thomas Mathews
+ VilliHaukka
## Gold donors
@@ -51,11 +51,11 @@ generous deed immortalized in the next stable release of Godot Engine.
Alexander Otto
Asdf
cheese65536
- Jake Bo
+ K9Kraken
Kris Michael
Manuele Finocchiaro
+ Nathanael Beisiegel
Officine Pixel S.n.c.
- Rémi Verschelde
Zaven Muradyan
Allen Schade
@@ -64,7 +64,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Bernhard Liebl
Catalin Moldovan
DeepSquid
- Duane Johnson
+ Fidget Sinner
Florian Breisch
Gary Oberbrunner
Johannes Wuensch
@@ -72,6 +72,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Joshua Lesperance
Libre-Dépanne
Matthew Bennett
+ Olafur Gislason
Paul LaMotte
Ranoller
Svenne Krap
@@ -82,20 +83,21 @@ generous deed immortalized in the next stable release of Godot Engine.
Chris Serino
Conrad Curry
Craig Smith
+ Daniel Egger
David Churchill
Dean Harmon
Dexter Miguel
- Garrett Dockins
Guilherme Felipe de C. G. da Silva
John
Justo Delgado Baudí
+ KTL
Laurence Bannister
Rami
Robert Willes
Robin Arys
+ Ronnie Ashlock
Rufus Xavier Sarsaparilla
ScottMakesGames
- Testus Maximus
Thomas Bjarnelöf
William Connell
Wojciech Chojnacki
@@ -109,21 +111,20 @@ generous deed immortalized in the next stable release of Godot Engine.
Chris Petrich
Chris Wilson
Cody Parker
+ Corey Auger
D
Daniel Eliasinski
E.G.
Eric Monson
flesk
- François Cantin
G Barnes
GGGames.org
Giovanni Solimeno
Hasen Judy
Heath Hayes
+ Jay Horton
Jeppe Zapp
- Jeremi Biernacki
joe513
- John O'Mahoney
Jordan M Lucas
Juraj Móza
Justin Arnold
@@ -137,10 +138,11 @@ generous deed immortalized in the next stable release of Godot Engine.
Patrick Schnorbus
Pete Goodwin
Phyronnaz
- SeokHui Lee
- Simon De Greve
+ Ruben Soares Luis
Sofox
+ Stoned Xander
Ted
+ Tim Dalporto
Trent McPheron
Vladimir
@@ -158,6 +160,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Arthur S. Muszynski
Aubrey Falconer
Avencherus
+ Bailey
Bastian Böhm
Benedikt
Benjamin Beshara
@@ -172,7 +175,8 @@ generous deed immortalized in the next stable release of Godot Engine.
Christian Winter
Christopher Schmitt
Collin Shooltz
- Daniel Egger
+ Daniel Delgado Corona
+ Daniel Johnson
Daniel Kaplan
DanielMaximiano
Daniel Mircea
@@ -180,18 +184,18 @@ generous deed immortalized in the next stable release of Godot Engine.
David Cravens
David May
Dominik Wetzel
+ Duy Kevin Nguyen
Edward Herbert
Eric Martini
Fabian Becker
fengjiongmax
Francesco Lisi
- Frédéric Alix
G3Dev sàrl
- Geequlim
Gerrit Großkopf
Gerrit Procee
Gilberto K. Otubo
Guldoman
+ Gumichan01
Heribert Hirth
hubert jenkins
Hunter Jones
@@ -211,7 +215,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Josh 'Cheeseness' Bush
Juan Negrier
Judd
- JuDelCo
Julian Murgia
Justin Luk
KC Chan
@@ -221,26 +224,26 @@ generous deed immortalized in the next stable release of Godot Engine.
Krzysztof Jankowski
Lars pfeffer
Linus Lind Lundgren
+ Luis Moraes
Macil
magodev
Martin Eigel
+ Martins Odabi
Matthew Fitzpatrick
- Matthias Hölzl
Max R.R. Collada
- memoryruins
+ Maxwell
mhilbrunner
Michael Dürwald
Michael Gringauz
Michael Labbe
Mikael Olsson
MoM
- monokrome
Moritz Laass
+ Natrim
nee
Neil Blakey-Milner
Nick Pavlica
Niclas Eriksen
- Nicolás Montaña
Nicolas SAN AGUSTIN
Niko Leopold
nivardus
@@ -257,6 +260,8 @@ 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
@@ -264,12 +269,11 @@ generous deed immortalized in the next stable release of Godot Engine.
Ryan Whited
Samuel El-Borai
Sasori Olkof
- Scott D. Yelich
Sootstone
+ Stefan Butucea
Theo Cranmore
Thibault Barbaroux
Thomas Bell
- Thomas Herzog & Xananax
Thomas Kurz
Tomasz Wacławek
Tom Larrow
diff --git a/SConstruct b/SConstruct
index 63105bfa84..cb1e9fd567 100644
--- a/SConstruct
+++ b/SConstruct
@@ -2,7 +2,6 @@
EnsureSConsVersion(0, 98, 1)
-
import string
import os
import os.path
@@ -10,9 +9,6 @@ import glob
import sys
import methods
-# moved below to compensate with module version string
-# methods.update_version()
-
# scan possible build platforms
platform_list = [] # list of platforms
@@ -53,9 +49,6 @@ for x in glob.glob("platform/*"):
module_list = methods.detect_modules()
-
-# print "Detected Platforms: "+str(platform_list)
-
methods.save_active_platforms(active_platforms, active_platform_ids)
custom_tools = ['default']
@@ -81,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 = []
@@ -101,12 +95,12 @@ env_base.Decider('MD5-timestamp')
# http://scons.org/doc/production/HTML/scons-user/ch06s04.html
env_base.SetOption('implicit_cache', 1)
-
env_base.__class__.android_add_maven_repository = methods.android_add_maven_repository
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
@@ -126,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
@@ -145,52 +140,53 @@ if profile:
opts = Variables(customs, ARGUMENTS)
# Target build options
-opts.Add('arch', "Platform-dependent architecture (arm/arm64/x86/x64/mips/etc)", '')
+opts.Add('arch', "Platform-dependent architecture (arm/arm64/x86/x64/mips/...)", '')
opts.Add(EnumVariable('bits', "Target platform bits", 'default', ('default', '32', '64')))
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(BoolVariable('tools', "Build the tools a.k.a. the Godot editor", True))
-opts.Add(BoolVariable('use_lto', 'Use linking time optimization', False))
+opts.Add(BoolVariable('tools', "Build the tools (a.k.a. the Godot editor)", True))
+opts.Add(BoolVariable('use_lto', 'Use link-time optimization', False))
# Components
opts.Add(BoolVariable('deprecated', "Enable deprecated features", True))
-opts.Add(BoolVariable('gdscript', "Build GDSCript support", True))
-opts.Add(BoolVariable('minizip', "Build minizip archive support", True))
-opts.Add(BoolVariable('xaudio2', "XAudio2 audio driver", False))
-opts.Add(BoolVariable('xml', "XML format support for resources", True))
+opts.Add(BoolVariable('gdscript', "Enable GDScript support", True))
+opts.Add(BoolVariable('minizip', "Enable ZIP archive support using minizip", True))
+opts.Add(BoolVariable('xaudio2', "Enable the XAudio2 audio driver", False))
+opts.Add(BoolVariable('xml', "Enable XML format support for resources", True))
# Advanced options
-opts.Add(BoolVariable('disable_3d', "Disable 3D nodes for smaller executable", False))
-opts.Add(BoolVariable('disable_advanced_gui', "Disable advanced 3D gui nodes and behaviors", False))
+opts.Add(BoolVariable('disable_3d', "Disable 3D nodes for a smaller executable", False))
+opts.Add(BoolVariable('disable_advanced_gui', "Disable advanced 3D GUI nodes and behaviors", False))
opts.Add('extra_suffix', "Custom extra suffix added to the base filename of all generated binary files", '')
-opts.Add('unix_global_settings_path', "UNIX-specific path to system-wide settings. Currently only used for templates", '')
opts.Add(BoolVariable('verbose', "Enable verbose output for the compilation", False))
-opts.Add(BoolVariable('vsproj', "Generate Visual Studio Project", False))
+opts.Add(BoolVariable('vsproj', "Generate a Visual Studio solution", False))
opts.Add(EnumVariable('warnings', "Set the level of warnings emitted during compilation", 'no', ('extra', 'all', 'moderate', 'no')))
-opts.Add(BoolVariable('progress', "Show a progress indicator during build", True))
+opts.Add(BoolVariable('progress', "Show a progress indicator during compilation", True))
opts.Add(BoolVariable('dev', "If yes, alias for verbose=yes warnings=all", False))
-opts.Add(EnumVariable('macports_clang', "Build using clang from MacPorts", 'no', ('no', '5.0', 'devel')))
+opts.Add(EnumVariable('macports_clang', "Build using Clang from MacPorts", 'no', ('no', '5.0', 'devel')))
+opts.Add(BoolVariable('no_editor_splash', "Don't use the custom splash screen for the editor", False))
# Thirdparty libraries
-opts.Add(BoolVariable('builtin_bullet', "Use the builtin bullet library", True))
-opts.Add(BoolVariable('builtin_enet', "Use the builtin enet library", True))
-opts.Add(BoolVariable('builtin_freetype', "Use the builtin freetype library", True))
-opts.Add(BoolVariable('builtin_libogg', "Use the builtin libogg library", True))
-opts.Add(BoolVariable('builtin_libpng', "Use the builtin libpng library", True))
-opts.Add(BoolVariable('builtin_libtheora', "Use the builtin libtheora library", True))
-opts.Add(BoolVariable('builtin_libvorbis', "Use the builtin libvorbis library", True))
-opts.Add(BoolVariable('builtin_libvpx', "Use the builtin libvpx library", True))
-opts.Add(BoolVariable('builtin_libwebp', "Use the builtin libwebp library", True))
-opts.Add(BoolVariable('builtin_mbedtls', "Use the builtin mbedTLS library", True))
-opts.Add(BoolVariable('builtin_opus', "Use the builtin opus library", True))
-opts.Add(BoolVariable('builtin_pcre2', "Use the builtin pcre2 library)", True))
-opts.Add(BoolVariable('builtin_recast', "Use the builtin recast library", True))
-opts.Add(BoolVariable('builtin_squish', "Use the builtin squish library", True))
-opts.Add(BoolVariable('builtin_thekla_atlas', "Use the builtin thekla_altas library", True))
-opts.Add(BoolVariable('builtin_zlib', "Use the builtin zlib library", True))
-opts.Add(BoolVariable('builtin_zstd', "Use the builtin zstd library", True))
-opts.Add(BoolVariable('no_editor_splash', "Don't use the custom splash screen for the editor", False))
+opts.Add(BoolVariable('builtin_bullet', "Use the built-in Bullet library", True))
+opts.Add(BoolVariable('builtin_enet', "Use the built-in ENet library", True))
+opts.Add(BoolVariable('builtin_freetype', "Use the built-in FreeType library", True))
+opts.Add(BoolVariable('builtin_libogg', "Use the built-in libogg library", True))
+opts.Add(BoolVariable('builtin_libpng', "Use the built-in libpng library", True))
+opts.Add(BoolVariable('builtin_libtheora', "Use the built-in libtheora library", True))
+opts.Add(BoolVariable('builtin_libvorbis', "Use the built-in libvorbis library", True))
+opts.Add(BoolVariable('builtin_libvpx', "Use the built-in libvpx library", True))
+opts.Add(BoolVariable('builtin_libwebp', "Use the built-in libwebp library", True))
+opts.Add(BoolVariable('builtin_libwebsockets', "Use the built-in libwebsockets library", True))
+opts.Add(BoolVariable('builtin_mbedtls', "Use the built-in mbedTLS library", True))
+opts.Add(BoolVariable('builtin_miniupnpc', "Use the built-in miniupnpc library", True))
+opts.Add(BoolVariable('builtin_opus', "Use the built-in Opus library", True))
+opts.Add(BoolVariable('builtin_pcre2', "Use the built-in PCRE2 library)", True))
+opts.Add(BoolVariable('builtin_recast', "Use the built-in Recast library", True))
+opts.Add(BoolVariable('builtin_squish', "Use the built-in squish library", True))
+opts.Add(BoolVariable('builtin_thekla_atlas', "Use the built-in thekla_altas library", True))
+opts.Add(BoolVariable('builtin_zlib', "Use the built-in zlib library", True))
+opts.Add(BoolVariable('builtin_zstd', "Use the built-in Zstd library", True))
# Compilation environment setup
opts.Add("CXX", "C++ compiler")
@@ -201,7 +197,6 @@ opts.Add("CXXFLAGS", "Custom flags for the C++ compiler")
opts.Add("CFLAGS", "Custom flags for the C compiler")
opts.Add("LINKFLAGS", "Custom flags for the linker")
-
# add platform specific options
for k in platform_opts.keys():
@@ -232,14 +227,6 @@ env_base.Append(CPPPATH=['#core', '#core/math', '#editor', '#drivers', '#'])
env_base.platform_exporters = platform_exporters
env_base.platform_apis = platform_apis
-"""
-sys.path.append("./platform/"+env_base["platform"])
-import detect
-detect.configure(env_base)
-sys.path.remove("./platform/"+env_base["platform"])
-sys.modules.pop('detect')
-"""
-
if (env_base['target'] == 'debug'):
env_base.Append(CPPDEFINES=['DEBUG_MEMORY_ALLOC', 'SCI_NAMESPACE'])
@@ -251,7 +238,6 @@ if not env_base['deprecated']:
env_base.platforms = {}
-
selected_platform = ""
if env_base['platform'] != "":
@@ -260,7 +246,6 @@ elif env_base['p'] != "":
selected_platform = env_base['p']
env_base["platform"] = selected_platform
-
if selected_platform in platform_list:
sys.path.append("./platform/" + selected_platform)
@@ -352,7 +337,6 @@ if selected_platform in platform_list:
else: # 'no'
env.Append(CCFLAGS=['-w'])
env.Append(CCFLAGS=['-Werror=return-type'])
- #env['platform_libsuffix'] = env['LIBSUFFIX']
suffix = "." + selected_platform
@@ -396,7 +380,17 @@ if selected_platform in platform_list:
sys.path.append(tmppath)
env.current_module = x
import config
- if (config.can_build(selected_platform)):
+ # can_build changed number of arguments between 3.0 (1) and 3.1 (2),
+ # so try both to preserve compatibility for 3.0 modules
+ can_build = False
+ try:
+ can_build = config.can_build(env, selected_platform)
+ except TypeError:
+ print("Warning: module '%s' uses a deprecated `can_build` "
+ "signature in its config.py file, it should be "
+ "`can_build(env, platform)`." % x)
+ can_build = config.can_build(selected_platform)
+ if (can_build):
config.configure(env)
env.module_list.append(x)
try:
@@ -419,10 +413,6 @@ if selected_platform in platform_list:
if (env.use_ptrcall):
env.Append(CPPDEFINES=['PTRCALL_ENABLED'])
-
- # to test 64 bits compiltion
- # env.Append(CPPFLAGS=['-m64'])
-
if env['tools']:
env.Append(CPPDEFINES=['TOOLS_ENABLED'])
if env['disable_3d']:
@@ -431,10 +421,8 @@ if selected_platform in platform_list:
env.Append(CPPDEFINES=['GDSCRIPT_ENABLED'])
if env['disable_advanced_gui']:
env.Append(CPPDEFINES=['ADVANCED_GUI_DISABLED'])
-
if env['minizip']:
env.Append(CPPDEFINES=['MINIZIP_ENABLED'])
-
if env['xml']:
env.Append(CPPDEFINES=['XML_ENABLED'])
@@ -482,11 +470,10 @@ if selected_platform in platform_list:
else:
print("No valid target platform selected.")
- print("The following were detected:")
+ print("The following platforms were detected:")
for x in platform_list:
print("\t" + x)
- print("\nPlease run scons again with argument: platform=<string>")
-
+ print("\nPlease run SCons again with the argument: platform=<string>")
# The following only makes sense when the env is defined, and assumes it is
if 'env' in locals():
diff --git a/compat.py b/compat.py
index 32c685f5b8..de99eef9c2 100644
--- a/compat.py
+++ b/compat.py
@@ -16,6 +16,8 @@ if sys.version_info < (3,):
return x
def iteritems(d):
return d.iteritems()
+ def itervalues(d):
+ return d.itervalues()
def escape_string(s):
if isinstance(s, unicode):
s = s.encode('ascii')
@@ -44,6 +46,8 @@ else:
return codecs.utf_8_decode(x)[0]
def iteritems(d):
return iter(d.items())
+ def itervalues(d):
+ return iter(d.values())
def charcode_to_c_escapes(c):
rev_result = []
while c >= 256:
diff --git a/core/SCsub b/core/SCsub
index 383aaf0e12..c508ecc37e 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -2,6 +2,8 @@
Import('env')
+import methods
+
env.core_sources = []
@@ -91,8 +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.CommandNoCache('#core/authors.gen.h', "../AUTHORS.md", methods.make_authors_header)
+
+# Donors
+env.Depends('#core/donors.gen.h', "../DONORS.md")
+env.CommandNoCache('#core/donors.gen.h', "../DONORS.md", methods.make_donors_header)
+# License
+env.Depends('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"])
+env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"], methods.make_license_header)
# Chain load SCsubs
SConscript('os/SCsub')
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index 9c484f313e..7a14e85f20 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -336,10 +336,23 @@ void _OS::set_borderless_window(bool p_borderless) {
OS::get_singleton()->set_borderless_window(p_borderless);
}
+bool _OS::get_window_per_pixel_transparency_enabled() const {
+ return OS::get_singleton()->get_window_per_pixel_transparency_enabled();
+}
+
+void _OS::set_window_per_pixel_transparency_enabled(bool p_enabled) {
+ OS::get_singleton()->set_window_per_pixel_transparency_enabled(p_enabled);
+}
+
bool _OS::get_borderless_window() const {
return OS::get_singleton()->get_borderless_window();
}
+void _OS::set_ime_active(const bool p_active) {
+
+ return OS::get_singleton()->set_ime_active(p_active);
+}
+
void _OS::set_ime_position(const Point2 &p_pos) {
return OS::get_singleton()->set_ime_position(p_pos);
@@ -391,7 +404,7 @@ Error _OS::shell_open(String p_uri) {
int _OS::execute(const String &p_path, const Vector<String> &p_arguments, bool p_blocking, Array p_output) {
- OS::ProcessID pid;
+ OS::ProcessID pid = -2;
List<String> args;
for (int i = 0; i < p_arguments.size(); i++)
args.push_back(p_arguments[i]);
@@ -404,6 +417,7 @@ int _OS::execute(const String &p_path, const Vector<String> &p_arguments, bool p
else
return pid;
}
+
Error _OS::kill(int p_pid) {
return OS::get_singleton()->kill(p_pid);
@@ -785,6 +799,11 @@ uint32_t _OS::get_ticks_msec() const {
return OS::get_singleton()->get_ticks_msec();
}
+uint64_t _OS::get_ticks_usec() const {
+
+ return OS::get_singleton()->get_ticks_usec();
+}
+
uint32_t _OS::get_splash_tick_msec() const {
return OS::get_singleton()->get_splash_tick_msec();
@@ -1068,6 +1087,9 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_borderless_window", "borderless"), &_OS::set_borderless_window);
ClassDB::bind_method(D_METHOD("get_borderless_window"), &_OS::get_borderless_window);
+ ClassDB::bind_method(D_METHOD("get_window_per_pixel_transparency_enabled"), &_OS::get_window_per_pixel_transparency_enabled);
+ ClassDB::bind_method(D_METHOD("set_window_per_pixel_transparency_enabled", "enabled"), &_OS::set_window_per_pixel_transparency_enabled);
+
ClassDB::bind_method(D_METHOD("set_ime_position", "position"), &_OS::set_ime_position);
ClassDB::bind_method(D_METHOD("set_screen_orientation", "orientation"), &_OS::set_screen_orientation);
@@ -1114,6 +1136,7 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &_OS::delay_usec);
ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &_OS::delay_msec);
ClassDB::bind_method(D_METHOD("get_ticks_msec"), &_OS::get_ticks_msec);
+ ClassDB::bind_method(D_METHOD("get_ticks_usec"), &_OS::get_ticks_usec);
ClassDB::bind_method(D_METHOD("get_splash_tick_msec"), &_OS::get_splash_tick_msec);
ClassDB::bind_method(D_METHOD("get_locale"), &_OS::get_locale);
ClassDB::bind_method(D_METHOD("get_latin_keyboard_variant"), &_OS::get_latin_keyboard_variant);
@@ -1185,6 +1208,7 @@ void _OS::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor"), "set_screen_orientation", "get_screen_orientation");
ADD_GROUP("Window", "window_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_borderless"), "set_borderless_window", "get_borderless_window");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_per_pixel_transparency_enabled"), "set_window_per_pixel_transparency_enabled", "get_window_per_pixel_transparency_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_fullscreen"), "set_window_fullscreen", "is_window_fullscreen");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_maximized"), "set_window_maximized", "is_window_maximized");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_minimized"), "set_window_minimized", "is_window_minimized");
@@ -2698,6 +2722,26 @@ Dictionary _Engine::get_version_info() const {
return Engine::get_singleton()->get_version_info();
}
+Dictionary _Engine::get_author_info() const {
+ return Engine::get_singleton()->get_author_info();
+}
+
+Array _Engine::get_copyright_info() const {
+ return Engine::get_singleton()->get_copyright_info();
+}
+
+Dictionary _Engine::get_donor_info() const {
+ return Engine::get_singleton()->get_donor_info();
+}
+
+Dictionary _Engine::get_license_info() const {
+ return Engine::get_singleton()->get_license_info();
+}
+
+String _Engine::get_license_text() const {
+ return Engine::get_singleton()->get_license_text();
+}
+
bool _Engine::is_in_physics_frame() const {
return Engine::get_singleton()->is_in_physics_frame();
}
@@ -2740,6 +2784,11 @@ void _Engine::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_main_loop"), &_Engine::get_main_loop);
ClassDB::bind_method(D_METHOD("get_version_info"), &_Engine::get_version_info);
+ ClassDB::bind_method(D_METHOD("get_author_info"), &_Engine::get_author_info);
+ ClassDB::bind_method(D_METHOD("get_copyright_info"), &_Engine::get_copyright_info);
+ ClassDB::bind_method(D_METHOD("get_donor_info"), &_Engine::get_donor_info);
+ ClassDB::bind_method(D_METHOD("get_license_info"), &_Engine::get_license_info);
+ ClassDB::bind_method(D_METHOD("get_license_text"), &_Engine::get_license_text);
ClassDB::bind_method(D_METHOD("is_in_physics_frame"), &_Engine::is_in_physics_frame);
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 625cac25a0..48b7b74005 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -180,6 +180,10 @@ public:
virtual void set_borderless_window(bool p_borderless);
virtual bool get_borderless_window() const;
+ virtual bool get_window_per_pixel_transparency_enabled() const;
+ virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
+
+ virtual void set_ime_active(const bool p_active);
virtual void set_ime_position(const Point2 &p_pos);
Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track);
@@ -273,6 +277,7 @@ public:
void delay_usec(uint32_t p_usec) const;
void delay_msec(uint32_t p_msec) const;
uint32_t get_ticks_msec() const;
+ uint64_t get_ticks_usec() const;
uint32_t get_splash_tick_msec() const;
bool can_use_threads() const;
@@ -686,6 +691,11 @@ public:
MainLoop *get_main_loop() const;
Dictionary get_version_info() const;
+ Dictionary get_author_info() const;
+ Array get_copyright_info() const;
+ Dictionary get_donor_info() const;
+ Dictionary get_license_info() const;
+ String get_license_text() const;
bool is_in_physics_frame() const;
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/color.cpp b/core/color.cpp
index b2f5889166..88e57ec6e2 100644
--- a/core/color.cpp
+++ b/core/color.cpp
@@ -37,38 +37,38 @@
uint32_t Color::to_argb32() const {
- uint32_t c = (uint8_t)(a * 255);
+ uint32_t c = (uint8_t)Math::round(a * 255);
c <<= 8;
- c |= (uint8_t)(r * 255);
+ c |= (uint8_t)Math::round(r * 255);
c <<= 8;
- c |= (uint8_t)(g * 255);
+ c |= (uint8_t)Math::round(g * 255);
c <<= 8;
- c |= (uint8_t)(b * 255);
+ c |= (uint8_t)Math::round(b * 255);
return c;
}
uint32_t Color::to_abgr32() const {
- uint32_t c = (uint8_t)(a * 255);
+ uint32_t c = (uint8_t)Math::round(a * 255);
c <<= 8;
- c |= (uint8_t)(b * 255);
+ c |= (uint8_t)Math::round(b * 255);
c <<= 8;
- c |= (uint8_t)(g * 255);
+ c |= (uint8_t)Math::round(g * 255);
c <<= 8;
- c |= (uint8_t)(r * 255);
+ c |= (uint8_t)Math::round(r * 255);
return c;
}
uint32_t Color::to_rgba32() const {
- uint32_t c = (uint8_t)(r * 255);
+ uint32_t c = (uint8_t)Math::round(r * 255);
c <<= 8;
- c |= (uint8_t)(g * 255);
+ c |= (uint8_t)Math::round(g * 255);
c <<= 8;
- c |= (uint8_t)(b * 255);
+ c |= (uint8_t)Math::round(b * 255);
c <<= 8;
- c |= (uint8_t)(a * 255);
+ c |= (uint8_t)Math::round(a * 255);
return c;
}
@@ -368,7 +368,7 @@ Color Color::named(const String &p_name) {
String _to_hex(float p_val) {
- int v = p_val * 255;
+ int v = Math::round(p_val * 255);
v = CLAMP(v, 0, 255);
String ret;
diff --git a/core/color_names.inc b/core/color_names.inc
index b05684acc6..3ae42648d0 100644
--- a/core/color_names.inc
+++ b/core/color_names.inc
@@ -3,7 +3,7 @@
static Map<String, Color> _named_colors;
static void _populate_named_colors() {
- if(!_named_colors.empty()) return;
+ if (!_named_colors.empty()) return;
_named_colors.insert("aliceblue", Color(0.94, 0.97, 1.00));
_named_colors.insert("antiquewhite", Color(0.98, 0.92, 0.84));
_named_colors.insert("aqua", Color(0.00, 1.00, 1.00));
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 ba0de95861..d68411a572 100644
--- a/core/dictionary.cpp
+++ b/core/dictionary.cpp
@@ -50,6 +50,32 @@ void Dictionary::get_key_list(List<Variant> *p_keys) const {
}
}
+Variant Dictionary::get_key_at_index(int p_index) const {
+
+ int index = 0;
+ for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
+ if (index == p_index) {
+ return E.key();
+ }
+ index++;
+ }
+
+ return Variant();
+}
+
+Variant Dictionary::get_value_at_index(int p_index) const {
+
+ int index = 0;
+ for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
+ if (index == p_index) {
+ return E.value();
+ }
+ index++;
+ }
+
+ return Variant();
+}
+
Variant &Dictionary::operator[](const Variant &p_key) {
return _p->variant_map[p_key];
diff --git a/core/dictionary.h b/core/dictionary.h
index 9eef265d5b..84a5cafe1d 100644
--- a/core/dictionary.h
+++ b/core/dictionary.h
@@ -47,6 +47,8 @@ class Dictionary {
public:
void get_key_list(List<Variant> *p_keys) const;
+ Variant get_key_at_index(int p_index) const;
+ Variant get_value_at_index(int p_index) const;
Variant &operator[](const Variant &p_key);
const Variant &operator[](const Variant &p_key) const;
diff --git a/core/engine.cpp b/core/engine.cpp
index b2c34a853c..7c8024b946 100644
--- a/core/engine.cpp
+++ b/core/engine.cpp
@@ -30,6 +30,9 @@
#include "engine.h"
+#include "authors.gen.h"
+#include "donors.gen.h"
+#include "license.gen.h"
#include "version.h"
#include "version_hash.gen.h"
@@ -111,6 +114,78 @@ Dictionary Engine::get_version_info() const {
return dict;
}
+static Array array_from_info(const char *const *info_list) {
+ Array arr;
+ for (int i = 0; info_list[i] != NULL; i++) {
+ arr.push_back(info_list[i]);
+ }
+ return arr;
+}
+
+static Array array_from_info_count(const char *const *info_list, int info_count) {
+ Array arr;
+ for (int i = 0; i < info_count; i++) {
+ arr.push_back(info_list[i]);
+ }
+ return arr;
+}
+
+Dictionary Engine::get_author_info() const {
+ Dictionary dict;
+
+ dict["lead_developers"] = array_from_info(AUTHORS_LEAD_DEVELOPERS);
+ dict["project_managers"] = array_from_info(AUTHORS_PROJECT_MANAGERS);
+ dict["founders"] = array_from_info(AUTHORS_FOUNDERS);
+ dict["developers"] = array_from_info(AUTHORS_DEVELOPERS);
+
+ return dict;
+}
+
+Array Engine::get_copyright_info() const {
+ Array components;
+ for (int component_index = 0; component_index < COPYRIGHT_INFO_COUNT; component_index++) {
+ const ComponentCopyright &cp_info = COPYRIGHT_INFO[component_index];
+ Dictionary component_dict;
+ component_dict["name"] = cp_info.name;
+ Array parts;
+ for (int i = 0; i < cp_info.part_count; i++) {
+ const ComponentCopyrightPart &cp_part = cp_info.parts[i];
+ Dictionary part_dict;
+ part_dict["files"] = array_from_info_count(cp_part.files, cp_part.file_count);
+ part_dict["copyright"] = array_from_info_count(cp_part.copyright_statements, cp_part.copyright_count);
+ part_dict["license"] = cp_part.license;
+ parts.push_back(part_dict);
+ }
+ component_dict["parts"] = parts;
+
+ components.push_back(component_dict);
+ }
+ return components;
+}
+
+Dictionary Engine::get_donor_info() const {
+ Dictionary donors;
+ donors["platinum_sponsors"] = array_from_info(DONORS_SPONSOR_PLAT);
+ donors["gold_sponsors"] = array_from_info(DONORS_SPONSOR_GOLD);
+ donors["mini_sponsors"] = array_from_info(DONORS_SPONSOR_MINI);
+ donors["gold_donors"] = array_from_info(DONORS_GOLD);
+ donors["silver_donors"] = array_from_info(DONORS_SILVER);
+ donors["bronze_donors"] = array_from_info(DONORS_BRONZE);
+ return donors;
+}
+
+Dictionary Engine::get_license_info() const {
+ Dictionary licenses;
+ for (int i = 0; i < LICENSE_COUNT; i++) {
+ licenses[LICENSE_NAMES[i]] = LICENSE_BODIES[i];
+ }
+ return licenses;
+}
+
+String Engine::get_license_text() const {
+ return String(GODOT_LICENSE_TEXT);
+}
+
void Engine::add_singleton(const Singleton &p_singleton) {
singletons.push_back(p_singleton);
diff --git a/core/engine.h b/core/engine.h
index 665992699a..031ba29cd6 100644
--- a/core/engine.h
+++ b/core/engine.h
@@ -118,6 +118,11 @@ public:
#endif
Dictionary get_version_info() const;
+ Dictionary get_author_info() const;
+ Array get_copyright_info() const;
+ Dictionary get_donor_info() const;
+ Dictionary get_license_info() const;
+ String get_license_text() const;
Engine();
};
diff --git a/core/error_macros.h b/core/error_macros.h
index 168b2e06fe..3587e01d54 100644
--- a/core/error_macros.h
+++ b/core/error_macros.h
@@ -311,14 +311,14 @@ extern bool _err_error_exists;
_err_error_exists = false; \
}
-#define WARN_DEPRECATED \
- { \
- static bool warning_shown=false;\
- if (!warning_shown) {\
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__,"This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \
- _err_error_exists = false; \
- warning_shown=true;\
- }\
+#define WARN_DEPRECATED \
+ { \
+ static bool warning_shown = false; \
+ if (!warning_shown) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \
+ _err_error_exists = false; \
+ warning_shown = true; \
+ } \
}
#endif
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 51fbe75dec..b0bed80a6f 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -1076,6 +1076,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)) {
@@ -2301,6 +2331,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha);
ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear);
ClassDB::bind_method(D_METHOD("normalmap_to_xy"), &Image::normalmap_to_xy);
+ ClassDB::bind_method(D_METHOD("rgbe_to_srgb"), &Image::rgbe_to_srgb);
ClassDB::bind_method(D_METHOD("bumpmap_to_normalmap", "bump_scale"), &Image::bumpmap_to_normalmap, DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("blit_rect", "src", "src_rect", "dst"), &Image::blit_rect);
@@ -2412,6 +2443,37 @@ void Image::normalmap_to_xy() {
convert(Image::FORMAT_LA8);
}
+Ref<Image> Image::rgbe_to_srgb() {
+
+ if (data.size() == 0)
+ return Ref<Image>();
+
+ ERR_FAIL_COND_V(format != FORMAT_RGBE9995, Ref<Image>());
+
+ Ref<Image> new_image;
+ new_image.instance();
+ new_image->create(width, height, 0, Image::FORMAT_RGB8);
+
+ lock();
+
+ new_image->lock();
+
+ for (int row = 0; row < height; row++) {
+ for (int col = 0; col < width; col++) {
+ new_image->set_pixel(col, row, get_pixel(col, row).to_srgb());
+ }
+ }
+
+ unlock();
+ new_image->unlock();
+
+ if (has_mipmaps()) {
+ new_image->generate_mipmaps();
+ }
+
+ return new_image;
+}
+
void Image::bumpmap_to_normalmap(float bump_scale) {
ERR_FAIL_COND(!_can_modify(format));
convert(Image::FORMAT_RF);
diff --git a/core/image.h b/core/image.h
index 80a0c339dd..43516e2c0b 100644
--- a/core/image.h
+++ b/core/image.h
@@ -220,6 +220,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
@@ -284,6 +285,7 @@ public:
void premultiply_alpha();
void srgb_to_linear();
void normalmap_to_xy();
+ Ref<Image> rgbe_to_srgb();
void bumpmap_to_normalmap(float bump_scale = 1.0);
void blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest);
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 9e301ccac5..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;
@@ -248,7 +277,9 @@ void HTTPClient::close() {
body_size = 0;
body_left = 0;
chunk_left = 0;
+ read_until_eof = false;
response_num = 0;
+ handshaking = false;
}
Error HTTPClient::poll() {
@@ -297,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:
@@ -352,10 +407,17 @@ Error HTTPClient::poll() {
chunked = false;
body_left = 0;
chunk_left = 0;
+ read_until_eof = false;
response_str.clear();
response_headers.clear();
response_num = RESPONSE_OK;
+ // Per the HTTP 1.1 spec, keep-alive is the default, but in practice
+ // it's safe to assume it only if the explicit header is found, allowing
+ // to handle body-up-to-EOF responses on naive servers; that's what Curl
+ // and browsers do
+ bool keep_alive = false;
+
for (int i = 0; i < responses.size(); i++) {
String header = responses[i].strip_edges();
@@ -365,13 +427,14 @@ Error HTTPClient::poll() {
if (s.begins_with("content-length:")) {
body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int();
body_left = body_size;
- }
- if (s.begins_with("transfer-encoding:")) {
+ } else if (s.begins_with("transfer-encoding:")) {
String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges();
if (encoding == "chunked") {
chunked = true;
}
+ } else if (s.begins_with("connection: keep-alive")) {
+ keep_alive = true;
}
if (i == 0 && responses[i].begins_with("HTTP")) {
@@ -384,11 +447,16 @@ Error HTTPClient::poll() {
}
}
- if (body_size == 0 && !chunked) {
+ if (body_size || chunked) {
- status = STATUS_CONNECTED; // Ready for new requests
- } else {
status = STATUS_BODY;
+ } else if (!keep_alive) {
+
+ read_until_eof = true;
+ status = STATUS_BODY;
+ } else {
+
+ status = STATUS_CONNECTED;
}
return OK;
}
@@ -515,34 +583,53 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
} else {
- int to_read = MIN(body_left, read_chunk_size);
+ int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size;
PoolByteArray ret;
ret.resize(to_read);
int _offset = 0;
- while (to_read > 0) {
+ while (read_until_eof || to_read > 0) {
int rec = 0;
{
PoolByteArray::Write w = ret.write();
err = _get_http_data(w.ptr() + _offset, to_read, rec);
}
- if (rec > 0) {
- body_left -= rec;
- to_read -= rec;
- _offset += rec;
- } else {
+ if (rec < 0) {
if (to_read > 0) // Ended up reading less
ret.resize(_offset);
break;
+ } else {
+ _offset += rec;
+ if (!read_until_eof) {
+ body_left -= rec;
+ to_read -= rec;
+ } else {
+ if (rec < to_read) {
+ ret.resize(_offset);
+ err = ERR_FILE_EOF;
+ break;
+ }
+ ret.resize(_offset + to_read);
+ }
}
}
- if (body_left == 0) {
- status = STATUS_CONNECTED;
+ if (!read_until_eof) {
+ if (body_left == 0) {
+ status = STATUS_CONNECTED;
+ }
+ return ret;
+ } else {
+ if (err == ERR_FILE_EOF) {
+ err = OK; // EOF is expected here
+ close();
+ return ret;
+ }
}
- return ret;
}
if (err != OK) {
+
close();
+
if (err == ERR_FILE_EOF) {
status = STATUS_DISCONNECTED; // Server disconnected
@@ -602,10 +689,12 @@ HTTPClient::HTTPClient() {
body_size = 0;
chunked = false;
body_left = 0;
+ read_until_eof = false;
chunk_left = 0;
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 839012e701..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;
@@ -173,6 +174,7 @@ private:
int chunk_left;
int body_size;
int body_left;
+ bool read_until_eof;
Ref<StreamPeerTCP> tcp_connection;
Ref<StreamPeer> connection;
diff --git a/core/io/logger.cpp b/core/io/logger.cpp
index 983b829d8d..786bec461b 100644
--- a/core/io/logger.cpp
+++ b/core/io/logger.cpp
@@ -112,7 +112,7 @@ void RotatedFileLogger::clear_old_backups() {
int max_backups = max_files - 1; // -1 for the current file
String basename = base_path.get_file().get_basename();
- String extension = "." + base_path.get_extension();
+ String extension = base_path.get_extension();
DirAccess *da = DirAccess::open(base_path.get_base_dir());
if (!da) {
@@ -123,7 +123,7 @@ void RotatedFileLogger::clear_old_backups() {
String f = da->get_next();
Set<String> backups;
while (f != String()) {
- if (!da->current_is_dir() && f.begins_with(basename) && f.ends_with(extension) && f != base_path.get_file()) {
+ if (!da->current_is_dir() && f.begins_with(basename) && f.get_extension() == extension && f != base_path.get_file()) {
backups.insert(f);
}
f = da->get_next();
@@ -152,7 +152,10 @@ void RotatedFileLogger::rotate_file() {
OS::Time time = OS::get_singleton()->get_time();
sprintf(timestamp, "-%04d-%02d-%02d-%02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.min, time.sec);
- String backup_name = base_path.get_basename() + timestamp + "." + base_path.get_extension();
+ String backup_name = base_path.get_basename() + timestamp;
+ if (base_path.get_extension() != String()) {
+ backup_name += "." + base_path.get_extension();
+ }
DirAccess *da = DirAccess::open(base_path.get_base_dir());
if (da) {
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
index cbe7f87d92..ffd3ecaed0 100644
--- a/core/io/multiplayer_api.cpp
+++ b/core/io/multiplayer_api.cpp
@@ -1,7 +1,92 @@
+/*************************************************************************/
+/* multiplayer_api.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 "core/io/multiplayer_api.h"
#include "core/io/marshalls.h"
#include "scene/main/node.h"
+_FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) {
+
+ switch (mode) {
+
+ case MultiplayerAPI::RPC_MODE_DISABLED: {
+ //do nothing
+ } break;
+ case MultiplayerAPI::RPC_MODE_REMOTE: {
+ //do nothing also, no need to call local
+ } break;
+ case MultiplayerAPI::RPC_MODE_REMOTESYNC:
+ case MultiplayerAPI::RPC_MODE_MASTERSYNC:
+ case MultiplayerAPI::RPC_MODE_SLAVESYNC:
+ case MultiplayerAPI::RPC_MODE_SYNC: {
+ //call it, sync always results in call
+ return true;
+ } break;
+ case MultiplayerAPI::RPC_MODE_MASTER: {
+ if (is_master)
+ r_skip_rpc = true; //no other master so..
+ return is_master;
+ } break;
+ case MultiplayerAPI::RPC_MODE_SLAVE: {
+ return !is_master;
+ } break;
+ }
+ return false;
+}
+
+_FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) {
+ switch (mode) {
+
+ case MultiplayerAPI::RPC_MODE_DISABLED: {
+ return false;
+ } break;
+ case MultiplayerAPI::RPC_MODE_REMOTE: {
+ return true;
+ } break;
+ case MultiplayerAPI::RPC_MODE_REMOTESYNC:
+ case MultiplayerAPI::RPC_MODE_SYNC: {
+ return true;
+ } break;
+ case MultiplayerAPI::RPC_MODE_MASTERSYNC:
+ case MultiplayerAPI::RPC_MODE_MASTER: {
+ return p_node->is_network_master();
+ } break;
+ case MultiplayerAPI::RPC_MODE_SLAVESYNC:
+ case MultiplayerAPI::RPC_MODE_SLAVE: {
+ return !p_node->is_network_master() && p_remote_id == p_node->get_network_master();
+ } break;
+ }
+
+ return false;
+}
+
void MultiplayerAPI::poll() {
if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED)
@@ -47,11 +132,11 @@ void MultiplayerAPI::set_root_node(Node *p_node) {
void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer) {
if (network_peer.is_valid()) {
- network_peer->disconnect("peer_connected", this, "add_peer");
- network_peer->disconnect("peer_disconnected", this, "del_peer");
- network_peer->disconnect("connection_succeeded", this, "connected_to_server");
- network_peer->disconnect("connection_failed", this, "connection_failed");
- network_peer->disconnect("server_disconnected", this, "server_disconnected");
+ network_peer->disconnect("peer_connected", this, "_add_peer");
+ network_peer->disconnect("peer_disconnected", this, "_del_peer");
+ network_peer->disconnect("connection_succeeded", this, "_connected_to_server");
+ network_peer->disconnect("connection_failed", this, "_connection_failed");
+ network_peer->disconnect("server_disconnected", this, "_server_disconnected");
clear();
}
@@ -61,11 +146,11 @@ void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_pee
ERR_FAIL_COND(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED);
if (network_peer.is_valid()) {
- network_peer->connect("peer_connected", this, "add_peer");
- network_peer->connect("peer_disconnected", this, "del_peer");
- network_peer->connect("connection_succeeded", this, "connected_to_server");
- network_peer->connect("connection_failed", this, "connection_failed");
- network_peer->connect("server_disconnected", this, "server_disconnected");
+ network_peer->connect("peer_connected", this, "_add_peer");
+ network_peer->connect("peer_disconnected", this, "_del_peer");
+ network_peer->connect("connection_succeeded", this, "_connected_to_server");
+ network_peer->connect("connection_failed", this, "_connection_failed");
+ network_peer->connect("server_disconnected", this, "_server_disconnected");
}
}
@@ -76,7 +161,7 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const {
void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
ERR_FAIL_COND(root_node == NULL);
- ERR_FAIL_COND(p_packet_len < 5);
+ ERR_FAIL_COND(p_packet_len < 1);
uint8_t packet_type = p_packet[0];
@@ -123,6 +208,11 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
}
} break;
+
+ case NETWORK_COMMAND_RAW: {
+
+ _process_raw(p_from, p_packet, p_packet_len);
+ } break;
}
}
@@ -167,11 +257,19 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int
}
void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
- if (!p_node->can_call_rpc(p_name, p_from))
- return;
ERR_FAIL_COND(p_offset >= p_packet_len);
+ // Check that remote can call the RPC on this node
+ RPCMode rpc_mode = RPC_MODE_DISABLED;
+ const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_name);
+ if (E) {
+ rpc_mode = E->get();
+ } else if (p_node->get_script_instance()) {
+ rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name);
+ }
+ ERR_FAIL_COND(!_can_call_mode(p_node, rpc_mode, p_from));
+
int argc = p_packet[p_offset];
Vector<Variant> args;
Vector<const Variant *> argp;
@@ -203,11 +301,18 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
- if (!p_node->can_call_rset(p_name, p_from))
- return;
-
ERR_FAIL_COND(p_offset >= p_packet_len);
+ // Check that remote can call the RSET on this node
+ RPCMode rset_mode = RPC_MODE_DISABLED;
+ const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_name);
+ if (E) {
+ rset_mode = E->get();
+ } else if (p_node->get_script_instance()) {
+ rset_mode = p_node->get_script_instance()->get_rset_mode(p_name);
+ }
+ ERR_FAIL_COND(!_can_call_mode(p_node, rset_mode, p_from));
+
Variant value;
decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset);
@@ -259,6 +364,8 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND(p_packet_len < 2);
+
String paths;
paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1);
@@ -458,84 +565,33 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
}
}
-void MultiplayerAPI::add_peer(int p_id) {
+void MultiplayerAPI::_add_peer(int p_id) {
connected_peers.insert(p_id);
path_get_cache.insert(p_id, PathGetCache());
emit_signal("network_peer_connected", p_id);
}
-void MultiplayerAPI::del_peer(int p_id) {
+void MultiplayerAPI::_del_peer(int p_id) {
connected_peers.erase(p_id);
path_get_cache.erase(p_id); //I no longer need your cache, sorry
emit_signal("network_peer_disconnected", p_id);
}
-void MultiplayerAPI::connected_to_server() {
+void MultiplayerAPI::_connected_to_server() {
emit_signal("connected_to_server");
}
-void MultiplayerAPI::connection_failed() {
+void MultiplayerAPI::_connection_failed() {
emit_signal("connection_failed");
}
-void MultiplayerAPI::server_disconnected() {
+void MultiplayerAPI::_server_disconnected() {
emit_signal("server_disconnected");
}
-bool _should_call_native(Node::RPCMode mode, bool is_master, bool &r_skip_rpc) {
-
- switch (mode) {
-
- case Node::RPC_MODE_DISABLED: {
- //do nothing
- } break;
- case Node::RPC_MODE_REMOTE: {
- //do nothing also, no need to call local
- } break;
- case Node::RPC_MODE_SYNC: {
- //call it, sync always results in call
- return true;
- } break;
- case Node::RPC_MODE_MASTER: {
- if (is_master)
- r_skip_rpc = true; //no other master so..
- return is_master;
- } break;
- case Node::RPC_MODE_SLAVE: {
- return !is_master;
- } break;
- }
- return false;
-}
-
-bool _should_call_script(ScriptInstance::RPCMode mode, bool is_master, bool &r_skip_rpc) {
- switch (mode) {
-
- case ScriptInstance::RPC_MODE_DISABLED: {
- //do nothing
- } break;
- case ScriptInstance::RPC_MODE_REMOTE: {
- //do nothing also, no need to call local
- } break;
- case ScriptInstance::RPC_MODE_SYNC: {
- //call it, sync always results in call
- return true;
- } break;
- case ScriptInstance::RPC_MODE_MASTER: {
- if (is_master)
- r_skip_rpc = true; //no other master so..
- return is_master;
- } break;
- case ScriptInstance::RPC_MODE_SLAVE: {
- return !is_master;
- } break;
- }
- return false;
-}
-
void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND(!p_node->is_inside_tree());
@@ -550,17 +606,17 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
//check that send mode can use local call
- const Map<StringName, Node::RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method);
+ const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method);
if (E) {
- call_local_native = _should_call_native(E->get(), is_master, skip_rpc);
+ call_local_native = _should_call_local(E->get(), is_master, skip_rpc);
}
if (call_local_native) {
// done below
} else if (p_node->get_script_instance()) {
//attempt with script
- ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method);
- call_local_script = _should_call_script(rpc_mode, is_master, skip_rpc);
+ RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method);
+ call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc);
}
}
@@ -606,10 +662,10 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
bool set_local = false;
- const Map<StringName, Node::RPCMode>::Element *E = p_node->get_node_rset_mode(p_property);
+ const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_property);
if (E) {
- set_local = _should_call_native(E->get(), is_master, skip_rset);
+ set_local = _should_call_local(E->get(), is_master, skip_rset);
}
if (set_local) {
@@ -623,9 +679,9 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
}
} else if (p_node->get_script_instance()) {
//attempt with script
- ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property);
+ RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property);
- set_local = _should_call_script(rpc_mode, is_master, skip_rset);
+ set_local = _should_call_local(rpc_mode, is_master, skip_rset);
if (set_local) {
@@ -648,6 +704,37 @@ 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, 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);
+ ERR_FAIL_COND_V(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED);
+
+ MAKE_ROOM(p_data.size() + 1);
+ 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);
+}
+
+void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) {
+
+ ERR_FAIL_COND(p_packet_len < 2);
+
+ PoolVector<uint8_t> out;
+ int len = p_packet_len - 1;
+ out.resize(len);
+ {
+ PoolVector<uint8_t>::Write w = out.write();
+ memcpy(&w[0], &p_packet[1], len);
+ }
+ emit_signal("network_peer_packet", p_from, out);
+}
+
int MultiplayerAPI::get_network_unique_id() const {
ERR_FAIL_COND_V(!network_peer.is_valid(), 0);
@@ -686,20 +773,21 @@ 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), 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);
ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server);
ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &MultiplayerAPI::get_rpc_sender_id);
- ClassDB::bind_method(D_METHOD("add_peer", "id"), &MultiplayerAPI::add_peer);
- ClassDB::bind_method(D_METHOD("del_peer", "id"), &MultiplayerAPI::del_peer);
+ ClassDB::bind_method(D_METHOD("_add_peer", "id"), &MultiplayerAPI::_add_peer);
+ ClassDB::bind_method(D_METHOD("_del_peer", "id"), &MultiplayerAPI::_del_peer);
ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer);
ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear);
- ClassDB::bind_method(D_METHOD("connected_to_server"), &MultiplayerAPI::connected_to_server);
- ClassDB::bind_method(D_METHOD("connection_failed"), &MultiplayerAPI::connection_failed);
- ClassDB::bind_method(D_METHOD("server_disconnected"), &MultiplayerAPI::server_disconnected);
+ ClassDB::bind_method(D_METHOD("_connected_to_server"), &MultiplayerAPI::_connected_to_server);
+ ClassDB::bind_method(D_METHOD("_connection_failed"), &MultiplayerAPI::_connection_failed);
+ ClassDB::bind_method(D_METHOD("_server_disconnected"), &MultiplayerAPI::_server_disconnected);
ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers);
ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections);
ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections);
@@ -708,9 +796,19 @@ void MultiplayerAPI::_bind_methods() {
ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("network_peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::POOL_BYTE_ARRAY, "packet")));
ADD_SIGNAL(MethodInfo("connected_to_server"));
ADD_SIGNAL(MethodInfo("connection_failed"));
ADD_SIGNAL(MethodInfo("server_disconnected"));
+
+ BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
+ BIND_ENUM_CONSTANT(RPC_MODE_REMOTE);
+ BIND_ENUM_CONSTANT(RPC_MODE_SYNC);
+ BIND_ENUM_CONSTANT(RPC_MODE_MASTER);
+ BIND_ENUM_CONSTANT(RPC_MODE_SLAVE);
+ BIND_ENUM_CONSTANT(RPC_MODE_REMOTESYNC);
+ BIND_ENUM_CONSTANT(RPC_MODE_MASTERSYNC);
+ BIND_ENUM_CONSTANT(RPC_MODE_SLAVESYNC);
}
MultiplayerAPI::MultiplayerAPI() {
diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h
index e7c6ffbea6..e47b1830e8 100644
--- a/core/io/multiplayer_api.h
+++ b/core/io/multiplayer_api.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* multiplayer_api.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_PROTOCOL_H
#define MULTIPLAYER_PROTOCOL_H
@@ -43,6 +73,7 @@ protected:
Node *_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len);
void _process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
void _process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
+ void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
void _send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount);
bool _send_confirm_path(NodePath p_path, PathSentCache *psc, int p_from);
@@ -53,6 +84,19 @@ public:
NETWORK_COMMAND_REMOTE_SET,
NETWORK_COMMAND_SIMPLIFY_PATH,
NETWORK_COMMAND_CONFIRM_PATH,
+ NETWORK_COMMAND_RAW,
+ };
+
+ enum RPCMode {
+
+ RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
+ RPC_MODE_REMOTE, // Using rpc() on it will call method / set property in all remote peers
+ RPC_MODE_SYNC, // Using rpc() on it will call method / set property in all remote peers and locally
+ RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote
+ RPC_MODE_SLAVE, // Using rpc() on it will call method for all slaves
+ RPC_MODE_REMOTESYNC, // Same as RPC_MODE_SYNC, compatibility
+ RPC_MODE_MASTERSYNC, // Using rpc() on it will call method / set property in the master peer and locally
+ RPC_MODE_SLAVESYNC, // Using rpc() on it will call method / set property in all slave peers and locally
};
void poll();
@@ -60,17 +104,18 @@ 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, 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);
// Called by Node.rset
void rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value);
- void add_peer(int p_id);
- void del_peer(int p_id);
- void connected_to_server();
- void connection_failed();
- void server_disconnected();
+ void _add_peer(int p_id);
+ void _del_peer(int p_id);
+ void _connected_to_server();
+ void _connection_failed();
+ void _server_disconnected();
bool has_network_peer() const { return network_peer.is_valid(); }
Vector<int> get_network_connected_peers() const;
@@ -84,4 +129,6 @@ public:
~MultiplayerAPI();
};
+VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode);
+
#endif // MULTIPLAYER_PROTOCOL_H
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/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp
index e01e2a84c5..16d5e3c282 100644
--- a/core/io/translation_loader_po.cpp
+++ b/core/io/translation_loader_po.cpp
@@ -175,7 +175,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
String prop = c.substr(0, p).strip_edges();
String value = c.substr(p + 1, c.length()).strip_edges();
- if (prop == "X-Language") {
+ if (prop == "X-Language" || prop == "Language") {
translation->set_locale(value);
}
}
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/aabb.h b/core/math/aabb.h
index 39b8f403e7..cdb8eb48a3 100644
--- a/core/math/aabb.h
+++ b/core/math/aabb.h
@@ -76,6 +76,7 @@ public:
_FORCE_INLINE_ bool smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t t0, real_t t1) const;
_FORCE_INLINE_ bool intersects_convex_shape(const Plane *p_planes, int p_plane_count) const;
+ _FORCE_INLINE_ bool inside_convex_shape(const Plane *p_planes, int p_plane_count) const;
bool intersects_plane(const Plane &p_plane) const;
_FORCE_INLINE_ bool has_point(const Vector3 &p_point) const;
@@ -207,6 +208,25 @@ bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count) con
return true;
}
+bool AABB::inside_convex_shape(const Plane *p_planes, int p_plane_count) const {
+
+ Vector3 half_extents = size * 0.5;
+ Vector3 ofs = position + half_extents;
+
+ for (int i = 0; i < p_plane_count; i++) {
+ const Plane &p = p_planes[i];
+ Vector3 point(
+ (p.normal.x < 0) ? -half_extents.x : half_extents.x,
+ (p.normal.y < 0) ? -half_extents.y : half_extents.y,
+ (p.normal.z < 0) ? -half_extents.z : half_extents.z);
+ point += ofs;
+ if (p.is_point_over(point))
+ return false;
+ }
+
+ return true;
+}
+
bool AABB::has_point(const Vector3 &p_point) const {
if (p_point.x < position.x)
diff --git a/core/math/delaunay.cpp b/core/math/delaunay.cpp
new file mode 100644
index 0000000000..8cae92b7c0
--- /dev/null
+++ b/core/math/delaunay.cpp
@@ -0,0 +1 @@
+#include "delaunay.h"
diff --git a/core/math/delaunay.h b/core/math/delaunay.h
new file mode 100644
index 0000000000..09aebc773f
--- /dev/null
+++ b/core/math/delaunay.h
@@ -0,0 +1,145 @@
+#ifndef DELAUNAY_H
+#define DELAUNAY_H
+
+#include "math_2d.h"
+
+class Delaunay2D {
+public:
+ struct Triangle {
+
+ int points[3];
+ bool bad;
+ Triangle() { bad = false; }
+ Triangle(int p_a, int p_b, int p_c) {
+ points[0] = p_a;
+ points[1] = p_b;
+ points[2] = p_c;
+ bad = false;
+ }
+ };
+
+ struct Edge {
+ int edge[2];
+ bool bad;
+ Edge() { bad = false; }
+ Edge(int p_a, int p_b) {
+ bad = false;
+ edge[0] = p_a;
+ edge[1] = p_b;
+ }
+ };
+
+ static bool circum_circle_contains(const Vector<Vector2> &p_vertices, const Triangle &p_triangle, int p_vertex) {
+
+ Vector2 p1 = p_vertices[p_triangle.points[0]];
+ Vector2 p2 = p_vertices[p_triangle.points[1]];
+ Vector2 p3 = p_vertices[p_triangle.points[2]];
+
+ real_t ab = p1.x * p1.x + p1.y * p1.y;
+ real_t cd = p2.x * p2.x + p2.y * p2.y;
+ real_t ef = p3.x * p3.x + p3.y * p3.y;
+
+ Vector2 circum(
+ (ab * (p3.y - p2.y) + cd * (p1.y - p3.y) + ef * (p2.y - p1.y)) / (p1.x * (p3.y - p2.y) + p2.x * (p1.y - p3.y) + p3.x * (p2.y - p1.y)),
+ (ab * (p3.x - p2.x) + cd * (p1.x - p3.x) + ef * (p2.x - p1.x)) / (p1.y * (p3.x - p2.x) + p2.y * (p1.x - p3.x) + p3.y * (p2.x - p1.x)));
+
+ circum *= 0.5;
+ float r = p1.distance_squared_to(circum);
+ float d = p_vertices[p_vertex].distance_squared_to(circum);
+ return d <= r;
+ }
+
+ static bool edge_compare(const Vector<Vector2> &p_vertices, const Edge &p_a, const Edge &p_b) {
+ if (p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[0]]) < CMP_EPSILON && p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[1]]) < CMP_EPSILON) {
+ return true;
+ }
+
+ if (p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[1]]) < CMP_EPSILON && p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[0]]) < CMP_EPSILON) {
+ return true;
+ }
+
+ return false;
+ }
+
+ static Vector<Triangle> triangulate(const Vector<Vector2> &p_points) {
+
+ Vector<Vector2> points = p_points;
+ Vector<Triangle> triangles;
+
+ Rect2 rect;
+ for (int i = 0; i < p_points.size(); i++) {
+ if (i == 0) {
+ rect.position = p_points[i];
+ } else {
+ rect.expand_to(p_points[i]);
+ }
+ }
+
+ float delta_max = MAX(rect.size.width, rect.size.height);
+ Vector2 center = rect.position + rect.size * 0.5;
+
+ points.push_back(Vector2(center.x - 20 * delta_max, center.y - delta_max));
+ points.push_back(Vector2(center.x, center.y + 20 * delta_max));
+ points.push_back(Vector2(center.x + 20 * delta_max, center.y - delta_max));
+
+ triangles.push_back(Triangle(p_points.size() + 0, p_points.size() + 1, p_points.size() + 2));
+
+ for (int i = 0; i < p_points.size(); i++) {
+ //std::cout << "Traitement du point " << *p << std::endl;
+ //std::cout << "_triangles contains " << _triangles.size() << " elements" << std::endl;
+
+ Vector<Edge> polygon;
+
+ for (int j = 0; j < triangles.size(); j++) {
+ if (circum_circle_contains(points, triangles[j], i)) {
+ triangles[j].bad = true;
+ polygon.push_back(Edge(triangles[j].points[0], triangles[j].points[1]));
+ polygon.push_back(Edge(triangles[j].points[1], triangles[j].points[2]));
+ polygon.push_back(Edge(triangles[j].points[2], triangles[j].points[0]));
+ }
+ }
+
+ for (int j = 0; j < triangles.size(); j++) {
+ if (triangles[j].bad) {
+ triangles.remove(j);
+ j--;
+ }
+ }
+
+ for (int j = 0; j < polygon.size(); j++) {
+ for (int k = j + 1; k < polygon.size(); k++) {
+ if (edge_compare(points, polygon[j], polygon[k])) {
+ polygon[j].bad = true;
+ polygon[k].bad = true;
+ }
+ }
+ }
+
+ for (int j = 0; j < polygon.size(); j++) {
+
+ if (polygon[j].bad) {
+ continue;
+ }
+ triangles.push_back(Triangle(polygon[j].edge[0], polygon[j].edge[1], i));
+ }
+ }
+
+ for (int i = 0; i < triangles.size(); i++) {
+ bool invalid = false;
+ for (int j = 0; j < 3; j++) {
+ if (triangles[i].points[j] >= p_points.size()) {
+ invalid = true;
+ break;
+ }
+ }
+ if (invalid) {
+ triangles.remove(i);
+ i--;
+ }
+ }
+
+ return triangles;
+ }
+};
+
+#endif // DELAUNAY_H
diff --git a/core/math/math_2d.h b/core/math/math_2d.h
index 611d47e3ff..25c39e5d7a 100644
--- a/core/math/math_2d.h
+++ b/core/math/math_2d.h
@@ -112,6 +112,7 @@ struct Vector2 {
_FORCE_INLINE_ static Vector2 linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t);
_FORCE_INLINE_ Vector2 linear_interpolate(const Vector2 &p_b, real_t p_t) const;
+ _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_b, real_t p_t) const;
Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const;
Vector2 slide(const Vector2 &p_normal) const;
@@ -263,6 +264,14 @@ Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const {
return res;
}
+Vector2 Vector2::slerp(const Vector2 &p_b, real_t p_t) const {
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_normalized() == false, Vector2());
+#endif
+ real_t theta = angle_to(p_b);
+ return rotated(theta * p_t);
+}
+
Vector2 Vector2::linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t) {
Vector2 res = p_a;
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 b0b05d1ec8..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];
@@ -826,3 +838,16 @@ void Basis::set_diagonal(const Vector3 p_diag) {
elements[2][1] = 0;
elements[2][2] = p_diag.z;
}
+
+Basis Basis::slerp(const Basis &target, const real_t &t) const {
+// TODO: implement this directly without using quaternions to make it more efficient
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_rotation() == false, Basis());
+ ERR_FAIL_COND_V(target.is_rotation() == false, Basis());
+#endif
+
+ Quat from(*this);
+ Quat to(target);
+
+ return Basis(from.slerp(to, t));
+}
diff --git a/core/math/matrix3.h b/core/math/matrix3.h
index fd383fc673..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;
@@ -155,6 +157,8 @@ public:
bool is_diagonal() const;
bool is_rotation() const;
+ Basis slerp(const Basis &target, const real_t &t) const;
+
operator String() const;
/* create / set */
diff --git a/core/math/quat.cpp b/core/math/quat.cpp
index 4f61401ac7..67c9048a41 100644
--- a/core/math/quat.cpp
+++ b/core/math/quat.cpp
@@ -98,6 +98,9 @@ void Quat::set_euler_yxz(const Vector3 &p_euler) {
// and similar for other axes.
// This implementation uses YXZ convention (Z is the first rotation).
Vector3 Quat::get_euler_yxz() const {
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_normalized() == false, Vector3(0, 0, 0));
+#endif
Basis m(*this);
return m.get_euler_yxz();
}
@@ -135,11 +138,17 @@ bool Quat::is_normalized() const {
}
Quat Quat::inverse() const {
+#ifdef MATH_CHECKS
+ 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());
+ ERR_FAIL_COND_V(q.is_normalized() == false, Quat());
+#endif
Quat to1;
real_t omega, cosom, sinom, scale0, scale1;
@@ -183,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);
@@ -202,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);
@@ -215,7 +230,10 @@ Quat::operator String() const {
return String::num(x) + ", " + String::num(y) + ", " + String::num(z) + ", " + String::num(w);
}
-Quat::Quat(const Vector3 &axis, const real_t &angle) {
+void Quat::set_axis_angle(const Vector3 &axis, const real_t &angle) {
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND(axis.is_normalized() == false);
+#endif
real_t d = axis.length();
if (d == 0)
set(0, 0, 0, 0);
diff --git a/core/math/quat.h b/core/math/quat.h
index ebc924504b..6dc8d66f60 100644
--- a/core/math/quat.h
+++ b/core/math/quat.h
@@ -64,11 +64,13 @@ public:
Quat slerpni(const Quat &q, const real_t &t) const;
Quat cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const;
+ void set_axis_angle(const Vector3 &axis, const real_t &angle);
_FORCE_INLINE_ void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
r_angle = 2 * Math::acos(w);
- r_axis.x = x / Math::sqrt(1 - w * w);
- r_axis.y = y / Math::sqrt(1 - w * w);
- r_axis.z = z / Math::sqrt(1 - w * w);
+ real_t r = ((real_t)1) / Math::sqrt(1 - w * w);
+ r_axis.x = x * r;
+ r_axis.y = y * r;
+ r_axis.z = z * r;
}
void operator*=(const Quat &q);
@@ -82,10 +84,12 @@ public:
}
_FORCE_INLINE_ Vector3 xform(const Vector3 &v) const {
-
- Quat q = *this * v;
- q *= this->inverse();
- return Vector3(q.x, q.y, q.z);
+#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);
}
_FORCE_INLINE_ void operator+=(const Quat &q);
@@ -115,7 +119,15 @@ public:
z = p_z;
w = p_w;
}
- Quat(const Vector3 &axis, const real_t &angle);
+ Quat(const Vector3 &axis, const real_t &angle) { set_axis_angle(axis, angle); }
+
+ Quat(const Vector3 &euler) { set_euler(euler); }
+ Quat(const Quat &q) {
+ x = q.x;
+ y = q.y;
+ z = q.z;
+ w = q.w;
+ }
Quat(const Vector3 &v0, const Vector3 &v1) // shortest arc
{
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/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp
index edd4ad3441..5475f733c3 100644
--- a/core/math/triangle_mesh.cpp
+++ b/core/math/triangle_mesh.cpp
@@ -88,6 +88,26 @@ int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, in
return index;
}
+void TriangleMesh::get_indices(PoolVector<int> *r_triangles_indices) const {
+
+ if (!valid)
+ return;
+
+ const int triangles_num = triangles.size();
+
+ // Parse vertices indices
+ PoolVector<Triangle>::Read triangles_read = triangles.read();
+
+ r_triangles_indices->resize(triangles_num * 3);
+ PoolVector<int>::Write r_indices_write = r_triangles_indices->write();
+
+ for (int i = 0; i < triangles_num; ++i) {
+ r_indices_write[3 * i + 0] = triangles_read[i].indices[0];
+ r_indices_write[3 * i + 1] = triangles_read[i].indices[1];
+ r_indices_write[3 * i + 2] = triangles_read[i].indices[2];
+ }
+}
+
void TriangleMesh::create(const PoolVector<Vector3> &p_faces) {
valid = false;
@@ -490,6 +510,222 @@ bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, V
return inters;
}
+bool TriangleMesh::intersect_convex_shape(const Plane *p_planes, int p_plane_count) const {
+ uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth);
+
+ //p_fully_inside = true;
+
+ enum {
+ TEST_AABB_BIT = 0,
+ VISIT_LEFT_BIT = 1,
+ VISIT_RIGHT_BIT = 2,
+ VISIT_DONE_BIT = 3,
+ VISITED_BIT_SHIFT = 29,
+ NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1,
+ VISITED_BIT_MASK = ~NODE_IDX_MASK,
+
+ };
+
+ int level = 0;
+
+ PoolVector<Triangle>::Read trianglesr = triangles.read();
+ PoolVector<Vector3>::Read verticesr = vertices.read();
+ PoolVector<BVH>::Read bvhr = bvh.read();
+
+ const Triangle *triangleptr = trianglesr.ptr();
+ const Vector3 *vertexptr = verticesr.ptr();
+ int pos = bvh.size() - 1;
+ const BVH *bvhptr = bvhr.ptr();
+
+ stack[0] = pos;
+ while (true) {
+
+ uint32_t node = stack[level] & NODE_IDX_MASK;
+ const BVH &b = bvhptr[node];
+ bool done = false;
+
+ switch (stack[level] >> VISITED_BIT_SHIFT) {
+ case TEST_AABB_BIT: {
+
+ bool valid = b.aabb.intersects_convex_shape(p_planes, p_plane_count);
+ if (!valid) {
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+
+ } else {
+
+ if (b.face_index >= 0) {
+
+ const Triangle &s = triangleptr[b.face_index];
+
+ for (int j = 0; j < 3; ++j) {
+ const Vector3 &point = vertexptr[s.indices[j]];
+ const Vector3 &next_point = vertexptr[s.indices[(j + 1) % 3]];
+ Vector3 res;
+ bool over = true;
+ for (int i = 0; i < p_plane_count; i++) {
+ const Plane &p = p_planes[i];
+
+ if (p.intersects_segment(point, next_point, &res)) {
+ bool inisde = true;
+ for (int k = 0; k < p_plane_count; k++) {
+ if (k == i) continue;
+ const Plane &pp = p_planes[k];
+ if (pp.is_point_over(res)) {
+ inisde = false;
+ break;
+ }
+ }
+ if (inisde) return true;
+ }
+
+ if (p.is_point_over(point)) {
+ over = false;
+ break;
+ }
+ }
+ if (over) return true;
+ }
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+
+ } else {
+
+ stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node;
+ }
+ }
+ continue;
+ }
+ case VISIT_LEFT_BIT: {
+
+ stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node;
+ stack[level + 1] = b.left | TEST_AABB_BIT;
+ level++;
+ continue;
+ }
+ case VISIT_RIGHT_BIT: {
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+ stack[level + 1] = b.right | TEST_AABB_BIT;
+ level++;
+ continue;
+ }
+ case VISIT_DONE_BIT: {
+
+ if (level == 0) {
+ done = true;
+ break;
+ } else
+ level--;
+ continue;
+ }
+ }
+
+ if (done)
+ break;
+ }
+
+ return false;
+}
+
+bool TriangleMesh::inside_convex_shape(const Plane *p_planes, int p_plane_count, Vector3 p_scale) const {
+ uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth);
+
+ enum {
+ TEST_AABB_BIT = 0,
+ VISIT_LEFT_BIT = 1,
+ VISIT_RIGHT_BIT = 2,
+ VISIT_DONE_BIT = 3,
+ VISITED_BIT_SHIFT = 29,
+ NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1,
+ VISITED_BIT_MASK = ~NODE_IDX_MASK,
+
+ };
+
+ int level = 0;
+
+ PoolVector<Triangle>::Read trianglesr = triangles.read();
+ PoolVector<Vector3>::Read verticesr = vertices.read();
+ PoolVector<BVH>::Read bvhr = bvh.read();
+
+ Transform scale(Basis().scaled(p_scale));
+
+ const Triangle *triangleptr = trianglesr.ptr();
+ const Vector3 *vertexptr = verticesr.ptr();
+ int pos = bvh.size() - 1;
+ const BVH *bvhptr = bvhr.ptr();
+
+ stack[0] = pos;
+ while (true) {
+
+ uint32_t node = stack[level] & NODE_IDX_MASK;
+ const BVH &b = bvhptr[node];
+ bool done = false;
+
+ switch (stack[level] >> VISITED_BIT_SHIFT) {
+ case TEST_AABB_BIT: {
+
+ bool intersects = scale.xform(b.aabb).intersects_convex_shape(p_planes, p_plane_count);
+ if (!intersects) return false;
+
+ bool inside = scale.xform(b.aabb).inside_convex_shape(p_planes, p_plane_count);
+ if (inside) {
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+
+ } else {
+
+ if (b.face_index >= 0) {
+ const Triangle &s = triangleptr[b.face_index];
+ for (int j = 0; j < 3; ++j) {
+ Vector3 point = scale.xform(vertexptr[s.indices[j]]);
+ for (int i = 0; i < p_plane_count; i++) {
+ const Plane &p = p_planes[i];
+ if (p.is_point_over(point)) return false;
+ }
+ }
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+
+ } else {
+
+ stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node;
+ }
+ }
+ continue;
+ }
+ case VISIT_LEFT_BIT: {
+
+ stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node;
+ stack[level + 1] = b.left | TEST_AABB_BIT;
+ level++;
+ continue;
+ }
+ case VISIT_RIGHT_BIT: {
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+ stack[level + 1] = b.right | TEST_AABB_BIT;
+ level++;
+ continue;
+ }
+ case VISIT_DONE_BIT: {
+
+ if (level == 0) {
+ done = true;
+ break;
+ } else
+ level--;
+ continue;
+ }
+ }
+
+ if (done)
+ break;
+ }
+
+ return true;
+}
+
bool TriangleMesh::is_valid() const {
return valid;
diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h
index 9f145f2afb..bf793fc50f 100644
--- a/core/math/triangle_mesh.h
+++ b/core/math/triangle_mesh.h
@@ -89,9 +89,15 @@ public:
bool is_valid() const;
bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const;
bool intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal) const;
+ bool intersect_convex_shape(const Plane *p_planes, int p_plane_count) const;
+ bool inside_convex_shape(const Plane *p_planes, int p_plane_count, Vector3 p_scale = Vector3(1, 1, 1)) const;
Vector3 get_area_normal(const AABB &p_aabb) const;
PoolVector<Face3> get_faces() const;
+ PoolVector<Triangle> get_triangles() const { return triangles; }
+ PoolVector<Vector3> get_vertices() const { return vertices; }
+ void get_indices(PoolVector<int> *p_triangles_indices) const;
+
void create(const PoolVector<Vector3> &p_faces);
TriangleMesh();
};
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 3bbfd7627c..433adf09ee 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -91,6 +91,7 @@ struct Vector3 {
/* Static Methods between 2 vector3s */
_FORCE_INLINE_ Vector3 linear_interpolate(const Vector3 &p_b, real_t p_t) const;
+ _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_b, real_t p_t) const;
Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const;
Vector3 cubic_interpolaten(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const;
@@ -218,6 +219,15 @@ Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const {
z + (p_t * (p_b.z - z)));
}
+Vector3 Vector3::slerp(const Vector3 &p_b, real_t p_t) const {
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_normalized() == false, Vector3());
+#endif
+
+ real_t theta = angle_to(p_b);
+ return rotated(cross(p_b), theta * p_t);
+}
+
real_t Vector3::distance_to(const Vector3 &p_b) const {
return (p_b - *this).length();
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 239700a4ab..1d2aeb7ba5 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -1677,6 +1677,7 @@ void Object::_bind_methods() {
#ifdef TOOLS_ENABLED
MethodInfo miget("_get", PropertyInfo(Variant::STRING, "property"));
miget.return_val.name = "Variant";
+ miget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
BIND_VMETHOD(miget);
MethodInfo plget("_get_property_list");
diff --git a/core/object.h b/core/object.h
index c405e22557..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"
@@ -55,7 +56,7 @@ enum PropertyHint {
PROPERTY_HINT_RANGE, ///< hint_text = "min,max,step,slider; //slider is optional"
PROPERTY_HINT_EXP_RANGE, ///< hint_text = "min,max,step", exponential edit
PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
- PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease)
+ PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout")
PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer)
PROPERTY_HINT_SPRITE_FRAME,
PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
@@ -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 854d554b10..5eed10e30c 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -672,6 +672,7 @@ OS::OS() {
_render_thread_mode = RENDER_THREAD_SAFE;
_allow_hidpi = false;
+ _allow_layered = false;
_stack_bottom = (void *)(&stack_bottom);
_logger = NULL;
diff --git a/core/os/os.h b/core/os/os.h
index f6404468b1..adf01a90e7 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -65,6 +65,7 @@ class OS {
int _exit_code;
int _orientation;
bool _allow_hidpi;
+ bool _allow_layered;
bool _use_vsync;
char *last_error;
@@ -102,6 +103,8 @@ public:
bool maximized;
bool always_on_top;
bool use_vsync;
+ bool layered_splash;
+ bool layered;
float get_aspect() const { return (float)width / (float)height; }
VideoMode(int p_width = 1024, int p_height = 600, bool p_fullscreen = false, bool p_resizable = true, bool p_borderless_window = false, bool p_maximized = false, bool p_always_on_top = false, bool p_use_vsync = false) {
width = p_width;
@@ -112,6 +115,8 @@ public:
maximized = p_maximized;
always_on_top = p_always_on_top;
use_vsync = p_use_vsync;
+ layered = false;
+ layered_splash = false;
}
};
@@ -220,6 +225,14 @@ public:
virtual void set_borderless_window(bool p_borderless) {}
virtual bool get_borderless_window() { return 0; }
+ virtual bool get_window_per_pixel_transparency_enabled() const { return false; }
+ virtual void set_window_per_pixel_transparency_enabled(bool p_enabled) {}
+
+ virtual uint8_t *get_layered_buffer_data() { return NULL; }
+ virtual Size2 get_layered_buffer_size() { return Size2(0, 0); }
+ virtual void swap_layered_buffer() {}
+
+ virtual void set_ime_active(const bool p_active) {}
virtual void set_ime_position(const Point2 &p_pos) {}
virtual void set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp) {}
@@ -481,6 +494,7 @@ public:
virtual void force_process_input(){};
bool has_feature(const String &p_feature);
+ bool is_layered_allowed() const { return _allow_layered; }
bool is_hidpi_allowed() const { return _allow_hidpi; }
OS();
virtual ~OS();
diff --git a/core/pool_allocator.cpp b/core/pool_allocator.cpp
index 017586b92a..8952314212 100644
--- a/core/pool_allocator.cpp
+++ b/core/pool_allocator.cpp
@@ -359,7 +359,7 @@ Error PoolAllocator::resize(ID p_mem, int p_new_size) {
//p_new_size = align(p_new_size)
int _free = free_mem; // - static_area_size;
- if ((_free + aligned(e->len)) - alloc_size < 0) {
+ if (uint32_t(_free + aligned(e->len)) < alloc_size) {
mt_unlock();
ERR_FAIL_V(ERR_OUT_OF_MEMORY);
};
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index ac4a4b7d15..b1fd66e566 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -137,7 +137,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]);
@@ -515,7 +515,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;
@@ -870,6 +874,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;
diff --git a/core/project_settings.h b/core/project_settings.h
index b01e7855aa..66f3ed954e 100644
--- a/core/project_settings.h
+++ b/core/project_settings.h
@@ -137,6 +137,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;
diff --git a/core/resource.cpp b/core/resource.cpp
index 179333aa14..87ff4d3c2a 100644
--- a/core/resource.cpp
+++ b/core/resource.cpp
@@ -187,7 +187,6 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res
void Resource::configure_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, Ref<Resource> > &remap_cache) {
- print_line("configure for local: " + get_class());
List<PropertyInfo> plist;
get_property_list(&plist);
@@ -226,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);
@@ -288,7 +292,7 @@ uint32_t Resource::hash_edited_version() const {
for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (E->get().type == Variant::OBJECT && E->get().hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ if (E->get().usage & PROPERTY_USAGE_STORAGE && E->get().type == Variant::OBJECT && E->get().hint == PROPERTY_HINT_RESOURCE_TYPE) {
RES res = get(E->get().name);
if (res.is_valid()) {
hash = hash_djb2_one_32(res->hash_edited_version(), hash);
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
index 75bcedbbc8..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);
@@ -567,22 +571,46 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) {
if (ScriptInstance *si = obj->get_script_instance()) {
if (!si->get_script().is_null()) {
- Set<StringName> members;
- si->get_script()->get_members(&members);
- for (Set<StringName>::Element *E = members.front(); E; E = E->next()) {
+ typedef Map<const Script *, Set<StringName> > ScriptMemberMap;
+ typedef Map<const Script *, Map<StringName, Variant> > ScriptConstantsMap;
+
+ ScriptMemberMap members;
+ members[si->get_script().ptr()] = Set<StringName>();
+ si->get_script()->get_members(&(members[si->get_script().ptr()]));
+
+ ScriptConstantsMap constants;
+ constants[si->get_script().ptr()] = Map<StringName, Variant>();
+ si->get_script()->get_constants(&(constants[si->get_script().ptr()]));
+
+ Ref<Script> base = si->get_script()->get_base_script();
+ while (base.is_valid()) {
+
+ members[base.ptr()] = Set<StringName>();
+ base->get_members(&(members[base.ptr()]));
+
+ constants[base.ptr()] = Map<StringName, Variant>();
+ base->get_constants(&(constants[base.ptr()]));
+
+ base = base->get_base_script();
+ }
- Variant m;
- if (si->get(E->get(), m)) {
- PropertyInfo pi(m.get_type(), String("Members/") + E->get());
- properties.push_back(PropertyDesc(pi, m));
+ for (ScriptMemberMap::Element *sm = members.front(); sm; sm = sm->next()) {
+ for (Set<StringName>::Element *E = sm->get().front(); E; E = E->next()) {
+ Variant m;
+ if (si->get(E->get(), m)) {
+ String script_path = sm->key() == si->get_script().ptr() ? "" : sm->key()->get_path().get_file() + "/";
+ PropertyInfo pi(m.get_type(), "Members/" + script_path + E->get());
+ properties.push_back(PropertyDesc(pi, m));
+ }
}
}
- Map<StringName, Variant> constants;
- si->get_script()->get_constants(&constants);
- for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
- PropertyInfo pi(E->value().get_type(), (String("Constants/") + E->key()));
- properties.push_back(PropertyDesc(pi, E->value()));
+ for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) {
+ for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) {
+ String script_path = sc->key() == si->get_script().ptr() ? "" : sc->key()->get_path().get_file() + "/";
+ PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key());
+ properties.push_back(PropertyDesc(pi, E->value()));
+ }
}
}
}
@@ -658,8 +686,10 @@ void ScriptDebuggerRemote::_set_object_property(ObjectID p_id, const String &p_p
return;
String prop_name = p_property;
- if (p_property.begins_with("Members/"))
- prop_name = p_property.substr(8, p_property.length());
+ if (p_property.begins_with("Members/")) {
+ Vector<String> ss = p_property.split("/");
+ prop_name = ss[ss.size() - 1];
+ }
obj->set(prop_name, p_value);
}
@@ -1044,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() :
@@ -1065,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 55a20c7478..2950b35109 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -31,6 +31,7 @@
#ifndef SCRIPT_LANGUAGE_H
#define SCRIPT_LANGUAGE_H
+#include "io/multiplayer_api.h"
#include "map.h"
#include "pair.h"
#include "resource.h"
@@ -53,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;
@@ -69,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();
};
@@ -157,16 +176,8 @@ public:
virtual bool is_placeholder() const { return false; }
- enum RPCMode {
- RPC_MODE_DISABLED,
- RPC_MODE_REMOTE,
- RPC_MODE_SYNC,
- RPC_MODE_MASTER,
- RPC_MODE_SLAVE,
- };
-
- virtual RPCMode get_rpc_mode(const StringName &p_method) const = 0;
- virtual RPCMode get_rset_mode(const StringName &p_variable) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0;
virtual ScriptLanguage *get_language() = 0;
virtual ~ScriptInstance();
@@ -236,6 +247,8 @@ public:
virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const = 0;
virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) = 0;
+ virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value) {}
+ virtual void remove_named_global_constant(const StringName &p_name) {}
/* MULTITHREAD FUNCTIONS */
@@ -290,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];
@@ -309,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;
@@ -330,8 +346,8 @@ public:
virtual bool is_placeholder() const { return true; }
- virtual RPCMode get_rpc_mode(const StringName &p_method) const { return RPC_MODE_DISABLED; }
- virtual RPCMode get_rset_mode(const StringName &p_variable) const { return RPC_MODE_DISABLED; }
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner);
~PlaceHolderScriptInstance();
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/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 85b7a16e6a..51f05468e2 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -753,6 +753,46 @@ Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p
return ret;
}
+Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int p_maxsplit) const {
+
+ Vector<String> ret;
+ const int len = length();
+ int from = 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 (end == 0)
+ break;
+
+ from = end - p_splitter.length();
+ }
+
+ ret.invert();
+ return ret;
+}
+
Vector<float> String::split_floats(const String &p_splitter, bool p_allow_empty) const {
Vector<float> ret;
diff --git a/core/ustring.h b/core/ustring.h
index 1ed694bb80..b57e9629d9 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -172,6 +172,7 @@ public:
String get_slicec(CharType p_splitter, int p_slice) const;
Vector<String> split(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const;
+ Vector<String> rsplit(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const;
Vector<String> split_spaces() const;
Vector<float> split_floats(const String &p_splitter, bool p_allow_empty = true) const;
Vector<float> split_floats_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const;
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_call.cpp b/core/variant_call.cpp
index bd1cde5a82..e6f36ecbf1 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -257,6 +257,7 @@ struct _VariantCall {
VCALL_LOCALMEM2R(String, insert);
VCALL_LOCALMEM0R(String, capitalize);
VCALL_LOCALMEM3R(String, split);
+ VCALL_LOCALMEM3R(String, rsplit);
VCALL_LOCALMEM2R(String, split_floats);
VCALL_LOCALMEM0R(String, to_upper);
VCALL_LOCALMEM0R(String, to_lower);
@@ -340,6 +341,7 @@ struct _VariantCall {
VCALL_LOCALMEM1R(Vector2, angle_to);
VCALL_LOCALMEM1R(Vector2, angle_to_point);
VCALL_LOCALMEM2R(Vector2, linear_interpolate);
+ VCALL_LOCALMEM2R(Vector2, slerp);
VCALL_LOCALMEM4R(Vector2, cubic_interpolate);
VCALL_LOCALMEM1R(Vector2, rotated);
VCALL_LOCALMEM0R(Vector2, tangent);
@@ -380,6 +382,7 @@ struct _VariantCall {
VCALL_LOCALMEM1R(Vector3, snapped);
VCALL_LOCALMEM2R(Vector3, rotated);
VCALL_LOCALMEM2R(Vector3, linear_interpolate);
+ VCALL_LOCALMEM2R(Vector3, slerp);
VCALL_LOCALMEM4R(Vector3, cubic_interpolate);
VCALL_LOCALMEM1R(Vector3, dot);
VCALL_LOCALMEM1R(Vector3, cross);
@@ -439,6 +442,9 @@ struct _VariantCall {
VCALL_LOCALMEM2R(Quat, slerp);
VCALL_LOCALMEM2R(Quat, slerpni);
VCALL_LOCALMEM4R(Quat, cubic_slerp);
+ VCALL_LOCALMEM0R(Quat, get_euler);
+ VCALL_LOCALMEM1(Quat, set_euler);
+ VCALL_LOCALMEM2(Quat, set_axis_angle);
VCALL_LOCALMEM0R(Color, to_rgba32);
VCALL_LOCALMEM0R(Color, to_argb32);
@@ -757,6 +763,7 @@ struct _VariantCall {
VCALL_PTR1R(Basis, xform_inv);
VCALL_PTR0R(Basis, get_orthogonal_index);
VCALL_PTR0R(Basis, orthonormalized);
+ VCALL_PTR2R(Basis, slerp);
VCALL_PTR0R(Transform, inverse);
VCALL_PTR0R(Transform, affine_inverse);
@@ -876,6 +883,11 @@ struct _VariantCall {
r_ret = Quat(((Vector3)(*p_args[0])), ((float)(*p_args[1])));
}
+ static void Quat_init3(Variant &r_ret, const Variant **p_args) {
+
+ r_ret = Quat(((Vector3)(*p_args[0])));
+ }
+
static void Color_init1(Variant &r_ret, const Variant **p_args) {
r_ret = Color(*p_args[0], *p_args[1], *p_args[2], *p_args[3]);
@@ -1150,7 +1162,7 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i
case RECT2: return (Rect2(*p_args[0]));
case VECTOR3: return (Vector3(*p_args[0]));
case PLANE: return (Plane(*p_args[0]));
- case QUAT: return (Quat(*p_args[0]));
+ case QUAT: return (p_args[0]->operator Quat());
case AABB:
return (::AABB(*p_args[0])); // 10
case BASIS: return (Basis(p_args[0]->operator Basis()));
@@ -1459,6 +1471,7 @@ void register_variant_methods() {
ADDFUNC2R(STRING, STRING, String, insert, INT, "position", STRING, "what", varray());
ADDFUNC0R(STRING, STRING, String, capitalize, varray());
ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, split, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0));
+ ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, rsplit, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0));
ADDFUNC2R(STRING, POOL_REAL_ARRAY, String, split_floats, STRING, "divisor", BOOL, "allow_empty", varray(true));
ADDFUNC0R(STRING, STRING, String, to_upper, varray());
@@ -1518,6 +1531,7 @@ void register_variant_methods() {
ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to, VECTOR2, "to", varray());
ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to_point, VECTOR2, "to", varray());
ADDFUNC2R(VECTOR2, VECTOR2, Vector2, linear_interpolate, VECTOR2, "b", REAL, "t", varray());
+ ADDFUNC2R(VECTOR2, VECTOR2, Vector2, slerp, VECTOR2, "b", REAL, "t", varray());
ADDFUNC4R(VECTOR2, VECTOR2, Vector2, cubic_interpolate, VECTOR2, "b", VECTOR2, "pre_a", VECTOR2, "post_b", REAL, "t", varray());
ADDFUNC1R(VECTOR2, VECTOR2, Vector2, rotated, REAL, "phi", varray());
ADDFUNC0R(VECTOR2, VECTOR2, Vector2, tangent, varray());
@@ -1557,6 +1571,7 @@ void register_variant_methods() {
ADDFUNC1R(VECTOR3, VECTOR3, Vector3, snapped, VECTOR3, "by", varray());
ADDFUNC2R(VECTOR3, VECTOR3, Vector3, rotated, VECTOR3, "axis", REAL, "phi", varray());
ADDFUNC2R(VECTOR3, VECTOR3, Vector3, linear_interpolate, VECTOR3, "b", REAL, "t", varray());
+ ADDFUNC2R(VECTOR3, VECTOR3, Vector3, slerp, VECTOR3, "b", REAL, "t", varray());
ADDFUNC4R(VECTOR3, VECTOR3, Vector3, cubic_interpolate, VECTOR3, "b", VECTOR3, "pre_a", VECTOR3, "post_b", REAL, "t", varray());
ADDFUNC1R(VECTOR3, REAL, Vector3, dot, VECTOR3, "b", varray());
ADDFUNC1R(VECTOR3, VECTOR3, Vector3, cross, VECTOR3, "b", varray());
@@ -1594,6 +1609,9 @@ void register_variant_methods() {
ADDFUNC2R(QUAT, QUAT, Quat, slerp, QUAT, "b", REAL, "t", varray());
ADDFUNC2R(QUAT, QUAT, Quat, slerpni, QUAT, "b", REAL, "t", varray());
ADDFUNC4R(QUAT, QUAT, Quat, cubic_slerp, QUAT, "b", QUAT, "pre_a", QUAT, "post_b", REAL, "t", varray());
+ ADDFUNC0R(QUAT, VECTOR3, Quat, get_euler, varray());
+ ADDFUNC1(QUAT, NIL, Quat, set_euler, VECTOR3, "euler", varray());
+ ADDFUNC2(QUAT, NIL, Quat, set_axis_angle, VECTOR3, "axis", REAL, "angle", varray());
ADDFUNC0R(COLOR, INT, Color, to_rgba32, varray());
ADDFUNC0R(COLOR, INT, Color, to_argb32, varray());
@@ -1786,6 +1804,7 @@ void register_variant_methods() {
ADDFUNC1R(BASIS, VECTOR3, Basis, xform, VECTOR3, "v", varray());
ADDFUNC1R(BASIS, VECTOR3, Basis, xform_inv, VECTOR3, "v", varray());
ADDFUNC0R(BASIS, INT, Basis, get_orthogonal_index, varray());
+ ADDFUNC2R(BASIS, BASIS, Basis, slerp, BASIS, "b", REAL, "t", varray());
ADDFUNC0R(TRANSFORM, TRANSFORM, Transform, inverse, varray());
ADDFUNC0R(TRANSFORM, TRANSFORM, Transform, affine_inverse, varray());
@@ -1816,6 +1835,7 @@ void register_variant_methods() {
_VariantCall::add_constructor(_VariantCall::Quat_init1, Variant::QUAT, "x", Variant::REAL, "y", Variant::REAL, "z", Variant::REAL, "w", Variant::REAL);
_VariantCall::add_constructor(_VariantCall::Quat_init2, Variant::QUAT, "axis", Variant::VECTOR3, "angle", Variant::REAL);
+ _VariantCall::add_constructor(_VariantCall::Quat_init3, Variant::QUAT, "euler", Variant::VECTOR3);
_VariantCall::add_constructor(_VariantCall::Color_init1, Variant::COLOR, "r", Variant::REAL, "g", Variant::REAL, "b", Variant::REAL, "a", Variant::REAL);
_VariantCall::add_constructor(_VariantCall::Color_init2, Variant::COLOR, "r", Variant::REAL, "g", Variant::REAL, "b", Variant::REAL);
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/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml
index acece05510..b5f5fed3f9 100644
--- a/doc/classes/@GDScript.xml
+++ b/doc/classes/@GDScript.xml
@@ -4,7 +4,7 @@
Built-in GDScript functions.
</brief_description>
<description>
- This contains the list of built-in gdscript functions. Mostly math functions and other utilities. Everything else is expanded by objects.
+ List of core built-in GDScript functions. Math functions and other utilities. Everything else is provided by objects. (Keywords: builtin, built in, global functions.)
</description>
<tutorials>
</tutorials>
@@ -485,6 +485,14 @@
Returns True/False whether [code]s[/code] is an infinity value (either positive infinity or negative infinity).
</description>
</method>
+ <method name="is_instance_valid">
+ <return type="bool">
+ </return>
+ <argument index="0" name="instance" type="Object">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="is_nan">
<return type="bool">
</return>
@@ -510,9 +518,9 @@
<method name="lerp">
<return type="float">
</return>
- <argument index="0" name="from" type="float">
+ <argument index="0" name="from" type="Variant">
</argument>
- <argument index="1" name="to" type="float">
+ <argument index="1" name="to" type="Variant">
</argument>
<argument index="2" name="weight" type="float">
</argument>
@@ -1137,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/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 840d884a8c..7f94676e93 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -29,9 +29,6 @@
<member name="Geometry" type="Geometry" setter="" getter="">
[Geometry] singleton
</member>
- <member name="GodotSharp" type="GodotSharp" setter="" getter="">
- [GodotSharp] singleton
- </member>
<member name="IP" type="IP" setter="" getter="">
[IP] singleton
</member>
diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml
index 730c395f10..b9061e0b87 100644
--- a/doc/classes/AABB.xml
+++ b/doc/classes/AABB.xml
@@ -7,7 +7,7 @@
AABB consists of a position, a size, and several utility functions. It is typically used for fast overlap tests.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/math/index.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/AcceptDialog.xml b/doc/classes/AcceptDialog.xml
index 99a3f59487..20d8bebd6b 100644
--- a/doc/classes/AcceptDialog.xml
+++ b/doc/classes/AcceptDialog.xml
@@ -21,7 +21,7 @@
<argument index="2" name="action" type="String" default="&quot;&quot;">
</argument>
<description>
- Adds a button with label [i]text[/i] and a custom [i]action[/i] to the dialog and returns the created button. [i]action[/i] will be passed to the [custom_action] signal when pressed.
+ Adds a button with label [i]text[/i] and a custom [i]action[/i] to the dialog and returns the created button. [i]action[/i] will be passed to the [signal custom_action] signal when pressed.
If [code]true[/code], [i]right[/i] will place the button to the right of any sibling buttons. Default value: [code]false[/code].
</description>
</method>
diff --git a/doc/classes/AnimatedSprite.xml b/doc/classes/AnimatedSprite.xml
index a6521c0cd1..a41ed0e689 100644
--- a/doc/classes/AnimatedSprite.xml
+++ b/doc/classes/AnimatedSprite.xml
@@ -60,6 +60,8 @@
<member name="playing" type="bool" setter="_set_playing" getter="_is_playing">
If [code]true[/code] the [member animation] is currently playing.
</member>
+ <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale">
+ </member>
</members>
<signals>
<signal name="animation_finished">
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index ea503f8aa9..c6b868c058 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -8,7 +8,7 @@
Animations are just data containers, and must be added to odes such as an [AnimationPlayer] or [AnimationTreePlayer] to be played back.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/animation/index.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/animation/index.html</link>
</tutorials>
<demos>
</demos>
@@ -24,6 +24,214 @@
Add a track to the Animation. The track type must be specified as any of the values in the TYPE_* enumeration.
</description>
</method>
+ <method name="animation_track_get_key_animation" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="animation_track_insert_key">
+ <return type="int">
+ </return>
+ <argument index="0" name="track" type="int">
+ </argument>
+ <argument index="1" name="time" type="float">
+ </argument>
+ <argument index="2" name="animation" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="animation_track_set_key_animation">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <argument index="2" name="animation" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="audio_track_get_key_end_offset" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="audio_track_get_key_start_offset" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="audio_track_get_key_stream" qualifiers="const">
+ <return type="Resource">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="audio_track_insert_key">
+ <return type="int">
+ </return>
+ <argument index="0" name="track" type="int">
+ </argument>
+ <argument index="1" name="time" type="float">
+ </argument>
+ <argument index="2" name="stream" type="Resource">
+ </argument>
+ <argument index="3" name="start_offset" type="float" default="0">
+ </argument>
+ <argument index="4" name="end_offset" type="float" default="0">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="audio_track_set_key_end_offset">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <argument index="2" name="offset" type="float">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="audio_track_set_key_start_offset">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <argument index="2" name="offset" type="float">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="audio_track_set_key_stream">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <argument index="2" name="stream" type="Resource">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="bezier_track_get_key_in_handle" qualifiers="const">
+ <return type="Vector2">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="bezier_track_get_key_out_handle" qualifiers="const">
+ <return type="Vector2">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="bezier_track_get_key_value" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="bezier_track_insert_key">
+ <return type="int">
+ </return>
+ <argument index="0" name="track" type="int">
+ </argument>
+ <argument index="1" name="time" type="float">
+ </argument>
+ <argument index="2" name="value" type="float">
+ </argument>
+ <argument index="3" name="in_handle" type="Vector2" default="Vector2( 0, 0 )">
+ </argument>
+ <argument index="4" name="out_handle" type="Vector2" default="Vector2( 0, 0 )">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="bezier_track_interpolate" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="track" type="int">
+ </argument>
+ <argument index="1" name="time" type="float">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="bezier_track_set_key_in_handle">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <argument index="2" name="in_handle" type="Vector2">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="bezier_track_set_key_out_handle">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <argument index="2" name="out_handle" type="Vector2">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="bezier_track_set_key_value">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <argument index="2" name="value" type="float">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="clear">
<return type="void">
</return>
@@ -347,6 +555,16 @@
Set the path of a track. Paths must be valid scene-tree paths to a node, and must be specified starting from the parent node of the node that will reproduce the animation. Tracks that control properties or bones must append their name after the path, separated by ":". Example: "character/skeleton:ankle" or "character/mesh:transform/local"
</description>
</method>
+ <method name="track_swap">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="with_idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="transform_track_insert_key">
<return type="int">
</return>
@@ -430,6 +648,12 @@
<constant name="TYPE_METHOD" value="2" enum="TrackType">
Method tracks call functions with given arguments per key.
</constant>
+ <constant name="TYPE_BEZIER" value="3" enum="TrackType">
+ </constant>
+ <constant name="TYPE_AUDIO" value="4" enum="TrackType">
+ </constant>
+ <constant name="TYPE_ANIMATION" value="5" enum="TrackType">
+ </constant>
<constant name="INTERPOLATION_NEAREST" value="0" enum="InterpolationType">
No interpolation (nearest value).
</constant>
@@ -448,5 +672,7 @@
<constant name="UPDATE_TRIGGER" value="2" enum="UpdateMode">
Update at the keyframes.
</constant>
+ <constant name="UPDATE_CAPTURE" value="3" enum="UpdateMode">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index 673bbbea59..e1b3c7a9c9 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -7,8 +7,8 @@
An animation player is used for general purpose playback of [Animation] resources. It contains a dictionary of animations (referenced by name) and custom blend times between their transitions. Additionally, animations can be played and blended in different channels.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/getting_started/step_by_step/animations.html
- http://docs.godotengine.org/en/3.0/tutorials/animation/index.html
+ <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/animations.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/animation/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/modules/webm/doc_classes/ResourceImporterWebm.xml b/doc/classes/AnimationTrackEditPlugin.xml
index 0cfab1baf0..f322a556b1 100644
--- a/modules/webm/doc_classes/ResourceImporterWebm.xml
+++ b/doc/classes/AnimationTrackEditPlugin.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.1">
+<class name="AnimationTrackEditPlugin" inherits="Reference" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/doc/classes/AnimationTreePlayer.xml b/doc/classes/AnimationTreePlayer.xml
index 49b4c6b43e..8c32d5f6a3 100644
--- a/doc/classes/AnimationTreePlayer.xml
+++ b/doc/classes/AnimationTreePlayer.xml
@@ -50,6 +50,14 @@
Returns the name of the [member master_player]'s [Animation] bound to this animation node.
</description>
</method>
+ <method name="animation_node_get_position" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="id" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="animation_node_set_animation">
<return type="void">
</return>
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index 5f85751c13..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>
@@ -282,7 +291,7 @@
</method>
<method name="sort">
<description>
- Sort the array using natural order and return reference to the array.
+ Sort the array using natural order.
</description>
</method>
<method name="sort_custom">
@@ -291,7 +300,7 @@
<argument index="1" name="func" type="String">
</argument>
<description>
- Sort the array using a custom method and return reference to the array. The arguments are an object that holds the method and the name of such method. The custom method receives two arguments (a pair of elements from the array) and must return true if the first argument is less than the second, and return false otherwise. Note: you cannot randomize the return value as the heapsort algorithm expects a deterministic result. Doing so will result in unexpected behavior.
+ Sort the array using a custom method. The arguments are an object that holds the method and the name of such method. The custom method receives two arguments (a pair of elements from the array) and must return true if the first argument is less than the second, and return false otherwise. Note: you cannot randomize the return value as the heapsort algorithm expects a deterministic result. Doing so will result in unexpected behavior.
[codeblock]
class MyCustomSorter:
static func sort(a, b):
diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index 38aab5231e..80d7b7783f 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -91,7 +91,7 @@
<argument index="0" name="surf_idx" type="int">
</argument>
<description>
- Return the length in indices of the index array in the requested surface (see [method add_surface]).
+ Return the length in indices of the index array in the requested surface (see [method add_surface_from_arrays]).
</description>
</method>
<method name="surface_get_array_len" qualifiers="const">
@@ -100,7 +100,7 @@
<argument index="0" name="surf_idx" type="int">
</argument>
<description>
- Return the length in vertices of the vertex array in the requested surface (see [method add_surface]).
+ Return the length in vertices of the vertex array in the requested surface (see [method add_surface_from_arrays]).
</description>
</method>
<method name="surface_get_arrays" qualifiers="const">
@@ -125,7 +125,7 @@
<argument index="0" name="surf_idx" type="int">
</argument>
<description>
- Return the format mask of the requested surface (see [method add_surface]).
+ Return the format mask of the requested surface (see [method add_surface_from_arrays]).
</description>
</method>
<method name="surface_get_material" qualifiers="const">
@@ -151,7 +151,7 @@
<argument index="0" name="surf_idx" type="int">
</argument>
<description>
- Return the primitive type of the requested surface (see [method add_surface]).
+ Return the primitive type of the requested surface (see [method add_surface_from_arrays]).
</description>
</method>
<method name="surface_remove">
diff --git a/doc/classes/AudioEffectEQ10.xml b/doc/classes/AudioEffectEQ10.xml
index f88a954417..daca342ace 100644
--- a/doc/classes/AudioEffectEQ10.xml
+++ b/doc/classes/AudioEffectEQ10.xml
@@ -16,7 +16,6 @@
Band 8 : 4000 Hz
Band 9 : 8000 Hz
Band 10 : 16000 Hz
-
See also [AudioEffectEQ], [AudioEffectEQ6], [AudioEffectEQ21].
</description>
<tutorials>
diff --git a/doc/classes/AudioEffectEQ21.xml b/doc/classes/AudioEffectEQ21.xml
index 86d2189f27..99d35604fc 100644
--- a/doc/classes/AudioEffectEQ21.xml
+++ b/doc/classes/AudioEffectEQ21.xml
@@ -27,7 +27,6 @@
Band 19 : 11000 Hz
Band 20 : 16000 Hz
Band 21 : 22000 Hz
-
See also [AudioEffectEQ], [AudioEffectEQ6], [AudioEffectEQ10].
</description>
<tutorials>
diff --git a/doc/classes/AudioEffectEQ6.xml b/doc/classes/AudioEffectEQ6.xml
index 8f789cdbdd..047c4c960f 100644
--- a/doc/classes/AudioEffectEQ6.xml
+++ b/doc/classes/AudioEffectEQ6.xml
@@ -12,7 +12,6 @@
Band 4 : 1000 Hz
Band 5 : 3200 Hz
Band 6 : 10000 Hz
-
See also [AudioEffectEQ], [AudioEffectEQ10], [AudioEffectEQ21].
</description>
<tutorials>
diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml
index 7b3ba632cc..51df1e99dd 100644
--- a/doc/classes/AudioServer.xml
+++ b/doc/classes/AudioServer.xml
@@ -7,7 +7,7 @@
AudioServer is a low level server interface for audio access. It is in charge of creating sample data (playable audio) as well as its playback via a voice interface.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/audio/audio_buses.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_buses.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml
index d332277248..15bbb1625c 100644
--- a/doc/classes/AudioStream.xml
+++ b/doc/classes/AudioStream.xml
@@ -7,7 +7,7 @@
Base class for audio streams. Audio streams are used for music playback, or other types of streamed sounds that don't fit or require more flexibility than a [Sample].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml
index 7d9fcf3e01..00d03d2b20 100644
--- a/doc/classes/AudioStreamPlayer.xml
+++ b/doc/classes/AudioStreamPlayer.xml
@@ -7,8 +7,8 @@
Plays background audio.
</description>
<tutorials>
- http://docs.godotengine.org/en/latest/learning/features/audio/index.html
- http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html
+ <link>http://docs.godotengine.org/en/latest/learning/features/audio/index.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/AudioStreamPlayer2D.xml b/doc/classes/AudioStreamPlayer2D.xml
index 81beab245d..03b0a3aa81 100644
--- a/doc/classes/AudioStreamPlayer2D.xml
+++ b/doc/classes/AudioStreamPlayer2D.xml
@@ -7,8 +7,8 @@
Plays audio that dampens with distance from screen center.
</description>
<tutorials>
- http://docs.godotengine.org/en/latest/learning/features/audio/index.html
- http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html
+ <link>http://docs.godotengine.org/en/latest/learning/features/audio/index.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml
index 311efb7495..2746938c1d 100644
--- a/doc/classes/AudioStreamPlayer3D.xml
+++ b/doc/classes/AudioStreamPlayer3D.xml
@@ -7,8 +7,8 @@
Plays a sound effect with directed sound effects, dampens with distance if needed, generates effect of hearable position in space.
</description>
<tutorials>
- http://docs.godotengine.org/en/latest/learning/features/audio/index.html
- http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html
+ <link>http://docs.godotengine.org/en/latest/learning/features/audio/index.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/AudioStreamSample.xml b/doc/classes/AudioStreamSample.xml
index 8b6651abe7..b6abda1a6f 100644
--- a/doc/classes/AudioStreamSample.xml
+++ b/doc/classes/AudioStreamSample.xml
@@ -13,6 +13,9 @@
<methods>
</methods>
<members>
+ <member name="data" type="PoolByteArray" setter="set_data" getter="get_data">
+ Contains the audio data in bytes.
+ </member>
<member name="format" type="int" setter="set_format" getter="get_format" enum="AudioStreamSample.Format">
Audio format. See FORMAT_* constants for values.
</member>
diff --git a/doc/classes/BakedLightmap.xml b/doc/classes/BakedLightmap.xml
index 45c60302a6..77895249e5 100644
--- a/doc/classes/BakedLightmap.xml
+++ b/doc/classes/BakedLightmap.xml
@@ -5,7 +5,7 @@
<description>
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/baked_lightmaps.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/baked_lightmaps.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index 554ed99964..fe8debe1a9 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -4,11 +4,12 @@
3x3 matrix datatype.
</brief_description>
<description>
- 3x3 matrix used for 3D rotation and scale. Contains 3 vector fields x,y and z as its columns, which can be interpreted as the local basis vectors of a transformation. Can also be accessed as array of 3D vectors. These vectors are orthogonal to each other, but are not necessarily normalized. Almost always used as orthogonal basis for a [Transform].
+ 3x3 matrix used for 3D rotation and scale. Contains 3 vector fields x,y and z as its columns, which can be interpreted as the local basis vectors of a transformation. Can also be accessed as array of 3D vectors. These vectors are orthogonal to each other, but are not necessarily normalized (due to scaling). Almost always used as orthogonal basis for a [Transform].
For such use, it is composed of a scaling and a rotation matrix, in that order (M = R.S).
</description>
<tutorials>
- http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html
+ <link>http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link>
+ <link>http://docs.godotengine.org/en/latest/tutorials/math/rotations.html</link>
</tutorials>
<demos>
</demos>
@@ -105,7 +106,7 @@
<argument index="1" name="phi" type="float">
</argument>
<description>
- Introduce an additional rotation around the given axis by phi (radians). Only relevant when the matrix is being used as a part of [Transform]. The axis must be a normalized vector.
+ Introduce an additional rotation around the given axis by phi (radians). The axis must be a normalized vector.
</description>
</method>
<method name="scaled">
@@ -114,7 +115,18 @@
<argument index="0" name="scale" type="Vector3">
</argument>
<description>
- Introduce an additional scaling specified by the given 3D scaling factor. Only relevant when the matrix is being used as a part of [Transform].
+ Introduce an additional scaling specified by the given 3D scaling factor.
+ </description>
+ </method>
+ <method name="slerp">
+ <return type="Basis">
+ </return>
+ <argument index="0" name="b" type="Basis">
+ </argument>
+ <argument index="1" name="t" type="float">
+ </argument>
+ <description>
+ Assuming that the matrix is a proper rotation matrix, slerp performs a spherical-linear interpolation with another rotation matrix.
</description>
</method>
<method name="tdotx">
diff --git a/doc/classes/Bone2D.xml b/doc/classes/Bone2D.xml
new file mode 100644
index 0000000000..7e714305cd
--- /dev/null
+++ b/doc/classes/Bone2D.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Bone2D" inherits="Node2D" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="apply_rest">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_index_in_skeleton" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_skeleton_rest" qualifiers="const">
+ <return type="Transform2D">
+ </return>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="default_length" type="float" setter="set_default_length" getter="get_default_length">
+ </member>
+ <member name="rest" type="Transform2D" setter="set_rest" getter="get_rest">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 1705808c04..a04e38af5c 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -11,8 +11,8 @@
Ultimately, a transform notification can be requested, which will notify the node that its global position changed in case the parent tree changed.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html
- http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html</link>
</tutorials>
<demos>
</demos>
@@ -566,6 +566,9 @@
<constant name="BLEND_MODE_PREMULT_ALPHA" value="4" enum="BlendMode">
Mix blending mode. Colors are assumed to be premultiplied by the alpha (opacity) value.
</constant>
+ <constant name="BLEND_MODE_DISABLED" value="5" enum="BlendMode">
+ Disable blending mode. Colors including alpha are written as is. Only applicable for render targets with a transparent background. No lighting will be applied.
+ </constant>
<constant name="NOTIFICATION_TRANSFORM_CHANGED" value="29">
Canvas item transform has changed. Only received if requested.
</constant>
diff --git a/doc/classes/CanvasItemMaterial.xml b/doc/classes/CanvasItemMaterial.xml
index 354bc10cd2..fe7194dcfe 100644
--- a/doc/classes/CanvasItemMaterial.xml
+++ b/doc/classes/CanvasItemMaterial.xml
@@ -36,9 +36,6 @@
<constant name="BLEND_MODE_PREMULT_ALPHA" value="4" enum="BlendMode">
Mix blending mode. Colors are assumed to be premultiplied by the alpha (opacity) value.
</constant>
- <constant name="BLEND_MODE_DISABLED" value="5" enum="BlendMode">
- Disable blending mode. Colors including alpha are written as is. Only applicable for render targets with a transparent background. No lighting will be applied.
- </constant>
<constant name="LIGHT_MODE_NORMAL" value="0" enum="LightMode">
Render the material using both light and non-light sensitive material properties.
</constant>
diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml
index 3e4d1b29f7..d4412e15c9 100644
--- a/doc/classes/CanvasLayer.xml
+++ b/doc/classes/CanvasLayer.xml
@@ -7,8 +7,8 @@
Canvas drawing layer. [CanvasItem] nodes that are direct or indirect children of a [code]CanvasLayer[/code] will be drawn in that layer. The layer is a numeric index that defines the draw order. The default 2D scene renders with index 0, so a [code]CanvasLayer[/code] with index -1 will be drawn below, and one with index 1 will be drawn above. This is very useful for HUDs (in layer 1+ or above), or backgrounds (in layer -1 or below).
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html
- http://docs.godotengine.org/en/3.0/tutorials/2d/canvas_layers.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/2d/canvas_layers.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/ClassDB.xml b/doc/classes/ClassDB.xml
index 6b2c540c19..e215585b74 100644
--- a/doc/classes/ClassDB.xml
+++ b/doc/classes/ClassDB.xml
@@ -112,7 +112,7 @@
<argument index="1" name="no_inheritance" type="bool" default="false">
</argument>
<description>
- Returns an array with all the signals of 'class' or its ancestry if 'no_inheritance' is false. Every element of the array is a [Dictionary] as described in [class_get_signal].
+ Returns an array with all the signals of 'class' or its ancestry if 'no_inheritance' is false. Every element of the array is a [Dictionary] as described in [method class_get_signal].
</description>
</method>
<method name="class_has_integer_constant" qualifiers="const">
diff --git a/doc/classes/CollisionShape.xml b/doc/classes/CollisionShape.xml
index 95fa1175c3..682c9340df 100644
--- a/doc/classes/CollisionShape.xml
+++ b/doc/classes/CollisionShape.xml
@@ -7,7 +7,7 @@
Editor facility for creating and editing collision shapes in 3D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area] to give it a detection shape, or add it to a [PhysicsBody] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/CollisionShape2D.xml b/doc/classes/CollisionShape2D.xml
index 3136f132bf..3312fad99c 100644
--- a/doc/classes/CollisionShape2D.xml
+++ b/doc/classes/CollisionShape2D.xml
@@ -7,7 +7,7 @@
Editor facility for creating and editing collision shapes in 2D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area2D] to give it a detection shape, or add it to a [PhysicsBody2D] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml
index 2617aaef17..104c9e0a98 100644
--- a/doc/classes/Color.xml
+++ b/doc/classes/Color.xml
@@ -129,9 +129,9 @@
<argument index="3" name="a" type="float" default="1">
</argument>
<description>
- Constructs a color from an HSV profile. [code]h[/code] is a value between 0 and 360. [code]s[/code] and [code]v[/code] are values between 0 and 1.
+ Constructs a color from an HSV profile. [code]h[/code], [code]s[/code], and [code]v[/code] are values between 0 and 1.
[codeblock]
- var c = Color.from_hsv(210, 0.5, 0.79, 0.8) # equivalent to HSV(210, 50, 79, 0.8) or Color8(100, 151, 201, 0.8)
+ var c = Color.from_hsv(0.58, 0.5, 0.79, 0.8) # equivalent to HSV(210, 50, 79, 0.8) or Color8(100, 151, 201, 0.8)
[/codeblock]
</description>
</method>
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/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml
index eb86dc8af8..9d7df14014 100644
--- a/doc/classes/ColorPickerButton.xml
+++ b/doc/classes/ColorPickerButton.xml
@@ -11,7 +11,7 @@
<demos>
</demos>
<methods>
- <method name="get_picker" qualifiers="const">
+ <method name="get_picker">
<return type="ColorPicker">
</return>
<description>
@@ -42,6 +42,10 @@
Emitted when the color changes.
</description>
</signal>
+ <signal name="popup_closed">
+ <description>
+ </description>
+ </signal>
</signals>
<constants>
</constants>
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/Control.xml b/doc/classes/Control.xml
index 4b6a9cca8a..52382337cf 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -13,8 +13,8 @@
[Theme] resources change the Control's appearance. If you change the [Theme] on a [code]Control[/code] node, it affects all of its children. To override some of the theme's parameters, call one of the [code]add_*_override[/code] methods, like [method add_font_override]. You can override the theme with the inspector.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/gui/index.html
- http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/gui/index.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html</link>
</tutorials>
<demos>
</demos>
@@ -51,7 +51,7 @@
<argument index="1" name="color" type="Color">
</argument>
<description>
- Overrides the color in the [theme] resource the node uses.
+ Overrides the color in the [member theme] resource the node uses.
</description>
</method>
<method name="add_constant_override">
@@ -62,7 +62,7 @@
<argument index="1" name="constant" type="int">
</argument>
<description>
- Overrides an integer constant in the [Theme] resource the node uses. If the [code]constant[/code] is invalid, Godot clears the override. See [member Theme.INVALID_CONSTANT] for more information.
+ Overrides an integer constant in the [member theme] resource the node uses. If the [code]constant[/code] is invalid, Godot clears the override. See [member Theme.INVALID_CONSTANT] for more information.
</description>
</method>
<method name="add_font_override">
@@ -73,7 +73,7 @@
<argument index="1" name="font" type="Font">
</argument>
<description>
- Overrides the [code]name[/code] font in the [theme] resource the node uses. If [code]font[/code] is empty, Godot clears the override.
+ Overrides the [code]name[/code] font in the [member theme] resource the node uses. If [code]font[/code] is empty, Godot clears the override.
</description>
</method>
<method name="add_icon_override">
@@ -84,7 +84,7 @@
<argument index="1" name="texture" type="Texture">
</argument>
<description>
- Overrides the [code]name[/code] icon in the [theme] resource the node uses. If [code]icon[/code] is empty, Godot clears the override.
+ Overrides the [code]name[/code] icon in the [member theme] resource the node uses. If [code]icon[/code] is empty, Godot clears the override.
</description>
</method>
<method name="add_shader_override">
@@ -95,7 +95,7 @@
<argument index="1" name="shader" type="Shader">
</argument>
<description>
- Overrides the [code]name[/code] shader in the [theme] resource the node uses. If [code]shader[/code] is empty, Godot clears the override.
+ Overrides the [code]name[/code] shader in the [member theme] resource the node uses. If [code]shader[/code] is empty, Godot clears the override.
</description>
</method>
<method name="add_stylebox_override">
@@ -106,7 +106,7 @@
<argument index="1" name="stylebox" type="StyleBox">
</argument>
<description>
- Overrides the [code]name[/code] [Stylebox] in the [theme] resource the node uses. If [code]stylebox[/code] is empty, Godot clears the override.
+ Overrides the [code]name[/code] [Stylebox] in the [member theme] resource the node uses. If [code]stylebox[/code] is empty, Godot clears the override.
</description>
</method>
<method name="can_drop_data" qualifiers="virtual">
diff --git a/doc/classes/Curve2D.xml b/doc/classes/Curve2D.xml
index 03db9c2ea1..26de8be42c 100644
--- a/doc/classes/Curve2D.xml
+++ b/doc/classes/Curve2D.xml
@@ -49,6 +49,26 @@
Returns the cache of points as a [PoolVector2Array].
</description>
</method>
+ <method name="get_closest_offset" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="to_point" type="Vector2">
+ </argument>
+ <description>
+ Returns the closest offset to [code]to_point[/code]. This offset is meant to be used in [method interpolate_baked].
+ [code]to_point[/code] must be in this curve's local space.
+ </description>
+ </method>
+ <method name="get_closest_point" qualifiers="const">
+ <return type="Vector2">
+ </return>
+ <argument index="0" name="to_point" type="Vector2">
+ </argument>
+ <description>
+ Returns the closest point (in curve's local space) to [code]to_point[/code].
+ [code]to_point[/code] must be in this curve's local space.
+ </description>
+ </method>
<method name="get_point_count" qualifiers="const">
<return type="int">
</return>
diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml
index f2308de12c..1355c74faf 100644
--- a/doc/classes/Curve3D.xml
+++ b/doc/classes/Curve3D.xml
@@ -56,6 +56,34 @@
Returns the cache of tilts as a [RealArray].
</description>
</method>
+ <method name="get_baked_up_vectors" qualifiers="const">
+ <return type="PoolVector3Array">
+ </return>
+ <description>
+ Returns the cache of up vectors as a [PoolVector3Array].
+ If [member up_vector_enabled] is [code]false[/code], the cache will be empty.
+ </description>
+ </method>
+ <method name="get_closest_offset" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="to_point" type="Vector3">
+ </argument>
+ <description>
+ Returns the closest offset to [code]to_point[/code]. This offset is meant to be used in one of the interpolate_baked* methods.
+ [code]to_point[/code] must be in this curve's local space.
+ </description>
+ </method>
+ <method name="get_closest_point" qualifiers="const">
+ <return type="Vector3">
+ </return>
+ <argument index="0" name="to_point" type="Vector3">
+ </argument>
+ <description>
+ Returns the closest point (in curve's local space) to [code]to_point[/code].
+ [code]to_point[/code] must be in this curve's local space.
+ </description>
+ </method>
<method name="get_point_count" qualifiers="const">
<return type="int">
</return>
@@ -124,6 +152,19 @@
Cubic interpolation tends to follow the curves better, but linear is faster (and often, precise enough).
</description>
</method>
+ <method name="interpolate_baked_up_vector" qualifiers="const">
+ <return type="Vector3">
+ </return>
+ <argument index="0" name="offset" type="float">
+ </argument>
+ <argument index="1" name="apply_tilt" type="bool" default="false">
+ </argument>
+ <description>
+ Returns an up vector within the curve at position [code]offset[/code], where [code]offset[/code] is measured as a distance in 3D units along the curve.
+ To do that, it finds the two cached up vectors where the [code]offset[/code] lies between, then interpolates the values. If [code]apply_tilt[/code] is [code]true[/code], an interpolated tilt is applied to the interpolated up vector.
+ If the curve has no up vectors, the function sends an error to the console, and returns (0, 1, 0).
+ </description>
+ </method>
<method name="interpolatef" qualifiers="const">
<return type="Vector3">
</return>
@@ -184,7 +225,7 @@
</argument>
<description>
Sets the tilt angle in radians for the point "idx". If the index is out of bounds, the function sends an error to the console.
- The tilt controls the rotation along the look-at axis an object traveling the path would have. In the case of a curve controlling a [PathFollow], this tilt is an offset over the natural tilt the PathFollow calculates.
+ The tilt controls the rotation along the look-at axis an object traveling the path would have. In the case of a curve controlling a [PathFollow] or [OrientedPathFollow], this tilt is an offset over the natural tilt the [PathFollow] or [OrientedPathFollow] calculates.
</description>
</method>
<method name="tessellate" qualifiers="const">
@@ -206,6 +247,9 @@
<member name="bake_interval" type="float" setter="set_bake_interval" getter="get_bake_interval">
The distance in meters between two adjacent cached points. Changing it forces the cache to be recomputed the next time the [method get_baked_points] or [method get_baked_length] function is called. The smaller the distance, the more points in the cache and the more memory it will consume, so use with care.
</member>
+ <member name="up_vector_enabled" type="bool" setter="set_up_vector_enabled" getter="is_up_vector_enabled">
+ If [code]true[/code], the curve will bake up vectors used for orientation. See [OrientedPathFollow]. Changing it forces the cache to be recomputed.
+ </member>
</members>
<constants>
</constants>
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/DirectionalLight.xml b/doc/classes/DirectionalLight.xml
index 5e492b74da..ef75182811 100644
--- a/doc/classes/DirectionalLight.xml
+++ b/doc/classes/DirectionalLight.xml
@@ -7,7 +7,7 @@
A DirectionalLight is a type of [Light] node that emits light constantly in one direction (the negative z axis of the node). It is used lights with strong intensity that are located far away from the scene to model sunlight or moonlight. The worldspace location of the DirectionalLight transform (origin) is ignored, only the basis is used do determine light direction.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Directory.xml b/doc/classes/Directory.xml
index 040ccf4462..d8ad208fa7 100644
--- a/doc/classes/Directory.xml
+++ b/doc/classes/Directory.xml
@@ -23,7 +23,7 @@
[/codeblock]
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/getting_started/step_by_step/filesystem.html
+ <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/filesystem.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/DynamicFont.xml b/doc/classes/DynamicFont.xml
index 03752c6e89..2e2904c16c 100644
--- a/doc/classes/DynamicFont.xml
+++ b/doc/classes/DynamicFont.xml
@@ -73,6 +73,10 @@
<member name="font_data" type="DynamicFontData" setter="set_font_data" getter="get_font_data">
The font data.
</member>
+ <member name="outline_color" type="Color" setter="set_outline_color" getter="get_outline_color">
+ </member>
+ <member name="outline_size" type="int" setter="set_outline_size" getter="get_outline_size">
+ </member>
<member name="size" type="int" setter="set_size" getter="get_size">
The font size.
</member>
diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml
index 406bd7b7df..e48eb82691 100644
--- a/doc/classes/EditorImportPlugin.xml
+++ b/doc/classes/EditorImportPlugin.xml
@@ -5,10 +5,7 @@
</brief_description>
<description>
EditorImportPlugins provide a way to extend the editor's resource import functionality. Use them to import resources from custom files or to provide alternatives to the editor's existing importers. Register your [EditorPlugin] with [method EditorPlugin.add_import_plugin].
-
EditorImportPlugins work by associating with specific file extensions and a resource type. See [method get_recognized_extension] and [method get_resource_type]). They may optionally specify some import presets that affect the import process. EditorImportPlugins are responsible for creating the resources and saving them in the [code].import[/code] directory.
-
-
Below is an example EditorImportPlugin that imports a [Mesh] from a file with the extension ".special" or ".spec":
[codeblock]
tool
diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml
new file mode 100644
index 0000000000..381eef5a40
--- /dev/null
+++ b/doc/classes/EditorInspector.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="EditorInspector" inherits="ScrollContainer" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <signals>
+ <signal name="object_id_selected">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="property_keyed">
+ <argument index="0" name="property" type="String">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="resource_selected">
+ <argument index="0" name="res" type="Object">
+ </argument>
+ <argument index="1" name="prop" type="String">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/EditorInspectorPlugin.xml b/doc/classes/EditorInspectorPlugin.xml
new file mode 100644
index 0000000000..9bda768b14
--- /dev/null
+++ b/doc/classes/EditorInspectorPlugin.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="EditorInspectorPlugin" inherits="Reference" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="add_custom_control">
+ <return type="void">
+ </return>
+ <argument index="0" name="control" type="Control">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="add_property_editor">
+ <return type="void">
+ </return>
+ <argument index="0" name="property" type="String">
+ </argument>
+ <argument index="1" name="editor" type="Control">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="add_property_editor_for_multiple_properties">
+ <return type="void">
+ </return>
+ <argument index="0" name="label" type="String">
+ </argument>
+ <argument index="1" name="properties" type="PoolStringArray">
+ </argument>
+ <argument index="2" name="editor" type="Control">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="can_handle" qualifiers="virtual">
+ <return type="bool">
+ </return>
+ <argument index="0" name="object" type="Object">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="parse_begin" qualifiers="virtual">
+ <return type="void">
+ </return>
+ <argument index="0" name="object" type="Object">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="parse_category" qualifiers="virtual">
+ <return type="void">
+ </return>
+ <argument index="0" name="object" type="Object">
+ </argument>
+ <argument index="1" name="category" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="parse_end" qualifiers="virtual">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="parse_property" qualifiers="virtual">
+ <return type="bool">
+ </return>
+ <argument index="0" name="object" type="Object">
+ </argument>
+ <argument index="1" name="type" type="int">
+ </argument>
+ <argument index="2" name="path" type="String">
+ </argument>
+ <argument index="3" name="hint" type="int">
+ </argument>
+ <argument index="4" name="hint_text" type="String">
+ </argument>
+ <argument index="5" name="usage" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml
index 006a7ca690..19bd7e6d52 100644
--- a/doc/classes/EditorInterface.xml
+++ b/doc/classes/EditorInterface.xml
@@ -4,7 +4,7 @@
Editor interface and main components.
</brief_description>
<description>
- Editor interface. Allows saving and (re-)loading scenes, rendering mesh previews, inspecting and editing resources and objects and provides access to [EditorSettings], [EditorFileSystem], [EditorResourcePreview]\ er, [ScriptEditor], the editor viewport, as well as information about scenes. Also see [EditorPlugin] and [EditorScript].
+ Editor interface. Allows saving and (re-)loading scenes, rendering mesh previews, inspecting and editing resources and objects and provides access to [EditorSettings], [EditorFileSystem], [EditorResourcePreview], [ScriptEditor], the editor viewport, as well as information about scenes. Also see [EditorPlugin] and [EditorScript].
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 846d6f18ff..f5fbf8e313 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -7,7 +7,7 @@
Plugins are used by the editor to extend functionality. The most common types of plugins are those which edit a given node or resource type, import plugins and export plugins.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/development/plugins/index.html
+ <link>http://docs.godotengine.org/en/3.0/development/plugins/index.html</link>
</tutorials>
<demos>
</demos>
@@ -81,7 +81,7 @@
<method name="add_export_plugin">
<return type="void">
</return>
- <argument index="0" name="exporter" type="EditorExportPlugin">
+ <argument index="0" name="plugin" type="EditorExportPlugin">
</argument>
<description>
</description>
@@ -94,6 +94,14 @@
<description>
</description>
</method>
+ <method name="add_inspector_plugin">
+ <return type="void">
+ </return>
+ <argument index="0" name="plugin" type="EditorInspectorPlugin">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="add_scene_import_plugin">
<return type="void">
</return>
@@ -114,7 +122,7 @@
<argument index="3" name="ud" type="Variant" default="null">
</argument>
<description>
- Adds a custom menu to 'Project > Tools' as [code]name[/code] that calls [code]callback[/code] on an instance of [code]handler[/code] with a parameter [code]ud[/code] when user activates it.
+ Adds a custom menu to 'Project &gt; Tools' as [code]name[/code] that calls [code]callback[/code] on an instance of [code]handler[/code] with a parameter [code]ud[/code] when user activates it.
</description>
</method>
<method name="add_tool_submenu_item">
@@ -135,6 +143,12 @@
This is used, for example, in shader editors to let the plugin know that it must apply the shader code being written by the user to the object.
</description>
</method>
+ <method name="build" qualifiers="virtual">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="clear" qualifiers="virtual">
<return type="void">
</return>
@@ -221,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>
@@ -341,7 +362,7 @@
<method name="remove_export_plugin">
<return type="void">
</return>
- <argument index="0" name="exporter" type="EditorExportPlugin">
+ <argument index="0" name="plugin" type="EditorExportPlugin">
</argument>
<description>
</description>
@@ -354,6 +375,14 @@
<description>
</description>
</method>
+ <method name="remove_inspector_plugin">
+ <return type="void">
+ </return>
+ <argument index="0" name="plugin" type="EditorInspectorPlugin">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="remove_scene_import_plugin">
<return type="void">
</return>
@@ -368,7 +397,7 @@
<argument index="0" name="name" type="String">
</argument>
<description>
- Removes a menu [code]name[/code] from 'Project > Tools'.
+ Removes a menu [code]name[/code] from 'Project &gt; Tools'.
</description>
</method>
<method name="save_external_data" qualifiers="virtual">
@@ -424,6 +453,12 @@
Emitted when user change main screen view (2D, 3D, Script, AssetLib). Works also with screens which are defined by plugins.
</description>
</signal>
+ <signal name="resource_saved">
+ <argument index="0" name="resource" type="Object">
+ </argument>
+ <description>
+ </description>
+ </signal>
<signal name="scene_changed">
<argument index="0" name="scene_root" type="Object">
</argument>
diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml
new file mode 100644
index 0000000000..9b5452ec14
--- /dev/null
+++ b/doc/classes/EditorProperty.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="EditorProperty" inherits="Container" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="get_edited_object">
+ <return type="Object">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_edited_property">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="update_property" qualifiers="virtual">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="checkable" type="bool" setter="set_checkable" getter="is_checkable">
+ </member>
+ <member name="checked" type="bool" setter="set_checked" getter="is_checked">
+ </member>
+ <member name="draw_red" type="bool" setter="set_draw_red" getter="is_draw_red">
+ </member>
+ <member name="keying" type="bool" setter="set_keying" getter="is_keying">
+ </member>
+ <member name="label" type="String" setter="set_label" getter="get_label">
+ </member>
+ <member name="read_only" type="bool" setter="set_read_only" getter="is_read_only">
+ </member>
+ </members>
+ <signals>
+ <signal name="multiple_properties_changed">
+ <argument index="0" name="properties" type="PoolStringArray">
+ </argument>
+ <argument index="1" name="value" type="Array">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="object_id_selected">
+ <argument index="0" name="property" type="String">
+ </argument>
+ <argument index="1" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="property_changed">
+ <argument index="0" name="property" type="String">
+ </argument>
+ <argument index="1" name="value" type="Nil">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="property_checked">
+ <argument index="0" name="property" type="String">
+ </argument>
+ <argument index="1" name="bool" type="String">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="property_keyed">
+ <argument index="0" name="property" type="String">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="property_keyed_with_value">
+ <argument index="0" name="property" type="String">
+ </argument>
+ <argument index="1" name="value" type="Nil">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="resource_selected">
+ <argument index="0" name="path" type="String">
+ </argument>
+ <argument index="1" name="resource" type="Object">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="selected">
+ <argument index="0" name="path" type="String">
+ </argument>
+ <argument index="1" name="focusable_idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
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/EditorSettings.xml b/doc/classes/EditorSettings.xml
index cdd9560eda..bd85075b7e 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -55,6 +55,18 @@
Get the list of favorite directories for this project.
</description>
</method>
+ <method name="get_project_metadata" qualifiers="const">
+ <return type="Variant">
+ </return>
+ <argument index="0" name="section" type="String">
+ </argument>
+ <argument index="1" name="key" type="String">
+ </argument>
+ <argument index="2" name="default" type="Variant" default="null">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_project_settings_dir" qualifiers="const">
<return type="String">
</return>
@@ -131,6 +143,18 @@
<description>
</description>
</method>
+ <method name="set_project_metadata">
+ <return type="void">
+ </return>
+ <argument index="0" name="section" type="String">
+ </argument>
+ <argument index="1" name="key" type="String">
+ </argument>
+ <argument index="2" name="data" type="Variant">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_recent_dirs">
<return type="void">
</return>
diff --git a/doc/classes/EditorSpatialGizmo.xml b/doc/classes/EditorSpatialGizmo.xml
index 58d2671968..3636442b85 100644
--- a/doc/classes/EditorSpatialGizmo.xml
+++ b/doc/classes/EditorSpatialGizmo.xml
@@ -24,8 +24,6 @@
</return>
<argument index="0" name="triangles" type="TriangleMesh">
</argument>
- <argument index="1" name="bounds" type="AABB">
- </argument>
<description>
Add collision triangles to the gizmo for picking. A [TriangleMesh] can be generated from a regular [Mesh] too. Call this function during [method redraw].
</description>
diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml
index 6384b4d0fd..f921b76b21 100644
--- a/doc/classes/Engine.xml
+++ b/doc/classes/Engine.xml
@@ -11,6 +11,34 @@
<demos>
</demos>
<methods>
+ <method name="get_author_info" qualifiers="const">
+ <return type="Dictionary">
+ </return>
+ <description>
+ Returns engine author information in a Dictionary.
+ "lead_developers" - Array of Strings, lead developer names
+ "founders" - Array of Strings, founder names
+ "project_managers" - Array of Strings, project manager names
+ "developers" - Array of Strings, developer names
+ </description>
+ </method>
+ <method name="get_copyright_info" qualifiers="const">
+ <return type="Array">
+ </return>
+ <description>
+ Returns an Array of copyright information Dictionaries.
+ "name" - String, component name
+ "parts" - Array of Dictionaries {"files", "copyright", "license"} describing subsections of the component
+ </description>
+ </method>
+ <method name="get_donor_info" qualifiers="const">
+ <return type="Dictionary">
+ </return>
+ <description>
+ Returns a Dictionary of Arrays of donor names.
+ {"platinum_sponsors", "gold_sponsors", "mini_sponsors", "gold_donors", "silver_donors", "bronze_donors"}
+ </description>
+ </method>
<method name="get_frames_drawn">
<return type="int">
</return>
@@ -25,6 +53,20 @@
Returns the frames per second of the running game.
</description>
</method>
+ <method name="get_license_info" qualifiers="const">
+ <return type="Dictionary">
+ </return>
+ <description>
+ Returns Dictionary of licenses used by Godot and included third party components.
+ </description>
+ </method>
+ <method name="get_license_text" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ Returns Godot license text.
+ </description>
+ </method>
<method name="get_main_loop" qualifiers="const">
<return type="MainLoop">
</return>
@@ -45,7 +87,6 @@
</return>
<description>
Returns the current engine version information in a Dictionary.
-
"major" - Holds the major version number as an int
"minor" - Holds the minor version number as an int
"patch" - Holds the patch version number as an int
@@ -77,6 +118,8 @@
<member name="iterations_per_second" type="int" setter="set_iterations_per_second" getter="get_iterations_per_second">
The number of fixed iterations per second (for fixed process and physics).
</member>
+ <member name="physics_jitter_fix" type="float" setter="set_physics_jitter_fix" getter="get_physics_jitter_fix">
+ </member>
<member name="target_fps" type="int" setter="set_target_fps" getter="get_target_fps">
The desired frames per second. If the hardware cannot keep up, this setting may not be respected. Defaults to 0, which indicates no limit.
</member>
diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml
index 18adaa645c..cd2584ed43 100644
--- a/doc/classes/Environment.xml
+++ b/doc/classes/Environment.xml
@@ -13,8 +13,8 @@
- Adjustments
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/environment_and_post_processing.html
- http://docs.godotengine.org/en/3.0/tutorials/3d/high_dynamic_range.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/environment_and_post_processing.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/high_dynamic_range.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/File.xml b/doc/classes/File.xml
index bd368e967a..20054ac9dc 100644
--- a/doc/classes/File.xml
+++ b/doc/classes/File.xml
@@ -9,20 +9,20 @@
[codeblock]
func save(content):
var file = File.new()
- file.open("user://save_game.dat", file.WRITE)
+ file.open("user://save_game.dat", File.WRITE)
file.store_string(content)
file.close()
func load():
var file = File.new()
- file.open("user://save_game.dat", file.READ)
+ file.open("user://save_game.dat", File.READ)
var content = file.get_as_text()
file.close()
return content
[/codeblock]
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/getting_started/step_by_step/filesystem.html
+ <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/filesystem.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml
index b165d8fb8c..247228d265 100644
--- a/doc/classes/FileDialog.xml
+++ b/doc/classes/FileDialog.xml
@@ -33,6 +33,12 @@
<description>
</description>
</method>
+ <method name="get_line_edit">
+ <return type="LineEdit">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_vbox">
<return type="VBoxContainer">
</return>
diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml
index 376d234ec3..3e47b167ef 100644
--- a/doc/classes/Font.xml
+++ b/doc/classes/Font.xml
@@ -24,6 +24,8 @@
</argument>
<argument index="4" name="clip_w" type="int" default="-1">
</argument>
+ <argument index="5" name="outline_modulate" type="Color" default="Color( 1, 1, 1, 1 )">
+ </argument>
<description>
Draw "string" into a canvas item using the font at a given position, with "modulate" color, and optionally clipping the width. "position" specifies the baseline, not the top. To draw from the top, [i]ascent[/i] must be added to the Y axis.
</description>
@@ -41,6 +43,8 @@
</argument>
<argument index="4" name="modulate" type="Color" default="Color( 1, 1, 1, 1 )">
</argument>
+ <argument index="5" name="outline" type="bool" default="false">
+ </argument>
<description>
Draw character "char" into a canvas item using the font at a given position, with "modulate" color, and optionally kerning if "next" is passed. clipping the width. "position" specifies the baseline, not the top. To draw from the top, [i]ascent[/i] must be added to the Y axis. The width used by the character is returned, making this function useful for drawing strings character by character.
</description>
@@ -75,6 +79,12 @@
Return the size of a string, taking kerning and advance into account.
</description>
</method>
+ <method name="has_outline" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="is_distance_field_hint" qualifiers="const">
<return type="bool">
</return>
diff --git a/doc/classes/GIProbe.xml b/doc/classes/GIProbe.xml
index fde91d09ac..77dea73564 100644
--- a/doc/classes/GIProbe.xml
+++ b/doc/classes/GIProbe.xml
@@ -5,7 +5,7 @@
<description>
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/gi_probes.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/gi_probes.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Geometry.xml b/doc/classes/Geometry.xml
index 78496700dc..ea2e2f7595 100644
--- a/doc/classes/Geometry.xml
+++ b/doc/classes/Geometry.xml
@@ -160,6 +160,21 @@
<description>
</description>
</method>
+ <method name="line_intersects_line_2d">
+ <return type="Variant">
+ </return>
+ <argument index="0" name="from_a" type="Vector2">
+ </argument>
+ <argument index="1" name="dir_a" type="Vector2">
+ </argument>
+ <argument index="2" name="from_b" type="Vector2">
+ </argument>
+ <argument index="3" name="dir_b" type="Vector2">
+ </argument>
+ <description>
+ Checks if the two lines ([code]from_a[/code], [code]dir_a[/code]) and ([code]from_b[/code], [code]dir_b[/code]) intersect. If yes, return the point of intersection as [Vector2]. If no intersection takes place, returns an empty [Variant]. Note that the lines are specified using direction vectors, not end points.
+ </description>
+ </method>
<method name="make_atlas">
<return type="Dictionary">
</return>
@@ -259,21 +274,6 @@
Checks if the two segments ([code]from_a[/code], [code]to_a[/code]) and ([code]from_b[/code], [code]to_b[/code]) intersect. If yes, return the point of intersection as [Vector2]. If no intersection takes place, returns an empty [Variant].
</description>
</method>
- <method name="line_intersects_line_2d">
- <return type="Variant">
- </return>
- <argument index="0" name="from_a" type="Vector2">
- </argument>
- <argument index="1" name="dir_a" type="Vector2">
- </argument>
- <argument index="2" name="from_b" type="Vector2">
- </argument>
- <argument index="3" name="dir_b" type="Vector2">
- </argument>
- <description>
- Checks if the two lines ([code]from_a[/code], [code]dir_a[/code]) and ([code]from_b[/code], [code]dir_b[/code]) intersect. If yes, return the point of intersection as [Vector2]. If no intersection takes place, returns an empty [Variant]. Note that the lines are specified using direction vectors, not end points.
- </description>
- </method>
<method name="segment_intersects_sphere">
<return type="PoolVector3Array">
</return>
diff --git a/doc/classes/GridContainer.xml b/doc/classes/GridContainer.xml
index 346ab9d357..8a8a9a2d24 100644
--- a/doc/classes/GridContainer.xml
+++ b/doc/classes/GridContainer.xml
@@ -11,6 +11,16 @@
<demos>
</demos>
<methods>
+ <method name="get_child_control_at_cell">
+ <return type="Control">
+ </return>
+ <argument index="0" name="row" type="int">
+ </argument>
+ <argument index="1" name="column" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="columns" type="int" setter="set_columns" getter="get_columns">
diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml
index 018b548ef1..08e2f649a0 100644
--- a/doc/classes/HTTPClient.xml
+++ b/doc/classes/HTTPClient.xml
@@ -10,8 +10,8 @@
For more information on HTTP, see https://developer.mozilla.org/en-US/docs/Web/HTTP (or read RFC 2616 to get it straight from the source: https://tools.ietf.org/html/rfc2616).
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/networking/http_client_class.html
- http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/networking/http_client_class.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml
index ec9f86993f..c5bb10a23a 100644
--- a/doc/classes/HTTPRequest.xml
+++ b/doc/classes/HTTPRequest.xml
@@ -8,7 +8,7 @@
Can be used to make HTTP requests, i.e. download or upload files or web content via HTTP.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index ea61aced83..9fc7672a80 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -210,6 +210,8 @@
<method name="generate_mipmaps">
<return type="int" enum="Error">
</return>
+ <argument index="0" name="renormalize" type="bool" default="false">
+ </argument>
<description>
Generates mipmaps for the image. Mipmaps are pre-calculated and lower resolution copies of the image. Mipmaps are automatically used if the image needs to be scaled down when rendered. This improves image quality and the performance of the rendering. Returns an error if the image is compressed, in a custom format or if the image's width/height is 0.
</description>
@@ -255,6 +257,14 @@
Returns the color of the pixel at [code](x, y)[/code] if the image is locked. If the image is unlocked it always returns a [Color] with the value [code](0, 0, 0, 1.0)[/code].
</description>
</method>
+ <method name="get_pixelv" qualifiers="const">
+ <return type="Color">
+ </return>
+ <argument index="0" name="src" type="Vector2">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_rect" qualifiers="const">
<return type="Image">
</return>
@@ -328,6 +338,7 @@
<argument index="0" name="buffer" type="PoolByteArray">
</argument>
<description>
+ Loads an image from the binary contents of a JPEG file.
</description>
</method>
<method name="load_png_from_buffer">
@@ -336,6 +347,7 @@
<argument index="0" name="buffer" type="PoolByteArray">
</argument>
<description>
+ Loads an image from the binary contents of a PNG file.
</description>
</method>
<method name="lock">
@@ -381,6 +393,12 @@
Resizes the image to the nearest power of 2 for the width and height. If [code]square[/code] is [code]true[/code] then set width and height to be the same.
</description>
</method>
+ <method name="rgbe_to_srgb">
+ <return type="Image">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="save_png" qualifiers="const">
<return type="int" enum="Error">
</return>
@@ -411,6 +429,16 @@
[/codeblock]
</description>
</method>
+ <method name="set_pixelv">
+ <return type="void">
+ </return>
+ <argument index="0" name="dst" type="Vector2">
+ </argument>
+ <argument index="1" name="color" type="Color">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="shrink_x2">
<return type="void">
</return>
diff --git a/doc/classes/ImageTexture.xml b/doc/classes/ImageTexture.xml
index 9a5937299c..0bff3317db 100644
--- a/doc/classes/ImageTexture.xml
+++ b/doc/classes/ImageTexture.xml
@@ -47,12 +47,12 @@
</description>
</method>
<method name="load">
- <return type="void">
+ <return type="int" enum="Error">
</return>
<argument index="0" name="path" type="String">
</argument>
<description>
- Load an [code]ImageTexture[/code].
+ Load an [code]ImageTexture[/code] from a file path.
</description>
</method>
<method name="set_data">
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index f537908625..a4346c1485 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -7,7 +7,7 @@
A Singleton that deals with inputs. This includes key presses, mouse buttons and movement, joypads, and input actions. Actions and their events can be set in the Project Settings / Input Map tab. Or be set with [InputMap].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/index.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/index.html</link>
</tutorials>
<demos>
</demos>
@@ -48,6 +48,14 @@
If the device has an accelerometer, this will return the acceleration. Otherwise, it returns an empty [Vector3].
</description>
</method>
+ <method name="get_action_strength" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="action" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_connected_joypads">
<return type="Array">
</return>
@@ -284,7 +292,15 @@
<argument index="2" name="hotspot" type="Vector2" default="Vector2( 0, 0 )">
</argument>
<description>
- Set a custom mouse cursor image, which is only visible inside the game window. The hotspot can also be specified. See enum [code]CURSOR_*[/code] for the list of shapes.
+ Set a custom mouse cursor image, which is only visible inside the game window. The hotspot can also be specified. Passing [code]null[/code] to the image parameter resets to the system cursor. See enum [code]CURSOR_*[/code] for the list of shapes.
+ </description>
+ </method>
+ <method name="set_default_cursor_shape">
+ <return type="void">
+ </return>
+ <argument index="0" name="shape" type="int" enum="Input.CursorShape" default="0">
+ </argument>
+ <description>
</description>
</method>
<method name="set_mouse_mode">
diff --git a/doc/classes/InputEvent.xml b/doc/classes/InputEvent.xml
index b30b144614..993d62d188 100644
--- a/doc/classes/InputEvent.xml
+++ b/doc/classes/InputEvent.xml
@@ -7,26 +7,25 @@
Base class of all sort of input event. See [method Node._input].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html
- http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link>
</tutorials>
<demos>
</demos>
<methods>
- <method name="action_match" qualifiers="const">
- <return type="bool">
+ <method name="as_text" qualifiers="const">
+ <return type="String">
</return>
- <argument index="0" name="event" type="InputEvent">
- </argument>
<description>
- Returns [code]true[/code] if this event matches [code]event[/code].
+ Returns a [String] representation of the event.
</description>
</method>
- <method name="as_text" qualifiers="const">
- <return type="String">
+ <method name="get_action_strength" qualifiers="const">
+ <return type="float">
</return>
+ <argument index="0" name="action" type="String">
+ </argument>
<description>
- Returns a [String] representation of the event.
</description>
</method>
<method name="is_action" qualifiers="const">
diff --git a/doc/classes/InputEventAction.xml b/doc/classes/InputEventAction.xml
index e50c43c045..16000231cb 100644
--- a/doc/classes/InputEventAction.xml
+++ b/doc/classes/InputEventAction.xml
@@ -7,7 +7,7 @@
Contains a generic action which can be targeted from several type of inputs. Actions can be created from the project settings menu [code]Project &gt; Project Settings &gt; Input Map[/code]. See [method Node._input].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html#actions
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html#actions</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventJoypadButton.xml b/doc/classes/InputEventJoypadButton.xml
index fdfdc10460..adaeae685e 100644
--- a/doc/classes/InputEventJoypadButton.xml
+++ b/doc/classes/InputEventJoypadButton.xml
@@ -7,7 +7,7 @@
Input event type for gamepad buttons. For joysticks see [InputEventJoypadMotion].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
@@ -15,7 +15,7 @@
</methods>
<members>
<member name="button_index" type="int" setter="set_button_index" getter="get_button_index">
- Button identifier. One of the [code]JOY_BUTTON_*[/code] constants from [@global Scope].
+ Button identifier. One of the [code]JOY_BUTTON_*[/code] constants from [@GlobalScope].
</member>
<member name="pressed" type="bool" setter="set_pressed" getter="is_pressed">
If [code]true[/code] the button's state is pressed. If [code]false[/code] the button's state is released.
diff --git a/doc/classes/InputEventJoypadMotion.xml b/doc/classes/InputEventJoypadMotion.xml
index 001dce9a03..f86aec4ce0 100644
--- a/doc/classes/InputEventJoypadMotion.xml
+++ b/doc/classes/InputEventJoypadMotion.xml
@@ -7,7 +7,7 @@
Stores information about joystick motions. One [code]InputEventJoypadMotion[/code] represents one axis at a time.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
@@ -15,7 +15,7 @@
</methods>
<members>
<member name="axis" type="int" setter="set_axis" getter="get_axis">
- Axis identifier. Use one of the [code]JOY_AXIS_*[/code] constants in [@global Scope].
+ Axis identifier. Use one of the [code]JOY_AXIS_*[/code] constants in [@GlobalScope].
</member>
<member name="axis_value" type="float" setter="set_axis_value" getter="get_axis_value">
Current position of the joystick on the given axis. The value ranges from [code]-1.0[/code] to [code]1.0[/code]. A value of [code]0[/code] means the axis is in its resting position.
diff --git a/doc/classes/InputEventKey.xml b/doc/classes/InputEventKey.xml
index dada5034f5..7503e53188 100644
--- a/doc/classes/InputEventKey.xml
+++ b/doc/classes/InputEventKey.xml
@@ -7,7 +7,7 @@
Stores key presses on the keyboard. Supports key presses, key releases and [member echo] events.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
@@ -27,7 +27,7 @@
If [code]true[/code] the key's state is pressed. If [code]false[/code] the key's state is released.
</member>
<member name="scancode" type="int" setter="set_scancode" getter="get_scancode">
- Key scancode, one of the [code]KEY_*[/code] constants in [@global Scope].
+ Key scancode, one of the [code]KEY_*[/code] constants in [@GlobalScope].
</member>
<member name="unicode" type="int" setter="set_unicode" getter="get_unicode">
Key unicode identifier when relevant.
diff --git a/doc/classes/InputEventMouse.xml b/doc/classes/InputEventMouse.xml
index 96a0116c3c..06de96890a 100644
--- a/doc/classes/InputEventMouse.xml
+++ b/doc/classes/InputEventMouse.xml
@@ -7,7 +7,7 @@
Stores general mouse events information.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventMouseButton.xml b/doc/classes/InputEventMouseButton.xml
index 73190c7283..50641dceed 100644
--- a/doc/classes/InputEventMouseButton.xml
+++ b/doc/classes/InputEventMouseButton.xml
@@ -7,7 +7,7 @@
Contains mouse click information. See [method Node._input].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/mouse_and_input_coordinates.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/mouse_and_input_coordinates.html</link>
</tutorials>
<demos>
</demos>
@@ -21,7 +21,7 @@
If [code]true[/code] the mouse button's state is a double-click. If [code]false[/code] the mouse button's state is released.
</member>
<member name="factor" type="float" setter="set_factor" getter="get_factor">
- TO TALK in PR, reduz said : i think it's used for apple touch but i don't remember what it does
+ Magnitude. Amount (or delta) of the event. Used for scroll events, indicates scroll amount (vertically or horizontally). Only supported on some platforms, sensitivity varies by platform. May be 0 if not supported.
</member>
<member name="pressed" type="bool" setter="set_pressed" getter="is_pressed">
If [code]true[/code] the mouse button's state is pressed. If [code]false[/code] the mouse button's state is released.
diff --git a/doc/classes/InputEventMouseMotion.xml b/doc/classes/InputEventMouseMotion.xml
index 83aa28c693..05e3e79d26 100644
--- a/doc/classes/InputEventMouseMotion.xml
+++ b/doc/classes/InputEventMouseMotion.xml
@@ -7,7 +7,7 @@
Contains mouse motion information. Supports relative, absolute positions and speed. See [method Node._input].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/mouse_and_input_coordinates.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/mouse_and_input_coordinates.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventScreenDrag.xml b/doc/classes/InputEventScreenDrag.xml
index a2ac8d587f..f777d90ccb 100644
--- a/doc/classes/InputEventScreenDrag.xml
+++ b/doc/classes/InputEventScreenDrag.xml
@@ -8,7 +8,7 @@
Contains screen drag information. See [method Node._input].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventScreenTouch.xml b/doc/classes/InputEventScreenTouch.xml
index 64b9550f9d..39cd0a9657 100644
--- a/doc/classes/InputEventScreenTouch.xml
+++ b/doc/classes/InputEventScreenTouch.xml
@@ -8,7 +8,7 @@
Stores multi-touch press/release information. Supports touch press, touch release and [member index] for multi-touch count and order.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventWithModifiers.xml b/doc/classes/InputEventWithModifiers.xml
index 2d046cbfd2..9c1814fedd 100644
--- a/doc/classes/InputEventWithModifiers.xml
+++ b/doc/classes/InputEventWithModifiers.xml
@@ -7,7 +7,7 @@
Contains keys events information with modifiers support like [code]SHIFT[/code] or [code]ALT[/code]. See [method Node._input].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputMap.xml b/doc/classes/InputMap.xml
index eeb225d445..2f5fb49dba 100644
--- a/doc/classes/InputMap.xml
+++ b/doc/classes/InputMap.xml
@@ -7,7 +7,7 @@
Manages all [InputEventAction] which can be created/modified from the project settings menu [code]Project &gt; Project Settings &gt; Input Map[/code] or in code with [method add_action] and [method action_add_event]. See [method Node._input].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html#inputmap
+ <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html#inputmap</link>
</tutorials>
<demos>
</demos>
@@ -34,6 +34,14 @@
Removes an [InputEvent] from an action.
</description>
</method>
+ <method name="action_erase_events">
+ <return type="void">
+ </return>
+ <argument index="0" name="action" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="action_has_event">
<return type="bool">
</return>
@@ -45,13 +53,26 @@
Returns [true] if an action has an [InputEvent] associated with it.
</description>
</method>
+ <method name="action_set_deadzone">
+ <return type="void">
+ </return>
+ <argument index="0" name="deadzone" type="String">
+ </argument>
+ <argument index="1" name="arg1" type="float">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="add_action">
<return type="void">
</return>
<argument index="0" name="action" type="String">
</argument>
+ <argument index="1" name="deadzone" type="float" default="0.5">
+ </argument>
<description>
- Adds an (empty) action to the [code]InputMap[/code]. An [InputEvent] can then be added to this action with [method action_add_event].
+ Adds an (empty) action to the [code]InputMap[/code], with a configurable [code]deadzone[/code].
+ An [InputEvent] can then be added to this action with [method action_add_event].
</description>
</method>
<method name="erase_action">
diff --git a/doc/classes/InstancePlaceholder.xml b/doc/classes/InstancePlaceholder.xml
index 5945e1068f..71c859fd9e 100644
--- a/doc/classes/InstancePlaceholder.xml
+++ b/doc/classes/InstancePlaceholder.xml
@@ -12,6 +12,16 @@
<demos>
</demos>
<methods>
+ <method name="create_instance">
+ <return type="Node">
+ </return>
+ <argument index="0" name="replace" type="bool" default="false">
+ </argument>
+ <argument index="1" name="custom_scene" type="PackedScene" default="null">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_instance_path" qualifiers="const">
<return type="String">
</return>
diff --git a/doc/classes/JavaScript.xml b/doc/classes/JavaScript.xml
index 1d40b990a9..17588717c2 100644
--- a/doc/classes/JavaScript.xml
+++ b/doc/classes/JavaScript.xml
@@ -7,7 +7,7 @@
The JavaScript singleton is implemented only in HTML5 export. It's used to access the browser's JavaScript context. This allows interaction with embedding pages or calling third-party JavaScript APIs.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/getting_started/workflow/export/exporting_for_web.html#calling-javascript-from-script
+ <link>http://docs.godotengine.org/en/3.0/getting_started/workflow/export/exporting_for_web.html#calling-javascript-from-script</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/KinematicBody.xml b/doc/classes/KinematicBody.xml
index 5553602db2..ae33ed5205 100644
--- a/doc/classes/KinematicBody.xml
+++ b/doc/classes/KinematicBody.xml
@@ -9,7 +9,7 @@
Kinematic Characters: KinematicBody also has an API for moving objects (the [method move_and_collide] and [method move_and_slide] methods) while performing collision tests. This makes them really useful to implement characters that collide against a world, but that don't require advanced physics.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/kinematic_character_2d.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/kinematic_character_2d.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml
index 24c28fc810..1e78a196b1 100644
--- a/doc/classes/Label.xml
+++ b/doc/classes/Label.xml
@@ -106,6 +106,8 @@
</theme_item>
<theme_item name="font_color_shadow" type="Color">
</theme_item>
+ <theme_item name="font_outline_modulate" type="Color">
+ </theme_item>
<theme_item name="line_spacing" type="int">
</theme_item>
<theme_item name="normal" type="StyleBox">
diff --git a/doc/classes/Light.xml b/doc/classes/Light.xml
index e05ed2d0b1..e9b36c2f9d 100644
--- a/doc/classes/Light.xml
+++ b/doc/classes/Light.xml
@@ -7,7 +7,7 @@
Light is the abstract base class for light nodes, so it shouldn't be used directly (It can't be instanced). Other types of light nodes inherit from it. Light contains the common variables and parameters used for lighting.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link>
</tutorials>
<demos>
</demos>
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/LineEdit.xml b/doc/classes/LineEdit.xml
index c31438283e..d7a0385bb3 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -112,7 +112,7 @@
<member name="secret" type="bool" setter="set_secret" getter="is_secret">
If [code]true[/code], every character is replaced with the secret character (see [member secret_character]).
</member>
- <member name="secret_character" type="string" setter="set_secret_character" getter="get_secret_character">
+ <member name="secret_character" type="String" setter="set_secret_character" getter="get_secret_character">
The character to use to mask secret input (defaults to "*"). Only a single character can be used as the secret character.
</member>
<member name="text" type="String" setter="set_text" getter="get_text">
diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml
index 591d715174..8c114a55c9 100644
--- a/doc/classes/MultiplayerAPI.xml
+++ b/doc/classes/MultiplayerAPI.xml
@@ -1,88 +1,78 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="MultiplayerAPI" inherits="Reference" category="Core" version="3.1">
<brief_description>
+ High Level Multiplayer API.
</brief_description>
<description>
+ This class implements most of the logic behind the high level multiplayer API.
+ By default, [SceneTree] has a reference to this class that is used to provide multiplayer capabilities (i.e. RPC/RSET) across the whole scene.
+ It is possible to override the MultiplayerAPI instance used by specific Nodes by setting the [member Node.custom_multiplayer] property, effectively allowing to run both client and server in the same scene.
</description>
<tutorials>
</tutorials>
<demos>
</demos>
<methods>
- <method name="add_peer">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- </description>
- </method>
<method name="clear">
<return type="void">
</return>
<description>
- </description>
- </method>
- <method name="connected_to_server">
- <return type="void">
- </return>
- <description>
- </description>
- </method>
- <method name="connection_failed">
- <return type="void">
- </return>
- <description>
- </description>
- </method>
- <method name="del_peer">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
+ Clears the current MultiplayerAPI network state (you shouldn't call this unless you know what you are doing).
</description>
</method>
<method name="get_network_connected_peers" qualifiers="const">
<return type="PoolIntArray">
</return>
<description>
+ Returns the peer IDs of all connected peers of this MultiplayerAPI's [member network_peer].
</description>
</method>
<method name="get_network_unique_id" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the unique peer ID of this MultiplayerAPI's [member network_peer].
</description>
</method>
<method name="get_rpc_sender_id" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the sender's peer ID for the RPC currently being executed.
+ NOTE: If not inside an RPC this method will return 0.
</description>
</method>
<method name="has_network_peer" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if there is a [member network_peer] set.
</description>
</method>
<method name="is_network_server" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if this MultiplayerAPI's [member network_peer] is in server mode (listening for connections).
</description>
</method>
<method name="poll">
<return type="void">
</return>
<description>
+ Method used for polling the MultiplayerAPI. You only need to worry about this if you are using [member Node.custom_multiplayer] override or you set [member SceneTree.multiplayer_poll] to [code]false[/code]. By default [SceneTree] will poll its MultiplayerAPI for you.
+ NOTE: This method results in RPCs and RSETs being called, so they will be executed in the same context of this function (e.g. [code]_process[/code], [code]physics[/code], [Thread]).
</description>
</method>
- <method name="server_disconnected">
- <return type="void">
+ <method name="send_bytes">
+ <return type="int" enum="Error">
</return>
+ <argument index="0" name="bytes" type="PoolByteArray">
+ </argument>
+ <argument index="1" name="id" type="int" default="0">
+ </argument>
<description>
+ Sends the given raw [code]bytes[/code] to a specific peer identified by [code]id[/code] (see [method NetworkedMultiplayerPeer.set_target_peer]). Default ID is [code]0[/code], i.e. broadcast to all peers.
</description>
</method>
<method name="set_root_node">
@@ -91,41 +81,83 @@
<argument index="0" name="node" type="Node">
</argument>
<description>
+ Sets the base root node to use for RPCs. Instead of an absolute path, a relative path will be used to find the node upon which the RPC should be executed.
+ This effectively allows to have different branches of the scene tree to be managed by different MultiplayerAPI, allowing for example to run both client and server in the same scene.
</description>
</method>
</methods>
<members>
<member name="network_peer" type="NetworkedMultiplayerPeer" setter="set_network_peer" getter="get_network_peer">
+ The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the MultiplayerAPI will become a network server (check with [method is_network_server]) and will set root node's network mode to master (see NETWORK_MODE_* constants in [Node]), or it will become a regular peer with root node set to slave. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to MultiplayerAPI's signals.
</member>
<member name="refuse_new_network_connections" type="bool" setter="set_refuse_new_network_connections" getter="is_refusing_new_network_connections">
+ If [code]true[/code] the MultiplayerAPI's [member network_peer] refuses new incoming connections.
</member>
</members>
<signals>
<signal name="connected_to_server">
<description>
+ Emitted whenever this MultiplayerAPI's [member network_peer] successfully connected to a server. Only emitted on clients.
</description>
</signal>
<signal name="connection_failed">
<description>
+ Emitted whenever this MultiplayerAPI's [member network_peer] fails to establish a connection to a server. Only emitted on clients.
</description>
</signal>
<signal name="network_peer_connected">
<argument index="0" name="id" type="int">
</argument>
<description>
+ Emitted whenever this MultiplayerAPI's [member network_peer] connects with a new peer. ID is the peer ID of the new peer. Clients get notified when other clients connect to the same server. Upon connecting to a server, a client also receives this signal for the server (with ID being 1).
</description>
</signal>
<signal name="network_peer_disconnected">
<argument index="0" name="id" type="int">
</argument>
<description>
+ Emitted whenever this MultiplayerAPI's [member network_peer] disconnects from a peer. Clients get notified when other clients disconnect from the same server.
+ </description>
+ </signal>
+ <signal name="network_peer_packet">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="packet" type="PoolByteArray">
+ </argument>
+ <description>
+ Emitted whenever this MultiplayerAPI's [member network_peer] receive a [code]packet[/code] with custom data (see [method send_bytes]). ID is the peer ID of the peer that sent the packet.
</description>
</signal>
<signal name="server_disconnected">
<description>
+ Emitted whenever this MultiplayerAPI's [member network_peer] disconnects from server. Only emitted on clients.
</description>
</signal>
</signals>
<constants>
+ <constant name="RPC_MODE_DISABLED" value="0" enum="RPCMode">
+ Used with [method Node.rpc_config] or [method Node.rset_config] to disable a method or property for all RPC calls, making it unavailable. Default for all methods.
+ </constant>
+ <constant name="RPC_MODE_REMOTE" value="1" enum="RPCMode">
+ Used with [method Node.rpc_config] or [method Node.rset_config] to set a method to be called or a property to be changed only on the remote end, not locally. Analogous to the [code]remote[/code] keyword. Calls and property changes are accepted from all remote peers, no matter if they are node's master or slaves.
+ </constant>
+ <constant name="RPC_MODE_SYNC" value="2" enum="RPCMode">
+ Behave like [code]RPC_MODE_REMOTE[/code] but also make the call or property change locally. Analogous to the [code]sync[/code] keyword.
+ </constant>
+ <constant name="RPC_MODE_MASTER" value="3" enum="RPCMode">
+ Used with [method Node.rpc_config] or [method Node.rset_config] to set a method to be called or a property to be changed only on the network master for this node. Analogous to the [code]master[/code] keyword. Only accepts calls or property changes from the node's network slaves, see [method Node.set_network_master].
+ </constant>
+ <constant name="RPC_MODE_SLAVE" value="4" enum="RPCMode">
+ Used with [method Node.rpc_config] or [method Node.rset_config] to set a method to be called or a property to be changed only on slaves for this node. Analogous to the [code]slave[/code] keyword. Only accepts calls or property changes from the node's network master, see [method Node.set_network_master].
+ </constant>
+ <constant name="RPC_MODE_REMOTESYNC" value="5" enum="RPCMode">
+ Behave like [code]RPC_MODE_REMOTE[/code] but also make the call or property change locally. Same as [code]RPC_MODE_SYNC[/code] which is only kept for compatibility. Analogous to the [code]remotesync[/code] keyword.
+ </constant>
+ <constant name="RPC_MODE_MASTERSYNC" value="6" enum="RPCMode">
+ Behave like [code]RPC_MODE_MASTER[/code] but also make the call or property change locally. Analogous to the [code]mastersync[/code] keyword.
+ </constant>
+ <constant name="RPC_MODE_SLAVESYNC" value="7" enum="RPCMode">
+ Behave like [code]RPC_MODE_SLAVE[/code] but also make the call or property change locally. Analogous to the [code]slavesync[/code] keyword.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/NetworkedMultiplayerPeer.xml b/doc/classes/NetworkedMultiplayerPeer.xml
index 71614c5f76..e878b3a746 100644
--- a/doc/classes/NetworkedMultiplayerPeer.xml
+++ b/doc/classes/NetworkedMultiplayerPeer.xml
@@ -7,7 +7,7 @@
Manages the connection to network peers. Assigns unique IDs to each client connected to the server.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html</link>
</tutorials>
<demos>
</demos>
@@ -46,7 +46,8 @@
<argument index="0" name="id" type="int">
</argument>
<description>
- The peer to which packets will be sent. Default value: [code]0[/code].
+ Sets the peer to which packets will be sent.
+ The [code]id[/code] can be one of: [code]TARGET_PEER_BROADCAST[/code] to send to all connected peers, [code]TARGET_PEER_SERVER[/code] to send to the peer acting as server, a valid peer ID to send to that specific peer, a negative peer ID to send to all peers except that one. Default: [code]TARGET_PEER_BROADCAST[/code]
</description>
</method>
</methods>
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index d43885a9d9..8bae412053 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -17,7 +17,7 @@
[b]Networking with nodes:[/b] After connecting to a server (or making one, see [NetworkedMultiplayerENet]) it is possible to use the built-in RPC (remote procedure call) system to communicate over the network. By calling [method rpc] with a method name, it will be called locally and in all connected peers (peers = clients and the server that accepts connections). To identify which node receives the RPC call Godot will use its [NodePath] (make sure node names are the same on all peers). Also take a look at the high-level networking tutorial and corresponding demos.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scenes_and_nodes.html
+ <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scenes_and_nodes.html</link>
</tutorials>
<demos>
</demos>
@@ -572,10 +572,10 @@
</return>
<argument index="0" name="method" type="String">
</argument>
- <argument index="1" name="mode" type="int" enum="Node.RPCMode">
+ <argument index="1" name="mode" type="int" enum="MultiplayerAPI.RPCMode">
</argument>
<description>
- Changes the RPC mode for the given [code]method[/code] to the given [code]mode[/code]. See [enum RPCMode]. An alternative is annotating methods and properties with the corresponding keywords ([code]remote[/code], [code]sync[/code], [code]master[/code], [code]slave[/code]). By default, methods are not exposed to networking (and RPCs). Also see [method rset] and [method rset_config] for properties.
+ Changes the RPC mode for the given [code]method[/code] to the given [code]mode[/code]. See [enum MultiplayerAPI.RPCMode]. An alternative is annotating methods and properties with the corresponding keywords ([code]remote[/code], [code]sync[/code], [code]master[/code], [code]slave[/code]). By default, methods are not exposed to networking (and RPCs). Also see [method rset] and [method rset_config] for properties.
</description>
</method>
<method name="rpc_id" qualifiers="vararg">
@@ -586,7 +586,7 @@
<argument index="1" name="method" type="String">
</argument>
<description>
- Sends a [method rpc] to a specific peer identified by [code]peer_id[/code]. Returns an empty [Variant].
+ Sends a [method rpc] to a specific peer identified by [code]peer_id[/code] (see [method NetworkedMultiplayerPeer.set_target_peer]). Returns an empty [Variant].
</description>
</method>
<method name="rpc_unreliable" qualifiers="vararg">
@@ -606,7 +606,7 @@
<argument index="1" name="method" type="String">
</argument>
<description>
- Sends a [method rpc] to a specific peer identified by [code]peer_id[/code] using an unreliable protocol. Returns an empty [Variant].
+ Sends a [method rpc] to a specific peer identified by [code]peer_id[/code] using an unreliable protocol (see [method NetworkedMultiplayerPeer.set_target_peer]). Returns an empty [Variant].
</description>
</method>
<method name="rset">
@@ -625,10 +625,10 @@
</return>
<argument index="0" name="property" type="String">
</argument>
- <argument index="1" name="mode" type="int" enum="Node.RPCMode">
+ <argument index="1" name="mode" type="int" enum="MultiplayerAPI.RPCMode">
</argument>
<description>
- Changes the RPC mode for the given [code]property[/code] to the given [code]mode[/code]. See [enum RPCMode]. An alternative is annotating methods and properties with the corresponding keywords ([code]remote[/code], [code]sync[/code], [code]master[/code], [code]slave[/code]). By default, properties are not exposed to networking (and RPCs). Also see [method rpc] and [method rpc_config] for methods.
+ Changes the RPC mode for the given [code]property[/code] to the given [code]mode[/code]. See [enum MultiplayerAPI.RPCMode]. An alternative is annotating methods and properties with the corresponding keywords ([code]remote[/code], [code]sync[/code], [code]master[/code], [code]slave[/code]). By default, properties are not exposed to networking (and RPCs). Also see [method rpc] and [method rpc_config] for methods.
</description>
</method>
<method name="rset_id">
@@ -641,7 +641,7 @@
<argument index="2" name="value" type="Variant">
</argument>
<description>
- Remotely changes the property's value on a specific peer identified by [code]peer_id[/code].
+ Remotely changes the property's value on a specific peer identified by [code]peer_id[/code] (see [method NetworkedMultiplayerPeer.set_target_peer]).
</description>
</method>
<method name="rset_unreliable">
@@ -665,7 +665,7 @@
<argument index="2" name="value" type="Variant">
</argument>
<description>
- Remotely changes property's value on a specific peer identified by [code]peer_id[/code] using an unreliable protocol.
+ Remotely changes property's value on a specific peer identified by [code]peer_id[/code] using an unreliable protocol (see [method NetworkedMultiplayerPeer.set_target_peer]).
</description>
</method>
<method name="set_display_folded">
@@ -703,7 +703,7 @@
<argument index="0" name="enable" type="bool">
</argument>
<description>
- Enables or disables internal physics for this node. Internal physics processing happens in isolation from the normal [method]_physics_process[/code] calls and is used by some nodes internally to guarantee proper functioning even if the node is paused or physics processing is disabled for scripting ([method set_physics_process]). Only useful for advanced uses to manipulate built-in nodes behaviour.
+ Enables or disables internal physics for this node. Internal physics processing happens in isolation from the normal [method _physics_process] calls and is used by some nodes internally to guarantee proper functioning even if the node is paused or physics processing is disabled for scripting ([method set_physics_process]). Only useful for advanced uses to manipulate built-in nodes behaviour.
</description>
</method>
<method name="set_process">
@@ -730,7 +730,7 @@
<argument index="0" name="enable" type="bool">
</argument>
<description>
- Enables or disabled internal processing for this node. Internal processing happens in isolation from the normal [method]_process[/code] calls and is used by some nodes internally to guarantee proper functioning even if the node is paused or processing is disabled for scripting ([method set_process]). Only useful for advanced uses to manipulate built-in nodes behaviour.
+ Enables or disabled internal processing for this node. Internal processing happens in isolation from the normal [method _process] calls and is used by some nodes internally to guarantee proper functioning even if the node is paused or processing is disabled for scripting ([method set_process]). Only useful for advanced uses to manipulate built-in nodes behaviour.
</description>
</method>
<method name="set_process_unhandled_input">
@@ -762,12 +762,14 @@
</method>
</methods>
<members>
- <member name="custom_multiplayer_api" type="MultiplayerAPI" setter="set_custom_multiplayer_api" getter="get_custom_multiplayer_api">
+ <member name="custom_multiplayer" type="MultiplayerAPI" setter="set_custom_multiplayer" getter="get_custom_multiplayer">
+ The override to the default [MultiplayerAPI]. Set to null to use the default SceneTree one.
</member>
<member name="filename" type="String" setter="set_filename" getter="get_filename">
When a scene is instanced from a file, its topmost node contains the filename from which it was loaded.
</member>
- <member name="multiplayer_api" type="MultiplayerAPI" setter="" getter="get_multiplayer_api">
+ <member name="multiplayer" type="MultiplayerAPI" setter="" getter="get_multiplayer">
+ The [MultiplayerAPI] instance associated with this node. Either the [member custom_multiplayer], or the default SceneTree one (if inside tree).
</member>
<member name="name" type="String" setter="set_name" getter="get_name">
The name of the node. This name is unique among the siblings (other child nodes from the same parent). When set to an existing name, the node will be automatically renamed
@@ -858,21 +860,6 @@
<constant name="NOTIFICATION_INTERNAL_PHYSICS_PROCESS" value="26">
Notification received every frame when the internal physics process flag is set (see [method set_physics_process_internal]).
</constant>
- <constant name="RPC_MODE_DISABLED" value="0" enum="RPCMode">
- Used with [method rpc_config] or [method rset_config] to disable a method or property for all RPC calls, making it unavailable. Default for all methods.
- </constant>
- <constant name="RPC_MODE_REMOTE" value="1" enum="RPCMode">
- Used with [method rpc_config] or [method rset_config] to set a method to be called or a property to be changed only on the remote end, not locally. Analogous to the [code]remote[/code] keyword.
- </constant>
- <constant name="RPC_MODE_SYNC" value="2" enum="RPCMode">
- Used with [method rpc_config] or [method rset_config] to set a method to be called or a property to be changed both on the remote end and locally. Analogous to the [code]sync[/code] keyword.
- </constant>
- <constant name="RPC_MODE_MASTER" value="3" enum="RPCMode">
- Used with [method rpc_config] or [method rset_config] to set a method to be called or a property to be changed only on the network master for this node. Analogous to the [code]master[/code] keyword. See [method set_network_master].
- </constant>
- <constant name="RPC_MODE_SLAVE" value="4" enum="RPCMode">
- Used with [method rpc_config] or [method rset_config] to set a method to be called or a property to be changed only on slaves for this node. Analogous to the [code]slave[/code] keyword. See [method set_network_master].
- </constant>
<constant name="PAUSE_MODE_INHERIT" value="0" enum="PauseMode">
Inherits pause mode from the node's parent. For the root node, it is equivalent to PAUSE_MODE_STOP. Default.
</constant>
diff --git a/doc/classes/Node2D.xml b/doc/classes/Node2D.xml
index a61678041f..13eabeca17 100644
--- a/doc/classes/Node2D.xml
+++ b/doc/classes/Node2D.xml
@@ -7,7 +7,7 @@
A 2D game object, with a position, rotation and scale. All 2D physics nodes and sprites inherit from Node2D. Use Node2D as a parent node to move, scale and rotate children in a 2D project. Also gives control on the node's render order.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index d38a89874c..1526b1be8c 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -94,17 +94,24 @@
<argument index="3" name="output" type="Array" default="[ ]">
</argument>
<description>
- Execute the file at the given path, optionally blocking until it returns.
- Platform path resolution will take place. The resolved file must exist and be executable.
- Returns a process id.
- For example:
+ Execute the file at the given path with the arguments passed as an array of strings. Platform path resolution will take place. The resolved file must exist and be executable.
+ The arguments are used in the given order and separated by a space, so [code]OS.execute('ping', ['-c', '3', 'godotengine.org'])[/code] will resolve to [code]ping -c 3 godotengine.org[/code] in the system's shell.
+ This method has slightly different behaviour based on whether the [code]blocking[/code] mode is enabled.
+ When [code]blocking[/code] is enabled, the Godot thread will pause its execution while waiting for the process to terminate. The shell output of the process will be written to the [code]output[/code] array as a single string. When the process terminates, the Godot thread will resume execution.
+ When [code]blocking[/code] is disabled, the Godot thread will continue while the new process runs. It is not possible to retrieve the shell output in non-blocking mode, so [code]output[/code] will be empty.
+ The return value also depends on the blocking mode. When blocking, the method will return -2 (no process ID information is available in blocking mode). When non-blocking, the method returns a process ID, which you can use to monitor the process (and potentially terminate it with [method kill]). If the process forking (non-blocking) or opening (blocking) fails, the method will return -1.
+ Example of blocking mode and retrieving the shell output:
[codeblock]
var output = []
- var pid = OS.execute('ls', [], true, output)
+ OS.execute('ls', ['-l', '/tmp'], true, output)
[/codeblock]
- If you wish to access a shell built-in or perform a composite command, a platform specific shell can be invoked. For example:
+ Example of non-blocking mode, running another instance of the project and storing its process ID:
[codeblock]
- var pid = OS.execute('CMD.exe', ['/C', 'cd %TEMP% &amp;&amp; dir'], true, output)
+ var pid = OS.execute(OS.get_executable_path(), [], false)
+ [/codeblock]
+ If you wish to access a shell built-in or perform a composite command, a platform-specific shell can be invoked. For example:
+ [codeblock]
+ OS.execute('CMD.exe', ['/C', 'cd %TEMP% &amp;&amp; dir'], true, output)
[/codeblock]
</description>
</method>
@@ -285,7 +292,6 @@
</argument>
<description>
Returns the dots per inch density of the specified screen.
-
On Android Devices, the actual screen densities are grouped into six generalized densities:
ldpi - 120 dpi
mdpi - 160 dpi
@@ -356,6 +362,13 @@
Returns the amount of time passed in milliseconds since the engine started.
</description>
</method>
+ <method name="get_ticks_usec" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the amount of time passed in microseconds since the engine started.
+ </description>
+ </method>
<method name="get_time" qualifiers="const">
<return type="Dictionary">
</return>
@@ -430,6 +443,12 @@
Returns the on-screen keyboard's height in pixels. Returns 0 if there is no keyboard or it is currently hidden.
</description>
</method>
+ <method name="get_window_safe_area" qualifiers="const">
+ <return type="Rect2">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="has_environment" qualifiers="const">
<return type="bool">
</return>
@@ -521,7 +540,8 @@
<argument index="0" name="pid" type="int">
</argument>
<description>
- Kill a process ID (this method can be used to kill processes that were not spawned by the game).
+ Kill (terminate) the process identified by the given process ID ([code]pid[/code]), e.g. the one returned by [method execute] in non-blocking mode.
+ Note that this method can also be used to kill processes that were not spawned by the game.
</description>
</method>
<method name="native_video_is_playing">
@@ -698,7 +718,7 @@
<member name="low_processor_usage_mode" type="bool" setter="set_low_processor_usage_mode" getter="is_in_low_processor_usage_mode">
If [code]true[/code] the engine optimizes for low processor usage by only refreshing the screen if needed. Can improve battery consumption on mobile.
</member>
- <member name="screen_orientation" type="int" setter="set_screen_orientation" getter="get_screen_orientation" enum="OS.ScreenOrientation">
+ <member name="screen_orientation" type="int" setter="set_screen_orientation" getter="get_screen_orientation" enum="_OS.ScreenOrientation">
The current screen orientation.
</member>
<member name="vsync_enabled" type="bool" setter="set_use_vsync" getter="is_vsync_enabled">
@@ -716,6 +736,8 @@
<member name="window_minimized" type="bool" setter="set_window_minimized" getter="is_window_minimized">
If [code]true[/code] the window is minimized.
</member>
+ <member name="window_per_pixel_transparency_enabled" type="bool" setter="set_window_per_pixel_transparency_enabled" getter="get_window_per_pixel_transparency_enabled">
+ </member>
<member name="window_position" type="Vector2" setter="set_window_position" getter="get_window_position">
The window position relative to the screen, the origin is the top left corner, +Y axis goes to the bottom and +X axis goes to the right.
</member>
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index 0717836366..ab49bc468c 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -16,7 +16,7 @@
</demos>
<methods>
<method name="_get" qualifiers="virtual">
- <return type="void">
+ <return type="Variant">
</return>
<argument index="0" name="property" type="String">
</argument>
diff --git a/doc/classes/OmniLight.xml b/doc/classes/OmniLight.xml
index 0ed133f52e..ff2e77ffbe 100644
--- a/doc/classes/OmniLight.xml
+++ b/doc/classes/OmniLight.xml
@@ -7,7 +7,7 @@
An OmniDirectional light is a type of [Light] node that emits lights in all directions. The light is attenuated through the distance and this attenuation can be configured by changing the energy, radius and attenuation parameters of [Light].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml
index 8cb53dd98e..c58c932b61 100644
--- a/doc/classes/OptionButton.xml
+++ b/doc/classes/OptionButton.xml
@@ -18,7 +18,7 @@
</argument>
<argument index="1" name="label" type="String">
</argument>
- <argument index="2" name="id" type="int">
+ <argument index="2" name="id" type="int" default="-1">
</argument>
<description>
Add an item, with a "texture" icon, text "label" and (optionally) id. If no "id" is passed, "id" becomes the item index. New items are appended at the end.
diff --git a/doc/classes/OrientedPathFollow.xml b/doc/classes/OrientedPathFollow.xml
new file mode 100644
index 0000000000..85d60936ad
--- /dev/null
+++ b/doc/classes/OrientedPathFollow.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="OrientedPathFollow" inherits="Spatial" category="Core" version="3.1">
+ <brief_description>
+ Oriented point sampler for a [Path].
+ </brief_description>
+ <description>
+ This node behaves like [PathFollow], except it uses its parent [Path] up vector information to enforce orientation.
+ Make sure to check if the curve of this node's parent [Path] has up vectors enabled. See [PathFollow] and [Curve3D] for further information.
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="cubic_interp" type="bool" setter="set_cubic_interpolation" getter="get_cubic_interpolation">
+ If [code]true[/code] the position between two cached points is interpolated cubically, and linearly otherwise.
+ The points along the [Curve3D] of the [Path] are precomputed before use, for faster calculations. The point at the requested offset is then calculated interpolating between two adjacent cached points. This may present a problem if the curve makes sharp turns, as the cached points may not follow the curve closely enough.
+ There are two answers to this problem: Either increase the number of cached points and increase memory consumption, or make a cubic interpolation between two points at the cost of (slightly) slower calculations.
+ </member>
+ <member name="h_offset" type="float" setter="set_h_offset" getter="get_h_offset">
+ The node's offset along the curve.
+ </member>
+ <member name="loop" type="bool" setter="set_loop" getter="has_loop">
+ If [code]true[/code], any offset outside the path's length will wrap around, instead of stopping at the ends. Use it for cyclic paths.
+ </member>
+ <member name="offset" type="float" setter="set_offset" getter="get_offset">
+ The distance from the first vertex, measured in 3D units along the path. This sets this node's position to a point within the path.
+ </member>
+ <member name="unit_offset" type="float" setter="set_unit_offset" getter="get_unit_offset">
+ The distance from the first vertex, considering 0.0 as the first vertex and 1.0 as the last. This is just another way of expressing the offset within the path, as the offset supplied is multiplied internally by the path's length.
+ </member>
+ <member name="v_offset" type="float" setter="set_v_offset" getter="get_v_offset">
+ The node's offset perpendicular to the curve.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/PacketPeerUDP.xml b/doc/classes/PacketPeerUDP.xml
index 1b821e06cf..d4e3d17de6 100644
--- a/doc/classes/PacketPeerUDP.xml
+++ b/doc/classes/PacketPeerUDP.xml
@@ -22,14 +22,14 @@
<return type="String">
</return>
<description>
- Return the IP of the remote peer that sent the last packet(that was received with [method get_packet] or [method get_var]).
+ Return the IP of the remote peer that sent the last packet(that was received with [method PacketPeer.get_packet] or [method PacketPeer.get_var]).
</description>
</method>
<method name="get_packet_port" qualifiers="const">
<return type="int">
</return>
<description>
- Return the port of the remote peer that sent the last packet(that was received with [method get_packet] or [method get_var]).
+ Return the port of the remote peer that sent the last packet(that was received with [method PacketPeer.get_packet] or [method PacketPeer.get_var]).
</description>
</method>
<method name="is_listening" qualifiers="const">
diff --git a/doc/classes/PanoramaSky.xml b/doc/classes/PanoramaSky.xml
index eb288d13be..402e65c573 100644
--- a/doc/classes/PanoramaSky.xml
+++ b/doc/classes/PanoramaSky.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PanoramaSky" inherits="Sky" category="Core" version="3.1">
<brief_description>
+ A type of [Sky] used to draw a background texture.
</brief_description>
<description>
+ A resource referenced in an [Environment] that is used to draw a background. The Panorama sky functions similar to skyboxes in other engines except it uses a equirectangular sky map instead of a cube map.
</description>
<tutorials>
</tutorials>
@@ -12,6 +14,7 @@
</methods>
<members>
<member name="panorama" type="Texture" setter="set_panorama" getter="get_panorama">
+ [Texture] to be applied to the PanoramaSky.
</member>
</members>
<constants>
diff --git a/doc/classes/Particles.xml b/doc/classes/Particles.xml
index 04177aca25..b03cf6cadb 100644
--- a/doc/classes/Particles.xml
+++ b/doc/classes/Particles.xml
@@ -22,6 +22,7 @@
<return type="void">
</return>
<description>
+ Restarts the particle emmission, clearing existing particles.
</description>
</method>
</methods>
@@ -33,14 +34,19 @@
Particle draw order. Uses [code]DRAW_ORDER_*[/code] values. Default value: [code]DRAW_ORDER_INDEX[/code].
</member>
<member name="draw_pass_1" type="Mesh" setter="set_draw_pass_mesh" getter="get_draw_pass_mesh">
+ [Mesh] that is drawn for the first draw pass.
</member>
<member name="draw_pass_2" type="Mesh" setter="set_draw_pass_mesh" getter="get_draw_pass_mesh">
+ [Mesh] that is drawn for the second draw pass.
</member>
<member name="draw_pass_3" type="Mesh" setter="set_draw_pass_mesh" getter="get_draw_pass_mesh">
+ [Mesh] that is drawn for the third draw pass.
</member>
<member name="draw_pass_4" type="Mesh" setter="set_draw_pass_mesh" getter="get_draw_pass_mesh">
+ [Mesh] that is drawn for the fourth draw pass.
</member>
<member name="draw_passes" type="int" setter="set_draw_passes" getter="get_draw_passes">
+ The number of draw passes when rendering particles.
</member>
<member name="emitting" type="bool" setter="set_emitting" getter="is_emitting">
If [code]true[/code] particles are being emitted. Default value: [code]true[/code].
@@ -62,6 +68,7 @@
If [code]true[/code] only [code]amount[/code] particles will be emitted. Default value: [code]false[/code].
</member>
<member name="preprocess" type="float" setter="set_pre_process_time" getter="get_pre_process_time">
+ Amount of time to preprocess the particles before animation starts. Lets you start the animation some time after particles have started emitting.
</member>
<member name="process_material" type="Material" setter="set_process_material" getter="get_process_material">
[Material] for processing particles. Can be a [ParticlesMaterial] or a [ShaderMaterial].
@@ -73,6 +80,7 @@
Speed scaling ratio. Default value: [code]1[/code].
</member>
<member name="visibility_aabb" type="AABB" setter="set_visibility_aabb" getter="get_visibility_aabb">
+ The [AABB] that determines the area of the world part of which needs to be visible on screen for the particle system to be active.
</member>
</members>
<constants>
@@ -86,6 +94,7 @@
Particles are drawn in order of depth.
</constant>
<constant name="MAX_DRAW_PASSES" value="4">
+ Maximum number of draw passes supported.
</constant>
</constants>
</class>
diff --git a/doc/classes/Path.xml b/doc/classes/Path.xml
index 621a513da3..5ece747aaf 100644
--- a/doc/classes/Path.xml
+++ b/doc/classes/Path.xml
@@ -16,6 +16,12 @@
<member name="curve" type="Curve3D" setter="set_curve" getter="get_curve">
</member>
</members>
+ <signals>
+ <signal name="curve_changed">
+ <description>
+ </description>
+ </signal>
+ </signals>
<constants>
</constants>
</class>
diff --git a/doc/classes/PhysicalBone.xml b/doc/classes/PhysicalBone.xml
new file mode 100644
index 0000000000..99f551b865
--- /dev/null
+++ b/doc/classes/PhysicalBone.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="PhysicalBone" inherits="PhysicsBody" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="get_simulate_physics">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_simulating_physics">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_static_body">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="body_offset" type="Transform" setter="set_body_offset" getter="get_body_offset">
+ </member>
+ <member name="bounce" type="float" setter="set_bounce" getter="get_bounce">
+ </member>
+ <member name="friction" type="float" setter="set_friction" getter="get_friction">
+ </member>
+ <member name="gravity_scale" type="float" setter="set_gravity_scale" getter="get_gravity_scale">
+ </member>
+ <member name="joint_offset" type="Transform" setter="set_joint_offset" getter="get_joint_offset">
+ </member>
+ <member name="joint_type" type="int" setter="set_joint_type" getter="get_joint_type" enum="PhysicalBone.JointType">
+ </member>
+ <member name="mass" type="float" setter="set_mass" getter="get_mass">
+ </member>
+ <member name="weight" type="float" setter="set_weight" getter="get_weight">
+ </member>
+ </members>
+ <constants>
+ <constant name="JOINT_TYPE_NONE" value="0" enum="JointType">
+ </constant>
+ <constant name="JOINT_TYPE_PIN" value="1" enum="JointType">
+ </constant>
+ <constant name="JOINT_TYPE_CONE" value="2" enum="JointType">
+ </constant>
+ <constant name="JOINT_TYPE_HINGE" value="3" enum="JointType">
+ </constant>
+ <constant name="JOINT_TYPE_SLIDER" value="4" enum="JointType">
+ </constant>
+ <constant name="JOINT_TYPE_6DOF" value="5" enum="JointType">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/Physics2DDirectSpaceState.xml b/doc/classes/Physics2DDirectSpaceState.xml
index b55702dac4..f0fee77a5a 100644
--- a/doc/classes/Physics2DDirectSpaceState.xml
+++ b/doc/classes/Physics2DDirectSpaceState.xml
@@ -7,7 +7,7 @@
Direct access object to a space in the [Physics2DServer]. It's used mainly to do queries against objects and areas residing in a given space.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Physics2DServer.xml b/doc/classes/Physics2DServer.xml
index 98535dd330..c302797704 100644
--- a/doc/classes/Physics2DServer.xml
+++ b/doc/classes/Physics2DServer.xml
@@ -140,6 +140,18 @@
Removes a shape from an area. It does not delete the shape, so it can be reassigned later.
</description>
</method>
+ <method name="area_set_area_monitor_callback">
+ <return type="void">
+ </return>
+ <argument index="0" name="area" type="RID">
+ </argument>
+ <argument index="1" name="receiver" type="Object">
+ </argument>
+ <argument index="2" name="method" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="area_set_collision_layer">
<return type="void">
</return>
@@ -180,6 +192,16 @@
5: The shape index of the area where the object entered/exited.
</description>
</method>
+ <method name="area_set_monitorable">
+ <return type="void">
+ </return>
+ <argument index="0" name="area" type="RID">
+ </argument>
+ <argument index="1" name="monitorable" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="area_set_param">
<return type="void">
</return>
@@ -566,7 +588,7 @@
<argument index="3" name="userdata" type="Variant" default="null">
</argument>
<description>
- Sets the function used to calculate physics for an object, if that object allows it (see [method body_set_omit_force integration]).
+ Sets the function used to calculate physics for an object, if that object allows it (see [method body_set_omit_force_integration]).
</description>
</method>
<method name="body_set_max_contacts_reported">
@@ -688,7 +710,7 @@
<argument index="1" name="space" type="RID">
</argument>
<description>
- Assigns a space to the body (see [method create_space]).
+ Assigns a space to the body (see [method space_create]).
</description>
</method>
<method name="body_set_state">
diff --git a/doc/classes/PhysicsBody.xml b/doc/classes/PhysicsBody.xml
index e252aaa048..14053c6a35 100644
--- a/doc/classes/PhysicsBody.xml
+++ b/doc/classes/PhysicsBody.xml
@@ -7,7 +7,7 @@
PhysicsBody is an abstract base class for implementing a physics body. All *Body types inherit from it.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/PhysicsBody2D.xml b/doc/classes/PhysicsBody2D.xml
index 42b1eac5e4..ccc704c7ec 100644
--- a/doc/classes/PhysicsBody2D.xml
+++ b/doc/classes/PhysicsBody2D.xml
@@ -7,7 +7,7 @@
PhysicsBody2D is an abstract base class for implementing a physics body. All *Body2D types inherit from it.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/PhysicsDirectSpaceState.xml b/doc/classes/PhysicsDirectSpaceState.xml
index fabf153dac..3f0e1a4f70 100644
--- a/doc/classes/PhysicsDirectSpaceState.xml
+++ b/doc/classes/PhysicsDirectSpaceState.xml
@@ -7,7 +7,7 @@
Direct access object to a space in the [PhysicsServer]. It's used mainly to do queries against objects and areas residing in a given space.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml
index dd2c322ee3..d45a3adc9c 100644
--- a/doc/classes/PhysicsServer.xml
+++ b/doc/classes/PhysicsServer.xml
@@ -149,6 +149,18 @@
Removes a shape from an area. It does not delete the shape, so it can be reassigned later.
</description>
</method>
+ <method name="area_set_area_monitor_callback">
+ <return type="void">
+ </return>
+ <argument index="0" name="area" type="RID">
+ </argument>
+ <argument index="1" name="receiver" type="Object">
+ </argument>
+ <argument index="2" name="method" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="area_set_collision_layer">
<return type="void">
</return>
@@ -189,6 +201,16 @@
5: The shape index of the area where the object entered/exited.
</description>
</method>
+ <method name="area_set_monitorable">
+ <return type="void">
+ </return>
+ <argument index="0" name="area" type="RID">
+ </argument>
+ <argument index="1" name="monitorable" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="area_set_param">
<return type="void">
</return>
@@ -605,7 +627,7 @@
<argument index="3" name="userdata" type="Variant" default="null">
</argument>
<description>
- Sets the function used to calculate physics for an object, if that object allows it (see [method body_set_omit_force integration]).
+ Sets the function used to calculate physics for an object, if that object allows it (see [method body_set_omit_force_integration]).
</description>
</method>
<method name="body_set_kinematic_safe_margin">
@@ -1391,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/Plane.xml b/doc/classes/Plane.xml
index 157bf42239..6f616401cb 100644
--- a/doc/classes/Plane.xml
+++ b/doc/classes/Plane.xml
@@ -7,7 +7,7 @@
Plane represents a normalized plane equation. Basically, "normal" is the normal of the plane (a,b,c normalized), and "d" is the distance from the origin to the plane (in the direction of "normal"). "Over" or "Above" the plane is considered the side of the plane towards where the normal is pointing.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/math/index.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
</tutorials>
<demos>
</demos>
@@ -24,7 +24,7 @@
<argument index="3" name="d" type="float">
</argument>
<description>
- Creates a plane from the three parameters "a", "b", "c" and "d".
+ Creates a plane from the four parameters "a", "b", "c" and "d".
</description>
</method>
<method name="Plane">
diff --git a/doc/classes/Polygon2D.xml b/doc/classes/Polygon2D.xml
index 429114a128..bf4519fd0a 100644
--- a/doc/classes/Polygon2D.xml
+++ b/doc/classes/Polygon2D.xml
@@ -11,11 +11,79 @@
<demos>
</demos>
<methods>
+ <method name="add_bone">
+ <return type="void">
+ </return>
+ <argument index="0" name="path" type="NodePath">
+ </argument>
+ <argument index="1" name="weights" type="PoolRealArray">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="clear_bones">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="erase_bone">
+ <return type="void">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_bone_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_bone_path" qualifiers="const">
+ <return type="NodePath">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_bone_weights" qualifiers="const">
+ <return type="PoolRealArray">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_bone_path">
+ <return type="void">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <argument index="1" name="path" type="NodePath">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_bone_weights">
+ <return type="void">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <argument index="1" name="weights" type="PoolRealArray">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="antialiased" type="bool" setter="set_antialiased" getter="get_antialiased">
If [code]true[/code] polygon edges will be anti-aliased. Default value: [code]false[/code].
</member>
+ <member name="bones" type="Array" setter="_set_bones" getter="_get_bones">
+ </member>
<member name="color" type="Color" setter="set_color" getter="get_color">
The polygon's fill color. If [code]texture[/code] is defined, it will be multiplied by this color. It will also be the default color for vertices not set in [code]vertex_colors[/code].
</member>
@@ -31,6 +99,8 @@
<member name="polygon" type="PoolVector2Array" setter="set_polygon" getter="get_polygon">
The polygon's list of vertices. The final point will be connected to the first.
</member>
+ <member name="skeleton" type="NodePath" setter="set_skeleton" getter="get_skeleton">
+ </member>
<member name="splits" type="PoolIntArray" setter="set_splits" getter="get_splits">
</member>
<member name="texture" type="Texture" setter="set_texture" getter="get_texture">
diff --git a/doc/classes/Popup.xml b/doc/classes/Popup.xml
index db8d927c9e..ae5bce5d7d 100644
--- a/doc/classes/Popup.xml
+++ b/doc/classes/Popup.xml
@@ -50,7 +50,7 @@
</methods>
<members>
<member name="popup_exclusive" type="bool" setter="set_exclusive" getter="is_exclusive">
- If [code]true[/code] the popup will not be hidden when a click event occurs outside of it, or when it receives the [code]ui_cancel[/code] action event.
+ If [code]true[/code] the popup will not be hidden when a click event occurs outside of it, or when it receives the [code]ui_cancel[/code] action event.
</member>
</members>
<signals>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 166a4be2b0..83d1246e2a 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -303,6 +303,14 @@
Return whether the item is a separator. If it is, it would be displayed as a line.
</description>
</method>
+ <method name="is_item_shortcut_disabled" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="remove_item">
<return type="void">
</return>
@@ -433,6 +441,16 @@
<description>
</description>
</method>
+ <method name="set_item_shortcut_disabled">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="disabled" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_item_submenu">
<return type="void">
</return>
@@ -489,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/PrimitiveMesh.xml b/doc/classes/PrimitiveMesh.xml
index c5bc3d6e47..0f0511258b 100644
--- a/doc/classes/PrimitiveMesh.xml
+++ b/doc/classes/PrimitiveMesh.xml
@@ -19,6 +19,10 @@
</method>
</methods>
<members>
+ <member name="custom_aabb" type="AABB" setter="set_custom_aabb" getter="get_custom_aabb">
+ </member>
+ <member name="flip_faces" type="bool" setter="set_flip_faces" getter="get_flip_faces">
+ </member>
<member name="material" type="Material" setter="set_material" getter="get_material">
The current [Material] of the primitive mesh.
</member>
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/ProceduralSky.xml b/doc/classes/ProceduralSky.xml
index 11af73f953..df0519b2ad 100644
--- a/doc/classes/ProceduralSky.xml
+++ b/doc/classes/ProceduralSky.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ProceduralSky" inherits="Sky" category="Core" version="3.1">
<brief_description>
+ Type of [Sky] that is generated procedurally based on user input parameters.
</brief_description>
<description>
+ ProceduralSky provides a way to create an effective background quickly by defining procedural parameters for the sun, the sky and the ground. The sky and ground are very similar, they are defined by a color at the horizon, another color, and finally an easing curve to interpolate between these two colors. Similarly the sun is described by a position in the sky, a color, and an easing curve. However, the sun also defines a minimum and maximum angle, these two values define at what distance the easing curve begins and ends from the sun, and thus end up defining the size of the sun in the sky.
+ The ProceduralSky is updated on the CPU after the parameters change and stored in a texture and then displayed as a background in the scene. This makes it relatively unsuitable for realtime updates during gameplay. But with a small texture size it is still feasible to update relatively frequently becuase it is updated on a background thread when multi-threading is available.
</description>
<tutorials>
</tutorials>
@@ -12,36 +15,52 @@
</methods>
<members>
<member name="ground_bottom_color" type="Color" setter="set_ground_bottom_color" getter="get_ground_bottom_color">
+ Color of the ground at the bottom.
</member>
<member name="ground_curve" type="float" setter="set_ground_curve" getter="get_ground_curve">
+ How quickly the [member ground_horizon_color] fades into the [member ground_bottom_color].
</member>
<member name="ground_energy" type="float" setter="set_ground_energy" getter="get_ground_energy">
+ Amount of energy contribution from the ground.
</member>
<member name="ground_horizon_color" type="Color" setter="set_ground_horizon_color" getter="get_ground_horizon_color">
+ Color of the ground at the horizon.
</member>
<member name="sky_curve" type="float" setter="set_sky_curve" getter="get_sky_curve">
+ How quickly the [member sky_horizon_color] fades into the [member sky_top_color].
</member>
<member name="sky_energy" type="float" setter="set_sky_energy" getter="get_sky_energy">
+ Amount of energy contribution from the sky.
</member>
<member name="sky_horizon_color" type="Color" setter="set_sky_horizon_color" getter="get_sky_horizon_color">
+ Color of the sky at the horizon.
</member>
<member name="sky_top_color" type="Color" setter="set_sky_top_color" getter="get_sky_top_color">
+ Color of the sky at the top.
</member>
<member name="sun_angle_max" type="float" setter="set_sun_angle_max" getter="get_sun_angle_max">
+ Distance from center of sun where it fades out completely.
</member>
<member name="sun_angle_min" type="float" setter="set_sun_angle_min" getter="get_sun_angle_min">
+ Distance from sun where it goes from solid to starting to fade.
</member>
<member name="sun_color" type="Color" setter="set_sun_color" getter="get_sun_color">
+ Color of the sun.
</member>
<member name="sun_curve" type="float" setter="set_sun_curve" getter="get_sun_curve">
+ How quickly the sun fades away between [member sun_angle_min] and [member sun_angle_max]
</member>
<member name="sun_energy" type="float" setter="set_sun_energy" getter="get_sun_energy">
+ Amount of energy contribution from the sun.
</member>
<member name="sun_latitude" type="float" setter="set_sun_latitude" getter="get_sun_latitude">
+ The suns height using polar coordinates.
</member>
<member name="sun_longitude" type="float" setter="set_sun_longitude" getter="get_sun_longitude">
+ The direction of the sun using polar coordinates.
</member>
<member name="texture_size" type="int" setter="set_texture_size" getter="get_texture_size" enum="ProceduralSky.TextureSize">
+ Size of [Texture] that the ProceduralSky will generate.
</member>
</members>
<constants>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 44bba8bd20..666f6b4710 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -156,6 +156,536 @@
</description>
</method>
</methods>
+ <members>
+ <member name="application/boot_splash/fullsize" type="bool" setter="" getter="">
+ Scale the boot splash image to the full window length when engine starts (will leave it as default pixel size otherwise).
+ </member>
+ <member name="application/boot_splash/image" type="String" setter="" getter="">
+ Path to an image used for boot splash.
+ </member>
+ <member name="application/config/custom_user_dir_name" type="String" setter="" getter="">
+ This directory is used for storing persistent data (user:// filesystem). If a custom name is set, then system paths will be used to store this on Desktop (AppData on Windows, user ~/.config on Unixes, etc), else the Godot config folder is used. This name needs to be unique, and it's recommended to set it to something before publishing.
+ the "use_custom_user_dir" setting must be enabled for this to take effect.
+ </member>
+ <member name="application/config/icon" type="String" setter="" getter="">
+ Icon used for the project, set when project loads. Exporters will use this icon when possible to.
+ </member>
+ <member name="application/config/name" type="String" setter="" getter="">
+ Name of the project. It is used from both project manager and by the exporters. Overriding this as name.locale allows setting it in multiple languages.
+ </member>
+ <member name="application/config/use_custom_user_dir" type="bool" setter="" getter="">
+ Allow the project to save to it's own custom user dir (in AppData on windows or ~/.config on unixes). This setting only works for desktop exporters. A name must be set in the "custom_user_dir_name" setting for this to take effect.
+ </member>
+ <member name="application/run/disable_stderr" type="bool" setter="" getter="">
+ Disable printing to stderr on exported build.
+ </member>
+ <member name="application/run/disable_stdout" type="bool" setter="" getter="">
+ Disable printing to stdout on exported build.
+ </member>
+ <member name="application/run/frame_delay_msec" type="int" setter="" getter="">
+ Force a delay between frames in the main loop. This may be useful if you plan to disable vsync.
+ </member>
+ <member name="application/run/low_processor_mode" type="bool" setter="" getter="">
+ Turn on low processor mode. This setting only works on desktops. The screen is not redrawn if nothing changes visually. This is meant for writing applications and editors, but is pretty useless (and can hurt performance) on games.
+ </member>
+ <member name="application/run/low_processor_mode_sleep_usec" type="int" setter="" getter="">
+ Amount of sleeping between frames when the low_processor_mode is enabled. This effectively reduces CPU usage when this mode is enabled.
+ </member>
+ <member name="application/run/main_scene" type="String" setter="" getter="">
+ Path to the main scene file that will be loaded when the project runs.
+ </member>
+ <member name="audio/channel_disable_threshold_db" type="float" setter="" getter="">
+ Audio buses will disable automatically when sound goes below a given DB threshold for a given time. This saves CPU as effects assigned to that bus will no longer do any processing.
+ </member>
+ <member name="audio/channel_disable_time" type="float" setter="" getter="">
+ Audio buses will disable automatically when sound goes below a given DB threshold for a given time. This saves CPU as effects assigned to that bus will no longer do any processing.
+ </member>
+ <member name="audio/driver" type="String" setter="" getter="">
+ </member>
+ <member name="audio/mix_rate" type="int" setter="" getter="">
+ Mix rate used for audio. In general, it's better to not touch this and leave it to the host operating system.
+ </member>
+ <member name="audio/output_latency" type="int" setter="" getter="">
+ </member>
+ <member name="audio/video_delay_compensation_ms" type="int" setter="" getter="">
+ Setting to harcode audio delay when playing video. Best to leave this untouched unless you know what you are doing.
+ </member>
+ <member name="compression/formats/gzip/compression_level" type="int" setter="" getter="">
+ Default compression level for gzip. Affects compressed scenes and resources.
+ </member>
+ <member name="compression/formats/zlib/compression_level" type="int" setter="" getter="">
+ Default compression level for zlib. Affects compressed scenes and resources.
+ </member>
+ <member name="compression/formats/zstd/compression_level" type="int" setter="" getter="">
+ Default compression level for zstd. Affects compressed scenes and resources.
+ </member>
+ <member name="compression/formats/zstd/long_distance_matching" type="bool" setter="" getter="">
+ Enable long distance matching in zstd.
+ </member>
+ <member name="compression/formats/zstd/window_log_size" type="int" setter="" getter="">
+ </member>
+ <member name="debug/settings/crash_handler/message" type="String" setter="" getter="">
+ </member>
+ <member name="debug/settings/fps/force_fps" type="int" setter="" getter="">
+ </member>
+ <member name="debug/settings/gdscript/max_call_stack" type="int" setter="" getter="">
+ Maximum call stack allowed for debugging GDScript.
+ </member>
+ <member name="debug/settings/profiler/max_functions" type="int" setter="" getter="">
+ Maximum amount of functions per frame allowed when profiling.
+ </member>
+ <member name="debug/settings/stdout/print_fps" type="bool" setter="" getter="">
+ Print frames per second to stdout. Not very useful in general.
+ </member>
+ <member name="debug/settings/stdout/verbose_stdout" type="bool" setter="" getter="">
+ Print more information to stdout when running. It shows info such as memory leaks, which scenes and resources are being loaded, etc.
+ </member>
+ <member name="debug/settings/visual_script/max_call_stack" type="int" setter="" getter="">
+ Maximum call stack in visual scripting, to avoid infinite recursion.
+ </member>
+ <member name="display/mouse_cursor/custom_image" type="String" setter="" getter="">
+ Custom image for the mouse cursor.
+ </member>
+ <member name="display/mouse_cursor/custom_image_hotspot" type="Vector2" setter="" getter="">
+ Hotspot for the custom mouse cursor image.
+ </member>
+ <member name="display/window/allow_per_pixel_transparency" type="bool" setter="" getter="">
+ Allow per pixel transparency in a Desktop window. This affects performance if not needed, so leave it off.
+ </member>
+ <member name="display/window/dpi/allow_hidpi" type="bool" setter="" getter="">
+ Allow HiDPI display on Windows and OSX. On Desktop Linux, this can't be enabled or disabled.
+ </member>
+ <member name="display/window/energy_saving/keep_screen_on" type="bool" setter="" getter="">
+ Force keep the screen on, so the screensaver does not take over. Works on Desktop and Mobile.
+ </member>
+ <member name="display/window/handheld/orientation" type="String" setter="" getter="">
+ Default orientation for cell phone or tablet.
+ </member>
+ <member name="display/window/per_pixel_transparency" type="bool" setter="" getter="">
+ </member>
+ <member name="display/window/per_pixel_transparency_splash" type="bool" setter="" getter="">
+ </member>
+ <member name="display/window/size/always_on_top" type="bool" setter="" getter="">
+ Force the window to be always on top.
+ </member>
+ <member name="display/window/size/borderless" type="bool" setter="" getter="">
+ Force the window to be borderless.
+ </member>
+ <member name="display/window/size/fullscreen" type="bool" setter="" getter="">
+ Set the window to full screen when it starts.
+ </member>
+ <member name="display/window/size/height" type="int" setter="" getter="">
+ Set the main window height. On desktop, this is the default window size. Stretch mode settings use this also as a reference when enabled.
+ </member>
+ <member name="display/window/size/resizable" type="bool" setter="" getter="">
+ Allow the window to be resizable by default.
+ </member>
+ <member name="display/window/size/test_height" type="int" setter="" getter="">
+ Test a different height for the window. The main use for this is to test with stretch modes.
+ </member>
+ <member name="display/window/size/test_width" type="int" setter="" getter="">
+ Test a different width for the window. The main use for this is to test with stretch modes.
+ </member>
+ <member name="display/window/size/width" type="int" setter="" getter="">
+ Set the main window width. On desktop, this is the default window size. Stretch mode settings use this also as a reference when enabled.
+ </member>
+ <member name="display/window/vsync/use_vsync" type="bool" setter="" getter="">
+ Use VSync. Don't be stupid, don't turn this off.
+ </member>
+ <member name="editor/active" type="bool" setter="" getter="">
+ Internal editor setting, don't touch.
+ </member>
+ <member name="gui/common/default_scroll_deadzone" type="int" setter="" getter="">
+ </member>
+ <member name="gui/common/swap_ok_cancel" type="bool" setter="" getter="">
+ Enable swap OK and Cancel buttons on dialogs. This is because Windows/MacOS/Desktop Linux may use them in different order, so the GUI swaps them depending on the host OS. Disable this behavior by turning this setting off.
+ </member>
+ <member name="gui/theme/custom" type="String" setter="" getter="">
+ Use a custom theme resource, set a path to it here.
+ </member>
+ <member name="gui/theme/custom_font" type="String" setter="" getter="">
+ USe a custom default font resource, set a path to it here.
+ </member>
+ <member name="gui/theme/use_hidpi" type="bool" setter="" getter="">
+ Make sure the theme used works with hidpi.
+ </member>
+ <member name="gui/timers/incremental_search_max_interval_msec" type="int" setter="" getter="">
+ Timer setting for incremental search in Tree, IntemList, etc. controls.
+ </member>
+ <member name="gui/timers/text_edit_idle_detect_sec" type="int" setter="" getter="">
+ Timer for detecting idle in the editor.
+ </member>
+ <member name="input/ui_accept" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_cancel" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_down" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_end" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_focus_next" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_focus_prev" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_home" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_left" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_page_down" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_page_up" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_right" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_select" type="Array" setter="" getter="">
+ </member>
+ <member name="input/ui_up" type="Array" setter="" getter="">
+ </member>
+ <member name="input_devices/pointing/emulate_mouse_from_touch" type="bool" setter="" getter="">
+ </member>
+ <member name="input_devices/pointing/emulate_touch_from_mouse" type="bool" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_1" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_10" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_11" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_12" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_13" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_14" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_15" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_16" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_17" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_18" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_19" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_2" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_20" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_3" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_4" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_5" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_6" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_7" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_8" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_physics/layer_9" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_1" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_10" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_11" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_12" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_13" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_14" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_15" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_16" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_17" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_18" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_19" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_2" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_20" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_3" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_4" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_5" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_6" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_7" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_8" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/2d_render/layer_9" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_1" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_10" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_11" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_12" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_13" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_14" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_15" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_16" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_17" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_18" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_19" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_2" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_20" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_3" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_4" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_5" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_6" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_7" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_8" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_physics/layer_9" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_1" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_10" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_11" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_12" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_13" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_14" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_15" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_16" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_17" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_18" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_19" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_2" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_20" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_3" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_4" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_5" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_6" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_7" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_8" type="String" setter="" getter="">
+ </member>
+ <member name="layer_names/3d_render/layer_9" type="String" setter="" getter="">
+ </member>
+ <member name="locale/fallback" type="String" setter="" getter="">
+ </member>
+ <member name="locale/test" type="String" setter="" getter="">
+ </member>
+ <member name="logging/file_logging/enable_file_logging" type="bool" setter="" getter="">
+ Log all output to a file.
+ </member>
+ <member name="logging/file_logging/log_path" type="String" setter="" getter="">
+ Path to logs withint he project. Using an user:// based path is recommended.
+ </member>
+ <member name="logging/file_logging/max_log_files" type="int" setter="" getter="">
+ Amount of log files (used for rotation)/
+ </member>
+ <member name="memory/limits/message_queue/max_size_kb" type="int" setter="" getter="">
+ Godot uses a message queue to defer some function calls. If you run out of space on it (you will see an error), you can increase the size here.
+ </member>
+ <member name="memory/limits/multithreaded_server/rid_pool_prealloc" type="int" setter="" getter="">
+ This is used by servers when used in multi threading mode (servers and visual). RIDs are preallocated to avoid stalling the server requesting them on threads. If servers get stalled too often when loading resources in a thread, increase this number.
+ </member>
+ <member name="network/limits/debugger_stdout/max_chars_per_second" type="int" setter="" getter="">
+ Maximum amount of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
+ </member>
+ <member name="network/limits/debugger_stdout/max_errors_per_frame" type="int" setter="" getter="">
+ Maximum amount of errors allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
+ </member>
+ <member name="network/limits/debugger_stdout/max_messages_per_frame" type="int" setter="" getter="">
+ Maximum amount of messages allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
+ </member>
+ <member name="network/limits/packet_peer_stream/max_buffer_po2" type="int" setter="" getter="">
+ Default size of packet peer stream for deserializing godot data. Over this size, data is dropped.
+ </member>
+ <member name="network/remote_fs/max_pages" type="int" setter="" getter="">
+ Maximum amount of pages used for remote filesystem (used by debugging).
+ </member>
+ <member name="network/remote_fs/page_read_ahead" type="int" setter="" getter="">
+ Amount of read ahead used by remote filesystem. Improves latency.
+ </member>
+ <member name="network/remote_fs/page_size" type="int" setter="" getter="">
+ Page size used by remote filesystem.
+ </member>
+ <member name="network/ssl/certificates" type="String" setter="" getter="">
+ If your game or application uses HTTPS, a certificates file is needed. It must be set here.
+ </member>
+ <member name="node/name_casing" type="int" setter="" getter="">
+ When creating nodes names automatically, set the type of casing in this project. This is mostly an editor setting.
+ </member>
+ <member name="node/name_num_separator" type="int" setter="" getter="">
+ What to use to separate node name from number. This is mostly an editor setting.
+ </member>
+ <member name="physics/2d/physics_engine" type="String" setter="" getter="">
+ </member>
+ <member name="physics/2d/thread_model" type="int" setter="" getter="">
+ Set whether physics is run on the main thread or a separate one. Running the server on a thread increases performance, but restricts API Access to only physics process.
+ </member>
+ <member name="physics/3d/physics_engine" type="String" setter="" getter="">
+ </member>
+ <member name="physics/common/physics_fps" type="int" setter="" getter="">
+ Frames per second used in the physics. Physics always needs a fixed amount of frames per second.
+ </member>
+ <member name="physics/common/physics_jitter_fix" type="float" setter="" getter="">
+ Fix to improve physics jitter, specially on monitors where refresh rate is different than physics FPS.
+ </member>
+ <member name="rendering/environment/default_clear_color" type="Color" setter="" getter="">
+ Default background clear color.
+ </member>
+ <member name="rendering/limits/buffers/blend_shape_max_buffer_size_kb" type="int" setter="" getter="">
+ Max buffer size for blend shapes. Any blend shape bigger than this will not work.
+ </member>
+ <member name="rendering/limits/buffers/canvas_polygon_buffer_size_kb" type="int" setter="" getter="">
+ Max buffer size for drawing polygons. Any polygon bigger than this will not work.
+ </member>
+ <member name="rendering/limits/buffers/canvas_polygon_index_buffer_size_kb" type="int" setter="" getter="">
+ Max index buffer size for drawing polygons. Any polygon bigger than this will not work.
+ </member>
+ <member name="rendering/limits/buffers/immediate_buffer_size_kb" type="int" setter="" getter="">
+ Max buffer size for drawing immediate objects (ImmediateGeometry nodes). Nodes using more than this size will not work.
+ </member>
+ <member name="rendering/limits/rendering/max_renderable_elements" type="int" setter="" getter="">
+ Max amount of elements renderable in a frame. If more than this are visible per frame, they will be dropped. Keep in mind elements refer to mesh surfaces and not mesh themselves.
+ </member>
+ <member name="rendering/limits/time/time_rollover_secs" type="int" setter="" getter="">
+ Shaders have a time variable that constantly increases. At some point it needs to be rolled back to zero to avoid numerical errors on shader animations. This setting specifies when.
+ </member>
+ <member name="rendering/quality/2d/use_pixel_snap" type="bool" setter="" getter="">
+ Force snapping of polygons to pixels in 2D rendering. May help in some pixel art styles.
+ </member>
+ <member name="rendering/quality/depth_prepass/disable_for_vendors" type="String" setter="" getter="">
+ Disable depth pre-pass for some GPU vendors (usually mobile), as their architecture already does this.
+ </member>
+ <member name="rendering/quality/depth_prepass/enable" type="bool" setter="" getter="">
+ Do a previous depth pass before rendering materials. This increases performance in scenes with high overdraw, when complex materials and lighting are used.
+ </member>
+ <member name="rendering/quality/directional_shadow/size" type="int" setter="" getter="">
+ Size in pixels of the directional shadow.
+ </member>
+ <member name="rendering/quality/directional_shadow/size.mobile" type="int" setter="" getter="">
+ </member>
+ <member name="rendering/quality/driver/driver_name" type="String" setter="" getter="">
+ </member>
+ <member name="rendering/quality/filters/anisotropic_filter_level" type="int" setter="" getter="">
+ Maximum Anisotropic filter level used for textures when anisotropy enabled.
+ </member>
+ <member name="rendering/quality/filters/use_nearest_mipmap_filter" type="bool" setter="" getter="">
+ Force to use nearest mipmap filtering when using mipmaps. This may increase performance in mobile as less memory bandwidth is used.
+ </member>
+ <member name="rendering/quality/intended_usage/framebuffer_allocation" type="int" setter="" getter="">
+ Strategy used for framebuffer allocation. The simpler it is, the less memory it uses (but the least features it supports).
+ </member>
+ <member name="rendering/quality/intended_usage/framebuffer_allocation.mobile" type="int" setter="" getter="">
+ </member>
+ <member name="rendering/quality/intended_usage/framebuffer_mode" type="int" setter="" getter="">
+ </member>
+ <member name="rendering/quality/reflections/high_quality_ggx" type="bool" setter="" getter="">
+ For reflection probes and panorama backgrounds (sky), use a high amount of samples to create ggx blurred versions (used for roughness).
+ </member>
+ <member name="rendering/quality/reflections/high_quality_ggx.mobile" type="bool" setter="" getter="">
+ </member>
+ <member name="rendering/quality/reflections/texture_array_reflections" type="bool" setter="" getter="">
+ For reflection probes and panorama backgrounds (sky), use a texure array instead of mipmaps. This reduces jitter noise on reflections, but costs more performance and memory.
+ </member>
+ <member name="rendering/quality/reflections/texture_array_reflections.mobile" type="bool" setter="" getter="">
+ </member>
+ <member name="rendering/quality/shading/force_vertex_shading" type="bool" setter="" getter="">
+ Force vertex shading for all rendering. This can increase performance a lot, but also reduces quality inmensely. Can work to optimize on very low end mobile.
+ </member>
+ <member name="rendering/quality/shading/force_vertex_shading.mobile" type="bool" setter="" getter="">
+ </member>
+ <member name="rendering/quality/shadow_atlas/quadrant_0_subdiv" type="int" setter="" getter="">
+ Subdivision quadrant size for shadow mapping. See shadow mapping documentation.
+ </member>
+ <member name="rendering/quality/shadow_atlas/quadrant_1_subdiv" type="int" setter="" getter="">
+ Subdivision quadrant size for shadow mapping. See shadow mapping documentation.
+ </member>
+ <member name="rendering/quality/shadow_atlas/quadrant_2_subdiv" type="int" setter="" getter="">
+ Subdivision quadrant size for shadow mapping. See shadow mapping documentation.
+ </member>
+ <member name="rendering/quality/shadow_atlas/quadrant_3_subdiv" type="int" setter="" getter="">
+ Subdivision quadrant size for shadow mapping. See shadow mapping documentation.
+ </member>
+ <member name="rendering/quality/shadow_atlas/size" type="int" setter="" getter="">
+ Size for shadow atlas (used for point and omni lights). See documentation.
+ </member>
+ <member name="rendering/quality/shadow_atlas/size.mobile" type="int" setter="" getter="">
+ </member>
+ <member name="rendering/quality/shadows/filter_mode" type="int" setter="" getter="">
+ Shadow filter mode. The more complex the filter, the more memory bandwidth required.
+ </member>
+ <member name="rendering/quality/shadows/filter_mode.mobile" type="int" setter="" getter="">
+ </member>
+ <member name="rendering/quality/subsurface_scattering/follow_surface" type="bool" setter="" getter="">
+ Improves quality of subsurface scattering, but cost significantly increases.
+ </member>
+ <member name="rendering/quality/subsurface_scattering/quality" type="int" setter="" getter="">
+ Quality setting for subsurface scaterring (samples taken).
+ </member>
+ <member name="rendering/quality/subsurface_scattering/scale" type="int" setter="" getter="">
+ </member>
+ <member name="rendering/quality/subsurface_scattering/weight_samples" type="bool" setter="" getter="">
+ Weight subsurface scattering samples. Helps to avoid reading samples from unrelated parts of the screen.
+ </member>
+ <member name="rendering/quality/voxel_cone_tracing/high_quality" type="bool" setter="" getter="">
+ Use high quality voxel cone tracing (looks better, but requires a higher end GPU).
+ </member>
+ <member name="rendering/threads/thread_model" type="int" setter="" getter="">
+ Thread model for rendering. Rendering on a thread can vastly improve performance, but syncinc to the main thread can cause a bit more jitter.
+ </member>
+ <member name="rendering/vram_compression/import_etc" type="bool" setter="" getter="">
+ If the project uses this compression (usually low end mobile), texture importer will import these.
+ </member>
+ <member name="rendering/vram_compression/import_etc2" type="bool" setter="" getter="">
+ If the project uses this compression (usually high end mobile), texture importer will import these.
+ </member>
+ <member name="rendering/vram_compression/import_pvrtc" type="bool" setter="" getter="">
+ If the project uses this compression (usually iOS), texture importer will import these.
+ </member>
+ <member name="rendering/vram_compression/import_s3tc" type="bool" setter="" getter="">
+ If the project uses this compression (usually Desktop and Consoles), texture importer will import these.
+ </member>
+ <member name="script" type="Script" setter="" getter="">
+ </member>
+ </members>
<constants>
</constants>
</class>
diff --git a/doc/classes/Quat.xml b/doc/classes/Quat.xml
index 33f2b9758b..c755e6b02a 100644
--- a/doc/classes/Quat.xml
+++ b/doc/classes/Quat.xml
@@ -4,13 +4,13 @@
Quaternion.
</brief_description>
<description>
- A 4-dimensional vector representing a rotation.
- The vector represents a 4 dimensional complex number where multiplication of the basis elements is not commutative (multiplying i with j gives a different result than multiplying j with i).
- Multiplying quaternions reproduces rotation sequences. However quaternions need to be often renormalized, or else they suffer from precision issues.
- It can be used to perform SLERP (spherical-linear interpolation) between two rotations.
+ A unit quaternion used for representing 3D rotations.
+ It is similar to [Basis], which implements matrix representation of rotations, and can be parametrized using both an axis-angle pair or Euler angles. But due to its compactness and the way it is stored in memory, certain operations (obtaining axis-angle and performing SLERP, in particular) are more efficient and robust against floating point errors.
+ Quaternions need to be (re)normalized.
</description>
<tutorials>
- http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions
+ <link>http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions</link>
+ <link>http://docs.godotengine.org/en/latest/tutorials/math/rotations.html</link>
</tutorials>
<demos>
</demos>
@@ -18,16 +18,19 @@
<method name="Quat">
<return type="Quat">
</return>
- <argument index="0" name="x" type="float">
- </argument>
- <argument index="1" name="y" type="float">
- </argument>
- <argument index="2" name="z" type="float">
+ <argument index="0" name="from" type="Basis">
</argument>
- <argument index="3" name="w" type="float">
+ <description>
+ Returns the rotation matrix corresponding to the given quaternion.
+ </description>
+ </method>
+ <method name="Quat">
+ <return type="Quat">
+ </return>
+ <argument index="0" name="euler" type="Vector3">
</argument>
<description>
- Returns a quaternion defined by these values.
+ Returns a quaternion that will perform a rotation specified by Euler angles (in the YXZ convention: first Z, then X, and Y last), given in the vector format as (X-angle, Y-angle, Z-angle).
</description>
</method>
<method name="Quat">
@@ -44,10 +47,16 @@
<method name="Quat">
<return type="Quat">
</return>
- <argument index="0" name="from" type="Basis">
+ <argument index="0" name="x" type="float">
+ </argument>
+ <argument index="1" name="y" type="float">
+ </argument>
+ <argument index="2" name="z" type="float">
+ </argument>
+ <argument index="3" name="w" type="float">
</argument>
<description>
- Returns the rotation matrix corresponding to the given quaternion.
+ Returns a quaternion defined by these values.
</description>
</method>
<method name="cubic_slerp">
@@ -74,6 +83,13 @@
Returns the dot product of two quaternions.
</description>
</method>
+ <method name="get_euler">
+ <return type="Vector3">
+ </return>
+ <description>
+ Return Euler angles (in the YXZ convention: first Z, then X, and Y last) corresponding to the rotation represented by the unit quaternion. Returned vector contains the rotation angles in the format (X-angle, Y-angle, Z-angle).
+ </description>
+ </method>
<method name="inverse">
<return type="Quat">
</return>
@@ -109,6 +125,22 @@
Returns a copy of the quaternion, normalized to unit length.
</description>
</method>
+ <method name="set_axis_angle">
+ <argument index="0" name="axis" type="Vector3">
+ </argument>
+ <argument index="1" name="angle" type="float">
+ </argument>
+ <description>
+ Set the quaternion to a rotation which rotates around axis by the specified angle, in radians. The axis must be a normalized vector.
+ </description>
+ </method>
+ <method name="set_euler">
+ <argument index="0" name="euler" type="Vector3">
+ </argument>
+ <description>
+ Set the quaternion to a rotation specified by Euler angles (in the YXZ convention: first Z, then X, and Y last), given in the vector format as (X-angle, Y-angle, Z-angle).
+ </description>
+ </method>
<method name="slerp">
<return type="Quat">
</return>
diff --git a/doc/classes/Range.xml b/doc/classes/Range.xml
index 545f5cc83b..fa7e20eff6 100644
--- a/doc/classes/Range.xml
+++ b/doc/classes/Range.xml
@@ -29,6 +29,10 @@
</method>
</methods>
<members>
+ <member name="allow_greater" type="bool" setter="set_allow_greater" getter="is_greater_allowed">
+ </member>
+ <member name="allow_lesser" type="bool" setter="set_allow_lesser" getter="is_lesser_allowed">
+ </member>
<member name="exp_edit" type="bool" setter="set_exp_ratio" getter="is_ratio_exp">
If [code]true[/code] and [code]min_value[/code] is greater than 0, [code]value[/code] will be represented exponentially rather than linearly.
</member>
diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml
index 1961aee8b5..1eea940da9 100644
--- a/doc/classes/Rect2.xml
+++ b/doc/classes/Rect2.xml
@@ -7,7 +7,7 @@
Rect2 consists of a position, a size, and several utility functions. It is typically used for fast overlap tests.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/math/index.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
</tutorials>
<demos>
</demos>
@@ -42,6 +42,7 @@
<return type="Rect2">
</return>
<description>
+ Returns a [code]Rect2[/code] with equivalent position and area, modified so that the top-left corner is the origin and [code]width[/code] and [code]height[/code] are positive.
</description>
</method>
<method name="clip">
diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml
index 36a0c9cfda..a9a897ebaf 100644
--- a/doc/classes/ReflectionProbe.xml
+++ b/doc/classes/ReflectionProbe.xml
@@ -5,7 +5,7 @@
<description>
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/reflection_probes.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/reflection_probes.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index a6c63cfb76..618f2f42b2 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -8,7 +8,7 @@
Note that assignments to [member bbcode_text] clear the tag stack and reconstruct it from the property's contents. Any edits made to [member bbcode_text] will erase previous edits made from other manual sources such as [method append_bbcode] and the [code]push_*[/code] / [method pop] methods.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/gui/bbcode_in_richtextlabel.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/gui/bbcode_in_richtextlabel.html</link>
</tutorials>
<demos>
</demos>
@@ -47,6 +47,13 @@
Clears the tag stack and sets [member bbcode_text] to an empty string.
</description>
</method>
+ <method name="get_content_height">
+ <return type="int">
+ </return>
+ <description>
+ Returns the height of the content.
+ </description>
+ </method>
<method name="get_line_count" qualifiers="const">
<return type="int">
</return>
@@ -75,13 +82,6 @@
Returns the number of visible lines.
</description>
</method>
- <method name="get_content_height">
- <return type="int">
- </return>
- <description>
- Returns the height of the content.
- </description>
- </method>
<method name="newline">
<return type="void">
</return>
@@ -328,12 +328,6 @@
</theme_item>
<theme_item name="font_color_shadow" type="Color">
</theme_item>
- <theme_item name="shadow_as_outline" type="int">
- </theme_item>
- <theme_item name="shadow_offset_x" type="int">
- </theme_item>
- <theme_item name="shadow_offset_y" type="int">
- </theme_item>
<theme_item name="italics_font" type="Font">
</theme_item>
<theme_item name="line_separation" type="int">
@@ -346,6 +340,12 @@
</theme_item>
<theme_item name="selection_color" type="Color">
</theme_item>
+ <theme_item name="shadow_as_outline" type="int">
+ </theme_item>
+ <theme_item name="shadow_offset_x" type="int">
+ </theme_item>
+ <theme_item name="shadow_offset_y" type="int">
+ </theme_item>
<theme_item name="table_hseparation" type="int">
</theme_item>
<theme_item name="table_vseparation" type="int">
diff --git a/doc/classes/RigidBody.xml b/doc/classes/RigidBody.xml
index 3190aed5ed..4253560f67 100644
--- a/doc/classes/RigidBody.xml
+++ b/doc/classes/RigidBody.xml
@@ -10,7 +10,7 @@
If you need to override the default physics behavior, you can write a custom force integration. See [member custom_integrator].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index dec0cbbcc3..f5a19ede0c 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -7,8 +7,8 @@
As one of the most important classes, the [code]SceneTree[/code] manages the hierarchy of nodes in a scene as well as scenes themselves. Nodes can be added, retrieved and removed. The whole scene tree (and thus the current scene) can be paused. Scenes can be loaded, switched and reloaded. You can also use the SceneTree to organize your nodes into groups: every node can be assigned as many groups as you want to create, e.g. a "enemy" group. You can then iterate these groups or even call methods and set properties on all the group's members at once.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scene_tree.html
- http://docs.godotengine.org/en/3.0/tutorials/viewports/multiple_resolutions.html
+ <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scene_tree.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/viewports/multiple_resolutions.html</link>
</tutorials>
<demos>
</demos>
@@ -34,7 +34,7 @@
<argument index="2" name="method" type="String">
</argument>
<description>
- Calls [code]method[/code] on each member of the given group, respecting the given [enum GROUP_CALL] flags.
+ Calls [code]method[/code] on each member of the given group, respecting the given [enum GroupCallFlags].
</description>
</method>
<method name="change_scene">
@@ -160,7 +160,7 @@
<argument index="2" name="notification" type="int">
</argument>
<description>
- Sends the given notification to all members of the [code]group[/code], respecting the given [enum GROUP_CALL] flags.
+ Sends the given notification to all members of the [code]group[/code], respecting the given [enum GroupCallFlags].
</description>
</method>
<method name="queue_delete">
@@ -220,7 +220,7 @@
<argument index="3" name="value" type="Variant">
</argument>
<description>
- Sets the given [code]property[/code] to [code]value[/code] on all members of the given group, respecting the given [enum GROUP_CALL] flags.
+ Sets the given [code]property[/code] to [code]value[/code] on all members of the given group, respecting the given [enum GroupCallFlags].
</description>
</method>
<method name="set_input_as_handled">
@@ -266,7 +266,12 @@
<member name="edited_scene_root" type="Node" setter="set_edited_scene_root" getter="get_edited_scene_root">
The root of the edited scene.
</member>
- <member name="multiplayer_api" type="MultiplayerAPI" setter="set_multiplayer_api" getter="get_multiplayer_api">
+ <member name="multiplayer" type="MultiplayerAPI" setter="set_multiplayer" getter="get_multiplayer">
+ The default [MultiplayerAPI] instance for this SceneTree.
+ </member>
+ <member name="multiplayer_poll" type="bool" setter="set_multiplayer_poll_enabled" getter="is_multiplayer_poll_enabled">
+ If [code]true[/code] (default) enable the automatic polling of the [MultiplayerAPI] for this SceneTree during [signal idle_frame].
+ When [code]false[/code] you need to manually call [method MultiplayerAPI.poll] for processing network packets and delivering RPCs/RSETs. This allows to run RPCs/RSETs in a different loop (e.g. physics, thread, specific time step) and for manual [Mutex] protecion when accessing the [MultiplayerAPI] from threads.
</member>
<member name="network_peer" type="NetworkedMultiplayerPeer" setter="set_network_peer" getter="get_network_peer">
The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the SceneTree will become a network server (check with [method is_network_server()]) and will set root node's network mode to master (see NETWORK_MODE_* constants in [Node]), or it will become a regular peer with root node set to slave. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to SceneTree's signals.
diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml
index 97f6a60f01..09c60afc2f 100644
--- a/doc/classes/Script.xml
+++ b/doc/classes/Script.xml
@@ -8,7 +8,7 @@
The 'new' method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scripting.html
+ <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scripting.html</link>
</tutorials>
<demos>
</demos>
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/ScrollContainer.xml b/doc/classes/ScrollContainer.xml
index 64b0fcfd83..02f58a88cb 100644
--- a/doc/classes/ScrollContainer.xml
+++ b/doc/classes/ScrollContainer.xml
@@ -11,6 +11,18 @@
<demos>
</demos>
<methods>
+ <method name="get_h_scrollbar">
+ <return type="HScrollBar">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_v_scrollbar">
+ <return type="VScrollBar">
+ </return>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="scroll_deadzone" type="int" setter="set_deadzone" getter="get_deadzone">
@@ -22,7 +34,7 @@
If [code]true[/code], enables horizontal scrolling.
</member>
<member name="scroll_vertical" type="int" setter="set_v_scroll" getter="get_v_scroll">
- The current horizontal scroll value.
+ The current vertical scroll value.
</member>
<member name="scroll_vertical_enabled" type="bool" setter="set_enable_v_scroll" getter="is_v_scroll_enabled">
If [code]true[/code], enables vertical scrolling.
@@ -42,4 +54,8 @@
</signals>
<constants>
</constants>
+ <theme_items>
+ <theme_item name="bg" type="StyleBox">
+ </theme_item>
+ </theme_items>
</class>
diff --git a/doc/classes/Shader.xml b/doc/classes/Shader.xml
index 732881c777..76049d8947 100644
--- a/doc/classes/Shader.xml
+++ b/doc/classes/Shader.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Shader" inherits="Resource" category="Core" version="3.1">
<brief_description>
- To be changed, ignore.
+ A custom shader program.
</brief_description>
<description>
- To be changed, ignore.
+ This class allows you to define a custom shader program that can be used for various materials to render objects.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/shading/index.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/shading/index.html</link>
</tutorials>
<demos>
</demos>
@@ -24,6 +24,7 @@
<return type="int" enum="Shader.Mode">
</return>
<description>
+ Returns the shader mode for the shader, eiter [code]MODE_CANVAS_ITEM[/code], [code]MODE_SPATIAL[/code] or [code]MODE_PARTICLES[/code]
</description>
</method>
<method name="has_param" qualifiers="const">
diff --git a/doc/classes/ShaderMaterial.xml b/doc/classes/ShaderMaterial.xml
index 4767686a8f..5abba9fba9 100644
--- a/doc/classes/ShaderMaterial.xml
+++ b/doc/classes/ShaderMaterial.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ShaderMaterial" inherits="Material" category="Core" version="3.1">
<brief_description>
+ A material that uses a custom [Shader] program.
</brief_description>
<description>
+ A material that uses a custom [Shader] program to render either items to screen or process particles. You can create multiple materials for the same shader but configure different values for the uniforms defined in the shader.
</description>
<tutorials>
</tutorials>
@@ -15,6 +17,7 @@
<argument index="0" name="param" type="String">
</argument>
<description>
+ Returns the current value set for this material of a uniform in the shader.
</description>
</method>
<method name="set_shader_param">
@@ -25,11 +28,13 @@
<argument index="1" name="value" type="Variant">
</argument>
<description>
+ Changes the value set for this material of a uniform in the shader.
</description>
</method>
</methods>
<members>
<member name="shader" type="Shader" setter="set_shader" getter="get_shader">
+ The [Shader] program used to render this material.
</member>
</members>
<constants>
diff --git a/doc/classes/Shape.xml b/doc/classes/Shape.xml
index 582e4f80c3..fcd01bc25a 100644
--- a/doc/classes/Shape.xml
+++ b/doc/classes/Shape.xml
@@ -7,7 +7,7 @@
Base class for all 3D shape resources. All 3D shapes that inherit from this can be set into a [PhysicsBody] or [Area].
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Shape2D.xml b/doc/classes/Shape2D.xml
index ad20bf607a..6c13496fc4 100644
--- a/doc/classes/Shape2D.xml
+++ b/doc/classes/Shape2D.xml
@@ -7,7 +7,7 @@
Base class for all 2D Shapes. All 2D shape types inherit from this.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Skeleton.xml b/doc/classes/Skeleton.xml
index 0208d56cfa..4d826002fe 100644
--- a/doc/classes/Skeleton.xml
+++ b/doc/classes/Skeleton.xml
@@ -5,6 +5,8 @@
</brief_description>
<description>
Skeleton provides a hierarchical interface for managing bones, including pose, rest and animation (see [Animation]). Skeleton will support rag doll dynamics in the future.
+ The overall transform of a bone with respect to the skeleton is determined by the following hierarchical order: rest pose, custom pose and pose.
+ Note that "global pose" below refers to the overall transform of the bone with respect to skeleton, so it not the actual global/world transform of the bone.
</description>
<tutorials>
</tutorials>
@@ -60,6 +62,7 @@
<argument index="0" name="bone_idx" type="int">
</argument>
<description>
+ Return the custom pose of the specified bone. Custom pose is applied on top of the rest pose.
</description>
</method>
<method name="get_bone_global_pose" qualifiers="const">
@@ -68,6 +71,7 @@
<argument index="0" name="bone_idx" type="int">
</argument>
<description>
+ Return the overall transform of the specified bone, with respect to the skeleton. Being relative to the skeleton frame, this is not the actual "global" transform of the bone.
</description>
</method>
<method name="get_bone_name" qualifiers="const">
@@ -76,7 +80,7 @@
<argument index="0" name="bone_idx" type="int">
</argument>
<description>
- Return the name of the bone at index "index"
+ Return the name of the bone at index "index".
</description>
</method>
<method name="get_bone_parent" qualifiers="const">
@@ -94,7 +98,7 @@
<argument index="0" name="bone_idx" type="int">
</argument>
<description>
- Return the pose transform for bone "bone_idx".
+ Return the pose transform of the specified bone. Pose is applied on top of the custom pose, which is applied on top the rest pose.
</description>
</method>
<method name="get_bone_rest" qualifiers="const">
@@ -112,6 +116,7 @@
<argument index="0" name="bone_idx" type="int">
</argument>
<description>
+ Return the combination of custom pose and pose. The returned transform is in skeleton's reference frame.
</description>
</method>
<method name="get_bound_child_nodes_to_bone" qualifiers="const">
@@ -131,6 +136,36 @@
<description>
</description>
</method>
+ <method name="physical_bones_add_collision_exception">
+ <return type="void">
+ </return>
+ <argument index="0" name="exception" type="RID">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="physical_bones_remove_collision_exception">
+ <return type="void">
+ </return>
+ <argument index="0" name="exception" type="RID">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="physical_bones_start_simulation">
+ <return type="void">
+ </return>
+ <argument index="0" name="bones" type="Array" default="[ ]">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="physical_bones_stop_simulation">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="set_bone_custom_pose">
<return type="void">
</return>
diff --git a/doc/classes/Skeleton2D.xml b/doc/classes/Skeleton2D.xml
new file mode 100644
index 0000000000..654c816ba8
--- /dev/null
+++ b/doc/classes/Skeleton2D.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Skeleton2D" inherits="Node2D" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="get_bone">
+ <return type="Bone2D">
+ </return>
+ <argument index="0" name="arg0" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_bone_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_skeleton" qualifiers="const">
+ <return type="RID">
+ </return>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <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/Spatial.xml b/doc/classes/Spatial.xml
index 9ef60109de..d9242d8c42 100644
--- a/doc/classes/Spatial.xml
+++ b/doc/classes/Spatial.xml
@@ -5,11 +5,10 @@
</brief_description>
<description>
Most basic 3D game object, with a 3D [Transform] and visibility settings. All other 3D game objects inherit from Spatial. Use Spatial as a parent node to move, scale, rotate and show/hide children in a 3D project.
-
Affine operations (rotate, scale, translate) happen in parent's local coordinate system, unless the Spatial object is set as top level. Affine operations in this coordinate system correspond to direct affine operations on the Spatial's transform. The word local below refers to this coordinate system. The coordinate system that is attached to the Spatial object itself is referred to as object-local coordinate system.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/introduction_to_3d.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/introduction_to_3d.html</link>
</tutorials>
<demos>
</demos>
@@ -284,7 +283,6 @@
</member>
<member name="rotation" type="Vector3" setter="set_rotation" getter="get_rotation">
Rotation part of the local transformation, specified in terms of YXZ-Euler angles in the format (X-angle, Y-angle, Z-angle), in radians.
-
Note that in the mathematical sense, rotation is a matrix and not a vector. The three Euler angles, which are the three indepdent parameters of the Euler-angle parametrization of the rotation matrix, are stored in a [Vector3] data structure not because the rotation is a vector, but only because [Vector3] exists as a convenient data-structure to store 3 floating point numbers. Therefore, applying affine operations on the rotation "vector" is not meaningful.
</member>
<member name="rotation_degrees" type="Vector3" setter="set_rotation_degrees" getter="get_rotation_degrees">
diff --git a/doc/classes/SpatialMaterial.xml b/doc/classes/SpatialMaterial.xml
index ace1aa846e..b45f2a13d4 100644
--- a/doc/classes/SpatialMaterial.xml
+++ b/doc/classes/SpatialMaterial.xml
@@ -5,7 +5,7 @@
<description>
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/spatial_material.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/spatial_material.html</link>
</tutorials>
<demos>
</demos>
@@ -84,6 +84,8 @@
</member>
<member name="flags_albedo_tex_force_srgb" type="bool" setter="set_flag" getter="get_flag">
</member>
+ <member name="flags_do_not_receive_shadows" type="bool" setter="set_flag" getter="get_flag">
+ </member>
<member name="flags_fixed_size" type="bool" setter="set_flag" getter="get_flag">
</member>
<member name="flags_no_depth_test" type="bool" setter="set_flag" getter="get_flag">
@@ -316,7 +318,9 @@
</constant>
<constant name="FLAG_ALBEDO_TEXTURE_FORCE_SRGB" value="13" enum="Flags">
</constant>
- <constant name="FLAG_MAX" value="14" enum="Flags">
+ <constant name="FLAG_DONT_RECEIVE_SHADOWS" value="14" enum="Flags">
+ </constant>
+ <constant name="FLAG_MAX" value="15" enum="Flags">
</constant>
<constant name="DIFFUSE_BURLEY" value="0" enum="DiffuseMode">
</constant>
diff --git a/doc/classes/SpotLight.xml b/doc/classes/SpotLight.xml
index 57a802d325..1f81e9e5c2 100644
--- a/doc/classes/SpotLight.xml
+++ b/doc/classes/SpotLight.xml
@@ -7,7 +7,7 @@
A SpotLight light is a type of [Light] node that emits lights in a specific direction, in the shape of a cone. The light is attenuated through the distance and this attenuation can be configured by changing the energy, radius and attenuation parameters of [Light]. TODO: Image of a spotlight.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/SpriteBase3D.xml b/doc/classes/SpriteBase3D.xml
index 5eb4eb09af..fd4b583928 100644
--- a/doc/classes/SpriteBase3D.xml
+++ b/doc/classes/SpriteBase3D.xml
@@ -11,6 +11,12 @@
<demos>
</demos>
<methods>
+ <method name="generate_triangle_mesh" qualifiers="const">
+ <return type="TriangleMesh">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_item_rect" qualifiers="const">
<return type="Rect2">
</return>
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/StreamPeerSSL.xml b/doc/classes/StreamPeerSSL.xml
index d7350ac1d5..3081abd5c4 100644
--- a/doc/classes/StreamPeerSSL.xml
+++ b/doc/classes/StreamPeerSSL.xml
@@ -7,7 +7,7 @@
SSL Stream peer. This object can be used to connect to SSL servers.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index 83fb76f287..0ba1066dfd 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -643,6 +643,20 @@
Returns the right side of the string from a given position.
</description>
</method>
+ <method name="rsplit">
+ <return type="PoolStringArray">
+ </return>
+ <argument index="0" name="divisor" type="String">
+ </argument>
+ <argument index="1" name="allow_empty" type="bool" default="True">
+ </argument>
+ <argument index="2" name="maxsplit" type="int" default="0">
+ </argument>
+ <description>
+ Splits the string by a [code]divisor[/code] string and returns an array of the substrings, starting from right. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",".
+ If [code]maxsplit[/code] is specified, then it is number of splits to do, default is 0 which splits all the items.
+ </description>
+ </method>
<method name="rstrip">
<return type="String">
</return>
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 76d94fbfbc..36408663f6 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -99,6 +99,8 @@
</argument>
<argument index="2" name="can_be_hidden" type="bool" default="true">
</argument>
+ <argument index="3" name="wrap_index" type="int" default="0">
+ </argument>
<description>
</description>
</method>
@@ -130,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>
@@ -218,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>
@@ -263,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>
@@ -300,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>
@@ -342,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>
@@ -384,7 +417,7 @@
<member name="v_scroll_speed" type="float" setter="set_v_scroll_speed" getter="get_v_scroll_speed">
If [code]true[/code], enables text wrapping when it goes beyond he edge of what is visible.
</member>
- <member name="wrap_lines" type="bool" setter="set_wrap" getter="is_wrapping">
+ <member name="wrap_enabled" type="bool" setter="set_wrap_enabled" getter="is_wrap_enabled">
</member>
</members>
<signals>
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/TextureRect.xml b/doc/classes/TextureRect.xml
index 7a4208ccea..95afc5d281 100644
--- a/doc/classes/TextureRect.xml
+++ b/doc/classes/TextureRect.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TextureRect" inherits="Control" category="Core" version="3.1">
<brief_description>
- Draws a sprite or a texture inside a User Interface. The texture can tile or not.
+ Control for drawing textures.
</brief_description>
<description>
- Use TextureRect to draw icons and sprites in your User Interfaces. To create panels and menu boxes, take a look at [NinePatchFrame]. Its Stretch Mode property controls the texture's scale and placement. It can scale, tile and stay centered inside its bounding rectangle. TextureRect is one of the 5 most common nodes to create game UI.
+ Used to draw icons and sprites in a user interface. The texture's placement can be controlled with the [member stretch_mode] property. It can scale, tile, or stay centered inside its bounding rectangle.
</description>
<tutorials>
</tutorials>
@@ -14,10 +14,10 @@
</methods>
<members>
<member name="expand" type="bool" setter="set_expand" getter="has_expand">
- If [code]true[/code], the texture scales to fit its bounding rectangle. Default value: [code]false[/code].
+ If [code]true[/code] the texture scales to fit its bounding rectangle. Default value: [code]false[/code].
</member>
<member name="stretch_mode" type="int" setter="set_stretch_mode" getter="get_stretch_mode" enum="TextureRect.StretchMode">
- Controls the texture's behavior when you resize the node's bounding rectangle. Set it to one of the [code]STRETCH_*[/code] constants. See the constants to learn more.
+ Controls the texture's behavior when resizing the node's bounding rectangle. See [enum StretchMode].
</member>
<member name="texture" type="Texture" setter="set_texture" getter="get_texture">
The node's [Texture] resource.
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index ec67370c79..656063771d 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -7,7 +7,7 @@
Node for 2D tile-based maps. Tilemaps use a [TileSet] which contain a list of tiles (textures plus optional collision, navigation, and/or occluder shapes) which are used to create grid-based maps.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/2d/using_tilemaps.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/2d/using_tilemaps.html</link>
</tutorials>
<demos>
</demos>
@@ -16,14 +16,14 @@
<return type="void">
</return>
<description>
- Clear all cells.
+ Clears all cells.
</description>
</method>
<method name="fix_invalid_tiles">
<return type="void">
</return>
<description>
- Clear cells that does not exist in the tileset.
+ Clears cells that do not exist in the tileset.
</description>
</method>
<method name="get_cell" qualifiers="const">
@@ -34,7 +34,7 @@
<argument index="1" name="y" type="int">
</argument>
<description>
- Return the tile index of the referenced cell.
+ Returns the tile index of the given cell.
</description>
</method>
<method name="get_cellv" qualifiers="const">
@@ -43,7 +43,7 @@
<argument index="0" name="position" type="Vector2">
</argument>
<description>
- Return the tile index of the cell referenced by a Vector2.
+ Returns the tile index of the cell given by a Vector2.
</description>
</method>
<method name="get_collision_layer_bit" qualifiers="const">
@@ -52,6 +52,7 @@
<argument index="0" name="bit" type="int">
</argument>
<description>
+ Returns [code]true[/code] if the given collision layer bit is set.
</description>
</method>
<method name="get_collision_mask_bit" qualifiers="const">
@@ -60,13 +61,14 @@
<argument index="0" name="bit" type="int">
</argument>
<description>
+ Returns [code]true[/code] if the given collision mask bit is set.
</description>
</method>
<method name="get_used_cells" qualifiers="const">
<return type="Array">
</return>
<description>
- Return an array of all cells containing a tile from the tileset (i.e. a tile index different from -1).
+ Returns an array of all cells containing a tile from the tileset (i.e. a tile index different from [code]-1[/code]).
</description>
</method>
<method name="get_used_cells_by_id" qualifiers="const">
@@ -75,12 +77,14 @@
<argument index="0" name="id" type="int">
</argument>
<description>
+ Returns an array of all cells with the given tile id.
</description>
</method>
<method name="get_used_rect">
<return type="Rect2">
</return>
<description>
+ Returns a rectangle enclosing the used (non-empty) tiles of the map.
</description>
</method>
<method name="is_cell_transposed" qualifiers="const">
@@ -91,7 +95,7 @@
<argument index="1" name="y" type="int">
</argument>
<description>
- Return whether the referenced cell is transposed, i.e. the X and Y axes are swapped (mirroring with regard to the (1,1) vector).
+ Returns [code]true[/code] if the given cell is transposed, i.e. the x and y axes are swapped.
</description>
</method>
<method name="is_cell_x_flipped" qualifiers="const">
@@ -102,7 +106,7 @@
<argument index="1" name="y" type="int">
</argument>
<description>
- Return whether the referenced cell is flipped over the X axis.
+ Returns [code]true[/code] if the given cell is flipped in the x axis.
</description>
</method>
<method name="is_cell_y_flipped" qualifiers="const">
@@ -113,7 +117,7 @@
<argument index="1" name="y" type="int">
</argument>
<description>
- Return whether the referenced cell is flipped over the Y axis.
+ Returns [code]true[/code] if the given cell is flipped in the y axis.
</description>
</method>
<method name="map_to_world" qualifiers="const">
@@ -124,8 +128,8 @@
<argument index="1" name="ignore_half_ofs" type="bool" default="false">
</argument>
<description>
- Return the absolute world position corresponding to the tilemap (grid-based) coordinates given as an argument.
- Optionally, the tilemap's potential half offset can be ignored.
+ Returns the global position corresponding to the given tilemap (grid-based) coordinates.
+ Optionally, the tilemap's half offset can be ignored.
</description>
</method>
<method name="set_cell">
@@ -146,9 +150,9 @@
<argument index="6" name="autotile_coord" type="Vector2" default="Vector2( 0, 0 )">
</argument>
<description>
- Set the tile index for the cell referenced by its grid-based X and Y coordinates.
- A tile index of -1 clears the cell.
- Optionally, the tile can also be flipped over the X and Y coordinates, transposed, or be given autotile coordinates.
+ 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.
</description>
</method>
<method name="set_cellv">
@@ -165,9 +169,9 @@
<argument index="4" name="transpose" type="bool" default="false">
</argument>
<description>
- Set the tile index for the cell referenced by a Vector2 of grid-based coordinates.
- A tile index of -1 clears the cell.
- Optionally, the tile can also be flipped over the X and Y axes or transposed.
+ 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.
</description>
</method>
<method name="set_collision_layer_bit">
@@ -178,7 +182,7 @@
<argument index="1" name="value" type="bool">
</argument>
<description>
- Set any collision layer to be [code]true[/code] or [code]false[/code].
+ Sets the given collision layer bit.
</description>
</method>
<method name="set_collision_mask_bit">
@@ -189,6 +193,7 @@
<argument index="1" name="value" type="bool">
</argument>
<description>
+ Sets the given collision mask bit.
</description>
</method>
<method name="update_bitmask_area">
@@ -197,7 +202,7 @@
<argument index="0" name="position" type="Vector2">
</argument>
<description>
- Applies autotiling rules to the cell (and its adjacent cells) referenced by its grid-based X and Y coordinates.
+ Applies autotiling rules to the cell (and its adjacent cells) referenced by its grid-based x and y coordinates.
</description>
</method>
<method name="update_bitmask_region">
@@ -208,8 +213,8 @@
<argument index="1" name="end" type="Vector2" default="Vector2( 0, 0 )">
</argument>
<description>
- Applies autotiling rules to the cells in the given region (specified by grid-based X and Y coordinates).
- Calling with invalid (or missing) parameters applies autotiling rules for the entire TileMap.
+ Applies autotiling rules to the cells in the given region (specified by grid-based x and y coordinates).
+ Calling with invalid (or missing) parameters applies autotiling rules for the entire tilemap.
</description>
</method>
<method name="world_to_map" qualifiers="const">
@@ -218,7 +223,7 @@
<argument index="0" name="world_position" type="Vector2">
</argument>
<description>
- Return the tilemap (grid-based) coordinates corresponding to the absolute world position given as an argument.
+ Returns the tilemap (grid-based) coordinatescorresponding to the given global position.
</description>
</method>
</methods>
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index 4e218f5595..bdf8634a6c 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -262,6 +262,14 @@
<description>
</description>
</method>
+ <method name="tile_get_z_index" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="tile_set_light_occluder">
<return type="void">
</return>
@@ -428,11 +436,23 @@
<description>
</description>
</method>
+ <method name="tile_set_z_index">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="z_index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<constants>
<constant name="BITMASK_2X2" value="0" enum="BitmaskMode">
</constant>
- <constant name="BITMASK_3X3" value="1" enum="BitmaskMode">
+ <constant name="BITMASK_3X3_MINIMAL" value="1" enum="BitmaskMode">
+ </constant>
+ <constant name="BITMASK_3X3" value="2" enum="BitmaskMode">
</constant>
<constant name="BIND_TOPLEFT" value="1" enum="AutotileBindings">
</constant>
diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml
index c51a52d911..d1c8722901 100644
--- a/doc/classes/Timer.xml
+++ b/doc/classes/Timer.xml
@@ -24,7 +24,7 @@
<argument index="0" name="time_sec" type="float" default="-1">
</argument>
<description>
- Starts the timer. Sets [code]wait_time[/code] to [code]time_sec[/code] if [code]time_sec[/code] > 0. This also resets the remaining time to [code]wait_time[/code].
+ Starts the timer. Sets [code]wait_time[/code] to [code]time_sec[/code] if [code]time_sec[/code] &gt; 0. This also resets the remaining time to [code]wait_time[/code].
Note: this method will not resume a paused timer. See [method set_paused].
</description>
</method>
diff --git a/doc/classes/ToolButton.xml b/doc/classes/ToolButton.xml
index 1dbfd63010..d8db95a854 100644
--- a/doc/classes/ToolButton.xml
+++ b/doc/classes/ToolButton.xml
@@ -5,7 +5,6 @@
</brief_description>
<description>
This is a helper class to generate a flat [Button] (see [method Button.set_flat]), creating a ToolButton is equivalent to:
-
[codeblock]
var btn = Button.new()
btn.set_flat(true)
diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml
index 4567f1681c..0dd8038b36 100644
--- a/doc/classes/Transform.xml
+++ b/doc/classes/Transform.xml
@@ -7,8 +7,8 @@
Represents one or many transformations in 3D space such as translation, rotation, or scaling. It consists of a [Basis] "basis" and an [Vector3] "origin". It is similar to a 3x4 matrix.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/math/index.html
- http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
+ <link>http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index 6448b26972..57e0f2825a 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -308,7 +308,7 @@
<argument index="0" name="child" type="Object">
</argument>
<description>
- Removes the child TreeItem at index [code]index[/code].
+ Removes the given child TreeItem.
</description>
</method>
<method name="select">
diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml
index 95405cc4f6..d82694d328 100644
--- a/doc/classes/Tween.xml
+++ b/doc/classes/Tween.xml
@@ -181,6 +181,12 @@
Returns [code]true[/code] if any tweens are currently running. Note that this method doesn't consider tweens that have ended.
</description>
</method>
+ <method name="is_stopped" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="remove">
<return type="bool">
</return>
@@ -340,7 +346,7 @@
</methods>
<members>
<member name="playback_process_mode" type="int" setter="set_tween_process_mode" getter="get_tween_process_mode" enum="Tween.TweenProcessMode">
- The tween's animation process thread. See [enum TweenProcessMode]. Default value: [enum TWEEN_PROCESS_IDLE].
+ The tween's animation process thread. See [enum TweenProcessMode]. Default value: [enum TWEEN_PROCESS_IDLE].
</member>
<member name="playback_speed" type="float" setter="set_speed_scale" getter="get_speed_scale">
The tween's speed multiplier. For example, set it to [code]1.0[/code] for normal speed, [code]2.0[/code] for two times normal speed, or [code]0.5[/code] for half of the normal speed. A value of [code]0[/code] pauses the animation, but see also [method set_active] or [method stop_all] for this.
diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml
index 5ffe807606..6ffeddf5c1 100644
--- a/doc/classes/Vector2.xml
+++ b/doc/classes/Vector2.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Vector2" category="Built-In Types" version="3.1">
<brief_description>
- Vector used for 2D Math.
+ Vector used for 2D math.
</brief_description>
<description>
- 2-element structure that can be used to represent positions in 2d-space, or any other pair of numeric values.
+ 2-element structure that can be used to represent positions in 2d space or any other pair of numeric values.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/math/index.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
</tutorials>
<demos>
</demos>
@@ -34,8 +34,8 @@
<return type="float">
</return>
<description>
- Returns the result of atan2 when called with the Vector's x and y as parameters (Math::atan2(x,y)).
- Be aware that it therefore returns an angle oriented clockwise with regard to the (0, 1) unit vector, and not an angle oriented counter-clockwise with regard to the (1, 0) unit vector (which would be the typical trigonometric representation of the angle when calling Math::atan2(y,x)).
+ Returns the vector's angle in radians with respect to the x-axis, or [code](1, 0)[/code] vector.
+ Equivalent to the result of atan2 when called with the vector's x and y as parameters: [code]atan2(x, y)[/code].
</description>
</method>
<method name="angle_to">
@@ -60,7 +60,7 @@
<return type="float">
</return>
<description>
- Returns the ratio of X to Y.
+ Returns the ratio of x to y.
</description>
</method>
<method name="bounce">
@@ -69,7 +69,14 @@
<argument index="0" name="n" type="Vector2">
</argument>
<description>
- Bounce returns the vector "bounced off" from the given plane, specified by its normal vector.
+ Returns the vector "bounced off" from a plane defined by the given normal.
+ </description>
+ </method>
+ <method name="ceil">
+ <return type="Vector2">
+ </return>
+ <description>
+ Returns the vector with all components rounded up.
</description>
</method>
<method name="clamped">
@@ -87,7 +94,7 @@
<argument index="0" name="with" type="Vector2">
</argument>
<description>
- Returns the 2-dimensional analog of the cross product with the given Vector2.
+ Returns the 2 dimensional analog of the cross product with the given vector.
</description>
</method>
<method name="cubic_interpolate">
@@ -102,7 +109,7 @@
<argument index="3" name="t" type="float">
</argument>
<description>
- Cubicly interpolates between this Vector and "b", using "pre_a" and "post_b" as handles, and returning the result at position "t". "t" should be a float of 0.0-1.0, a percentage of how far along the interpolation is.
+ Cubicly interpolates between this vector and [code]b[/code] using [code]pre_a[/code] and [code]post_b[/code] as handles, and returns the result at position [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], or a percentage of how far along the interpolation is.
</description>
</method>
<method name="distance_squared_to">
@@ -111,7 +118,7 @@
<argument index="0" name="to" type="Vector2">
</argument>
<description>
- Returns the squared distance to vector "b". Prefer this function over "distance_to" if you need to sort vectors or need the squared distance for some formula.
+ Returns the squared distance to vector [code]b[/code]. Prefer this function over [method distance_to] if you need to sort vectors or need the squared distance for some formula.
</description>
</method>
<method name="distance_to">
@@ -120,7 +127,7 @@
<argument index="0" name="to" type="Vector2">
</argument>
<description>
- Returns the distance to vector "b".
+ Returns the distance to vector [code]b[/code].
</description>
</method>
<method name="dot">
@@ -129,35 +136,35 @@
<argument index="0" name="with" type="Vector2">
</argument>
<description>
- Returns the dot product with vector "b".
+ Returns the dot product with vector [code]b[/code].
</description>
</method>
<method name="floor">
<return type="Vector2">
</return>
<description>
- Remove the fractional part of x and y.
+ Returns the vector with all components rounded down.
</description>
</method>
<method name="is_normalized">
<return type="bool">
</return>
<description>
- Returns whether the vector is normalized or not.
+ Returns [code]true[/code] if the vector is normalized.
</description>
</method>
<method name="length">
<return type="float">
</return>
<description>
- Returns the length of the vector.
+ Returns the vector's length.
</description>
</method>
<method name="length_squared">
<return type="float">
</return>
<description>
- Returns the squared length of the vector. Prefer this function over "length" if you need to sort vectors or need the squared length for some formula.
+ Returns the vector's length squared. Prefer this function over [member length] if you need to sort vectors or need the squared length for some formula.
</description>
</method>
<method name="linear_interpolate">
@@ -168,14 +175,14 @@
<argument index="1" name="t" type="float">
</argument>
<description>
- Returns the result of the linear interpolation between this vector and "b", by amount "t". "t" should be a float of 0.0-1.0, a percentage of how far along the interpolation is.
+ Returns the result of the linear interpolation between this vector and [code]b[/code] by amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], a percentage of how far along the interpolation is.
</description>
</method>
<method name="normalized">
<return type="Vector2">
</return>
<description>
- Returns a normalized vector to unit length.
+ Returns the vector scaled to unit length. Equivalent to [code]v / v.length()[/code].
</description>
</method>
<method name="reflect">
@@ -184,7 +191,7 @@
<argument index="0" name="n" type="Vector2">
</argument>
<description>
- Reflects the vector along the given plane, specified by its normal vector.
+ Returns the vector reflected from a plane defined by the given normal.
</description>
</method>
<method name="rotated">
@@ -193,7 +200,26 @@
<argument index="0" name="phi" type="float">
</argument>
<description>
- Rotates the vector by "phi" radians.
+ Returns the vector rotated by [code]phi[/code] radians.
+ </description>
+ </method>
+ <method name="round">
+ <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">
+ <return type="Vector2">
+ </return>
+ <argument index="0" name="b" type="Vector2">
+ </argument>
+ <argument index="1" name="t" type="float">
+ </argument>
+ <description>
+ Returns the result of SLERP between this vector and "b", by amount "t". "t" should be a float of 0.0-1.0, a percentage of how far along the interpolation is.
+ Both vectors need to be normalized.
</description>
</method>
<method name="slide">
@@ -202,7 +228,7 @@
<argument index="0" name="n" type="Vector2">
</argument>
<description>
- Slide returns the component of the vector along the given plane, specified by its normal vector.
+ Returns the component of the vector along a plane defined by the given normal.
</description>
</method>
<method name="snapped">
@@ -211,7 +237,7 @@
<argument index="0" name="by" type="Vector2">
</argument>
<description>
- Snaps the vector to a grid with the given size.
+ Returns the vector snapped to a grid with the given size.
</description>
</method>
<method name="tangent">
@@ -224,10 +250,10 @@
</methods>
<members>
<member name="x" type="float" setter="" getter="">
- X component of the vector.
+ The vector's x component.
</member>
<member name="y" type="float" setter="" getter="">
- Y component of the vector.
+ The vector's y component.
</member>
</members>
<constants>
diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml
index 940d4dd6cf..62a480166a 100644
--- a/doc/classes/Vector3.xml
+++ b/doc/classes/Vector3.xml
@@ -7,7 +7,7 @@
Vector3 is one of the core classes of the engine, and includes several built-in helper functions to perform basic vector math operations.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/math/index.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
</tutorials>
<demos>
</demos>
@@ -38,7 +38,7 @@
<argument index="0" name="to" type="Vector3">
</argument>
<description>
- Returns the vector's minimum angle to the vector [code]to[/code].
+ Returns the minimum angle to the given vector.
</description>
</method>
<method name="bounce">
@@ -47,7 +47,7 @@
<argument index="0" name="n" type="Vector3">
</argument>
<description>
- Bounce returns the vector "bounced off" from the given plane, specified by its normal vector.
+ Returns the vector "bounced off" from a plane defined by the given normal.
</description>
</method>
<method name="ceil">
@@ -87,7 +87,7 @@
<argument index="0" name="b" type="Vector3">
</argument>
<description>
- Returns the squared distance to [code]b[/code]. Prefer this function over distance_to if you need to sort vectors or need the squared distance for some formula.
+ Returns the squared distance to [code]b[/code]. Prefer this function over [method distance_to] if you need to sort vectors or need the squared distance for some formula.
</description>
</method>
<method name="distance_to">
@@ -96,7 +96,7 @@
<argument index="0" name="b" type="Vector3">
</argument>
<description>
- Returns the distance to b.
+ Returns the distance to [code]b[/code].
</description>
</method>
<method name="dot">
@@ -105,7 +105,7 @@
<argument index="0" name="b" type="Vector3">
</argument>
<description>
- Returns the dot product with b.
+ Returns the dot product with [code]b[/code].
</description>
</method>
<method name="floor">
@@ -119,28 +119,28 @@
<return type="Vector3">
</return>
<description>
- Returns the inverse of the vector. This is the same as Vector3( 1.0 / v.x, 1.0 / v.y, 1.0 / v.z )
+ Returns the inverse of the vector. This is the same as [code]Vector3( 1.0 / v.x, 1.0 / v.y, 1.0 / v.z )[/code].
</description>
</method>
<method name="is_normalized">
<return type="bool">
</return>
<description>
- Returns whether the vector is normalized or not.
+ Returns [code]true[/code] if the vector is normalized.
</description>
</method>
<method name="length">
<return type="float">
</return>
<description>
- Returns the length of the vector.
+ Returns the vector's length.
</description>
</method>
<method name="length_squared">
<return type="float">
</return>
<description>
- Returns the length of the vector, squared. Prefer this function over "length" if you need to sort vectors or need the squared length for some formula.
+ Returns the vector's length squared. Prefer this function over [method length] if you need to sort vectors or need the squared length for some formula.
</description>
</method>
<method name="linear_interpolate">
@@ -151,28 +151,28 @@
<argument index="1" name="t" type="float">
</argument>
<description>
- Linearly interpolates the vector to a given one (b), by the given amount (t). (t) should be a float of 0.0-1.0, a percentage of how far along the interpolation is.
+ Returns the result of the linear interpolation between this vector and [code]b[/code] by amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], a percentage of how far along the interpolation is.
</description>
</method>
<method name="max_axis">
<return type="int">
</return>
<description>
- Returns AXIS_X, AXIS_Y or AXIS_Z depending on which axis is the largest.
+ Returns the axis of the vector's largest value. See [code]AXIS_*[/code] constants.
</description>
</method>
<method name="min_axis">
<return type="int">
</return>
<description>
- Returns AXIS_X, AXIS_Y or AXIS_Z depending on which axis is the smallest.
+ Returns the axis of the vector's smallest value. See [code]AXIS_*[/code] constants.
</description>
</method>
<method name="normalized">
<return type="Vector3">
</return>
<description>
- Returns a copy of the normalized vector to unit length. This is the same as v / v.length().
+ Returns the vector scaled to unit length. Equivalent to [code]v / v.length()[/code].
</description>
</method>
<method name="outer">
@@ -181,7 +181,7 @@
<argument index="0" name="b" type="Vector3">
</argument>
<description>
- Returns the outer product with b.
+ Returns the outer product with [code]b[/code].
</description>
</method>
<method name="reflect">
@@ -190,7 +190,7 @@
<argument index="0" name="n" type="Vector3">
</argument>
<description>
- Reflects the vector along the given plane, specified by its normal vector.
+ Returns the vector reflected from a plane defined by the given normal.
</description>
</method>
<method name="rotated">
@@ -201,7 +201,26 @@
<argument index="1" name="phi" type="float">
</argument>
<description>
- Rotates the vector around some axis by phi radians. The axis must be a normalized vector.
+ Rotates the vector around a given axis by [code]phi[/code] radians. The axis must be a normalized vector.
+ </description>
+ </method>
+ <method name="round">
+ <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">
+ <return type="Vector3">
+ </return>
+ <argument index="0" name="b" type="Vector3">
+ </argument>
+ <argument index="1" name="t" type="float">
+ </argument>
+ <description>
+ Returns the result of SLERP between this vector and "b", by amount "t". "t" should be a float of 0.0-1.0, a percentage of how far along the interpolation is.
+ Both vectors need to be normalized.
</description>
</method>
<method name="slide">
@@ -210,7 +229,7 @@
<argument index="0" name="n" type="Vector3">
</argument>
<description>
- Slide returns the component of the vector along the given plane, specified by its normal vector.
+ Returns the component of the vector along a plane defined by the given normal.
</description>
</method>
<method name="snapped">
@@ -232,18 +251,18 @@
</methods>
<members>
<member name="x" type="float" setter="" getter="">
- X component of the vector.
+ The vector's x component.
</member>
<member name="y" type="float" setter="" getter="">
- Y component of the vector.
+ The vector's y component.
</member>
<member name="z" type="float" setter="" getter="">
- Z component of the vector.
+ The vector's z component.
</member>
</members>
<constants>
<constant name="AXIS_X" value="0">
- Enumerated value for the X axis. Returned by functions like max_axis or min_axis.
+ Enumerated value for the X axis. Returned by [method max_axis] and [method min_axis].
</constant>
<constant name="AXIS_Y" value="1">
Enumerated value for the Y axis.
diff --git a/doc/classes/VideoPlayer.xml b/doc/classes/VideoPlayer.xml
index d2639590a1..9ffa3aa52b 100644
--- a/doc/classes/VideoPlayer.xml
+++ b/doc/classes/VideoPlayer.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VideoPlayer" inherits="Control" category="Core" version="3.1">
<brief_description>
- Control to play video files.
+ Control for playing video streams.
</brief_description>
<description>
- This control has the ability to play video streams. The only format accepted is the OGV Theora, so any other format must be converted before using in a project.
+ Control node for playing video streams. Supported formats are WebM and OGV Theora.
</description>
<tutorials>
</tutorials>
@@ -15,51 +15,56 @@
<return type="String">
</return>
<description>
- Get the name of the video stream.
+ Returns the video stream's name.
</description>
</method>
<method name="get_video_texture">
<return type="Texture">
</return>
<description>
- Get the current frame of the video as a [Texture].
+ Returns the current frame as a [Texture].
</description>
</method>
<method name="is_playing" qualifiers="const">
<return type="bool">
</return>
<description>
- Get whether or not the video is playing.
+ Returns [code]true[/code] if the video is playing.
</description>
</method>
<method name="play">
<return type="void">
</return>
<description>
- Start the video playback.
+ Starts the video playback.
</description>
</method>
<method name="stop">
<return type="void">
</return>
<description>
- Stop the video playback.
+ Stops the video playback.
</description>
</method>
</methods>
<members>
<member name="audio_track" type="int" setter="set_audio_track" getter="get_audio_track">
+ The embedded audio track to play.
</member>
<member name="autoplay" type="bool" setter="set_autoplay" getter="has_autoplay">
+ If [code]true[/code] playback starts when the scene loads. Default value: [code]false[/code].
</member>
<member name="buffering_msec" type="int" setter="set_buffering_msec" getter="get_buffering_msec">
- The amount of milliseconds to store in buffer while playing.
+ Amount of time in milliseconds to store in buffer while playing.
</member>
<member name="bus" type="String" setter="set_bus" getter="get_bus">
+ Audio bus to use for sound playback.
</member>
<member name="expand" type="bool" setter="set_expand" getter="has_expand">
+ If [code]true[/code] the video scales to the control size. Default value: [code]true[/code].
</member>
<member name="paused" type="bool" setter="set_paused" getter="is_paused">
+ If [code]true[/code] the video is paused.
</member>
<member name="stream" type="VideoStream" setter="set_stream" getter="get_stream">
</member>
@@ -67,14 +72,16 @@
The current position of the stream, in seconds.
</member>
<member name="volume" type="float" setter="set_volume" getter="get_volume">
- The volume of the audio track as a linear value.
+ Audio volume as a linear value.
</member>
<member name="volume_db" type="float" setter="set_volume_db" getter="get_volume_db">
+ Audio volume in dB.
</member>
</members>
<signals>
<signal name="finished">
<description>
+ Emitted when playback is finished.
</description>
</signal>
</signals>
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 4878f7d932..af0712d357 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -6,14 +6,14 @@
<description>
A Viewport creates a different view into the screen, or a sub-view inside another viewport. Children 2D Nodes will display on it, and children Camera 3D nodes will render on it too.
Optionally, a viewport can have its own 2D or 3D world, so they don't share what they draw with other viewports.
- If a viewport is a child of a [Control], it will automatically take up its same rect and position, otherwise they must be set manually.
+ If a viewport is a child of a [ViewportContainer], it will automatically take up its size, otherwise it must be set manually.
Viewports can also choose to be audio listeners, so they generate positional audio depending on a 2D or 3D camera child of it.
Also, viewports can be assigned to different screens in case the devices have multiple screens.
Finally, viewports can also behave as render targets, in which case they will not be visible unless the associated texture is used to draw.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html
- http://docs.godotengine.org/en/3.0/tutorials/viewports/index.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link>
+ <link>http://docs.godotengine.org/en/3.0/tutorials/viewports/index.html</link>
</tutorials>
<demos>
</demos>
@@ -276,7 +276,7 @@
Do not update the render target.
</constant>
<constant name="UPDATE_ONCE" value="1" enum="UpdateMode">
- Update the render target once, then switch to [code]UPDATE_DISABLED[/code]
+ Update the render target once, then switch to [code]UPDATE_DISABLED[/code].
</constant>
<constant name="UPDATE_WHEN_VISIBLE" value="2" enum="UpdateMode">
Update the render target only when it is visible. This is the default value.
@@ -329,6 +329,7 @@
Objects are displayed without light information.
</constant>
<constant name="DEBUG_DRAW_OVERDRAW" value="2" enum="DebugDraw">
+ Objected are displayed semi-transparent with additive blending so you can see where they intersect.
</constant>
<constant name="DEBUG_DRAW_WIREFRAME" value="3" enum="DebugDraw">
Objects are displayed in wireframe style.
@@ -353,10 +354,13 @@
<constant name="USAGE_3D_NO_EFFECTS" value="3" enum="Usage">
</constant>
<constant name="CLEAR_MODE_ALWAYS" value="0" enum="ClearMode">
+ Always clear the render target before drawing.
</constant>
<constant name="CLEAR_MODE_NEVER" value="1" enum="ClearMode">
+ Never clear the render target.
</constant>
<constant name="CLEAR_MODE_ONLY_NEXT_FRAME" value="2" enum="ClearMode">
+ Clear the render target next frame, then switch to [code]CLEAR_MODE_NEVER[/code].
</constant>
</constants>
</class>
diff --git a/doc/classes/ViewportTexture.xml b/doc/classes/ViewportTexture.xml
index 83ffc6bcfd..67f1e09c75 100644
--- a/doc/classes/ViewportTexture.xml
+++ b/doc/classes/ViewportTexture.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ViewportTexture" inherits="Texture" category="Core" version="3.1">
<brief_description>
+ Texture which displays the content of a [Viewport].
</brief_description>
<description>
+ Displays the content of a [Viewport] node as a dynamic [Texture]. This can be used to mix controls, 2D, and 3D elements in the same scene.
+ To create a ViewportTexture in code, use the [method Viewport.get_texture] method on the target viewport.
</description>
<tutorials>
</tutorials>
@@ -12,6 +15,7 @@
</methods>
<members>
<member name="viewport_path" type="NodePath" setter="set_viewport_path_in_scene" getter="get_viewport_path_in_scene">
+ The path to the [Viewport] node to display. This is relative to the scene root, not to the node which uses the texture.
</member>
</members>
<constants>
diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml
index baebddf3e0..893db84640 100644
--- a/doc/classes/VisualServer.xml
+++ b/doc/classes/VisualServer.xml
@@ -388,14 +388,17 @@
</argument>
<argument index="4" name="uvs" type="PoolVector2Array" default="PoolVector2Array( )">
</argument>
- <argument index="5" name="texture" type="RID">
+ <argument index="5" name="bones" type="PoolIntArray" default="PoolIntArray( )">
</argument>
- <argument index="6" name="count" type="int" default="-1">
+ <argument index="6" name="weights" type="PoolRealArray" default="PoolRealArray( )">
</argument>
- <argument index="7" name="normal_map" type="RID">
+ <argument index="7" name="texture" type="RID">
+ </argument>
+ <argument index="8" name="count" type="int" default="-1">
+ </argument>
+ <argument index="9" name="normal_map" type="RID">
</argument>
<description>
- Adds a triangle array to the [CanvasItem]'s draw commands.
</description>
</method>
<method name="canvas_item_clear">
@@ -3330,6 +3333,32 @@
Sets the texture's image data. If it's a CubeMap, it sets the image data at a cube side.
</description>
</method>
+ <method name="texture_set_data_partial">
+ <return type="void">
+ </return>
+ <argument index="0" name="texture" type="RID">
+ </argument>
+ <argument index="1" name="image" type="Image">
+ </argument>
+ <argument index="2" name="src_x" type="int">
+ </argument>
+ <argument index="3" name="src_y" type="int">
+ </argument>
+ <argument index="4" name="src_w" type="int">
+ </argument>
+ <argument index="5" name="src_h" type="int">
+ </argument>
+ <argument index="6" name="dst_x" type="int">
+ </argument>
+ <argument index="7" name="dst_y" type="int">
+ </argument>
+ <argument index="8" name="dst_mip" type="int">
+ </argument>
+ <argument index="9" name="cube_side" type="int" enum="VisualServer.CubeMapSide" default="0">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="texture_set_flags">
<return type="void">
</return>
diff --git a/doc/classes/WebSocketClient.xml b/doc/classes/WebSocketClient.xml
deleted file mode 100644
index 496baa5787..0000000000
--- a/doc/classes/WebSocketClient.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebSocketClient" inherits="WebSocketMultiplayerPeer" category="Core" version="3.1">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <demos>
- </demos>
- <methods>
- <method name="connect_to_url">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="url" type="String">
- </argument>
- <argument index="1" name="protocols" type="PoolStringArray" default="PoolStringArray( )">
- </argument>
- <argument index="2" name="gd_mp_api" type="bool" default="false">
- </argument>
- <description>
- </description>
- </method>
- <method name="disconnect_from_host">
- <return type="void">
- </return>
- <description>
- </description>
- </method>
- </methods>
- <members>
- <member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled">
- </member>
- </members>
- <signals>
- <signal name="connection_closed">
- <description>
- </description>
- </signal>
- <signal name="connection_error">
- <description>
- </description>
- </signal>
- <signal name="connection_established">
- <argument index="0" name="protocol" type="String">
- </argument>
- <description>
- </description>
- </signal>
- <signal name="data_received">
- <description>
- </description>
- </signal>
- </signals>
- <constants>
- </constants>
-</class>
diff --git a/doc/classes/WebSocketPeer.xml b/doc/classes/WebSocketPeer.xml
deleted file mode 100644
index 276249e65a..0000000000
--- a/doc/classes/WebSocketPeer.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebSocketPeer" inherits="PacketPeer" category="Core" version="3.1">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <demos>
- </demos>
- <methods>
- <method name="close">
- <return type="void">
- </return>
- <description>
- </description>
- </method>
- <method name="get_write_mode" qualifiers="const">
- <return type="int" enum="WebSocketPeer.WriteMode">
- </return>
- <description>
- </description>
- </method>
- <method name="is_connected_to_host" qualifiers="const">
- <return type="bool">
- </return>
- <description>
- </description>
- </method>
- <method name="set_write_mode">
- <return type="void">
- </return>
- <argument index="0" name="mode" type="int" enum="WebSocketPeer.WriteMode">
- </argument>
- <description>
- </description>
- </method>
- <method name="was_string_packet" qualifiers="const">
- <return type="bool">
- </return>
- <description>
- </description>
- </method>
- </methods>
- <constants>
- <constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode">
- </constant>
- <constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode">
- </constant>
- </constants>
-</class>
diff --git a/doc/classes/WebSocketServer.xml b/doc/classes/WebSocketServer.xml
deleted file mode 100644
index 9a7108952f..0000000000
--- a/doc/classes/WebSocketServer.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebSocketServer" inherits="WebSocketMultiplayerPeer" category="Core" version="3.1">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <demos>
- </demos>
- <methods>
- <method name="has_peer" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- </description>
- </method>
- <method name="is_listening" qualifiers="const">
- <return type="bool">
- </return>
- <description>
- </description>
- </method>
- <method name="listen">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="port" type="int">
- </argument>
- <argument index="1" name="protocols" type="PoolStringArray" default="PoolStringArray( )">
- </argument>
- <argument index="2" name="gd_mp_api" type="bool" default="false">
- </argument>
- <description>
- </description>
- </method>
- <method name="stop">
- <return type="void">
- </return>
- <description>
- </description>
- </method>
- </methods>
- <signals>
- <signal name="client_connected">
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="protocol" type="String">
- </argument>
- <description>
- </description>
- </signal>
- <signal name="client_disconnected">
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- </description>
- </signal>
- <signal name="data_received">
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- </description>
- </signal>
- </signals>
- <constants>
- </constants>
-</class>
diff --git a/doc/classes/World.xml b/doc/classes/World.xml
index 9fc0e139b5..540848e40a 100644
--- a/doc/classes/World.xml
+++ b/doc/classes/World.xml
@@ -7,7 +7,7 @@
Class that has everything pertaining to a world. A physics space, a visual scenario and a sound space. Spatial nodes register their resources into the current world.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/World2D.xml b/doc/classes/World2D.xml
index 5f6a5b8ad4..780cdd181a 100644
--- a/doc/classes/World2D.xml
+++ b/doc/classes/World2D.xml
@@ -7,7 +7,7 @@
Class that has everything pertaining to a 2D world. A physics space, a visual scenario and a sound space. 2D nodes register their resources into the current 2D world.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/WorldEnvironment.xml b/doc/classes/WorldEnvironment.xml
index 422ca3a558..e68ad2800e 100644
--- a/doc/classes/WorldEnvironment.xml
+++ b/doc/classes/WorldEnvironment.xml
@@ -9,7 +9,7 @@
The [code]WorldEnvironment[/code] allows the user to specify default lighting parameters (e.g. ambient lighting), various post-processing effects (e.g. SSAO, DOF, Tonemapping), and how to draw the background (e.g. solid color, skybox). Usually, these are added in order to improve the realism/color balance of the scene.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/environment_and_post_processing.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/environment_and_post_processing.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py
index 8074ce45ea..93ad823d42 100644..100755
--- a/doc/tools/makerst.py
+++ b/doc/tools/makerst.py
@@ -4,11 +4,15 @@
import codecs
import sys
import os
+import re
import xml.etree.ElementTree as ET
input_list = []
cur_file = ""
+# http(s)://docs.godotengine.org/<langcode>/<tag>/path/to/page.html(#fragment-tag)
+godot_docs_pattern = re.compile('^http(?:s)?:\/\/docs\.godotengine\.org\/(?:[a-zA-Z0-9\.\-_]*)\/(?:[a-zA-Z0-9\.\-_]*)\/(.*)\.html(#.*)?$')
+
for arg in sys.argv[1:]:
if arg.endswith(os.sep):
arg = arg[:-1]
@@ -106,6 +110,7 @@ def make_class_list(class_list, columns):
f.close()
+
def rstize_text(text, cclass):
# Linebreak + tabs in the XML should become two line breaks unless in a "codeblock"
pos = 0
@@ -154,9 +159,19 @@ def rstize_text(text, cclass):
text = pre_text + "\n\n" + post_text
pos += 2
+ next_brac_pos = text.find('[')
+
+ # Escape \ character, otherwise it ends up as an escape character in rst
+ pos = 0
+ while True:
+ pos = text.find('\\', pos, next_brac_pos)
+ if pos == -1:
+ break
+ text = text[:pos] + "\\\\" + text[pos + 1:]
+ pos += 2
+
# Escape * character to avoid interpreting it as emphasis
pos = 0
- next_brac_pos = text.find('[');
while True:
pos = text.find('*', pos, next_brac_pos)
if pos == -1:
@@ -258,15 +273,17 @@ def rstize_text(text, cclass):
elif cmd == 'code':
tag_text = '``'
inside_code = True
+ elif cmd.startswith('enum '):
+ tag_text = make_enum(cmd[5:])
else:
tag_text = make_type(tag_text)
escape_post = True
# Properly escape things like `[Node]s`
- if escape_post and post_text and post_text[0].isalnum(): # not punctuation, escape
+ if escape_post and post_text and post_text[0].isalnum(): # not punctuation, escape
post_text = '\ ' + post_text
- next_brac_pos = post_text.find('[',0)
+ next_brac_pos = post_text.find('[', 0)
iter_pos = 0
while not inside_code:
iter_pos = post_text.find('*', iter_pos, next_brac_pos)
@@ -286,7 +303,6 @@ def rstize_text(text, cclass):
else:
iter_pos += 1
-
text = pre_text + tag_text + post_text
pos = len(pre_text) + len(tag_text)
@@ -299,16 +315,27 @@ def make_type(t):
return ':ref:`' + t + '<class_' + t.lower() + '>`'
return t
+
def make_enum(t):
global class_names
p = t.find(".")
+ # Global enums such as Error are relative to @GlobalScope.
if p >= 0:
c = t[0:p]
- e = t[p+1:]
- if c in class_names:
- return ':ref:`' + e + '<enum_' + c.lower() + '_' + e.lower() + '>`'
+ e = t[p + 1:]
+ # Variant enums live in GlobalScope but still use periods.
+ if c == "Variant":
+ c = "@GlobalScope"
+ e = "Variant." + e
+ else:
+ # Things in GlobalScope don't have a period.
+ c = "@GlobalScope"
+ e = t
+ if c in class_names:
+ return ':ref:`' + e + '<enum_' + c.lower() + '_' + e.lower() + '>`'
return t
+
def make_method(
f,
name,
@@ -340,7 +367,10 @@ def make_method(
if not event:
if -1 in mdata['argidx']:
- t += make_type(mdata[-1].attrib['type'])
+ if 'enum' in mdata[-1].attrib:
+ t += make_enum(mdata[-1].attrib['enum'])
+ else:
+ t += make_type(mdata[-1].attrib['type'])
else:
t += 'void'
t += ' '
@@ -362,7 +392,10 @@ def make_method(
else:
s += ' '
- s += make_type(arg.attrib['type'])
+ if 'enum' in arg.attrib:
+ s += make_enum(arg.attrib['enum'])
+ else:
+ s += make_type(arg.attrib['type'])
if 'name' in arg.attrib:
s += ' ' + arg.attrib['name']
else:
@@ -559,6 +592,32 @@ def make_rst_class(node):
f.write(make_heading('Description', '-'))
f.write(rstize_text(descr.text.strip(), name) + "\n\n")
+ global godot_docs_pattern
+ tutorials = node.find('tutorials')
+ if tutorials != None and len(tutorials) > 0:
+ f.write(make_heading('Tutorials', '-'))
+ for t in tutorials:
+ link = t.text.strip()
+ match = godot_docs_pattern.search(link);
+ if match:
+ groups = match.groups()
+ if match.lastindex == 2:
+ # Doc reference with fragment identifier: emit direct link to section with reference to page, for example:
+ # `#calling-javascript-from-script in Exporting For Web`
+ f.write("- `" + groups[1] + " <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n")
+ # Commented out alternative: Instead just emit:
+ # `Subsection in Exporting For Web`
+ # f.write("- `Subsection <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n")
+ elif match.lastindex == 1:
+ # Doc reference, for example:
+ # `Math`
+ f.write("- :doc:`../" + groups[0] + "`\n")
+ else:
+ # External link, for example:
+ # `http://enet.bespin.org/usergroup0.html`
+ f.write("- `" + link + " <" + link + ">`_\n")
+ f.write("\n")
+
methods = node.find('methods')
if methods != None and len(list(methods)) > 0:
f.write(make_heading('Member Function Description', '-'))
diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp
index 6e451eabcd..ef7858b4ca 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));
@@ -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,
@@ -370,6 +346,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 +383,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 +418,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 ea0d3ec706..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) {}
@@ -127,7 +127,26 @@ public:
String path;
};
+ struct DummySurface {
+ uint32_t format;
+ VS::PrimitiveType primitive;
+ PoolVector<uint8_t> array;
+ int vertex_count;
+ PoolVector<uint8_t> index_array;
+ int index_count;
+ AABB aabb;
+ Vector<PoolVector<uint8_t> > blend_shapes;
+ Vector<AABB> bone_aabbs;
+ };
+
+ struct DummyMesh : public RID_Data {
+ Vector<DummySurface> surfaces;
+ int blend_shape_count;
+ VS::BlendShapeMode blend_shape_mode;
+ };
+
mutable RID_Owner<DummyTexture> texture_owner;
+ mutable RID_Owner<DummyMesh> mesh_owner;
RID texture_create() {
@@ -154,6 +173,19 @@ public:
t->image->create(t->width, t->height, false, t->format, p_image->get_data());
}
+ void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side) {
+ DummyTexture *t = texture_owner.get(p_texture);
+
+ ERR_FAIL_COND(!t);
+ ERR_FAIL_COND(t->format != p_image->get_format());
+ ERR_FAIL_COND(p_image.is_null());
+ ERR_FAIL_COND(src_w <= 0 || src_h <= 0);
+ ERR_FAIL_COND(src_x < 0 || src_y < 0 || src_x + src_w > p_image->get_width() || src_y + src_h > p_image->get_height());
+ ERR_FAIL_COND(dst_x < 0 || dst_y < 0 || dst_x + src_w > t->width || dst_y + src_h > t->height);
+
+ t->image->blit_rect(p_image, Rect2(src_x, src_y, src_w, src_h), Vector2(dst_x, dst_y));
+ }
+
Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const {
DummyTexture *t = texture_owner.getornull(p_texture);
ERR_FAIL_COND_V(!t, Ref<Image>());
@@ -203,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 */
@@ -243,46 +276,128 @@ public:
/* MESH API */
- RID mesh_create() { return RID(); }
-
- void mesh_add_surface_from_arrays(RID p_mesh, VS::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), uint32_t p_compress_format = Mesh::ARRAY_COMPRESS_DEFAULT) {}
- void mesh_add_surface(RID p_mesh, uint32_t p_format, VS::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>()) {}
+ RID mesh_create() {
+ DummyMesh *mesh = memnew(DummyMesh);
+ ERR_FAIL_COND_V(!mesh, RID());
+ mesh->blend_shape_count = 0;
+ mesh->blend_shape_mode = VS::BLEND_SHAPE_MODE_NORMALIZED;
+ return mesh_owner.make_rid(mesh);
+ }
- void mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry::MeshData &p_mesh_data) {}
- void mesh_add_surface_from_planes(RID p_mesh, const PoolVector<Plane> &p_planes) {}
+ void mesh_add_surface(RID p_mesh, uint32_t p_format, VS::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>()) {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND(!m);
+
+ m->surfaces.push_back(DummySurface());
+ DummySurface *s = &m->surfaces[m->surfaces.size() - 1];
+ s->format = p_format;
+ s->primitive = p_primitive;
+ s->array = p_array;
+ s->vertex_count = p_vertex_count;
+ s->index_array = p_index_array;
+ s->index_count = p_index_count;
+ s->aabb = p_aabb;
+ s->blend_shapes = p_blend_shapes;
+ s->bone_aabbs = p_bone_aabbs;
+ }
- void mesh_set_blend_shape_count(RID p_mesh, int p_amount) {}
- int mesh_get_blend_shape_count(RID p_mesh) const { return 0; }
+ void mesh_set_blend_shape_count(RID p_mesh, int p_amount) {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND(!m);
+ m->blend_shape_count = p_amount;
+ }
+ int mesh_get_blend_shape_count(RID p_mesh) const {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, 0);
+ return m->blend_shape_count;
+ }
- void mesh_set_blend_shape_mode(RID p_mesh, VS::BlendShapeMode p_mode) {}
- VS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const { return VS::BLEND_SHAPE_MODE_NORMALIZED; }
+ void mesh_set_blend_shape_mode(RID p_mesh, VS::BlendShapeMode p_mode) {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND(!m);
+ m->blend_shape_mode = p_mode;
+ }
+ VS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, VS::BLEND_SHAPE_MODE_NORMALIZED);
+ return m->blend_shape_mode;
+ }
void mesh_surface_update_region(RID p_mesh, int p_surface, int p_offset, const PoolVector<uint8_t> &p_data) {}
void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) {}
RID mesh_surface_get_material(RID p_mesh, int p_surface) const { return RID(); }
- int mesh_surface_get_array_len(RID p_mesh, int p_surface) const { return 0; }
- int mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const { return 0; }
+ int mesh_surface_get_array_len(RID p_mesh, int p_surface) const {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, 0);
+
+ return m->surfaces[p_surface].vertex_count;
+ }
+ int mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, 0);
+
+ return m->surfaces[p_surface].index_count;
+ }
PoolVector<uint8_t> mesh_surface_get_array(RID p_mesh, int p_surface) const {
- PoolVector<uint8_t> p;
- return p;
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, PoolVector<uint8_t>());
+
+ return m->surfaces[p_surface].array;
}
PoolVector<uint8_t> mesh_surface_get_index_array(RID p_mesh, int p_surface) const {
- PoolVector<uint8_t> p;
- return p;
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, PoolVector<uint8_t>());
+
+ return m->surfaces[p_surface].index_array;
}
- uint32_t mesh_surface_get_format(RID p_mesh, int p_surface) const { return 0; }
- VS::PrimitiveType mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const { return VS::PRIMITIVE_POINTS; }
+ uint32_t mesh_surface_get_format(RID p_mesh, int p_surface) const {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, 0);
- AABB mesh_surface_get_aabb(RID p_mesh, int p_surface) const { return AABB(); }
- Vector<PoolVector<uint8_t> > mesh_surface_get_blend_shapes(RID p_mesh, int p_surface) const { return Vector<PoolVector<uint8_t> >(); }
- Vector<AABB> mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const { return Vector<AABB>(); }
+ return m->surfaces[p_surface].format;
+ }
+ VS::PrimitiveType mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, VS::PRIMITIVE_POINTS);
+
+ return m->surfaces[p_surface].primitive;
+ }
- void mesh_remove_surface(RID p_mesh, int p_index) {}
- int mesh_get_surface_count(RID p_mesh) const { return 0; }
+ AABB mesh_surface_get_aabb(RID p_mesh, int p_surface) const {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, AABB());
+
+ return m->surfaces[p_surface].aabb;
+ }
+ Vector<PoolVector<uint8_t> > mesh_surface_get_blend_shapes(RID p_mesh, int p_surface) const {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, Vector<PoolVector<uint8_t> >());
+
+ return m->surfaces[p_surface].blend_shapes;
+ }
+ Vector<AABB> mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, Vector<AABB>());
+
+ return m->surfaces[p_surface].bone_aabbs;
+ }
+
+ void mesh_remove_surface(RID p_mesh, int p_index) {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND(!m);
+ ERR_FAIL_COND(p_index >= m->surfaces.size());
+
+ m->surfaces.remove(p_index);
+ }
+ int mesh_get_surface_count(RID p_mesh) const {
+ DummyMesh *m = mesh_owner.getornull(p_mesh);
+ ERR_FAIL_COND_V(!m, 0);
+ return m->surfaces.size();
+ }
void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) {}
AABB mesh_get_custom_aabb(RID p_mesh) const { return AABB(); }
@@ -294,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; }
@@ -333,6 +452,7 @@ public:
RID skeleton_create() { return RID(); }
void skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) {}
+ void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) {}
int skeleton_get_bone_count(RID p_skeleton) const { return 0; }
void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) {}
Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { return Transform(); }
@@ -584,7 +704,14 @@ public:
RID canvas_light_occluder_create() { return RID(); }
void canvas_light_occluder_set_polylines(RID p_occluder, const PoolVector<Vector2> &p_lines) {}
- VS::InstanceType get_base_type(RID p_rid) const { return VS::INSTANCE_NONE; }
+ VS::InstanceType get_base_type(RID p_rid) const {
+ if (mesh_owner.owns(p_rid)) {
+ return VS::INSTANCE_MESH;
+ }
+
+ return VS::INSTANCE_NONE;
+ }
+
bool free(RID p_rid) {
if (texture_owner.owns(p_rid)) {
diff --git a/drivers/dummy/texture_loader_dummy.cpp b/drivers/dummy/texture_loader_dummy.cpp
new file mode 100644
index 0000000000..6d3e176bbb
--- /dev/null
+++ b/drivers/dummy/texture_loader_dummy.cpp
@@ -0,0 +1,87 @@
+/*************************************************************************/
+/* texture_loader_dummy.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 "texture_loader_dummy.h"
+#include "core/os/file_access.h"
+#include "print_string.h"
+#include <string.h>
+
+RES ResourceFormatDummyTexture::load(const String &p_path, const String &p_original_path, Error *r_error) {
+ unsigned int width = 8;
+ unsigned int height = 8;
+
+ //We just use some format
+ Image::Format fmt = Image::FORMAT_RGB8;
+ int rowsize = 3 * width;
+
+ PoolVector<uint8_t> dstbuff;
+
+ dstbuff.resize(rowsize * height);
+
+ PoolVector<uint8_t>::Write dstbuff_write = dstbuff.write();
+
+ uint8_t *data = dstbuff_write.ptr();
+
+ uint8_t **row_p = memnew_arr(uint8_t *, height);
+
+ for (unsigned int i = 0; i < height; i++) {
+ row_p[i] = 0; //No colors any more, I want them to turn black
+ }
+
+ memdelete_arr(row_p);
+
+ Ref<Image> img = memnew(Image(width, height, 0, fmt, dstbuff));
+
+ Ref<ImageTexture> texture = memnew(ImageTexture);
+ texture->create_from_image(img);
+
+ if (r_error)
+ *r_error = OK;
+
+ return texture;
+}
+
+void ResourceFormatDummyTexture::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("png");
+ p_extensions->push_back("hdr");
+ p_extensions->push_back("jpg");
+ p_extensions->push_back("tga");
+}
+
+bool ResourceFormatDummyTexture::handles_type(const String &p_type) const {
+ return ClassDB::is_parent_class(p_type, "Texture");
+}
+
+String ResourceFormatDummyTexture::get_resource_type(const String &p_path) const {
+ String extension = p_path.get_extension().to_lower();
+ if (extension == "png" || extension == "hdr" || extension == "jpg" || extension == "tga")
+ return "ImageTexture";
+ return "";
+}
diff --git a/modules/webm/resource_importer_webm.h b/drivers/dummy/texture_loader_dummy.h
index d61e6e2a93..f0a355ec12 100644
--- a/modules/webm/resource_importer_webm.h
+++ b/drivers/dummy/texture_loader_dummy.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_importer_webm.h */
+/* texture_loader_dummy.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,29 +28,20 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RESOURCEIMPORTERWEBM_H
-#define RESOURCEIMPORTERWEBM_H
+#ifndef TEXTURE_LOADER_DUMMY_H
+#define TEXTURE_LOADER_DUMMY_H
-#include "io/resource_import.h"
+#include "core/io/resource_loader.h"
+#include "scene/resources/texture.h"
-class ResourceImporterWebm : public ResourceImporter {
- GDCLASS(ResourceImporterWebm, ResourceImporter)
+class ResourceFormatDummyTexture : public ResourceFormatLoader {
public:
- virtual String get_importer_name() const;
- virtual String get_visible_name() const;
+ 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 String get_save_extension() const;
- virtual String get_resource_type() const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
- virtual int get_preset_count() const;
- virtual String get_preset_name(int p_idx) const;
-
- 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;
-
- 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);
-
- ResourceImporterWebm();
+ virtual ~ResourceFormatDummyTexture() {}
};
-#endif // RESOURCEIMPORTERWEBM_H
+#endif // TEXTURE_LOADER_DUMMY_H
diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp
index cc8e3277b9..daa421d45c 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;
}
@@ -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 9339167c8e..ab48e682d6 100644
--- a/drivers/gles2/rasterizer_gles2.cpp
+++ b/drivers/gles2/rasterizer_gles2.cpp
@@ -316,7 +316,11 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c
glViewport(0, 0, window_w, window_h);
glDisable(GL_BLEND);
glDepthMask(GL_FALSE);
- glClearColor(p_color.r, p_color.g, p_color.b, p_color.a);
+ if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ } else {
+ glClearColor(p_color.r, p_color.g, p_color.b, 1.0);
+ }
glClear(GL_COLOR_BUFFER_BIT);
canvas->canvas_begin();
@@ -340,6 +344,27 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c
storage->free(texture);
+ if (OS::get_singleton()->is_layered_allowed()) {
+ if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED)
+ Size2 wndsize = OS::get_singleton()->get_layered_buffer_size();
+ uint8_t *data = OS::get_singleton()->get_layered_buffer_data();
+ if (data) {
+ glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data);
+ OS::get_singleton()->swap_layered_buffer();
+
+ return;
+ }
+#endif
+ } else {
+ //clear alpha
+ glColorMask(false, false, false, true);
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glColorMask(true, true, true, true);
+ }
+ }
+
OS::get_singleton()->swap_buffers();
}
@@ -373,6 +398,28 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re
}
void RasterizerGLES2::end_frame(bool p_swap_buffers) {
+
+ if (OS::get_singleton()->is_layered_allowed()) {
+ if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED)
+ Size2 wndsize = OS::get_singleton()->get_layered_buffer_size();
+ uint8_t *data = OS::get_singleton()->get_layered_buffer_data();
+ if (data) {
+ glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data);
+ OS::get_singleton()->swap_layered_buffer();
+
+ return;
+ }
+#endif
+ } else {
+ //clear alpha
+ glColorMask(false, false, false, true);
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glColorMask(true, true, true, true);
+ }
+ }
+
if (p_swap_buffers)
OS::get_singleton()->swap_buffers();
else
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 6e7e1793e1..b268d4c723 100644
--- a/drivers/gles2/rasterizer_storage_gles2.cpp
+++ b/drivers/gles2/rasterizer_storage_gles2.cpp
@@ -674,6 +674,15 @@ 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 +1191,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 +1210,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 +1229,15 @@ 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) {
}
@@ -1303,7 +1324,6 @@ Transform2D RasterizerStorageGLES2::skeleton_bone_get_transform_2d(RID p_skeleto
}
void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) {
-
}
void RasterizerStorageGLES2::update_dirty_skeletons() {
diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h
index b735f2e148..b2c8b620a6 100644
--- a/drivers/gles2/rasterizer_storage_gles2.h
+++ b/drivers/gles2/rasterizer_storage_gles2.h
@@ -176,12 +176,15 @@ public:
bool active;
GLenum tex_id;
+
uint16_t stored_cube_sides;
RenderTarget *render_target;
Ref<Image> images[6];
+ bool redraw_if_visible;
+
Texture() {
flags = 0;
width = 0;
@@ -205,6 +208,8 @@ public:
proxy = NULL;
render_target = NULL;
+
+ redraw_if_visible = false;
}
_ALWAYS_INLINE_ Texture *get_ptr() {
@@ -264,6 +269,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 +515,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 ad6c2f850a..aa55e72083 100644
--- a/drivers/gles2/shader_compiler_gles2.cpp
+++ b/drivers/gles2/shader_compiler_gles2.cpp
@@ -712,7 +712,7 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[VS::SHADER_CANVAS_ITEM].renames["WORLD_MATRIX"] = "modelview_matrix";
actions[VS::SHADER_CANVAS_ITEM].renames["PROJECTION_MATRIX"] = "projection_matrix";
- actions[VS::SHADER_CANVAS_ITEM].renames["EXTRA_MATRIX"] == "extra_matrix";
+ actions[VS::SHADER_CANVAS_ITEM].renames["EXTRA_MATRIX"] = "extra_matrix";
actions[VS::SHADER_CANVAS_ITEM].renames["TIME"] = "time";
actions[VS::SHADER_CANVAS_ITEM].renames["AT_LIGHT_PASS"] = "at_light_pass";
actions[VS::SHADER_CANVAS_ITEM].renames["INSTANCE_CUSTOM"] = "instance_custom";
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index ff423bf0d0..f859e49b5b 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -193,11 +193,11 @@ void RasterizerCanvasGLES3::canvas_end() {
state.using_ninepatch = false;
}
-RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map) {
+RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map, bool p_force) {
RasterizerStorageGLES3::Texture *tex_return = NULL;
- if (p_texture == state.current_tex) {
+ if (p_texture == state.current_tex && !p_force) {
tex_return = state.current_tex_ptr;
} else if (p_texture.is_valid()) {
@@ -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)
@@ -232,7 +236,7 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con
state.current_tex_ptr = NULL;
}
- if (p_normal_map == state.current_normal) {
+ if (p_normal_map == state.current_normal && !p_force) {
//do none
state.canvas_shader.set_uniform(CanvasShaderGLES3::USE_DEFAULT_NORMAL, state.current_normal.is_valid());
@@ -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;
}
@@ -1086,7 +1095,7 @@ void RasterizerCanvasGLES3::_copy_texscreen(const Rect2 &p_rect) {
state.using_texture_rect = true;
_set_texture_rect_mode(false);
- _bind_canvas_texture(state.current_tex, state.current_normal);
+ _bind_canvas_texture(state.current_tex, state.current_normal, true);
glEnable(GL_BLEND);
}
@@ -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;
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index bfaf1fdb4b..c7f2e54efb 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -123,7 +123,7 @@ public:
virtual void canvas_end();
_FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false);
- _FORCE_INLINE_ RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map);
+ _FORCE_INLINE_ RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map, bool p_force = false);
_FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs);
_FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const int *p_bones, const float *p_weights);
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index 0fb69494f4..1abdaa5f80 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -290,7 +290,11 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c
glViewport(0, 0, window_w, window_h);
glDisable(GL_BLEND);
glDepthMask(GL_FALSE);
- glClearColor(p_color.r, p_color.g, p_color.b, 1.0);
+ if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ } else {
+ glClearColor(p_color.r, p_color.g, p_color.b, 1.0);
+ }
glClear(GL_COLOR_BUFFER_BIT);
canvas->canvas_begin();
@@ -329,6 +333,27 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c
storage->free(texture); // free since it's only one frame that stays there
+ if (OS::get_singleton()->is_layered_allowed()) {
+ if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED)
+ Size2 wndsize = OS::get_singleton()->get_layered_buffer_size();
+ uint8_t *data = OS::get_singleton()->get_layered_buffer_data();
+ if (data) {
+ glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data);
+ OS::get_singleton()->swap_layered_buffer();
+
+ return;
+ }
+#endif
+ } else {
+ //clear alpha
+ glColorMask(false, false, false, true);
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glColorMask(true, true, true, true);
+ }
+ }
+
OS::get_singleton()->swap_buffers();
}
@@ -365,6 +390,27 @@ void RasterizerGLES3::blit_render_target_to_screen(RID p_render_target, const Re
void RasterizerGLES3::end_frame(bool p_swap_buffers) {
+ if (OS::get_singleton()->is_layered_allowed()) {
+ if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED)
+ Size2 wndsize = OS::get_singleton()->get_layered_buffer_size();
+ uint8_t *data = OS::get_singleton()->get_layered_buffer_data();
+ if (data) {
+ glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data);
+ OS::get_singleton()->swap_layered_buffer();
+
+ return;
+ }
+#endif
+ } else {
+ //clear alpha
+ glColorMask(false, false, false, true);
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glColorMask(true, true, true, true);
+ }
+ }
+
if (p_swap_buffers)
OS::get_singleton()->swap_buffers();
else
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 8da2c2f9c2..9d0fb462f4 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);
@@ -2363,10 +2397,9 @@ void RasterizerSceneGLES3::_draw_sky(RasterizerStorageGLES3::Sky *p_sky, const C
ERR_FAIL_COND(!tex);
glActiveTexture(GL_TEXTURE0);
- if (tex->proxy && tex->proxy->tex_id)
- glBindTexture(tex->target, tex->proxy->tex_id);
- else
- glBindTexture(tex->target, tex->tex_id);
+ tex = tex->get_ptr(); //resolve for proxies
+
+ glBindTexture(tex->target, tex->tex_id);
if (storage->config.srgb_decode_supported && tex->srgb && !tex->using_srgb) {
@@ -2508,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
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index a6faeef473..524212b9c1 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -140,6 +140,7 @@ public:
float reflection_multiplier;
float subsurface_scatter_width;
float ambient_occlusion_affect_light;
+ float ambient_occlusion_affect_ssao;
uint32_t fog_depth_enabled;
float fog_depth_begin;
@@ -151,6 +152,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[3];
} ubo_data;
@@ -385,6 +387,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 +468,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 +547,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 945df35456..eb25d6c7a1 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);
@@ -1356,6 +1363,8 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
ERR_FAIL_COND(!texture);
}
+ texture = texture->get_ptr(); //resolve for proxies
+
glBindVertexArray(0);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
@@ -3814,12 +3823,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) {
@@ -3830,6 +3839,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) {
@@ -3847,11 +3857,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;
@@ -3863,6 +3884,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;
@@ -3877,6 +3899,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) {
@@ -3890,12 +3913,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;
}
}
@@ -3956,7 +4000,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];
@@ -3987,7 +4031,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];
@@ -4013,7 +4057,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) {
@@ -4039,6 +4083,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);
@@ -4054,7 +4130,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;
@@ -4081,7 +4157,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;
@@ -4103,7 +4179,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) {
@@ -4129,6 +4205,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);
@@ -4177,7 +4306,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();
@@ -5895,9 +6024,9 @@ void RasterizerStorageGLES3::update_particles() {
tex = resources.white_tex;
} break;
}
-
} else {
+ t = t->get_ptr(); //resolve for proxies
target = t->target;
tex = t->tex_id;
}
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index 6b626cbd00..80df21941b 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 */
@@ -756,6 +759,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 +769,7 @@ public:
int xform_floats;
int color_floats;
+ int custom_data_floats;
bool dirty_aabb;
bool dirty_data;
@@ -776,11 +781,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 +799,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..9ad16ac2a2 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -898,6 +898,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
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";
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..ed8df04377 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -90,6 +90,7 @@ 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;
bool fog_depth_enabled;
highp float fog_depth_begin;
@@ -322,7 +323,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 +401,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)
@@ -670,6 +683,7 @@ 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;
bool fog_depth_enabled;
highp float fog_depth_begin;
@@ -2128,18 +2142,16 @@ 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);
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/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 1e34d63b13..05dfd69f58 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -244,7 +244,7 @@ OS::TimeZoneInfo OS_Unix::get_time_zone_info() const {
void OS_Unix::delay_usec(uint32_t p_usec) const {
- struct timespec rem = { p_usec / 1000000, (p_usec % 1000000) * 1000 };
+ struct timespec rem = { static_cast<time_t>(p_usec / 1000000), static_cast<long>((p_usec % 1000000) * 1000) };
while (nanosleep(&rem, &rem) == EINTR) {
}
}
diff --git a/drivers/unix/socket_helpers.h b/drivers/unix/socket_helpers.h
index 5ef9ad3088..5b42c13eae 100644
--- a/drivers/unix/socket_helpers.h
+++ b/drivers/unix/socket_helpers.h
@@ -124,6 +124,13 @@ static int _socket_create(IP::Type &p_type, int type, int protocol) {
WARN_PRINT("Unable to set/unset IPv4 address mapping over IPv6");
}
}
+ if (protocol == IPPROTO_UDP && p_type != IP::TYPE_IPV6) {
+ // Enable broadcasting for UDP sockets if it's not IPv6 only (IPv6 has no broadcast option).
+ int broadcast = 1;
+ if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast)) != 0) {
+ WARN_PRINT("Error when enabling broadcasting");
+ }
+ }
return sockfd;
}
diff --git a/drivers/unix/stream_peer_tcp_posix.cpp b/drivers/unix/stream_peer_tcp_posix.cpp
index 6d798f32f9..44b9ef1d7c 100644
--- a/drivers/unix/stream_peer_tcp_posix.cpp
+++ b/drivers/unix/stream_peer_tcp_posix.cpp
@@ -297,6 +297,7 @@ Error StreamPeerTCPPosix::read(uint8_t *p_buffer, int p_bytes, int &r_received,
status = STATUS_NONE;
peer_port = 0;
peer_host = IP_Address();
+ r_received = total_read;
return ERR_FILE_EOF;
} else {
diff --git a/drivers/windows/stream_peer_tcp_winsock.cpp b/drivers/windows/stream_peer_tcp_winsock.cpp
index cb501ce35d..19c937170b 100644
--- a/drivers/windows/stream_peer_tcp_winsock.cpp
+++ b/drivers/windows/stream_peer_tcp_winsock.cpp
@@ -212,6 +212,7 @@ Error StreamPeerTCPWinsock::read(uint8_t *p_buffer, int p_bytes, int &r_received
_block(sockfd, true, false);
} else if (read == 0) {
disconnect_from_host();
+ r_received = total_read;
return ERR_FILE_EOF;
} else {
diff --git a/editor/SCsub b/editor/SCsub
index 4ca6b9e3fd..a9343f7f36 100644
--- a/editor/SCsub
+++ b/editor/SCsub
@@ -7,7 +7,6 @@ import os
import os.path
from compat import encode_utf8, byte_to_str, open_utf8, escape_string
-
def make_certs_header(target, source, env):
src = source[0].srcnode().abspath
@@ -147,268 +146,6 @@ def make_translations_header(target, source, env):
g.close()
-
-def make_authors_header(target, source, env):
-
- sections = ["Project Founders", "Lead Developer", "Project Manager", "Developers"]
- sections_id = ["dev_founders", "dev_lead", "dev_manager", "dev_names"]
-
- src = source[0].srcnode().abspath
- dst = target[0].srcnode().abspath
- f = open_utf8(src, "r")
- g = open_utf8(dst, "w")
-
- g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
- g.write("#ifndef _EDITOR_AUTHORS_H\n")
- g.write("#define _EDITOR_AUTHORS_H\n")
-
- current_section = ""
- reading = False
-
- def close_section():
- g.write("\t0\n")
- g.write("};\n")
-
- for line in f:
- if reading:
- if line.startswith(" "):
- g.write("\t\"" + escape_string(line.strip()) + "\",\n")
- continue
- if line.startswith("## "):
- if reading:
- close_section()
- reading = False
- for i in range(len(sections)):
- if line.strip().endswith(sections[i]):
- current_section = escape_string(sections_id[i])
- reading = True
- g.write("static const char *" + current_section + "[] = {\n")
- break
-
- if reading:
- close_section()
-
- g.write("#endif\n")
-
- g.close()
- f.close()
-
-def make_donors_header(target, source, env):
-
- sections = ["Platinum sponsors", "Gold sponsors", "Mini sponsors", "Gold donors", "Silver donors", "Bronze donors"]
- sections_id = ["donor_s_plat", "donor_s_gold", "donor_s_mini", "donor_gold", "donor_silver", "donor_bronze"]
-
- src = source[0].srcnode().abspath
- dst = target[0].srcnode().abspath
- f = open_utf8(src, "r")
- g = open_utf8(dst, "w")
-
- g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
- g.write("#ifndef _EDITOR_DONORS_H\n")
- g.write("#define _EDITOR_DONORS_H\n")
-
- current_section = ""
- reading = False
-
- def close_section():
- g.write("\t0\n")
- g.write("};\n")
-
- for line in f:
- if reading >= 0:
- if line.startswith(" "):
- g.write("\t\"" + escape_string(line.strip()) + "\",\n")
- continue
- if line.startswith("## "):
- if reading:
- close_section()
- reading = False
- for i in range(len(sections)):
- if line.strip().endswith(sections[i]):
- current_section = escape_string(sections_id[i])
- reading = True
- g.write("static const char *" + current_section + "[] = {\n")
- break
-
- if reading:
- close_section()
-
- g.write("#endif\n")
-
- g.close()
- f.close()
-
-
-def make_license_header(target, source, env):
-
- src_copyright = source[0].srcnode().abspath
- src_license = source[1].srcnode().abspath
- dst = target[0].srcnode().abspath
- f = open_utf8(src_license, "r")
- fc = open_utf8(src_copyright, "r")
- g = open_utf8(dst, "w")
-
- g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
- g.write("#ifndef _EDITOR_LICENSE_H\n")
- g.write("#define _EDITOR_LICENSE_H\n")
- g.write("static const char *about_license =")
-
- for line in f:
- escaped_string = escape_string(line.strip())
- g.write("\n\t\"" + escaped_string + "\\n\"")
-
- g.write(";\n")
-
- tp_current = 0
- tp_file = ""
- tp_comment = ""
- tp_copyright = ""
- tp_license = ""
-
- tp_licensename = ""
- tp_licensebody = ""
-
- tp = []
- tp_licensetext = []
- for line in fc:
- if line.startswith("#"):
- continue
-
- if line.startswith("Files:"):
- tp_file = line[6:].strip()
- tp_current = 1
- elif line.startswith("Comment:"):
- tp_comment = line[8:].strip()
- tp_current = 2
- elif line.startswith("Copyright:"):
- tp_copyright = line[10:].strip()
- tp_current = 3
- elif line.startswith("License:"):
- if tp_current != 0:
- tp_license = line[8:].strip()
- tp_current = 4
- else:
- tp_licensename = line[8:].strip()
- tp_current = 5
- elif line.startswith(" "):
- if tp_current == 1:
- tp_file += "\n" + line.strip()
- elif tp_current == 3:
- tp_copyright += "\n" + line.strip()
- elif tp_current == 5:
- if line.strip() == ".":
- tp_licensebody += "\n"
- else:
- tp_licensebody += line[1:]
- else:
- if tp_current != 0:
- if tp_current == 5:
- tp_licensetext.append([tp_licensename, tp_licensebody])
-
- tp_licensename = ""
- tp_licensebody = ""
- else:
- added = False
- for i in tp:
- if i[0] == tp_comment:
- i[1].append([tp_file, tp_copyright, tp_license])
- added = True
- break
- if not added:
- tp.append([tp_comment,[[tp_file, tp_copyright, tp_license]]])
-
- tp_file = []
- tp_comment = ""
- tp_copyright = []
- tp_license = ""
- tp_current = 0
-
- tp_licensetext.append([tp_licensename, tp_licensebody])
-
- about_thirdparty = ""
- about_tp_copyright_count = ""
- about_tp_license = ""
- about_tp_copyright = ""
- about_tp_file = ""
-
- for i in tp:
- about_thirdparty += "\t\"" + i[0] + "\",\n"
- about_tp_copyright_count += str(len(i[1])) + ", "
- for j in i[1]:
- file_body = ""
- copyright_body = ""
- for k in j[0].split("\n"):
- if file_body != "":
- file_body += "\\n\"\n"
- escaped_string = escape_string(k.strip())
- file_body += "\t\"" + escaped_string
- for k in j[1].split("\n"):
- if copyright_body != "":
- copyright_body += "\\n\"\n"
- escaped_string = escape_string(k.strip())
- copyright_body += "\t\"" + escaped_string
-
- about_tp_file += "\t" + file_body + "\",\n"
- about_tp_copyright += "\t" + copyright_body + "\",\n"
- about_tp_license += "\t\"" + j[2] + "\",\n"
-
- about_license_name = ""
- about_license_body = ""
-
- for i in tp_licensetext:
- body = ""
- for j in i[1].split("\n"):
- if body != "":
- body += "\\n\"\n"
- escaped_string = escape_string(j.strip())
- body += "\t\"" + escaped_string
-
- about_license_name += "\t\"" + i[0] + "\",\n"
- about_license_body += "\t" + body + "\",\n"
-
- g.write("static const char *about_thirdparty[] = {\n")
- g.write(about_thirdparty)
- g.write("\t0\n")
- g.write("};\n")
- g.write("#define THIRDPARTY_COUNT " + str(len(tp)) + "\n")
-
- g.write("static const int about_tp_copyright_count[] = {\n\t")
- g.write(about_tp_copyright_count)
- g.write("0\n};\n")
-
- g.write("static const char *about_tp_file[] = {\n")
- g.write(about_tp_file)
- g.write("\t0\n")
- g.write("};\n")
-
- g.write("static const char *about_tp_copyright[] = {\n")
- g.write(about_tp_copyright)
- g.write("\t0\n")
- g.write("};\n")
-
- g.write("static const char *about_tp_license[] = {\n")
- g.write(about_tp_license)
- g.write("\t0\n")
- g.write("};\n")
-
- g.write("static const char *about_license_name[] = {\n")
- g.write(about_license_name)
- g.write("\t0\n")
- g.write("};\n")
- g.write("#define LICENSE_COUNT " + str(len(tp_licensetext)) + "\n")
-
- g.write("static const char *about_license_body[] = {\n")
- g.write(about_license_body)
- g.write("\t0\n")
- g.write("};\n")
-
- g.write("#endif\n")
-
- g.close()
- fc.close()
- f.close()
-
-
def _make_doc_data_class_path(to_path):
g = open_utf8(os.path.join(to_path,"doc_data_class_path.gen.h"), "w")
g.write("static const int _doc_data_class_path_count = " + str(len(env.doc_class_path)) + ";\n")
@@ -455,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
@@ -466,26 +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)
-
- # Authors
- env.Depends('#editor/authors.gen.h', "../AUTHORS.md")
- env.Command('#editor/authors.gen.h', "../AUTHORS.md", make_authors_header)
-
- # Donors
- env.Depends('#editor/donors.gen.h', "../DONORS.md")
- env.Command('#editor/donors.gen.h', "../DONORS.md", make_donors_header)
-
- # License
- env.Depends('#editor/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"])
- env.Command('#editor/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"], make_license_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_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
new file mode 100644
index 0000000000..197599442b
--- /dev/null
+++ b/editor/animation_bezier_editor.cpp
@@ -0,0 +1,1183 @@
+#include "animation_bezier_editor.h"
+
+float AnimationBezierTrackEdit::_bezier_h_to_pixel(float p_h) {
+ float h = p_h;
+ h = (h - v_scroll) / v_zoom;
+ h = (get_size().height / 2) - h;
+ return h;
+}
+
+static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, const Vector2 &control_1, const Vector2 &control_2, const Vector2 &end) {
+ /* Formula from Wikipedia article on Bezier curves. */
+ real_t omt = (1.0 - t);
+ real_t omt2 = omt * omt;
+ real_t omt3 = omt2 * omt;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
+
+ return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
+}
+
+void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
+
+ float scale = timeline->get_zoom_scale();
+ int limit = timeline->get_name_limit();
+ int right_limit = get_size().width - timeline->get_buttons_width();
+
+ //selection may have altered the order of keys
+ Map<float, int> key_order;
+
+ for (int i = 0; i < animation->track_get_key_count(p_track); i++) {
+ float ofs = animation->track_get_key_time(p_track, i);
+ if (moving_selection && track == p_track && selection.has(i)) {
+ ofs += moving_selection_offset.x;
+ }
+
+ key_order[ofs] = i;
+ }
+
+ for (Map<float, int>::Element *E = key_order.front(); E; E = E->next()) {
+
+ int i = E->get();
+
+ if (!E->next())
+ break;
+
+ int i_n = E->next()->get();
+
+ float offset = animation->track_get_key_time(p_track, i);
+ float height = animation->bezier_track_get_key_value(p_track, i);
+ Vector2 out_handle = animation->bezier_track_get_key_out_handle(p_track, i);
+ if (track == p_track && moving_handle != 0 && moving_handle_key == i) {
+ out_handle = moving_handle_right;
+ }
+
+ if (moving_selection && track == p_track && selection.has(i)) {
+ offset += moving_selection_offset.x;
+ height += moving_selection_offset.y;
+ }
+
+ out_handle += Vector2(offset, height);
+
+ float offset_n = animation->track_get_key_time(p_track, i_n);
+ float height_n = animation->bezier_track_get_key_value(p_track, i_n);
+ Vector2 in_handle = animation->bezier_track_get_key_in_handle(p_track, i_n);
+ if (track == p_track && moving_handle != 0 && moving_handle_key == i_n) {
+ in_handle = moving_handle_left;
+ }
+
+ if (moving_selection && track == p_track && selection.has(i_n)) {
+ offset_n += moving_selection_offset.x;
+ height_n += moving_selection_offset.y;
+ }
+
+ in_handle += Vector2(offset_n, height_n);
+
+ Vector2 start(offset, height);
+ Vector2 end(offset_n, height_n);
+
+ int from_x = (offset - timeline->get_value()) * scale + limit;
+ int point_start = from_x;
+ int to_x = (offset_n - timeline->get_value()) * scale + limit;
+ int point_end = to_x;
+
+ if (from_x > right_limit) //not visible
+ continue;
+
+ if (to_x < limit) //not visible
+ continue;
+
+ from_x = MAX(from_x, limit);
+ to_x = MIN(to_x, right_limit);
+
+ Vector<Vector2> lines;
+
+ Vector2 prev_pos;
+
+ for (int j = from_x; j <= to_x; j++) {
+
+ float t = (j - limit) / scale + timeline->get_value();
+
+ float h;
+
+ if (j == point_end) {
+ h = end.y; //make sure it always connects
+ } else if (j == point_start) {
+ h = start.y; //make sure it always connects
+ } else { //custom interpolation, used because it needs to show paths affected by moving the selection or handles
+ int iterations = 10;
+ float low = 0;
+ float high = 1;
+ float middle;
+
+ //narrow high and low as much as possible
+ for (int k = 0; k < iterations; k++) {
+
+ middle = (low + high) / 2;
+
+ Vector2 interp = _bezier_interp(middle, start, out_handle, in_handle, end);
+
+ if (interp.x < t) {
+ low = middle;
+ } else {
+ high = middle;
+ }
+ }
+
+ //interpolate the result:
+ Vector2 low_pos = _bezier_interp(low, start, out_handle, in_handle, end);
+ Vector2 high_pos = _bezier_interp(high, start, out_handle, in_handle, end);
+
+ float c = (t - low_pos.x) / (high_pos.x - low_pos.x);
+
+ h = low_pos.linear_interpolate(high_pos, c).y;
+ }
+
+ h = _bezier_h_to_pixel(h);
+
+ Vector2 pos(j, h);
+
+ if (j > from_x) {
+ lines.push_back(prev_pos);
+ lines.push_back(pos);
+ }
+ prev_pos = pos;
+ }
+
+ if (lines.size() >= 2) {
+ draw_multiline(lines, p_color);
+ }
+ }
+}
+
+void AnimationBezierTrackEdit::_draw_line_clipped(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, int p_clip_left, int p_clip_right) {
+
+ Vector2 from = p_from;
+ Vector2 to = p_to;
+
+ if (from.x == to.x)
+ return;
+ if (to.x < from.x) {
+ SWAP(to, from);
+ }
+
+ if (to.x < p_clip_left)
+ return;
+
+ if (from.x > p_clip_right)
+ return;
+
+ if (to.x > p_clip_right) {
+ float c = (p_clip_right - from.x) / (to.x - from.x);
+ to = from.linear_interpolate(to, c);
+ }
+
+ if (from.x < p_clip_left) {
+ float c = (p_clip_left - from.x) / (to.x - from.x);
+ from = from.linear_interpolate(to, c);
+ }
+
+ draw_line(from, to, p_color);
+}
+
+void AnimationBezierTrackEdit::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) {
+ bezier_icon = get_icon("KeyBezierPoint", "EditorIcons");
+ bezier_handle_icon = get_icon("KeyBezierHandle", "EditorIcons");
+ selected_icon = get_icon("KeyBezierSelected", "EditorIcons");
+ if (handle_mode_option->get_item_count() == 0) {
+ handle_mode_option->add_icon_item(get_icon("BezierHandlesFree", "EditorIcons"), TTR("Free"), HANDLE_MODE_FREE);
+ handle_mode_option->add_icon_item(get_icon("BezierHandlesBalanced", "EditorIcons"), TTR("Balanced"), HANDLE_MODE_BALANCED);
+ handle_mode_option->add_icon_item(get_icon("BezierHandlesMirror", "EditorIcons"), TTR("Mirror"), HANDLE_MODE_MIRROR);
+ }
+ }
+ if (p_what == NOTIFICATION_RESIZED) {
+
+ int right_limit = get_size().width - timeline->get_buttons_width();
+ int hsep = get_constant("hseparation", "ItemList");
+ int vsep = get_constant("vseparation", "ItemList");
+
+ handle_mode_option->set_position(Vector2(right_limit + hsep, get_size().height - handle_mode_option->get_combined_minimum_size().height - vsep));
+ handle_mode_option->set_size(Vector2(timeline->get_buttons_width() - hsep * 2, handle_mode_option->get_combined_minimum_size().height));
+ }
+ if (p_what == NOTIFICATION_DRAW) {
+ if (animation.is_null())
+ return;
+
+ int limit = timeline->get_name_limit();
+
+ if (has_focus()) {
+ Color accent = get_color("accent_color", "Editor");
+ accent.a *= 0.7;
+ draw_rect(Rect2(Point2(), get_size()), accent, false);
+ }
+
+ Ref<Font> font = get_font("font", "Label");
+ Color color = get_color("font_color", "Label");
+ int hsep = get_constant("hseparation", "ItemList");
+ int vsep = get_constant("vseparation", "ItemList");
+ Color linecolor = color;
+ linecolor.a = 0.2;
+
+ draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor);
+
+ int right_limit = get_size().width - timeline->get_buttons_width();
+
+ draw_line(Point2(right_limit, 0), Point2(right_limit, get_size().height), linecolor);
+
+ Ref<Texture> close_icon = get_icon("Close", "EditorIcons");
+
+ close_icon_rect.position = Vector2(get_size().width - close_icon->get_width() - hsep, hsep);
+ close_icon_rect.size = close_icon->get_size();
+ draw_texture(close_icon, close_icon_rect.position);
+
+ String base_path = animation->track_get_path(track);
+ int end = base_path.find(":");
+ if (end != -1) {
+ base_path = base_path.substr(0, end + 1);
+ }
+
+ // NAMES AND ICON
+ int vofs = vsep;
+ int margin = 0;
+
+ {
+ int ofs = 0;
+
+ NodePath path = animation->track_get_path(track);
+
+ Node *node = NULL;
+
+ if (root && root->has_node(path)) {
+ node = root->get_node(path);
+ }
+
+ String text;
+
+ int h = font->get_height();
+
+ if (node) {
+ Ref<Texture> icon;
+ if (has_icon(node->get_class(), "EditorIcons")) {
+ icon = get_icon(node->get_class(), "EditorIcons");
+ } else {
+ icon = get_icon("Node", "EditorIcons");
+ }
+
+ h = MAX(h, icon->get_height());
+
+ draw_texture(icon, Point2(ofs, vofs + int(h - icon->get_height()) / 2));
+
+ margin = icon->get_width();
+
+ text = node->get_name();
+ ofs += hsep;
+ ofs += icon->get_width();
+
+ Vector2 string_pos = Point2(ofs, vofs + (h - font->get_height()) / 2 + font->get_ascent());
+ string_pos = string_pos.floor();
+ draw_string(font, string_pos, text, color, limit - ofs - hsep);
+
+ vofs += h + vsep;
+ }
+ }
+
+ // RELATED TRACKS TITLES
+
+ Map<int, Color> subtrack_colors;
+ subtracks.clear();
+
+ for (int i = 0; i < animation->get_track_count(); i++) {
+ if (animation->track_get_type(i) != Animation::TYPE_BEZIER)
+ continue;
+ String path = animation->track_get_path(i);
+ if (!path.begins_with(base_path))
+ continue; //another node
+ path = path.replace_first(base_path, "");
+
+ Color cc = color;
+ Rect2 rect = Rect2(margin, vofs, limit - margin - hsep, font->get_height() + vsep);
+ if (i != track) {
+ cc.a *= 0.7;
+ uint32_t hash = path.hash();
+ hash = ((hash >> 16) ^ hash) * 0x45d9f3b;
+ hash = ((hash >> 16) ^ hash) * 0x45d9f3b;
+ hash = (hash >> 16) ^ hash;
+ float h = (hash % 65535) / 65536.0;
+ Color subcolor;
+ subcolor.set_hsv(h, 0.2, 0.8);
+ subcolor.a = 0.5;
+ draw_rect(Rect2(0, vofs + font->get_height() * 0.1, margin - hsep, font->get_height() * 0.8), subcolor);
+ subtrack_colors[i] = subcolor;
+
+ subtracks[i] = rect;
+ } else {
+ Color ac = get_color("accent_color", "Editor");
+ ac.a = 0.5;
+ draw_rect(rect, ac);
+ }
+ draw_string(font, Point2(margin, vofs + font->get_ascent()), path, cc, limit - margin - hsep);
+
+ vofs += font->get_height() + vsep;
+ }
+
+ Color accent = get_color("accent_color", "Editor");
+
+ { //guides
+ float min_left_scale = font->get_height() + vsep;
+
+ float scale = 1;
+
+ while (scale / v_zoom < min_left_scale * 2) {
+ scale *= 5;
+ }
+
+ bool first = true;
+ int prev_iv = 0;
+ for (int i = font->get_height(); i < get_size().height; i++) {
+
+ float ofs = get_size().height / 2 - i;
+ ofs *= v_zoom;
+ ofs += v_scroll;
+
+ int iv = int(ofs / scale);
+ if (ofs < 0)
+ iv -= 1;
+ if (!first && iv != prev_iv) {
+
+ Color lc = linecolor;
+ lc.a *= 0.5;
+ draw_line(Point2(limit, i), Point2(right_limit, i), lc);
+ Color c = color;
+ c.a *= 0.5;
+ draw_string(font, Point2(limit + 8, i - 2), itos((iv + 1) * scale), c);
+ }
+
+ first = false;
+ prev_iv = iv;
+ }
+ }
+
+ { //draw OTHER curves
+
+ float scale = timeline->get_zoom_scale();
+ Ref<Texture> point = get_icon("KeyValue", "EditorIcons");
+ for (Map<int, Color>::Element *E = subtrack_colors.front(); E; E = E->next()) {
+
+ _draw_track(E->key(), E->get());
+
+ for (int i = 0; i < animation->track_get_key_count(E->key()); i++) {
+
+ float offset = animation->track_get_key_time(E->key(), i);
+ float value = animation->bezier_track_get_key_value(E->key(), i);
+
+ Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value));
+
+ if (pos.x >= limit && pos.x <= right_limit) {
+ draw_texture(point, pos - point->get_size() / 2, E->get());
+ }
+ }
+ }
+
+ //draw edited curve
+ _draw_track(track, accent);
+ }
+
+ //draw editor handles
+ {
+
+ float scale = timeline->get_zoom_scale();
+ edit_points.clear();
+
+ for (int i = 0; i < animation->track_get_key_count(track); i++) {
+
+ float offset = animation->track_get_key_time(track, i);
+ float value = animation->bezier_track_get_key_value(track, i);
+
+ if (moving_selection && selection.has(i)) {
+ offset += moving_selection_offset.x;
+ value += moving_selection_offset.y;
+ }
+
+ Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value));
+
+ Vector2 in_vec = animation->bezier_track_get_key_in_handle(track, i);
+ if (moving_handle != 0 && moving_handle_key == i) {
+ in_vec = moving_handle_left;
+ }
+ Vector2 pos_in = Vector2(((offset + in_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + in_vec.y));
+
+ Vector2 out_vec = animation->bezier_track_get_key_out_handle(track, i);
+
+ if (moving_handle != 0 && moving_handle_key == i) {
+ out_vec = moving_handle_right;
+ }
+
+ Vector2 pos_out = Vector2(((offset + out_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + out_vec.y));
+
+ _draw_line_clipped(pos, pos_in, accent, limit, right_limit);
+ _draw_line_clipped(pos, pos_out, accent, limit, right_limit);
+
+ EditPoint ep;
+ if (pos.x >= limit && pos.x <= right_limit) {
+ ep.point_rect.position = (pos - bezier_icon->get_size() / 2).floor();
+ ep.point_rect.size = bezier_icon->get_size();
+ if (selection.has(i)) {
+ draw_texture(selected_icon, ep.point_rect.position);
+ } else {
+ draw_texture(bezier_icon, ep.point_rect.position);
+ }
+ ep.point_rect = ep.point_rect.grow(ep.point_rect.size.width * 0.5);
+ }
+ if (pos_in.x >= limit && pos_in.x <= right_limit) {
+ ep.in_rect.position = (pos_in - bezier_handle_icon->get_size() / 2).floor();
+ ep.in_rect.size = bezier_handle_icon->get_size();
+ draw_texture(bezier_handle_icon, ep.in_rect.position);
+ ep.in_rect = ep.in_rect.grow(ep.in_rect.size.width * 0.5);
+ }
+ if (pos_out.x >= limit && pos_out.x <= right_limit) {
+ ep.out_rect.position = (pos_out - bezier_handle_icon->get_size() / 2).floor();
+ ep.out_rect.size = bezier_handle_icon->get_size();
+ draw_texture(bezier_handle_icon, ep.out_rect.position);
+ ep.out_rect = ep.out_rect.grow(ep.out_rect.size.width * 0.5);
+ }
+ edit_points.push_back(ep);
+ }
+ }
+
+ if (box_selecting) {
+ Color bs = accent;
+ bs.a *= 0.5;
+ Vector2 bs_from = box_selection_from;
+ Vector2 bs_to = box_selection_to;
+ if (bs_from.x > bs_to.x) {
+ SWAP(bs_from.x, bs_to.x);
+ }
+ if (bs_from.y > bs_to.y) {
+ SWAP(bs_from.y, bs_to.y);
+ }
+ draw_rect(Rect2(bs_from, bs_to - bs_from), bs);
+ }
+
+#if 0
+ // KEYFAMES //
+
+ {
+
+ float scale = timeline->get_zoom_scale();
+ int limit_end = get_size().width - timeline->get_buttons_width();
+
+ for (int i = 0; i < animation->track_get_key_count(track); i++) {
+
+ float offset = animation->track_get_key_time(track, i) - timeline->get_value();
+ if (editor->is_key_selected(track, i) && editor->is_moving_selection()) {
+ offset += editor->get_moving_selection_offset();
+ }
+ offset = offset * scale + limit;
+ draw_key(i, scale, int(offset), editor->is_key_selected(track, i), limit, limit_end);
+ }
+ }
+#endif
+ }
+}
+
+Ref<Animation> AnimationBezierTrackEdit::get_animation() const {
+ return animation;
+}
+
+void AnimationBezierTrackEdit::set_animation_and_track(const Ref<Animation> &p_animation, int p_track) {
+
+ animation = p_animation;
+ track = p_track;
+ update();
+}
+
+Size2 AnimationBezierTrackEdit::get_minimum_size() const {
+
+ return Vector2(1, 1);
+}
+
+void AnimationBezierTrackEdit::set_undo_redo(UndoRedo *p_undo_redo) {
+ undo_redo = p_undo_redo;
+}
+
+void AnimationBezierTrackEdit::set_timeline(AnimationTimelineEdit *p_timeline) {
+ timeline = p_timeline;
+ timeline->connect("zoom_changed", this, "_zoom_changed");
+}
+void AnimationBezierTrackEdit::set_editor(AnimationTrackEditor *p_editor) {
+ editor = p_editor;
+}
+
+void AnimationBezierTrackEdit::_play_position_draw() {
+
+ if (!animation.is_valid() || play_position_pos < 0)
+ return;
+
+ float scale = timeline->get_zoom_scale();
+ int h = get_size().height;
+
+ int px = (-timeline->get_value() + play_position_pos) * scale + timeline->get_name_limit();
+
+ if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) {
+ Color color = get_color("accent_color", "Editor");
+ play_position->draw_line(Point2(px, 0), Point2(px, h), color);
+ }
+}
+
+void AnimationBezierTrackEdit::set_play_position(float p_pos) {
+
+ play_position_pos = p_pos;
+ play_position->update();
+}
+
+void AnimationBezierTrackEdit::update_play_position() {
+ play_position->update();
+}
+
+void AnimationBezierTrackEdit::set_root(Node *p_root) {
+ root = p_root;
+}
+void AnimationBezierTrackEdit::_zoom_changed() {
+ update();
+}
+
+String AnimationBezierTrackEdit::get_tooltip(const Point2 &p_pos) const {
+
+ return Control::get_tooltip(p_pos);
+}
+
+void AnimationBezierTrackEdit::_clear_selection() {
+ selection.clear();
+ update();
+}
+
+void AnimationBezierTrackEdit::_clear_selection_for_anim(const Ref<Animation> &p_anim) {
+
+ if (!(animation == p_anim))
+ return;
+ //selection.clear();
+ _clear_selection();
+}
+
+void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos) {
+
+ if (!(animation == p_anim))
+ return;
+
+ int idx = animation->track_find_key(p_track, p_pos, true);
+ ERR_FAIL_COND(idx < 0);
+
+ selection.insert(idx);
+ update();
+}
+
+void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
+
+ if (p_event->is_pressed()) {
+ if (ED_GET_SHORTCUT("animation_editor/duplicate_selection")->is_shortcut(p_event)) {
+ duplicate_selection();
+ accept_event();
+ }
+
+ if (ED_GET_SHORTCUT("animation_editor/delete_selection")->is_shortcut(p_event)) {
+ delete_selection();
+ accept_event();
+ }
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
+ if (mb->get_command()) {
+ timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05);
+ } else {
+ if (v_zoom < 1000) {
+ v_zoom *= 1.2;
+ }
+ }
+ update();
+ }
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
+ if (mb->get_command()) {
+ timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05);
+ } else {
+ if (v_zoom > 0.01) {
+ v_zoom /= 1.2;
+ }
+ }
+ update();
+ }
+
+ if (mb.is_valid() && mb->get_button_index() == BUTTON_MIDDLE) {
+
+ if (mb->is_pressed()) {
+ int x = mb->get_position().x - timeline->get_name_limit();
+ panning_timeline_from = x / timeline->get_zoom_scale();
+ panning_timeline = true;
+ panning_timeline_at = timeline->get_value();
+ } else {
+ panning_timeline = false;
+ }
+ }
+
+ if (mb.is_valid() && mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) {
+
+ menu_insert_key = mb->get_position();
+ Vector2 popup_pos = get_global_transform().xform(mb->get_position());
+
+ menu->clear();
+ menu->add_icon_item(bezier_icon, TTR("Insert Key Here"), MENU_KEY_INSERT);
+ if (selection.size()) {
+ menu->add_separator();
+ menu->add_icon_item(get_icon("Duplicate", "EditorIcons"), TTR("Duplicate Selected Key(s)"), MENU_KEY_DUPLICATE);
+ menu->add_separator();
+ menu->add_icon_item(get_icon("Remove", "EditorIcons"), TTR("Delete Selected Key(s)"), MENU_KEY_DELETE);
+ }
+
+ menu->set_as_minsize();
+ menu->set_position(popup_pos);
+ menu->popup();
+ }
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ if (close_icon_rect.has_point(mb->get_position())) {
+ emit_signal("close_request");
+ return;
+ }
+ for (Map<int, Rect2>::Element *E = subtracks.front(); E; E = E->next()) {
+ if (E->get().has_point(mb->get_position())) {
+ set_animation_and_track(animation, E->key());
+ return;
+ }
+ }
+
+ for (int i = 0; i < edit_points.size(); i++) {
+
+ //first check point
+ //command makes it ignore the main point, so control point editors can be force-edited
+ //path 2D editing in the 3D and 2D editors works the same way
+ if (!mb->get_command()) {
+ if (edit_points[i].point_rect.has_point(mb->get_position())) {
+ if (mb->get_shift()) {
+ //add to selection
+ if (selection.has(i)) {
+ selection.erase(i);
+ } else {
+ selection.insert(i);
+ }
+ update();
+ select_single_attempt = -1;
+ } else if (selection.has(i)) {
+ moving_selection_attempt = true;
+ moving_selection = false;
+ moving_selection_from_key = i;
+ moving_selection_offset = Vector2();
+ select_single_attempt = i;
+ update();
+ } else {
+
+ moving_selection_attempt = true;
+ moving_selection = true;
+ moving_selection_from_key = i;
+ moving_selection_offset = Vector2();
+ selection.clear();
+ selection.insert(i);
+ update();
+ }
+ return;
+ }
+ }
+
+ if (edit_points[i].in_rect.has_point(mb->get_position())) {
+ moving_handle = -1;
+ moving_handle_key = i;
+ moving_handle_left = animation->bezier_track_get_key_in_handle(track, i);
+ moving_handle_right = animation->bezier_track_get_key_out_handle(track, i);
+ update();
+ return;
+ }
+
+ if (edit_points[i].out_rect.has_point(mb->get_position())) {
+ moving_handle = 1;
+ moving_handle_key = i;
+ moving_handle_left = animation->bezier_track_get_key_in_handle(track, i);
+ moving_handle_right = animation->bezier_track_get_key_out_handle(track, i);
+ update();
+ return;
+ ;
+ }
+ }
+
+ //insert new point
+ if (mb->get_command() && mb->get_position().x >= timeline->get_name_limit() && mb->get_position().x < get_size().width - timeline->get_buttons_width()) {
+
+ Array new_point;
+ new_point.resize(5);
+
+ float h = (get_size().height / 2 - mb->get_position().y) * v_zoom + v_scroll;
+
+ new_point[0] = h;
+ new_point[1] = -0.25;
+ new_point[2] = 0;
+ new_point[3] = 0.25;
+ new_point[4] = 0;
+
+ float time = ((mb->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
+ while (animation->track_find_key(track, time, true) != -1) {
+ time += 0.001;
+ }
+
+ undo_redo->create_action("Add Bezier Point");
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, time);
+ undo_redo->commit_action();
+
+ //then attempt to move
+ int index = animation->track_find_key(track, time, true);
+ ERR_FAIL_COND(index == -1);
+ _clear_selection();
+ selection.insert(index);
+
+ moving_selection_attempt = true;
+ moving_selection = false;
+ moving_selection_from_key = index;
+ moving_selection_offset = Vector2();
+ select_single_attempt = -1;
+ update();
+
+ return;
+ }
+
+ //box select
+ if (mb->get_position().x >= timeline->get_name_limit() && mb->get_position().x < get_size().width - timeline->get_buttons_width()) {
+ box_selecting_attempt = true;
+ box_selecting = false;
+ box_selecting_add = false;
+ box_selection_from = mb->get_position();
+ return;
+ }
+ }
+
+ if (box_selecting_attempt && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ if (box_selecting) {
+ //do actual select
+ if (!box_selecting_add) {
+ _clear_selection();
+ }
+
+ Vector2 bs_from = box_selection_from;
+ Vector2 bs_to = box_selection_to;
+ if (bs_from.x > bs_to.x) {
+ SWAP(bs_from.x, bs_to.x);
+ }
+ if (bs_from.y > bs_to.y) {
+ SWAP(bs_from.y, bs_to.y);
+ }
+ Rect2 selection_rect(bs_from, bs_to - bs_from);
+
+ for (int i = 0; i < edit_points.size(); i++) {
+
+ if (edit_points[i].point_rect.intersects(selection_rect)) {
+ selection.insert(i);
+ }
+ }
+ } else {
+ _clear_selection(); //clicked and nothing happened, so clear the selection
+ }
+ box_selecting_attempt = false;
+ box_selecting = false;
+ update();
+ }
+
+ if (moving_handle != 0 && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ undo_redo->create_action("Move Bezier Points");
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, moving_handle_left);
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, moving_handle_right);
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, animation->bezier_track_get_key_in_handle(track, moving_handle_key));
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, animation->bezier_track_get_key_out_handle(track, moving_handle_key));
+ undo_redo->commit_action();
+
+ moving_handle = 0;
+ update();
+ }
+
+ if (moving_selection_attempt && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ if (moving_selection) {
+ //combit it
+
+ undo_redo->create_action("Move Bezier Points");
+
+ List<AnimMoveRestore> to_restore;
+ // 1-remove the keys
+ for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
+
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, E->get());
+ }
+ // 2- remove overlapped keys
+ for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float newtime = animation->track_get_key_time(track, E->get()) + moving_selection_offset.x;
+
+ int idx = animation->track_find_key(track, newtime, true);
+ if (idx == -1)
+ continue;
+
+ if (selection.has(idx))
+ continue; //already in selection, don't save
+
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", track, newtime);
+ AnimMoveRestore amr;
+
+ amr.key = animation->track_get_key_value(track, idx);
+ amr.track = track;
+ amr.time = newtime;
+
+ to_restore.push_back(amr);
+ }
+
+ // 3-move the keys (re insert them)
+ for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float newpos = animation->track_get_key_time(track, E->get()) + moving_selection_offset.x;
+ /*
+ if (newpos<0)
+ continue; //no add at the beginning
+ */
+ Array key = animation->track_get_key_value(track, E->get());
+ float h = key[0];
+ h += moving_selection_offset.y;
+ key[0] = h;
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, newpos, key, 1);
+ }
+
+ // 4-(undo) remove inserted keys
+ for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float newpos = animation->track_get_key_time(track, E->get()) + moving_selection_offset.x;
+ /*
+ if (newpos<0)
+ continue; //no remove what no inserted
+ */
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, newpos);
+ }
+
+ // 5-(undo) reinsert keys
+ for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float oldpos = animation->track_get_key_time(track, E->get());
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, oldpos, animation->track_get_key_value(track, E->get()), 1);
+ }
+
+ // 6-(undo) reinsert overlapped keys
+ for (List<AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
+
+ AnimMoveRestore &amr = E->get();
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, 1);
+ }
+
+ // 6-(undo) reinsert overlapped keys
+ for (List<AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
+
+ AnimMoveRestore &amr = E->get();
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, 1);
+ }
+
+ undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
+ undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
+
+ // 7-reselect
+
+ for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float oldpos = animation->track_get_key_time(track, E->get());
+ float newpos = oldpos + moving_selection_offset.x;
+
+ undo_redo->add_do_method(this, "_select_at_anim", animation, track, newpos);
+ undo_redo->add_undo_method(this, "_select_at_anim", animation, track, oldpos);
+ }
+
+ undo_redo->commit_action();
+
+ moving_selection = false;
+ } else if (select_single_attempt != -1) {
+ selection.clear();
+ selection.insert(select_single_attempt);
+ }
+
+ moving_selection_attempt = false;
+ update();
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_MIDDLE) {
+ v_scroll += mm->get_relative().y * v_zoom;
+ if (v_scroll > 100000)
+ v_scroll = 100000;
+ if (v_scroll < -100000)
+ v_scroll = -100000;
+
+ int x = mm->get_position().x - timeline->get_name_limit();
+ float ofs = x / timeline->get_zoom_scale();
+ float diff = ofs - panning_timeline_from;
+ timeline->set_value(panning_timeline_at - diff);
+
+ update();
+ }
+ if (moving_selection_attempt && mm.is_valid()) {
+
+ if (!moving_selection) {
+ moving_selection = true;
+ select_single_attempt = -1;
+ }
+
+ float y = (get_size().height / 2 - mm->get_position().y) * v_zoom + v_scroll;
+ float x = ((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
+
+ moving_selection_offset = Vector2(x - animation->track_get_key_time(track, moving_selection_from_key), y - animation->bezier_track_get_key_value(track, moving_selection_from_key));
+ update();
+ }
+
+ if (box_selecting_attempt && mm.is_valid()) {
+
+ if (!box_selecting) {
+ box_selecting = true;
+ box_selecting_add = mm->get_shift();
+ }
+
+ box_selection_to = mm->get_position();
+
+ if (get_local_mouse_position().y < 0) {
+ //avoid cursor from going too above, so it does not lose focus with viewport
+ warp_mouse(Vector2(get_local_mouse_position().x, 0));
+ }
+ update();
+ }
+
+ if (moving_handle != 0 && mm.is_valid()) {
+
+ float y = (get_size().height / 2 - mm->get_position().y) * v_zoom + v_scroll;
+ float x = ((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
+
+ Vector2 key_pos = Vector2(animation->track_get_key_time(track, moving_handle_key), animation->bezier_track_get_key_value(track, moving_handle_key));
+
+ Vector2 moving_handle_value = Vector2(x, y) - key_pos;
+
+ moving_handle_left = animation->bezier_track_get_key_in_handle(track, moving_handle_key);
+ moving_handle_right = animation->bezier_track_get_key_out_handle(track, moving_handle_key);
+
+ if (moving_handle == -1) {
+ moving_handle_left = moving_handle_value;
+ if (moving_handle_left.x > 0) {
+ moving_handle_left.x = 0;
+ }
+
+ if (handle_mode_option->get_selected() == HANDLE_MODE_BALANCED) {
+ Vector2 scale = Vector2(timeline->get_zoom_scale(), v_zoom);
+ moving_handle_right = (-(moving_handle_left * scale).normalized() * (moving_handle_right * scale).length()) / scale;
+
+ } else if (handle_mode_option->get_selected() == HANDLE_MODE_MIRROR) {
+ moving_handle_right = -moving_handle_left;
+ }
+ }
+
+ if (moving_handle == 1) {
+ moving_handle_right = moving_handle_value;
+ if (moving_handle_right.x < 0) {
+ moving_handle_right.x = 0;
+ }
+
+ if (handle_mode_option->get_selected() == HANDLE_MODE_BALANCED) {
+ Vector2 scale = Vector2(timeline->get_zoom_scale(), v_zoom);
+ moving_handle_left = (-(moving_handle_right * scale).normalized() * (moving_handle_left * scale).length()) / scale;
+ } else if (handle_mode_option->get_selected() == HANDLE_MODE_MIRROR) {
+ moving_handle_left = -moving_handle_right;
+ }
+ }
+
+ update();
+ }
+}
+
+void AnimationBezierTrackEdit::_menu_selected(int p_index) {
+
+ switch (p_index) {
+ case MENU_KEY_INSERT: {
+
+ Array new_point;
+ new_point.resize(5);
+
+ float h = (get_size().height / 2 - menu_insert_key.y) * v_zoom + v_scroll;
+
+ new_point[0] = h;
+ new_point[1] = -0.25;
+ new_point[2] = 0;
+ new_point[3] = 0.25;
+ new_point[4] = 0;
+
+ float time = ((menu_insert_key.x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
+ while (animation->track_find_key(track, time, true) != -1) {
+ time += 0.001;
+ }
+
+ undo_redo->create_action("Add Bezier Point");
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, time);
+ undo_redo->commit_action();
+
+ } break;
+ case MENU_KEY_DUPLICATE: {
+ duplicate_selection();
+ } break;
+ case MENU_KEY_DELETE: {
+ delete_selection();
+ } break;
+ }
+}
+
+void AnimationBezierTrackEdit::duplicate_selection() {
+
+ if (selection.size() == 0)
+ return;
+
+ float top_time = 1e10;
+ for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float t = animation->track_get_key_time(track, E->get());
+ if (t < top_time)
+ top_time = t;
+ }
+
+ undo_redo->create_action(TTR("Anim Duplicate Keys"));
+
+ List<Pair<int, float> > new_selection_values;
+
+ for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float t = animation->track_get_key_time(track, E->get());
+ float dst_time = t + (timeline->get_play_position() - top_time);
+ int existing_idx = animation->track_find_key(track, dst_time, true);
+
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, dst_time, animation->track_get_key_value(track, E->get()), animation->track_get_key_transition(track, E->get()));
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, dst_time);
+
+ Pair<int, float> p;
+ p.first = track;
+ p.second = dst_time;
+ new_selection_values.push_back(p);
+
+ if (existing_idx != -1) {
+
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, dst_time, animation->track_get_key_value(track, existing_idx), animation->track_get_key_transition(track, existing_idx));
+ }
+ }
+
+ undo_redo->commit_action();
+
+ //reselect duplicated
+
+ selection.clear();
+ for (List<Pair<int, float> >::Element *E = new_selection_values.front(); E; E = E->next()) {
+
+ int track = E->get().first;
+ float time = E->get().second;
+
+ int existing_idx = animation->track_find_key(track, time, true);
+
+ if (existing_idx == -1)
+ continue;
+
+ selection.insert(existing_idx);
+ }
+
+ update();
+}
+
+void AnimationBezierTrackEdit::delete_selection() {
+ if (selection.size()) {
+ undo_redo->create_action(TTR("Anim Delete Keys"));
+
+ for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
+
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, E->get());
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, animation->track_get_key_time(track, E->get()), animation->track_get_key_value(track, E->get()), 1);
+ }
+ undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
+ undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
+ undo_redo->commit_action();
+ //selection.clear();
+ }
+}
+
+void AnimationBezierTrackEdit::set_block_animation_update_ptr(bool *p_block_ptr) {
+ block_animation_update_ptr = p_block_ptr;
+}
+
+void AnimationBezierTrackEdit::_bind_methods() {
+
+ ClassDB::bind_method("_zoom_changed", &AnimationBezierTrackEdit::_zoom_changed);
+ ClassDB::bind_method("_menu_selected", &AnimationBezierTrackEdit::_menu_selected);
+ ClassDB::bind_method("_gui_input", &AnimationBezierTrackEdit::_gui_input);
+ ClassDB::bind_method("_play_position_draw", &AnimationBezierTrackEdit::_play_position_draw);
+
+ ClassDB::bind_method("_clear_selection", &AnimationBezierTrackEdit::_clear_selection);
+ ClassDB::bind_method("_clear_selection_for_anim", &AnimationBezierTrackEdit::_clear_selection);
+ ClassDB::bind_method("_select_at_anim", &AnimationBezierTrackEdit::_clear_selection);
+
+ ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::REAL, "position"), PropertyInfo(Variant::BOOL, "drag")));
+ ADD_SIGNAL(MethodInfo("remove_request", PropertyInfo(Variant::INT, "track")));
+ ADD_SIGNAL(MethodInfo("insert_key", PropertyInfo(Variant::REAL, "ofs")));
+ ADD_SIGNAL(MethodInfo("select_key", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "single")));
+ ADD_SIGNAL(MethodInfo("deselect_key", PropertyInfo(Variant::INT, "index")));
+ ADD_SIGNAL(MethodInfo("clear_selection"));
+ ADD_SIGNAL(MethodInfo("close_request"));
+
+ ADD_SIGNAL(MethodInfo("move_selection_begin"));
+ ADD_SIGNAL(MethodInfo("move_selection", PropertyInfo(Variant::REAL, "ofs")));
+ ADD_SIGNAL(MethodInfo("move_selection_commit"));
+ ADD_SIGNAL(MethodInfo("move_selection_cancel"));
+}
+
+AnimationBezierTrackEdit::AnimationBezierTrackEdit() {
+ undo_redo = NULL;
+ timeline = NULL;
+ root = NULL;
+ menu = NULL;
+ block_animation_update_ptr = NULL;
+
+ moving_selection_attempt = false;
+ moving_selection = false;
+ select_single_attempt = -1;
+ box_selecting = false;
+ box_selecting_attempt = false;
+
+ moving_handle = 0;
+
+ play_position_pos = 0;
+ play_position = memnew(Control);
+ play_position->set_mouse_filter(MOUSE_FILTER_PASS);
+ add_child(play_position);
+ play_position->set_anchors_and_margins_preset(PRESET_WIDE);
+ play_position->connect("draw", this, "_play_position_draw");
+ set_focus_mode(FOCUS_CLICK);
+
+ v_scroll = 0;
+ v_zoom = 1;
+
+ panning_timeline = false;
+ set_clip_contents(true);
+ handle_mode = HANDLE_MODE_FREE;
+ handle_mode_option = memnew(OptionButton);
+ add_child(handle_mode_option);
+
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect("id_pressed", this, "_menu_selected");
+
+ //set_mouse_filter(MOUSE_FILTER_PASS); //scroll has to work too for selection
+}
diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h
new file mode 100644
index 0000000000..544690844a
--- /dev/null
+++ b/editor/animation_bezier_editor.h
@@ -0,0 +1,141 @@
+#ifndef ANIMATION_BEZIER_EDITOR_H
+#define ANIMATION_BEZIER_EDITOR_H
+
+#include "animation_track_editor.h"
+
+class AnimationBezierTrackEdit : public Control {
+
+ GDCLASS(AnimationBezierTrackEdit, Control)
+
+ enum HandleMode {
+ HANDLE_MODE_FREE,
+ HANDLE_MODE_BALANCED,
+ HANDLE_MODE_MIRROR
+ };
+
+ enum {
+ MENU_KEY_INSERT,
+ MENU_KEY_DUPLICATE,
+ MENU_KEY_DELETE
+ };
+
+ HandleMode handle_mode;
+ OptionButton *handle_mode_option;
+
+ AnimationTimelineEdit *timeline;
+ UndoRedo *undo_redo;
+ Node *root;
+ Control *play_position; //separate control used to draw so updates for only position changed are much faster
+ float play_position_pos;
+
+ Ref<Animation> animation;
+ int track;
+
+ Vector<Rect2> view_rects;
+
+ Ref<Texture> bezier_icon;
+ Ref<Texture> bezier_handle_icon;
+ Ref<Texture> selected_icon;
+
+ Rect2 close_icon_rect;
+
+ Map<int, Rect2> subtracks;
+
+ float v_scroll;
+ float v_zoom;
+
+ PopupMenu *menu;
+
+ void _zoom_changed();
+
+ void _gui_input(const Ref<InputEvent> &p_event);
+ void _menu_selected(int p_index);
+
+ bool *block_animation_update_ptr; //used to block all tracks re-gen (speed up)
+
+ void _play_position_draw();
+
+ Vector2 insert_at_pos;
+
+ bool moving_selection_attempt;
+ int select_single_attempt;
+ bool moving_selection;
+ int moving_selection_from_key;
+
+ Vector2 moving_selection_offset;
+
+ bool box_selecting_attempt;
+ bool box_selecting;
+ bool box_selecting_add;
+ Vector2 box_selection_from;
+ Vector2 box_selection_to;
+
+ int moving_handle; //0 no move -1 or +1 out
+ int moving_handle_key;
+ Vector2 moving_handle_left;
+ Vector2 moving_handle_right;
+
+ void _clear_selection();
+ void _clear_selection_for_anim(const Ref<Animation> &p_anim);
+ void _select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos);
+
+ Vector2 menu_insert_key;
+
+ struct AnimMoveRestore {
+
+ int track;
+ float time;
+ Variant key;
+ float transition;
+ };
+
+ AnimationTrackEditor *editor;
+
+ struct EditPoint {
+ Rect2 point_rect;
+ Rect2 in_rect;
+ Rect2 out_rect;
+ };
+
+ Vector<EditPoint> edit_points;
+
+ Set<int> selection;
+
+ bool panning_timeline;
+ float panning_timeline_from;
+ float panning_timeline_at;
+
+ void _draw_line_clipped(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, int p_clip_left, int p_clip_right);
+ void _draw_track(int p_track, const Color &p_color);
+
+ float _bezier_h_to_pixel(float p_h);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ virtual String get_tooltip(const Point2 &p_pos) const;
+
+ Ref<Animation> get_animation() const;
+
+ void set_animation_and_track(const Ref<Animation> &p_animation, int p_track);
+ virtual Size2 get_minimum_size() const;
+
+ void set_undo_redo(UndoRedo *p_undo_redo);
+ void set_timeline(AnimationTimelineEdit *p_timeline);
+ void set_editor(AnimationTrackEditor *p_editor);
+ void set_root(Node *p_root);
+
+ void set_block_animation_update_ptr(bool *p_block_ptr);
+
+ void set_play_position(float p_pos);
+ void update_play_position();
+
+ void duplicate_selection();
+ void delete_selection();
+
+ AnimationBezierTrackEdit();
+};
+
+#endif // ANIMATION_BEZIER_EDITOR_H
diff --git a/editor/animation_editor.cpp b/editor/animation_editor.cpp
deleted file mode 100644
index f7c8cac93f..0000000000
--- a/editor/animation_editor.cpp
+++ /dev/null
@@ -1,4145 +0,0 @@
-/*************************************************************************/
-/* animation_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_editor.h"
-
-#include "editor/plugins/animation_player_editor_plugin.h"
-#include "editor_node.h"
-#include "editor_settings.h"
-#include "io/resource_saver.h"
-#include "os/keyboard.h"
-#include "os/os.h"
-#include "pair.h"
-#include "scene/gui/separator.h"
-#include "scene/main/viewport.h"
-
-/* Missing to fix:
-
- *Set
- *Find better source for hint for edited value keys
- * + button on track to add a key
- * when clicked for first time, erase selection of not selected at first
- * automatically create discrete/continuous tracks!!
- *when create track do undo/redo
-*/
-
-class AnimationCurveEdit : public Control {
- GDCLASS(AnimationCurveEdit, Control);
-
-public:
- enum Mode {
- MODE_DISABLED,
- MODE_SINGLE,
- MODE_MULTIPLE
- };
-
-private:
- Set<float> multiples;
- float transition;
- Mode mode;
-
- LineEdit *value_edit;
-
- void _notification(int p_what) {
-
- if (p_what == NOTIFICATION_DRAW) {
-
- RID ci = get_canvas_item();
-
- Size2 s = get_size();
- Rect2 r(Point2(), s);
-
- //r=r.grow(3);
- Ref<StyleBox> sb = get_stylebox("normal", "LineEdit");
- sb->draw(ci, r);
- r.size -= sb->get_minimum_size();
- r.position += sb->get_offset();
- //VisualServer::get_singleton()->canvas_item_add
-
- Ref<Font> f = get_font("font", "Label");
- r = r.grow(-2);
- Color color = get_color("font_color", "Label");
-
- int points = 48;
- if (mode == MODE_MULTIPLE) {
-
- Color mcolor = color;
- mcolor.a *= 0.3;
-
- Set<float>::Element *E = multiples.front();
- for (int j = 0; j < 16; j++) {
-
- if (!E)
- break;
-
- float prev = 1.0;
- float exp = E->get();
- bool flip = false; //hint_text=="attenuation";
-
- for (int i = 1; i <= points; i++) {
-
- float ifl = i / float(points);
- float iflp = (i - 1) / float(points);
-
- float h = 1.0 - Math::ease(ifl, exp);
-
- if (flip) {
- ifl = 1.0 - ifl;
- iflp = 1.0 - iflp;
- }
-
- VisualServer::get_singleton()->canvas_item_add_line(ci, r.position + Point2(iflp * r.size.width, prev * r.size.height), r.position + Point2(ifl * r.size.width, h * r.size.height), mcolor);
- prev = h;
- }
-
- E = E->next();
- }
- }
-
- float exp = transition;
- if (mode != MODE_DISABLED) {
-
- float prev = 1.0;
-
- bool flip = false; //hint_text=="attenuation";
-
- for (int i = 1; i <= points; i++) {
-
- float ifl = i / float(points);
- float iflp = (i - 1) / float(points);
-
- float h = 1.0 - Math::ease(ifl, exp);
-
- if (flip) {
- ifl = 1.0 - ifl;
- iflp = 1.0 - iflp;
- }
-
- VisualServer::get_singleton()->canvas_item_add_line(ci, r.position + Point2(iflp * r.size.width, prev * r.size.height), r.position + Point2(ifl * r.size.width, h * r.size.height), color);
- prev = h;
- }
- }
-
- if (mode == MODE_DISABLED) {
- f->draw(ci, Point2(5, 5 + f->get_ascent()), TTR("Disabled"), color);
- } else if (mode == MODE_MULTIPLE) {
- f->draw(ci, Point2(5, 5 + f->get_ascent() + value_edit->get_size().height), TTR("All Selection"), color);
- }
- }
- }
-
- void _gui_input(const Ref<InputEvent> &p_ev) {
-
- Ref<InputEventMouseMotion> mm = p_ev;
- if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
-
- if (mode == MODE_DISABLED)
- return;
-
- value_edit->release_focus();
-
- float rel = mm->get_relative().x;
- if (rel == 0)
- return;
-
- bool flip = false;
-
- if (flip)
- rel = -rel;
-
- float val = transition;
- if (val == 0)
- return;
- bool sg = val < 0;
- val = Math::absf(val);
-
- val = Math::log(val) / Math::log((float)2.0);
- //logspace
- val += rel * 0.05;
- //
-
- val = Math::pow((float)2.0, val);
- if (sg)
- val = -val;
-
- force_transition(val);
- }
- }
-
- void _edit_value_changed(const String &p_value_str) {
-
- force_transition(p_value_str.to_float());
- }
-
-public:
- static void _bind_methods() {
-
- //ClassDB::bind_method("_update_obj",&AnimationKeyEdit::_update_obj);
- ClassDB::bind_method("_gui_input", &AnimationCurveEdit::_gui_input);
- ClassDB::bind_method("_edit_value_changed", &AnimationCurveEdit::_edit_value_changed);
- ADD_SIGNAL(MethodInfo("transition_changed"));
- }
-
- void set_mode(Mode p_mode) {
-
- mode = p_mode;
- value_edit->set_visible(mode != MODE_DISABLED);
- update();
- }
-
- void clear_multiples() {
- multiples.clear();
- update();
- }
- void set_multiple(float p_transition) {
-
- multiples.insert(p_transition);
- }
-
- void set_transition(float p_transition) {
- transition = Math::stepify(p_transition, 0.01);
- value_edit->set_text(String::num(transition));
- update();
- }
-
- float get_transition() const {
- return transition;
- }
-
- void force_transition(float p_value) {
- if (mode == MODE_DISABLED)
- return;
- set_transition(p_value);
- emit_signal("transition_changed", p_value);
- }
-
- AnimationCurveEdit() {
-
- transition = 1.0;
- set_default_cursor_shape(CURSOR_HSPLIT);
- mode = MODE_DISABLED;
-
- value_edit = memnew(LineEdit);
- value_edit->hide();
- value_edit->connect("text_entered", this, "_edit_value_changed");
- add_child(value_edit);
- }
-};
-
-class AnimationKeyEdit : public Object {
-
- GDCLASS(AnimationKeyEdit, Object);
-
-public:
- bool setting;
- bool hidden;
-
- static void _bind_methods() {
-
- ClassDB::bind_method("_update_obj", &AnimationKeyEdit::_update_obj);
- ClassDB::bind_method("_key_ofs_changed", &AnimationKeyEdit::_key_ofs_changed);
- }
-
- //PopupDialog *ke_dialog;
-
- void _fix_node_path(Variant &value) {
-
- NodePath np = value;
-
- if (np == NodePath())
- return;
-
- Node *root = EditorNode::get_singleton()->get_tree()->get_root();
-
- Node *np_node = root->get_node(np);
- ERR_FAIL_COND(!np_node);
-
- Node *edited_node = root->get_node(base);
- ERR_FAIL_COND(!edited_node);
-
- value = edited_node->get_path_to(np_node);
- }
-
- void _update_obj(const Ref<Animation> &p_anim) {
- if (setting)
- return;
- if (hidden)
- return;
- if (!(animation == p_anim))
- return;
- notify_change();
- }
-
- void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) {
- if (hidden)
- return;
- if (!(animation == p_anim))
- return;
- if (from != key_ofs)
- return;
- key_ofs = to;
- if (setting)
- return;
- notify_change();
- }
-
- bool _set(const StringName &p_name, const Variant &p_value) {
-
- int key = animation->track_find_key(track, key_ofs, true);
- ERR_FAIL_COND_V(key == -1, false);
-
- String name = p_name;
- if (name == "time") {
-
- float new_time = p_value;
- if (new_time == key_ofs)
- return true;
-
- int existing = animation->track_find_key(track, new_time, true);
-
- setting = true;
- undo_redo->create_action(TTR("Anim Change Keyframe Time"), UndoRedo::MERGE_ENDS);
-
- Variant val = animation->track_get_key_value(track, key);
- float trans = animation->track_get_key_transition(track, key);
-
- undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key);
- undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans);
- undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, new_time);
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans);
- undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs);
-
- if (existing != -1) {
- Variant v = animation->track_get_key_value(track, existing);
- float trans = animation->track_get_key_transition(track, existing);
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, new_time, v, trans);
- }
-
- undo_redo->commit_action();
- setting = false;
-
- return true;
- } else if (name == "easing") {
-
- float val = p_value;
- float prev_val = animation->track_get_key_transition(track, key);
- setting = true;
- undo_redo->create_action(TTR("Anim Change Transition"), UndoRedo::MERGE_ENDS);
- undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", track, key, val);
- undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", track, key, prev_val);
- undo_redo->add_do_method(this, "_update_obj", animation);
- undo_redo->add_undo_method(this, "_update_obj", animation);
- undo_redo->commit_action();
- setting = false;
- return true;
- }
-
- switch (animation->track_get_type(track)) {
-
- case Animation::TYPE_TRANSFORM: {
-
- Dictionary d_old = animation->track_get_key_value(track, key);
- Dictionary d_new = d_old;
- d_new[p_name] = p_value;
- setting = true;
- undo_redo->create_action(TTR("Anim Change Transform"));
- undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
- undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
- undo_redo->add_do_method(this, "_update_obj", animation);
- undo_redo->add_undo_method(this, "_update_obj", animation);
- undo_redo->commit_action();
- setting = false;
- return true;
-
- } break;
- case Animation::TYPE_VALUE: {
-
- if (name == "value") {
-
- Variant value = p_value;
-
- if (value.get_type() == Variant::NODE_PATH) {
-
- _fix_node_path(value);
- }
-
- setting = true;
- undo_redo->create_action(TTR("Anim Change Keyframe Value"), UndoRedo::MERGE_ENDS);
- Variant prev = animation->track_get_key_value(track, key);
- undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, value);
- undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, prev);
- undo_redo->add_do_method(this, "_update_obj", animation);
- undo_redo->add_undo_method(this, "_update_obj", animation);
- undo_redo->commit_action();
- setting = false;
- return true;
- }
-
- } break;
- case Animation::TYPE_METHOD: {
-
- Dictionary d_old = animation->track_get_key_value(track, key);
- Dictionary d_new = d_old;
-
- bool change_notify_deserved = false;
- bool mergeable = false;
-
- if (name == "name") {
-
- d_new["method"] = p_value;
- }
-
- if (name == "arg_count") {
-
- Vector<Variant> args = d_old["args"];
- args.resize(p_value);
- d_new["args"] = args;
- change_notify_deserved = true;
- }
-
- if (name.begins_with("args/")) {
-
- Vector<Variant> args = d_old["args"];
- int idx = name.get_slice("/", 1).to_int();
- ERR_FAIL_INDEX_V(idx, args.size(), false);
-
- String what = name.get_slice("/", 2);
- if (what == "type") {
- Variant::Type t = Variant::Type(int(p_value));
-
- if (t != args[idx].get_type()) {
- Variant::CallError err;
- if (Variant::can_convert(args[idx].get_type(), t)) {
- Variant old = args[idx];
- Variant *ptrs[1] = { &old };
- args[idx] = Variant::construct(t, (const Variant **)ptrs, 1, err);
- } else {
-
- args[idx] = Variant::construct(t, NULL, 0, err);
- }
- change_notify_deserved = true;
- d_new["args"] = args;
- }
- }
- if (what == "value") {
-
- Variant value = p_value;
- if (value.get_type() == Variant::NODE_PATH) {
-
- _fix_node_path(value);
- }
-
- args[idx] = value;
- d_new["args"] = args;
- mergeable = true;
- }
- }
-
- if (mergeable)
- undo_redo->create_action(TTR("Anim Change Call"), UndoRedo::MERGE_ENDS);
- else
- undo_redo->create_action(TTR("Anim Change Call"));
-
- Variant prev = animation->track_get_key_value(track, key);
- setting = true;
- undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
- undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
- undo_redo->add_do_method(this, "_update_obj", animation);
- undo_redo->add_undo_method(this, "_update_obj", animation);
- undo_redo->commit_action();
- setting = false;
- if (change_notify_deserved)
- notify_change();
- return true;
- } break;
- }
-
- return false;
- }
-
- bool _get(const StringName &p_name, Variant &r_ret) const {
-
- int key = animation->track_find_key(track, key_ofs, true);
- ERR_FAIL_COND_V(key == -1, false);
-
- String name = p_name;
- if (name == "time") {
- r_ret = key_ofs;
- return true;
- } else if (name == "easing") {
- r_ret = animation->track_get_key_transition(track, key);
- return true;
- }
-
- switch (animation->track_get_type(track)) {
-
- case Animation::TYPE_TRANSFORM: {
-
- Dictionary d = animation->track_get_key_value(track, key);
- ERR_FAIL_COND_V(!d.has(name), false);
- r_ret = d[p_name];
- return true;
-
- } break;
- case Animation::TYPE_VALUE: {
-
- if (name == "value") {
- r_ret = animation->track_get_key_value(track, key);
- return true;
- }
-
- } break;
- case Animation::TYPE_METHOD: {
-
- Dictionary d = animation->track_get_key_value(track, key);
-
- if (name == "name") {
-
- ERR_FAIL_COND_V(!d.has("method"), false);
- r_ret = d["method"];
- return true;
- }
-
- ERR_FAIL_COND_V(!d.has("args"), false);
-
- Vector<Variant> args = d["args"];
-
- if (name == "arg_count") {
-
- r_ret = args.size();
- return true;
- }
-
- if (name.begins_with("args/")) {
-
- int idx = name.get_slice("/", 1).to_int();
- ERR_FAIL_INDEX_V(idx, args.size(), false);
-
- String what = name.get_slice("/", 2);
- if (what == "type") {
- r_ret = args[idx].get_type();
- return true;
- }
- if (what == "value") {
- r_ret = args[idx];
- return true;
- }
- }
-
- } break;
- }
-
- return false;
- }
- void _get_property_list(List<PropertyInfo> *p_list) const {
-
- if (animation.is_null())
- return;
-
- ERR_FAIL_INDEX(track, animation->get_track_count());
- int key = animation->track_find_key(track, key_ofs, true);
- ERR_FAIL_COND(key == -1);
-
- p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01"));
-
- switch (animation->track_get_type(track)) {
-
- case Animation::TYPE_TRANSFORM: {
-
- p_list->push_back(PropertyInfo(Variant::VECTOR3, "location"));
- p_list->push_back(PropertyInfo(Variant::QUAT, "rotation"));
- p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
-
- } break;
- case Animation::TYPE_VALUE: {
-
- Variant v = animation->track_get_key_value(track, key);
-
- if (hint.type != Variant::NIL) {
-
- PropertyInfo pi = hint;
- pi.name = "value";
- p_list->push_back(pi);
- } else {
-
- PropertyHint hint = PROPERTY_HINT_NONE;
- String hint_string;
-
- if (v.get_type() == Variant::OBJECT) {
- //could actually check the object property if exists..? yes i will!
- Ref<Resource> res = v;
- if (res.is_valid()) {
-
- hint = PROPERTY_HINT_RESOURCE_TYPE;
- hint_string = res->get_class();
- }
- }
-
- if (v.get_type() != Variant::NIL)
- p_list->push_back(PropertyInfo(v.get_type(), "value", hint, hint_string));
- }
-
- } break;
- case Animation::TYPE_METHOD: {
-
- p_list->push_back(PropertyInfo(Variant::STRING, "name"));
- p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,5,1"));
-
- Dictionary d = animation->track_get_key_value(track, key);
- ERR_FAIL_COND(!d.has("args"));
- Vector<Variant> args = d["args"];
- String vtypes;
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
-
- if (i > 0)
- vtypes += ",";
- vtypes += Variant::get_type_name(Variant::Type(i));
- }
-
- for (int i = 0; i < args.size(); i++) {
-
- p_list->push_back(PropertyInfo(Variant::INT, "args/" + itos(i) + "/type", PROPERTY_HINT_ENUM, vtypes));
- if (args[i].get_type() != Variant::NIL)
- p_list->push_back(PropertyInfo(args[i].get_type(), "args/" + itos(i) + "/value"));
- }
-
- } break;
- }
-
- /*
- if (animation->track_get_type(track)!=Animation::TYPE_METHOD)
- p_list->push_back( PropertyInfo( Variant::REAL, "easing", PROPERTY_HINT_EXP_EASING));
- */
- }
-
- UndoRedo *undo_redo;
- Ref<Animation> animation;
- int track;
- float key_ofs;
-
- PropertyInfo hint;
- NodePath base;
-
- void notify_change() {
-
- _change_notify();
- }
-
- AnimationKeyEdit() {
- hidden = true;
- key_ofs = 0;
- track = -1;
- setting = false;
- }
-};
-
-void AnimationKeyEditor::_menu_add_track(int p_type) {
-
- ERR_FAIL_COND(!animation.is_valid());
-
- switch (p_type) {
-
- case ADD_TRACK_MENU_ADD_CALL_TRACK: {
- if (root) {
- call_select->popup_centered_ratio();
- break;
- }
- } break;
- case ADD_TRACK_MENU_ADD_VALUE_TRACK:
- case ADD_TRACK_MENU_ADD_TRANSFORM_TRACK: {
-
- undo_redo->create_action(TTR("Anim Add Track"));
- undo_redo->add_do_method(animation.ptr(), "add_track", p_type);
- undo_redo->add_do_method(animation.ptr(), "track_set_path", animation->get_track_count(), ".");
- undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
- undo_redo->commit_action();
-
- } break;
- }
-}
-
-void AnimationKeyEditor::_anim_duplicate_keys(bool transpose) {
- //duplicait!
- if (selection.size() && animation.is_valid() && selected_track >= 0 && selected_track < animation->get_track_count()) {
-
- int top_track = 0x7FFFFFFF;
- float top_time = 1e10;
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- const SelectedKey &sk = E->key();
-
- float t = animation->track_get_key_time(sk.track, sk.key);
- if (t < top_time)
- top_time = t;
- if (sk.track < top_track)
- top_track = sk.track;
- }
- ERR_FAIL_COND(top_track == 0x7FFFFFFF || top_time == 1e10);
-
- //
-
- int start_track = transpose ? selected_track : top_track;
-
- undo_redo->create_action(TTR("Anim Duplicate Keys"));
-
- List<Pair<int, float> > new_selection_values;
-
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- const SelectedKey &sk = E->key();
-
- float t = animation->track_get_key_time(sk.track, sk.key);
-
- float dst_time = t + (timeline_pos - top_time);
- int dst_track = sk.track + (start_track - top_track);
-
- if (dst_track < 0 || dst_track >= animation->get_track_count())
- continue;
-
- if (animation->track_get_type(dst_track) != animation->track_get_type(sk.track))
- continue;
-
- int existing_idx = animation->track_find_key(dst_track, dst_time, true);
-
- undo_redo->add_do_method(animation.ptr(), "track_insert_key", dst_track, dst_time, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", dst_track, dst_time);
-
- Pair<int, float> p;
- p.first = dst_track;
- p.second = dst_time;
- new_selection_values.push_back(p);
-
- if (existing_idx != -1) {
-
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", dst_track, dst_time, animation->track_get_key_value(dst_track, existing_idx), animation->track_get_key_transition(dst_track, existing_idx));
- }
- }
-
- undo_redo->commit_action();
-
- //reselect duplicated
-
- Map<SelectedKey, KeyInfo> new_selection;
- for (List<Pair<int, float> >::Element *E = new_selection_values.front(); E; E = E->next()) {
-
- int track = E->get().first;
- float time = E->get().second;
-
- int existing_idx = animation->track_find_key(track, time, true);
-
- if (existing_idx == -1)
- continue;
- SelectedKey sk2;
- sk2.track = track;
- sk2.key = existing_idx;
-
- KeyInfo ki;
- ki.pos = time;
-
- new_selection[sk2] = ki;
- }
-
- selection = new_selection;
- track_editor->update();
- _edit_if_single_selection();
- }
-}
-
-void AnimationKeyEditor::_menu_track(int p_type) {
-
- ERR_FAIL_COND(!animation.is_valid());
-
- last_menu_track_opt = p_type;
- switch (p_type) {
-
- case TRACK_MENU_SCALE:
- case TRACK_MENU_SCALE_PIVOT: {
-
- scale_dialog->popup_centered(Size2(200, 100));
- } break;
- case TRACK_MENU_MOVE_UP: {
-
- int idx = selected_track;
- if (idx > 0 && idx < animation->get_track_count()) {
- undo_redo->create_action(TTR("Move Anim Track Up"));
- undo_redo->add_do_method(animation.ptr(), "track_move_down", idx);
- undo_redo->add_undo_method(animation.ptr(), "track_move_up", idx - 1);
- undo_redo->commit_action();
- selected_track = idx - 1;
- }
-
- } break;
- case TRACK_MENU_MOVE_DOWN: {
-
- int idx = selected_track;
- if (idx >= 0 && idx < animation->get_track_count() - 1) {
- undo_redo->create_action(TTR("Move Anim Track Down"));
- undo_redo->add_do_method(animation.ptr(), "track_move_up", idx);
- undo_redo->add_undo_method(animation.ptr(), "track_move_down", idx + 1);
- undo_redo->commit_action();
- selected_track = idx + 1;
- }
-
- } break;
- case TRACK_MENU_REMOVE: {
-
- int idx = selected_track;
- if (idx >= 0 && idx < animation->get_track_count()) {
- undo_redo->create_action(TTR("Remove Anim Track"));
- undo_redo->add_do_method(animation.ptr(), "remove_track", idx);
- undo_redo->add_undo_method(animation.ptr(), "add_track", animation->track_get_type(idx), idx);
- undo_redo->add_undo_method(animation.ptr(), "track_set_path", idx, animation->track_get_path(idx));
- //todo interpolation
- for (int i = 0; i < animation->track_get_key_count(idx); i++) {
-
- Variant v = animation->track_get_key_value(idx, i);
- float time = animation->track_get_key_time(idx, i);
- float trans = animation->track_get_key_transition(idx, i);
-
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", idx, time, v);
- undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", idx, i, trans);
- }
-
- undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_type", idx, animation->track_get_interpolation_type(idx));
- if (animation->track_get_type(idx) == Animation::TYPE_VALUE) {
- undo_redo->add_undo_method(animation.ptr(), "value_track_set_update_mode", idx, animation->value_track_get_update_mode(idx));
- }
-
- undo_redo->commit_action();
- }
-
- } break;
- case TRACK_MENU_DUPLICATE:
- case TRACK_MENU_DUPLICATE_TRANSPOSE: {
-
- _anim_duplicate_keys(p_type == TRACK_MENU_DUPLICATE_TRANSPOSE);
- } break;
- case TRACK_MENU_SET_ALL_TRANS_LINEAR:
- case TRACK_MENU_SET_ALL_TRANS_CONSTANT:
- case TRACK_MENU_SET_ALL_TRANS_OUT:
- case TRACK_MENU_SET_ALL_TRANS_IN:
- case TRACK_MENU_SET_ALL_TRANS_INOUT:
- case TRACK_MENU_SET_ALL_TRANS_OUTIN: {
-
- if (!selection.size() || !animation.is_valid())
- break;
-
- float t = 0;
- switch (p_type) {
- case TRACK_MENU_SET_ALL_TRANS_LINEAR: t = 1.0; break;
- case TRACK_MENU_SET_ALL_TRANS_CONSTANT: t = 0.0; break;
- case TRACK_MENU_SET_ALL_TRANS_OUT: t = 0.5; break;
- case TRACK_MENU_SET_ALL_TRANS_IN: t = 2.0; break;
- case TRACK_MENU_SET_ALL_TRANS_INOUT: t = -0.5; break;
- case TRACK_MENU_SET_ALL_TRANS_OUTIN: t = -2.0; break;
- }
-
- undo_redo->create_action(TTR("Set Transitions to:") + " " + rtos(t));
-
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- const SelectedKey &sk = E->key();
-
- undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", sk.track, sk.key, t);
- undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", sk.track, sk.key, animation->track_get_key_transition(sk.track, sk.key));
- }
-
- undo_redo->commit_action();
-
- } break;
- case TRACK_MENU_NEXT_STEP: {
-
- if (animation.is_null())
- break;
- float step = animation->get_step();
- if (step == 0)
- step = 1;
-
- float pos = timeline_pos;
-
- pos = Math::stepify(pos + step, step);
- if (pos > animation->get_length())
- pos = animation->get_length();
- timeline_pos = pos;
- track_pos->update();
- emit_signal("timeline_changed", pos, true);
-
- } break;
- case TRACK_MENU_PREV_STEP: {
- if (animation.is_null())
- break;
- float step = animation->get_step();
- if (step == 0)
- step = 1;
-
- float pos = timeline_pos;
- pos = Math::stepify(pos - step, step);
- if (pos < 0)
- pos = 0;
- timeline_pos = pos;
- track_pos->update();
- emit_signal("timeline_changed", pos, true);
-
- } break;
-
- case TRACK_MENU_OPTIMIZE: {
-
- optimize_dialog->popup_centered(Size2(250, 180));
- } break;
- case TRACK_MENU_CLEAN_UP: {
-
- cleanup_dialog->popup_centered_minsize(Size2(300, 0));
- } break;
- case TRACK_MENU_CLEAN_UP_CONFIRM: {
-
- if (cleanup_all->is_pressed()) {
- List<StringName> names;
- AnimationPlayerEditor::singleton->get_player()->get_animation_list(&names);
- for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
- _cleanup_animation(AnimationPlayerEditor::singleton->get_player()->get_animation(E->get()));
- }
- } else {
- _cleanup_animation(animation);
- }
- } break;
- case CURVE_SET_LINEAR: {
- curve_edit->force_transition(1.0);
-
- } break;
- case CURVE_SET_IN: {
-
- curve_edit->force_transition(4.0);
-
- } break;
- case CURVE_SET_OUT: {
-
- curve_edit->force_transition(0.25);
- } break;
- case CURVE_SET_INOUT: {
- curve_edit->force_transition(-4);
-
- } break;
- case CURVE_SET_OUTIN: {
-
- curve_edit->force_transition(-0.25);
- } break;
- case CURVE_SET_CONSTANT: {
-
- curve_edit->force_transition(0);
- } break;
- }
-}
-
-void AnimationKeyEditor::_cleanup_animation(Ref<Animation> p_animation) {
-
- for (int i = 0; i < p_animation->get_track_count(); i++) {
-
- bool prop_exists = false;
- Variant::Type valid_type = Variant::NIL;
- Object *obj = NULL;
-
- RES res;
- Vector<StringName> leftover_path;
-
- Node *node = root->get_node_and_resource(p_animation->track_get_path(i), res, leftover_path);
-
- if (res.is_valid()) {
- obj = res.ptr();
- } else if (node) {
- obj = node;
- }
-
- if (obj && p_animation->track_get_type(i) == Animation::TYPE_VALUE) {
- valid_type = obj->get_static_property_type_indexed(leftover_path, &prop_exists);
- }
-
- if (!obj && cleanup_tracks->is_pressed()) {
-
- p_animation->remove_track(i);
- i--;
- continue;
- }
-
- if (!prop_exists || p_animation->track_get_type(i) != Animation::TYPE_VALUE || cleanup_keys->is_pressed() == false)
- continue;
-
- for (int j = 0; j < p_animation->track_get_key_count(i); j++) {
-
- Variant v = p_animation->track_get_key_value(i, j);
-
- if (!Variant::can_convert(v.get_type(), valid_type)) {
- p_animation->track_remove_key(i, j);
- j--;
- }
- }
-
- if (p_animation->track_get_key_count(i) == 0 && cleanup_tracks->is_pressed()) {
- p_animation->remove_track(i);
- i--;
- }
- }
-
- undo_redo->clear_history();
- _update_paths();
-}
-
-void AnimationKeyEditor::_animation_optimize() {
-
- animation->optimize(optimize_linear_error->get_value(), optimize_angular_error->get_value(), optimize_max_angle->get_value());
- track_editor->update();
- undo_redo->clear_history();
-}
-
-float AnimationKeyEditor::_get_zoom_scale() const {
-
- float zv = zoom->get_value();
- if (zv < 1) {
- zv = 1.0 - zv;
- return Math::pow(1.0f + zv, 8.0f) * 100;
- } else {
- return 1.0 / Math::pow(zv, 8.0f) * 100;
- }
-}
-
-void AnimationKeyEditor::_track_position_draw() {
-
- if (!animation.is_valid()) {
- return;
- }
-
- Ref<StyleBox> style = get_stylebox("normal", "TextEdit");
- Size2 size = track_editor->get_size() - style->get_minimum_size();
- Size2 ofs = style->get_offset();
-
- int settings_limit = size.width - right_data_size_cache;
- int name_limit = settings_limit * name_column_ratio;
-
- float keys_from = h_scroll->get_value();
- float zoom_scale = _get_zoom_scale();
- float keys_to = keys_from + (settings_limit - name_limit) / zoom_scale;
-
- //will move to separate control! (for speedup)
- if (timeline_pos >= keys_from && timeline_pos < keys_to) {
- //draw position
- int pixel = (timeline_pos - h_scroll->get_value()) * zoom_scale;
- pixel += name_limit;
- track_pos->draw_line(ofs + Point2(pixel, 0), ofs + Point2(pixel, size.height), get_color("accent_color", "Editor"));
- }
-}
-
-void AnimationKeyEditor::_track_editor_draw() {
-
- if (animation.is_valid() && animation->get_track_count()) {
- if (selected_track < 0)
- selected_track = 0;
- else if (selected_track >= animation->get_track_count())
- selected_track = animation->get_track_count() - 1;
- }
-
- track_pos->update();
- Control *te = track_editor;
- Ref<StyleBox> style = get_stylebox("normal", "TextEdit");
- te->draw_style_box(style, Rect2(Point2(), track_editor->get_size()));
-
- if (te->has_focus()) {
- te->draw_style_box(get_stylebox("bg_focus", "Tree"), Rect2(Point2(), track_editor->get_size()));
- }
-
- if (!animation.is_valid()) {
- v_scroll->hide();
- h_scroll->hide();
- length->set_editable(false);
- step->set_editable(false);
- loop->set_disabled(true);
- menu_add_track->set_disabled(true);
- menu_track->set_disabled(true);
- edit_button->set_disabled(true);
- key_editor_tab->hide();
- move_up_button->set_disabled(true);
- move_down_button->set_disabled(true);
- remove_button->set_disabled(true);
-
- return;
- }
-
- length->set_editable(true);
- step->set_editable(true);
- loop->set_disabled(false);
- menu_add_track->set_disabled(false);
- menu_track->set_disabled(false);
- edit_button->set_disabled(false);
- move_up_button->set_disabled(false);
- move_down_button->set_disabled(false);
- remove_button->set_disabled(false);
- if (edit_button->is_pressed())
- key_editor_tab->show();
-
- te_drawing = true;
-
- Size2 size = te->get_size() - style->get_minimum_size();
- Size2 ofs = style->get_offset();
-
- Ref<Font> font = te->get_font("font", "Tree");
- int sep = get_constant("vseparation", "Tree");
- int hsep = get_constant("hseparation", "Tree");
- Color color = get_color("font_color", "Tree");
- Color sepcolor = color;
- sepcolor.a = 0.2;
- Color timecolor = color;
- timecolor.a = 0.2;
- Color hover_color = color;
- hover_color.a = 0.05;
- Color select_color = color;
- select_color.a = 0.1;
- Color invalid_path_color = get_color("error_color", "Editor");
- Color track_select_color = get_color("highlighted_font_color", "Editor");
-
- Ref<Texture> remove_icon = get_icon("Remove", "EditorIcons");
- Ref<Texture> move_up_icon = get_icon("MoveUp", "EditorIcons");
- Ref<Texture> move_down_icon = get_icon("MoveDown", "EditorIcons");
- Ref<Texture> remove_icon_hl = get_icon("RemoveHl", "EditorIcons");
- Ref<Texture> move_up_icon_hl = get_icon("MoveUpHl", "EditorIcons");
- Ref<Texture> move_down_icon_hl = get_icon("MoveDownHl", "EditorIcons");
- Ref<Texture> add_key_icon = get_icon("TrackAddKey", "EditorIcons");
- Ref<Texture> add_key_icon_hl = get_icon("TrackAddKeyHl", "EditorIcons");
- Ref<Texture> down_icon = get_icon("select_arrow", "Tree");
- Ref<Texture> checked = get_icon("checked", "Tree");
- Ref<Texture> unchecked = get_icon("unchecked", "Tree");
-
- Ref<Texture> wrap_icon[2] = {
- get_icon("InterpWrapClamp", "EditorIcons"),
- get_icon("InterpWrapLoop", "EditorIcons"),
- };
-
- Ref<Texture> interp_icon[3] = {
- get_icon("InterpRaw", "EditorIcons"),
- get_icon("InterpLinear", "EditorIcons"),
- get_icon("InterpCubic", "EditorIcons")
- };
- Ref<Texture> cont_icon[3] = {
- get_icon("TrackContinuous", "EditorIcons"),
- get_icon("TrackDiscrete", "EditorIcons"),
- get_icon("TrackTrigger", "EditorIcons")
- };
- Ref<Texture> type_icon[3] = {
- get_icon("KeyValue", "EditorIcons"),
- get_icon("KeyXform", "EditorIcons"),
- get_icon("KeyCall", "EditorIcons")
- };
-
- Ref<Texture> valid_icon = get_icon("KeyValid", "EditorIcons");
- Ref<Texture> invalid_icon = get_icon("KeyInvalid", "EditorIcons");
- const Color modulate_selected = Color(0x84 / 255.0, 0xc2 / 255.0, 0xff / 255.0);
-
- Ref<Texture> hsize_icon = get_icon("Hsize", "EditorIcons");
-
- int right_separator_ofs = right_data_size_cache;
-
- int h = font->get_height() + sep;
-
- int fit = (size.height / h) - 1;
- int total = animation->get_track_count();
- if (total < fit) {
- v_scroll->hide();
- v_scroll->set_max(total);
- v_scroll->set_page(fit);
- } else {
- v_scroll->show();
- v_scroll->set_max(total);
- v_scroll->set_page(fit);
- }
-
- int left_check_ofs = checked->get_width();
- int settings_limit = size.width - right_separator_ofs;
- int name_limit = settings_limit * name_column_ratio;
-
- Color linecolor = color;
- linecolor.a = 0.2;
- te->draw_line(ofs + Point2(name_limit, 0), ofs + Point2(name_limit, size.height), linecolor);
- te->draw_line(ofs + Point2(settings_limit, 0), ofs + Point2(settings_limit, size.height), linecolor);
- te->draw_texture(hsize_icon, ofs + Point2(name_limit - hsize_icon->get_width() - hsep, (h - hsize_icon->get_height()) / 2));
-
- te->draw_line(ofs + Point2(0, h), ofs + Point2(size.width, h), linecolor);
- // draw time
-
- float keys_from;
- float keys_to;
- float zoom_scale;
-
- {
-
- int zoomw = settings_limit - name_limit;
-
- float scale = _get_zoom_scale();
- zoom_scale = scale;
-
- float l = animation->get_length();
- if (l <= 0)
- l = 0.001; //avoid crashor
-
- int end_px = (l - h_scroll->get_value()) * scale;
- int begin_px = -h_scroll->get_value() * scale;
- Color notimecol = get_color("dark_color_2", "Editor");
-
- {
-
- te->draw_rect(Rect2(ofs + Point2(name_limit, 0), Point2(zoomw - 1, h)), notimecol);
-
- if (begin_px < zoomw && end_px > 0) {
-
- if (begin_px < 0)
- begin_px = 0;
- if (end_px > zoomw)
- end_px = zoomw;
-
- te->draw_rect(Rect2(ofs + Point2(name_limit + begin_px, 0), Point2(end_px - begin_px - 1, h)), timecolor);
- }
- }
-
- keys_from = h_scroll->get_value();
- keys_to = keys_from + zoomw / scale;
-
- {
- float time_min = 0;
- float time_max = animation->get_length();
- for (int i = 0; i < animation->get_track_count(); i++) {
-
- if (animation->track_get_key_count(i) > 0) {
-
- float beg = animation->track_get_key_time(i, 0);
- if (beg < time_min)
- time_min = beg;
- float end = animation->track_get_key_time(i, animation->track_get_key_count(i) - 1);
- if (end > time_max)
- time_max = end;
- }
- }
-
- float extra = (zoomw / scale) * 0.5;
-
- if (time_min < -0.001)
- time_min -= extra;
- time_max += extra;
- h_scroll->set_min(time_min);
- h_scroll->set_max(time_max);
-
- if (zoomw / scale < (time_max - time_min)) {
- h_scroll->show();
-
- } else {
-
- h_scroll->hide();
- }
- }
-
- h_scroll->set_page(zoomw / scale);
-
- Color color_time_sec = color;
- Color color_time_dec = color;
- color_time_dec.a *= 0.5;
-#define SC_ADJ 100
- int min = 30;
- int dec = 1;
- int step = 1;
- int decimals = 2;
- bool step_found = false;
-
- const int period_width = font->get_char_size('.').width;
- int max_digit_width = font->get_char_size('0').width;
- for (int i = 1; i <= 9; i++) {
- const int digit_width = font->get_char_size('0' + i).width;
- max_digit_width = MAX(digit_width, max_digit_width);
- }
- const int max_sc = int(Math::ceil(zoomw / scale));
- const int max_sc_width = String::num(max_sc).length() * max_digit_width;
-
- while (!step_found) {
-
- min = max_sc_width;
- if (decimals > 0)
- min += period_width + max_digit_width * decimals;
-
- static const int _multp[3] = { 1, 2, 5 };
- for (int i = 0; i < 3; i++) {
-
- step = (_multp[i] * dec);
- if (step * scale / SC_ADJ > min) {
- step_found = true;
- break;
- }
- }
- if (step_found)
- break;
- dec *= 10;
- decimals--;
- if (decimals < 0)
- decimals = 0;
- }
-
- for (int i = 0; i < zoomw; i++) {
-
- float pos = h_scroll->get_value() + double(i) / scale;
- float prev = h_scroll->get_value() + (double(i) - 1.0) / scale;
-
- int sc = int(Math::floor(pos * SC_ADJ));
- int prev_sc = int(Math::floor(prev * SC_ADJ));
- bool sub = (sc % SC_ADJ);
-
- if ((sc / step) != (prev_sc / step) || (prev_sc < 0 && sc >= 0)) {
-
- int scd = sc < 0 ? prev_sc : sc;
- te->draw_line(ofs + Point2(name_limit + i, 0), ofs + Point2(name_limit + i, h), linecolor);
- te->draw_string(font, ofs + Point2(name_limit + i + 3, (h - font->get_height()) / 2 + font->get_ascent()).floor(), String::num((scd - (scd % step)) / double(SC_ADJ), decimals), sub ? color_time_dec : color_time_sec, zoomw - i);
- }
- }
- }
-
- color.a *= 0.5;
-
- for (int i = 0; i < fit; i++) {
-
- //this code sucks, i always forget how it works
-
- int idx = v_scroll->get_value() + i;
- if (idx >= animation->get_track_count())
- break;
- int y = h + i * h + sep;
-
- bool prop_exists = false;
- Variant::Type valid_type = Variant::NIL;
- Object *obj = NULL;
-
- RES res;
- Vector<StringName> leftover_path;
-
- Node *node = root ? root->get_node_and_resource(animation->track_get_path(idx), res, leftover_path) : (Node *)NULL;
-
- if (res.is_valid()) {
- obj = res.ptr();
- } else if (node) {
- obj = node;
- }
-
- if (obj && animation->track_get_type(idx) == Animation::TYPE_VALUE) {
- // While leftover_path might be still empty, we wouldn't be able to get here anyway
- valid_type = obj->get_static_property_type_indexed(leftover_path, &prop_exists);
- }
-
- // Draw background color of the whole track
- if (/*mouse_over.over!=MouseOver::OVER_NONE &&*/ idx == mouse_over.track) {
- Color sepc = hover_color;
- te->draw_rect(Rect2(ofs + Point2(0, y), Size2(size.width, h - 1)), sepc);
- }
-
- if (selected_track == idx) {
- Color tc = select_color;
- //tc.a*=0.7;
- te->draw_rect(Rect2(ofs + Point2(0, y), Size2(size.width - 1, h - 1)), tc);
- }
-
- // Draw track enabled state check box
- Ref<Texture> check_box = animation->track_is_enabled(idx) ? checked : unchecked;
- te->draw_texture(check_box, ofs + Point2(0, y + (h - checked->get_height()) / 2).floor());
-
- // Draw track type glyph and node path
- te->draw_texture(type_icon[animation->track_get_type(idx)], ofs + Point2(left_check_ofs + sep, y + (h - type_icon[0]->get_height()) / 2).floor());
- NodePath np = animation->track_get_path(idx);
- Node *n = root ? root->get_node(np) : (Node *)NULL;
- Color ncol = color;
- if (n && editor_selection->is_selected(n))
- ncol = track_select_color;
- te->draw_string(font, Point2(ofs + Point2(left_check_ofs + sep + type_icon[0]->get_width() + sep, y + font->get_ascent() + (sep / 2))).floor(), np, ncol, name_limit - (left_check_ofs + sep) - (type_icon[0]->get_width() + sep) - 5);
-
- // Draw separator line below track area
- if (!obj)
- te->draw_line(ofs + Point2(0, y + h / 2), ofs + Point2(name_limit, y + h / 2), invalid_path_color);
-
- te->draw_line(ofs + Point2(0, y + h), ofs + Point2(size.width, y + h), sepcolor);
-
- Point2 icon_ofs = ofs + Point2(size.width, y + (h - remove_icon->get_height()) / 2).floor();
- icon_ofs.y += 4 * EDSCALE;
-
- /* icon_ofs.x-=remove_icon->get_width();
-
- te->draw_texture((mouse_over.over==MouseOver::OVER_REMOVE && mouse_over.track==idx)?remove_icon_hl:remove_icon,icon_ofs);
- icon_ofs.x-=hsep;
- icon_ofs.x-=move_down_icon->get_width();
- te->draw_texture((mouse_over.over==MouseOver::OVER_DOWN && mouse_over.track==idx)?move_down_icon_hl:move_down_icon,icon_ofs);
- icon_ofs.x-=hsep;
- icon_ofs.x-=move_up_icon->get_width();
- te->draw_texture((mouse_over.over==MouseOver::OVER_UP && mouse_over.track==idx)?move_up_icon_hl:move_up_icon,icon_ofs);
- icon_ofs.x-=hsep;
- te->draw_line(Point2(icon_ofs.x,ofs.y+y),Point2(icon_ofs.x,ofs.y+y+h),sepcolor);
-
- icon_ofs.x-=hsep;
- */
- track_ofs[0] = size.width - icon_ofs.x + ofs.x;
- icon_ofs.x -= down_icon->get_width();
- te->draw_texture(down_icon, icon_ofs - Size2(0, 4 * EDSCALE));
-
- int wrap_type = animation->track_get_interpolation_loop_wrap(idx) ? 1 : 0;
- icon_ofs.x -= hsep;
- icon_ofs.x -= wrap_icon[wrap_type]->get_width();
- te->draw_texture(wrap_icon[wrap_type], icon_ofs);
-
- icon_ofs.x -= hsep;
- te->draw_line(Point2(icon_ofs.x, ofs.y + y), Point2(icon_ofs.x, ofs.y + y + h), sepcolor);
-
- track_ofs[1] = size.width - icon_ofs.x + ofs.x;
-
- icon_ofs.x -= down_icon->get_width();
- te->draw_texture(down_icon, icon_ofs - Size2(0, 4 * EDSCALE));
-
- int interp_type = animation->track_get_interpolation_type(idx);
- ERR_CONTINUE(interp_type < 0 || interp_type >= 3);
- icon_ofs.x -= hsep;
- icon_ofs.x -= interp_icon[interp_type]->get_width();
- te->draw_texture(interp_icon[interp_type], icon_ofs);
-
- icon_ofs.x -= hsep;
- te->draw_line(Point2(icon_ofs.x, ofs.y + y), Point2(icon_ofs.x, ofs.y + y + h), sepcolor);
-
- track_ofs[2] = size.width - icon_ofs.x + ofs.x;
-
- if (animation->track_get_type(idx) == Animation::TYPE_VALUE) {
-
- int umode = animation->value_track_get_update_mode(idx);
-
- icon_ofs.x -= hsep;
- icon_ofs.x -= down_icon->get_width();
- te->draw_texture(down_icon, icon_ofs - Size2(0, 4 * EDSCALE));
-
- icon_ofs.x -= hsep;
- icon_ofs.x -= cont_icon[umode]->get_width();
- te->draw_texture(cont_icon[umode], icon_ofs);
- } else {
-
- icon_ofs.x -= hsep * 2 + cont_icon[0]->get_width() + down_icon->get_width();
- }
-
- icon_ofs.x -= hsep;
- te->draw_line(Point2(icon_ofs.x, ofs.y + y), Point2(icon_ofs.x, ofs.y + y + h), sepcolor);
-
- track_ofs[3] = size.width - icon_ofs.x + ofs.x;
-
- icon_ofs.x -= hsep;
- icon_ofs.x -= add_key_icon->get_width();
- te->draw_texture((mouse_over.over == MouseOver::OVER_ADD_KEY && mouse_over.track == idx) ? add_key_icon_hl : add_key_icon, icon_ofs);
- track_ofs[4] = size.width - icon_ofs.x + ofs.x;
-
- //draw the keys;
- int tt = animation->track_get_type(idx);
- float key_vofs = Math::floor((float)(h - type_icon[tt]->get_height()) / 2);
- float key_hofs = -Math::floor((float)type_icon[tt]->get_height() / 2);
-
- int kc = animation->track_get_key_count(idx);
- bool first = true;
-
- for (int i = 0; i < kc; i++) {
-
- float time = animation->track_get_key_time(idx, i);
- if (time < keys_from)
- continue;
- if (time > keys_to) {
-
- if (first && i > 0 && animation->track_get_key_value(idx, i) == animation->track_get_key_value(idx, i - 1)) {
- //draw whole line
- te->draw_line(ofs + Vector2(name_limit, y + h / 2), ofs + Point2(settings_limit, y + h / 2), color);
- }
-
- break;
- }
-
- float x = key_hofs + name_limit + (time - keys_from) * zoom_scale;
-
- Ref<Texture> tex = type_icon[tt];
- Color modulate = Color(1, 1, 1);
-
- bool is_hover = false;
- bool is_selected = false;
-
- SelectedKey sk;
- sk.key = i;
- sk.track = idx;
- if (selection.has(sk)) {
-
- if (click.click == ClickOver::CLICK_MOVE_KEYS)
- continue;
- is_selected = true;
- }
-
- if (mouse_over.over == MouseOver::OVER_KEY && mouse_over.track == idx && mouse_over.over_key == i)
- is_hover = true;
-
- Variant value = animation->track_get_key_value(idx, i);
-
- if (prop_exists && !Variant::can_convert(value.get_type(), valid_type)) {
-
- tex = invalid_icon;
- if (is_hover)
- modulate = Color(1.5, 1.5, 1.5);
- else
- modulate = Color(1, 1, 1);
- } else if (is_selected) {
-
- tex = valid_icon;
- modulate = modulate_selected;
- if (is_hover)
- modulate = modulate.lightened(0.2);
- } else if (is_hover) {
-
- tex = valid_icon;
- modulate = Color(1, 1, 1);
- }
-
- if (first && i > 0 && value == animation->track_get_key_value(idx, i - 1)) {
-
- te->draw_line(ofs + Vector2(name_limit, y + h / 2), ofs + Point2(x, y + h / 2), color);
- }
-
- if (i < kc - 1 && value == animation->track_get_key_value(idx, i + 1)) {
- float x_n = key_hofs + name_limit + (animation->track_get_key_time(idx, i + 1) - keys_from) * zoom_scale;
-
- x_n = MIN(x_n, settings_limit);
- te->draw_line(ofs + Point2(x_n, y + h / 2), ofs + Point2(x, y + h / 2), color);
- }
-
- te->draw_texture(tex, ofs + Point2(x, y + key_vofs).floor(), modulate);
-
- first = false;
- }
- }
-
- switch (click.click) {
- case ClickOver::CLICK_SELECT_KEYS: {
-
- Color box_color = get_color("accent_color", "Editor");
- box_color.a = 0.35;
- te->draw_rect(Rect2(click.at, click.to - click.at), box_color);
-
- } break;
- case ClickOver::CLICK_MOVE_KEYS: {
-
- float from_t = 1e20;
-
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
- float t = animation->track_get_key_time(E->key().track, E->key().key);
- if (t < from_t)
- from_t = t;
- }
-
- float motion = from_t + (click.to.x - click.at.x) / zoom_scale;
- if (step->get_value())
- motion = Math::stepify(motion, step->get_value());
-
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
-
- int idx = E->key().track;
- int i = idx - (int)v_scroll->get_value();
- if (i < 0 || i >= fit)
- continue;
- int y = h + i * h + sep;
-
- float key_vofs = Math::floor((float)(h - valid_icon->get_height()) / 2);
- float key_hofs = -Math::floor((float)valid_icon->get_height() / 2);
-
- float time = animation->track_get_key_time(idx, E->key().key);
- float diff = time - from_t;
-
- float t = motion + diff;
-
- float x = (t - keys_from) * zoom_scale;
- //x+=click.to.x - click.at.x;
- if (x < 0 || x >= (settings_limit - name_limit))
- continue;
-
- x += name_limit;
-
- te->draw_texture(valid_icon, ofs + Point2(x + key_hofs, y + key_vofs).floor(), modulate_selected);
- }
- } break;
- default: {};
- }
-
- te_drawing = false;
-}
-
-void AnimationKeyEditor::_track_name_changed(const String &p_name) {
-
- ERR_FAIL_COND(!animation.is_valid());
- undo_redo->create_action(TTR("Anim Track Rename"));
- undo_redo->add_do_method(animation.ptr(), "track_set_path", track_name_editing, p_name);
- undo_redo->add_undo_method(animation.ptr(), "track_set_path", track_name_editing, animation->track_get_path(track_name_editing));
- undo_redo->commit_action();
- track_name->hide();
-}
-
-void AnimationKeyEditor::_track_menu_selected(int p_idx) {
-
- ERR_FAIL_COND(!animation.is_valid());
-
- if (interp_editing != -1) {
-
- ERR_FAIL_INDEX(interp_editing, animation->get_track_count());
- undo_redo->create_action(TTR("Anim Track Change Interpolation"));
- undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_type", interp_editing, p_idx);
- undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_type", interp_editing, animation->track_get_interpolation_type(interp_editing));
- undo_redo->commit_action();
- } else if (cont_editing != -1) {
-
- ERR_FAIL_INDEX(cont_editing, animation->get_track_count());
-
- undo_redo->create_action(TTR("Anim Track Change Value Mode"));
- undo_redo->add_do_method(animation.ptr(), "value_track_set_update_mode", cont_editing, p_idx);
- undo_redo->add_undo_method(animation.ptr(), "value_track_set_update_mode", cont_editing, animation->value_track_get_update_mode(cont_editing));
- undo_redo->commit_action();
- } else if (wrap_editing != -1) {
-
- ERR_FAIL_INDEX(wrap_editing, animation->get_track_count());
-
- undo_redo->create_action(TTR("Anim Track Change Wrap Mode"));
- undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_loop_wrap", wrap_editing, p_idx ? true : false);
- undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_loop_wrap", wrap_editing, animation->track_get_interpolation_loop_wrap(wrap_editing));
- undo_redo->commit_action();
- } else {
- switch (p_idx) {
-
- case RIGHT_MENU_DUPLICATE:
- _anim_duplicate_keys();
- break;
- case RIGHT_MENU_DUPLICATE_TRANSPOSE:
- _anim_duplicate_keys(true);
- break;
- case RIGHT_MENU_REMOVE:
- _anim_delete_keys();
- break;
- }
- }
-}
-
-struct _AnimMoveRestore {
-
- int track;
- float time;
- Variant key;
- float transition;
-};
-
-void AnimationKeyEditor::_clear_selection_for_anim(const Ref<Animation> &p_anim) {
-
- if (!(animation == p_anim))
- return;
- //selection.clear();
- _clear_selection();
-}
-
-void AnimationKeyEditor::_select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos) {
-
- if (!(animation == p_anim))
- return;
-
- int idx = animation->track_find_key(p_track, p_pos, true);
- ERR_FAIL_COND(idx < 0);
-
- SelectedKey sk;
- sk.track = p_track;
- sk.key = idx;
- KeyInfo ki;
- ki.pos = p_pos;
-
- selection.insert(sk, ki);
-}
-
-PropertyInfo AnimationKeyEditor::_find_hint_for_track(int p_idx, NodePath &r_base_path) {
-
- r_base_path = NodePath();
- ERR_FAIL_COND_V(!animation.is_valid(), PropertyInfo());
- ERR_FAIL_INDEX_V(p_idx, animation->get_track_count(), PropertyInfo());
-
- if (!root)
- return PropertyInfo();
-
- NodePath path = animation->track_get_path(p_idx);
-
- if (!root->has_node_and_resource(path))
- return PropertyInfo();
-
- RES res;
- Vector<StringName> leftover_path;
- Node *node = root->get_node_and_resource(path, res, leftover_path, true);
-
- if (node) {
- r_base_path = node->get_path();
- }
-
- if (leftover_path.empty())
- return PropertyInfo();
-
- Variant property_info_base;
- if (res.is_valid())
- property_info_base = res;
- else if (node)
- property_info_base = node;
-
- for (int i = 0; i < leftover_path.size() - 1; i++) {
- property_info_base = property_info_base.get_named(leftover_path[i]);
- }
-
- List<PropertyInfo> pinfo;
- property_info_base.get_property_list(&pinfo);
-
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
-
- if (E->get().name == leftover_path[leftover_path.size() - 1]) {
- return E->get();
- }
- }
-
- return PropertyInfo();
-}
-
-void AnimationKeyEditor::_curve_transition_changed(float p_what) {
-
- if (selection.size() == 0)
- return;
- if (selection.size() == 1)
- undo_redo->create_action(TTR("Edit Node Curve"), UndoRedo::MERGE_ENDS);
- else
- undo_redo->create_action(TTR("Edit Selection Curve"), UndoRedo::MERGE_ENDS);
-
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
-
- int track = E->key().track;
- int key = E->key().key;
- float prev_val = animation->track_get_key_transition(track, key);
- undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", track, key, p_what);
- undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", track, key, prev_val);
- }
-
- undo_redo->commit_action();
-}
-
-void AnimationKeyEditor::_toggle_edit_curves() {
-
- if (edit_button->is_pressed())
- key_editor_tab->show();
- else
- key_editor_tab->hide();
-}
-
-bool AnimationKeyEditor::_edit_if_single_selection() {
-
- if (selection.size() != 1) {
-
- if (selection.size() == 0) {
- curve_edit->set_mode(AnimationCurveEdit::MODE_DISABLED);
- //print_line("disable");
- } else {
-
- curve_edit->set_mode(AnimationCurveEdit::MODE_MULTIPLE);
- curve_edit->set_transition(1.0);
- curve_edit->clear_multiples();
- //add all
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
-
- curve_edit->set_multiple(animation->track_get_key_transition(E->key().track, E->key().key));
- }
- //print_line("multiple");
- }
- return false;
- }
- curve_edit->set_mode(AnimationCurveEdit::MODE_SINGLE);
- //print_line("regular");
-
- int idx = selection.front()->key().track;
- int key = selection.front()->key().key;
- {
-
- key_edit->animation = animation;
- key_edit->track = idx;
- key_edit->key_ofs = animation->track_get_key_time(idx, key);
- key_edit->hint = _find_hint_for_track(idx, key_edit->base);
- key_edit->notify_change();
-
- curve_edit->set_transition(animation->track_get_key_transition(idx, key));
-
- /*key_edit_dialog->set_size( Size2( 200,200) );
- key_edit_dialog->set_position( track_editor->get_global_position() + ofs + mpos +Point2(-100,20));
- key_edit_dialog->popup();*/
- }
-
- return true;
-}
-
-void AnimationKeyEditor::_anim_delete_keys() {
- if (selection.size()) {
- undo_redo->create_action(TTR("Anim Delete Keys"));
-
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
- }
- undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
- undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
- undo_redo->commit_action();
- //selection.clear();
- accept_event();
- _edit_if_single_selection();
- }
-}
-
-void AnimationKeyEditor::_track_editor_gui_input(const Ref<InputEvent> &p_input) {
-
- Control *te = track_editor;
- Ref<StyleBox> style = get_stylebox("normal", "TextEdit");
-
- if (!animation.is_valid()) {
- return;
- }
-
- Size2 size = te->get_size() - style->get_minimum_size();
- Size2 ofs = style->get_offset();
-
- Ref<Font> font = te->get_font("font", "Tree");
- int sep = get_constant("vseparation", "Tree");
- int hsep = get_constant("hseparation", "Tree");
- Ref<Texture> remove_icon = get_icon("Remove", "EditorIcons");
- Ref<Texture> move_up_icon = get_icon("MoveUp", "EditorIcons");
- Ref<Texture> move_down_icon = get_icon("MoveDown", "EditorIcons");
- Ref<Texture> down_icon = get_icon("select_arrow", "Tree");
- Ref<Texture> hsize_icon = get_icon("Hsize", "EditorIcons");
- Ref<Texture> add_key_icon = get_icon("TrackAddKey", "EditorIcons");
- Ref<Texture> check_icon = get_icon("checked", "Tree");
-
- Ref<Texture> wrap_icon[2] = {
- get_icon("InterpWrapClamp", "EditorIcons"),
- get_icon("InterpWrapLoop", "EditorIcons"),
- };
- Ref<Texture> interp_icon[3] = {
- get_icon("InterpRaw", "EditorIcons"),
- get_icon("InterpLinear", "EditorIcons"),
- get_icon("InterpCubic", "EditorIcons")
- };
- Ref<Texture> cont_icon[3] = {
- get_icon("TrackContinuous", "EditorIcons"),
- get_icon("TrackDiscrete", "EditorIcons"),
- get_icon("TrackTrigger", "EditorIcons")
- };
- Ref<Texture> type_icon[3] = {
- get_icon("KeyValue", "EditorIcons"),
- get_icon("KeyXform", "EditorIcons"),
- get_icon("KeyCall", "EditorIcons")
- };
- int right_separator_ofs = right_data_size_cache;
-
- int h = font->get_height() + sep;
-
- int fit = (size.height / h) - 1;
- int total = animation->get_track_count();
- if (total < fit) {
- v_scroll->hide();
- } else {
- v_scroll->show();
- v_scroll->set_max(total);
- v_scroll->set_page(fit);
- }
-
- int left_check_ofs = check_icon->get_width();
- int settings_limit = size.width - right_separator_ofs;
- int name_limit = settings_limit * name_column_ratio;
-
- Ref<InputEventKey> key = p_input;
- if (key.is_valid()) {
-
- if (key->get_scancode() == KEY_D && key->is_pressed() && key->get_command()) {
-
- if (key->get_shift())
- _menu_track(TRACK_MENU_DUPLICATE_TRANSPOSE);
- else
- _menu_track(TRACK_MENU_DUPLICATE);
-
- accept_event();
-
- } else if (key->get_scancode() == KEY_DELETE && key->is_pressed() && click.click == ClickOver::CLICK_NONE) {
-
- _anim_delete_keys();
- } else if (animation.is_valid() && animation->get_track_count() > 0) {
-
- if (key->is_pressed() && (key->is_action("ui_up") || key->is_action("ui_page_up"))) {
-
- if (key->is_action("ui_up"))
- selected_track--;
- if (v_scroll->is_visible_in_tree() && key->is_action("ui_page_up"))
- selected_track--;
-
- if (selected_track < 0)
- selected_track = 0;
-
- if (v_scroll->is_visible_in_tree()) {
- if (v_scroll->get_value() > selected_track)
- v_scroll->set_value(selected_track);
- }
-
- track_editor->update();
- accept_event();
- }
-
- if (key->is_pressed() && (key->is_action("ui_down") || key->is_action("ui_page_down"))) {
-
- if (key->is_action("ui_down"))
- selected_track++;
- else if (v_scroll->is_visible_in_tree() && key->is_action("ui_page_down"))
- selected_track += v_scroll->get_page();
-
- if (selected_track >= animation->get_track_count())
- selected_track = animation->get_track_count() - 1;
-
- if (v_scroll->is_visible_in_tree() && v_scroll->get_page() + v_scroll->get_value() < selected_track + 1) {
- v_scroll->set_value(selected_track - v_scroll->get_page() + 1);
- }
-
- track_editor->update();
- accept_event();
- }
- }
- }
-
- Ref<InputEventMouseButton> mb = p_input;
-
- if (mb.is_valid()) {
-
- if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) {
-
- if (mb->get_command()) {
-
- zoom->set_value(zoom->get_value() + zoom->get_step());
- } else {
-
- v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() * mb->get_factor() / 8);
- }
- }
-
- if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed()) {
-
- if (mb->get_command()) {
-
- zoom->set_value(zoom->get_value() - zoom->get_step());
- } else {
-
- v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * mb->get_factor() / 8);
- }
- }
-
- if (mb->get_button_index() == BUTTON_WHEEL_RIGHT && mb->is_pressed()) {
-
- h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() * mb->get_factor() / 8);
- }
-
- if (mb->get_button_index() == BUTTON_WHEEL_LEFT && mb->is_pressed()) {
-
- v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * mb->get_factor() / 8);
- }
-
- if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) {
-
- Point2 mpos = mb->get_position() - ofs;
-
- if (selection.size() == 0) {
- // Auto-select on right-click if nothing is selected
- // Note: This code is pretty much duplicated from the left click code,
- // both codes could be moved into a function to avoid the duplicated code.
- Point2 mpos = mb->get_position() - ofs;
-
- if (mpos.y < h) {
- return;
- }
-
- mpos.y -= h;
-
- int idx = mpos.y / h;
- idx += v_scroll->get_value();
- if (idx < 0 || idx >= animation->get_track_count())
- return;
-
- if (mpos.x < name_limit) {
- } else if (mpos.x < settings_limit) {
- float pos = mpos.x - name_limit;
- pos /= _get_zoom_scale();
- pos += h_scroll->get_value();
- float w_time = (type_icon[0]->get_width() / _get_zoom_scale()) / 2.0;
-
- int kidx = animation->track_find_key(idx, pos);
- int kidx_n = kidx + 1;
- int key = -1;
-
- if (kidx >= 0 && kidx < animation->track_get_key_count(idx)) {
-
- float kpos = animation->track_get_key_time(idx, kidx);
- if (ABS(pos - kpos) <= w_time) {
-
- key = kidx;
- }
- }
-
- if (key == -1 && kidx_n >= 0 && kidx_n < animation->track_get_key_count(idx)) {
-
- float kpos = animation->track_get_key_time(idx, kidx_n);
- if (ABS(pos - kpos) <= w_time) {
-
- key = kidx_n;
- }
- }
-
- if (key == -1) {
-
- click.click = ClickOver::CLICK_SELECT_KEYS;
- click.at = mb->get_position();
- click.to = click.at;
- click.shift = mb->get_shift();
- selected_track = idx;
- track_editor->update();
- //drag select region
- return;
- }
-
- SelectedKey sk;
- sk.track = idx;
- sk.key = key;
- KeyInfo ki;
- ki.pos = animation->track_get_key_time(idx, key);
- click.shift = mb->get_shift();
- click.selk = sk;
-
- if (!mb->get_shift() && !selection.has(sk))
- _clear_selection();
-
- selection.insert(sk, ki);
-
- click.click = ClickOver::CLICK_MOVE_KEYS;
- click.at = mb->get_position();
- click.to = click.at;
- update();
- selected_track = idx;
- track_editor->update();
-
- if (_edit_if_single_selection() && mb->get_command()) {
- edit_button->set_pressed(true);
- key_editor_tab->show();
- }
- }
- }
-
- if (selection.size()) {
- // User has right clicked and we have a selection, show a popup menu with options
- track_menu->clear();
- track_menu->set_size(Point2(1, 1));
- track_menu->add_item(TTR("Duplicate Selection"), RIGHT_MENU_DUPLICATE);
- track_menu->add_item(TTR("Duplicate Transposed"), RIGHT_MENU_DUPLICATE_TRANSPOSE);
- track_menu->add_item(TTR("Remove Selection"), RIGHT_MENU_REMOVE);
-
- track_menu->set_position(te->get_global_position() + mpos);
-
- interp_editing = -1;
- cont_editing = -1;
- wrap_editing = -1;
-
- track_menu->popup();
- }
- }
-
- if (mb->get_button_index() == BUTTON_LEFT && !(mb->get_button_mask() & ~BUTTON_MASK_LEFT)) {
-
- if (mb->is_pressed()) {
-
- Point2 mpos = mb->get_position() - ofs;
-
- if (mpos.y < h) {
-
- if (mpos.x < name_limit && mpos.x > (name_limit - hsep - hsize_icon->get_width())) {
-
- click.click = ClickOver::CLICK_RESIZE_NAMES;
- click.at = mb->get_position();
- click.to = click.at;
- click.at.y = name_limit;
- }
-
- if (mpos.x >= name_limit && mpos.x < settings_limit) {
- //seek
- //int zoomw = settings_limit-name_limit;
- float scale = _get_zoom_scale();
- float pos = h_scroll->get_value() + (mpos.x - name_limit) / scale;
- if (animation->get_step())
- pos = Math::stepify(pos, animation->get_step());
-
- if (pos < 0)
- pos = 0;
- if (pos >= animation->get_length())
- pos = animation->get_length();
- timeline_pos = pos;
- click.click = ClickOver::CLICK_DRAG_TIMELINE;
- click.at = mb->get_position();
- click.to = click.at;
- emit_signal("timeline_changed", pos, false);
- }
-
- return;
- }
-
- mpos.y -= h;
-
- int idx = mpos.y / h;
- idx += v_scroll->get_value();
- if (idx < 0)
- return;
-
- if (idx >= animation->get_track_count()) {
-
- if (mpos.x >= name_limit && mpos.x < settings_limit) {
-
- click.click = ClickOver::CLICK_SELECT_KEYS;
- click.at = mb->get_position();
- click.to = click.at;
- //drag select region
- }
-
- return;
- }
-
- if (mpos.x < left_check_ofs) {
- // Checkbox on the very left to enable/disable tracks.
-
- animation->track_set_enabled(idx, !animation->track_is_enabled(idx));
-
- } else if (mpos.x < name_limit - (type_icon[0]->get_width() / 2.0)) {
- //name column
-
- // area
- if (idx != selected_track) {
-
- selected_track = idx;
- track_editor->update();
- return;
- }
-
- Rect2 area(ofs.x + left_check_ofs + sep, ofs.y + ((int(mpos.y) / h) + 1) * h, name_limit - left_check_ofs - sep, h);
- track_name->set_text(animation->track_get_path(idx));
- track_name->set_position(te->get_global_position() + area.position);
- track_name->set_size(area.size);
- track_name->show_modal();
- track_name->grab_focus();
- track_name->select_all();
- track_name_editing = idx;
-
- } else if (mpos.x < settings_limit) {
-
- float pos = mpos.x - name_limit;
- pos /= _get_zoom_scale();
- pos += h_scroll->get_value();
- float w_time = (type_icon[0]->get_width() / _get_zoom_scale()) / 2.0;
-
- int kidx = animation->track_find_key(idx, pos);
- int kidx_n = kidx + 1;
- int key = -1;
-
- if (kidx >= 0 && kidx < animation->track_get_key_count(idx)) {
-
- float kpos = animation->track_get_key_time(idx, kidx);
- if (ABS(pos - kpos) <= w_time) {
-
- key = kidx;
- }
- }
-
- if (key == -1 && kidx_n >= 0 && kidx_n < animation->track_get_key_count(idx)) {
-
- float kpos = animation->track_get_key_time(idx, kidx_n);
- if (ABS(pos - kpos) <= w_time) {
-
- key = kidx_n;
- }
- }
-
- if (key == -1) {
-
- click.click = ClickOver::CLICK_SELECT_KEYS;
- click.at = mb->get_position();
- click.to = click.at;
- click.shift = mb->get_shift();
- selected_track = idx;
- track_editor->update();
- //drag select region
- return;
- }
-
- SelectedKey sk;
- sk.track = idx;
- sk.key = key;
- KeyInfo ki;
- ki.pos = animation->track_get_key_time(idx, key);
- click.shift = mb->get_shift();
- click.selk = sk;
-
- if (!mb->get_shift() && !selection.has(sk))
- _clear_selection();
-
- selection.insert(sk, ki);
-
- click.click = ClickOver::CLICK_MOVE_KEYS;
- click.at = mb->get_position();
- click.to = click.at;
- update();
- selected_track = idx;
- track_editor->update();
-
- if (_edit_if_single_selection() && mb->get_command()) {
- edit_button->set_pressed(true);
- key_editor_tab->show();
- }
- } else {
- //button column
- int ofsx = size.width - mpos.x;
- if (ofsx < 0)
- return;
- /*
- if (ofsx < remove_icon->get_width()) {
-
- undo_redo->create_action("Remove Anim Track");
- undo_redo->add_do_method(animation.ptr(),"remove_track",idx);
- undo_redo->add_undo_method(animation.ptr(),"add_track",animation->track_get_type(idx),idx);
- undo_redo->add_undo_method(animation.ptr(),"track_set_path",idx,animation->track_get_path(idx));
- //todo interpolation
- for(int i=0;i<animation->track_get_key_count(idx);i++) {
-
- Variant v = animation->track_get_key_value(idx,i);
- float time = animation->track_get_key_time(idx,i);
- float trans = animation->track_get_key_transition(idx,i);
-
- undo_redo->add_undo_method(animation.ptr(),"track_insert_key",idx,time,v);
- undo_redo->add_undo_method(animation.ptr(),"track_set_key_transition",idx,i,trans);
-
- }
-
- undo_redo->add_undo_method(animation.ptr(),"track_set_interpolation_type",idx,animation->track_get_interpolation_type(idx));
- if (animation->track_get_type(idx)==Animation::TYPE_VALUE) {
- undo_redo->add_undo_method(animation.ptr(),"value_track_set_continuous",idx,animation->value_track_is_continuous(idx));
-
- }
-
- undo_redo->commit_action();
-
-
- return;
- }
-
- ofsx-=hsep+remove_icon->get_width();
-
- if (ofsx < move_down_icon->get_width()) {
-
- if (idx < animation->get_track_count() -1) {
- undo_redo->create_action("Move Anim Track Down");
- undo_redo->add_do_method(animation.ptr(),"track_move_up",idx);
- undo_redo->add_undo_method(animation.ptr(),"track_move_down",idx+1);
- undo_redo->commit_action();
- }
- return;
- }
-
- ofsx-=hsep+move_down_icon->get_width();
-
- if (ofsx < move_up_icon->get_width()) {
-
- if (idx >0) {
- undo_redo->create_action("Move Anim Track Up");
- undo_redo->add_do_method(animation.ptr(),"track_move_down",idx);
- undo_redo->add_undo_method(animation.ptr(),"track_move_up",idx-1);
- undo_redo->commit_action();
- }
- return;
- }
-
-
- ofsx-=hsep*3+move_up_icon->get_width();
- */
-
- if (ofsx < track_ofs[1]) {
-
- track_menu->clear();
- track_menu->set_size(Point2(1, 1));
- static const char *interp_name[2] = { "Clamp Loop Interp", "Wrap Loop Interp" };
- for (int i = 0; i < 2; i++) {
- track_menu->add_icon_item(wrap_icon[i], interp_name[i]);
- }
-
- int popup_y = ofs.y + ((int(mpos.y) / h) + 2) * h;
- int popup_x = size.width - track_ofs[1];
-
- track_menu->set_position(te->get_global_position() + Point2(popup_x, popup_y));
-
- wrap_editing = idx;
- interp_editing = -1;
- cont_editing = -1;
-
- track_menu->popup();
-
- return;
- }
-
- if (ofsx < track_ofs[2]) {
-
- track_menu->clear();
- track_menu->set_size(Point2(1, 1));
- static const char *interp_name[3] = { "Nearest", "Linear", "Cubic" };
- for (int i = 0; i < 3; i++) {
- track_menu->add_icon_item(interp_icon[i], interp_name[i]);
- }
-
- int popup_y = ofs.y + ((int(mpos.y) / h) + 2) * h;
- int popup_x = size.width - track_ofs[2];
-
- track_menu->set_position(te->get_global_position() + Point2(popup_x, popup_y));
-
- interp_editing = idx;
- cont_editing = -1;
- wrap_editing = -1;
-
- track_menu->popup();
-
- return;
- }
-
- if (ofsx < track_ofs[3]) {
-
- track_menu->clear();
- track_menu->set_size(Point2(1, 1));
- String cont_name[3] = { TTR("Continuous"), TTR("Discrete"), TTR("Trigger") };
- for (int i = 0; i < 3; i++) {
- track_menu->add_icon_item(cont_icon[i], cont_name[i]);
- }
-
- int popup_y = ofs.y + ((int(mpos.y) / h) + 2) * h;
- int popup_x = size.width - track_ofs[3];
-
- track_menu->set_position(te->get_global_position() + Point2(popup_x, popup_y));
-
- interp_editing = -1;
- wrap_editing = -1;
- cont_editing = idx;
-
- track_menu->popup();
-
- return;
- }
-
- if (ofsx < track_ofs[4]) {
-
- Animation::TrackType tt = animation->track_get_type(idx);
-
- float pos = timeline_pos;
- int existing = animation->track_find_key(idx, pos, true);
-
- Variant newval;
-
- if (tt == Animation::TYPE_TRANSFORM) {
- Dictionary d;
- d["location"] = Vector3();
- d["rotation"] = Quat();
- d["scale"] = Vector3();
- newval = d;
-
- } else if (tt == Animation::TYPE_METHOD) {
-
- Dictionary d;
- d["method"] = "";
- d["args"] = Vector<Variant>();
-
- newval = d;
- } else if (tt == Animation::TYPE_VALUE) {
-
- NodePath np;
- PropertyInfo inf = _find_hint_for_track(idx, np);
- if (inf.type != Variant::NIL) {
-
- Variant::CallError err;
- newval = Variant::construct(inf.type, NULL, 0, err);
- }
-
- if (newval.get_type() == Variant::NIL) {
- //popup a new type
- cvi_track = idx;
- cvi_pos = pos;
-
- type_menu->set_position(get_global_position() + mpos + ofs);
- type_menu->popup();
- return;
- }
- }
-
- undo_redo->create_action(TTR("Anim Add Key"));
-
- undo_redo->add_do_method(animation.ptr(), "track_insert_key", idx, pos, newval, 1);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", idx, pos);
-
- if (existing != -1) {
- Variant v = animation->track_get_key_value(idx, existing);
- float trans = animation->track_get_key_transition(idx, existing);
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", idx, pos, v, trans);
- }
-
- undo_redo->commit_action();
-
- return;
- }
- }
-
- } else {
-
- switch (click.click) {
- case ClickOver::CLICK_SELECT_KEYS: {
-
- float zoom_scale = _get_zoom_scale();
- float keys_from = h_scroll->get_value();
- float keys_to = keys_from + (settings_limit - name_limit) / zoom_scale;
-
- float from_time = keys_from + (click.at.x - (name_limit + ofs.x)) / zoom_scale;
- float to_time = keys_from + (click.to.x - (name_limit + ofs.x)) / zoom_scale;
-
- if (to_time < from_time)
- SWAP(from_time, to_time);
-
- if (from_time > keys_to || to_time < keys_from)
- break;
-
- if (from_time < keys_from)
- from_time = keys_from;
-
- if (to_time >= keys_to)
- to_time = keys_to;
-
- int from_track = int(click.at.y - ofs.y - h - sep) / h + v_scroll->get_value();
- int to_track = int(click.to.y - ofs.y - h - sep) / h + v_scroll->get_value();
- int from_mod = int(click.at.y - ofs.y - sep) % h;
- int to_mod = int(click.to.y - ofs.y - sep) % h;
-
- if (to_track < from_track) {
-
- SWAP(from_track, to_track);
- SWAP(from_mod, to_mod);
- }
-
- if ((from_mod > (h / 2)) && ((click.at.y - ofs.y) >= (h + sep))) {
- from_track++;
- }
-
- if (to_mod < h / 2) {
- to_track--;
- }
-
- if (from_track > to_track) {
- if (!click.shift)
- _clear_selection();
- _edit_if_single_selection();
- break;
- }
-
- int tracks_from = v_scroll->get_value();
- int tracks_to = v_scroll->get_value() + fit - 1;
- if (tracks_to >= animation->get_track_count())
- tracks_to = animation->get_track_count() - 1;
-
- tracks_from = 0;
- tracks_to = animation->get_track_count() - 1;
- if (to_track > tracks_to)
- to_track = tracks_to;
- if (from_track < tracks_from)
- from_track = tracks_from;
-
- if (from_track > tracks_to || to_track < tracks_from) {
- if (!click.shift)
- _clear_selection();
- _edit_if_single_selection();
- break;
- }
-
- if (!click.shift)
- _clear_selection();
-
- int higher_track = 0x7FFFFFFF;
- for (int i = from_track; i <= to_track; i++) {
-
- int kc = animation->track_get_key_count(i);
- for (int j = 0; j < kc; j++) {
-
- float t = animation->track_get_key_time(i, j);
- if (t < from_time)
- continue;
- if (t > to_time)
- break;
-
- if (i < higher_track)
- higher_track = i;
-
- SelectedKey sk;
- sk.track = i;
- sk.key = j;
- KeyInfo ki;
- ki.pos = t;
- selection[sk] = ki;
- }
- }
-
- if (higher_track != 0x7FFFFFFF) {
- selected_track = higher_track;
- track_editor->update();
- }
-
- _edit_if_single_selection();
-
- } break;
- case ClickOver::CLICK_MOVE_KEYS: {
-
- if (selection.empty())
- break;
- if (click.at == click.to) {
-
- if (!click.shift) {
-
- KeyInfo ki = selection[click.selk];
- _clear_selection();
- selection[click.selk] = ki;
- _edit_if_single_selection();
- }
-
- break;
- }
-
- float from_t = 1e20;
-
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
- float t = animation->track_get_key_time(E->key().track, E->key().key);
- if (t < from_t)
- from_t = t;
- }
-
- float motion = from_t + (click.to.x - click.at.x) / _get_zoom_scale();
- if (step->get_value())
- motion = Math::stepify(motion, step->get_value());
-
- undo_redo->create_action(TTR("Anim Move Keys"));
-
- List<_AnimMoveRestore> to_restore;
-
- // 1-remove the keys
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
- }
- // 2- remove overlapped keys
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- float newtime = E->get().pos - from_t + motion;
- int idx = animation->track_find_key(E->key().track, newtime, true);
- if (idx == -1)
- continue;
- SelectedKey sk;
- sk.key = idx;
- sk.track = E->key().track;
- if (selection.has(sk))
- continue; //already in selection, don't save
-
- undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newtime);
- _AnimMoveRestore amr;
-
- amr.key = animation->track_get_key_value(E->key().track, idx);
- amr.track = E->key().track;
- amr.time = newtime;
- amr.transition = animation->track_get_key_transition(E->key().track, idx);
-
- to_restore.push_back(amr);
- }
-
- // 3-move the keys (re insert them)
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- float newpos = E->get().pos - from_t + motion;
- /*
- if (newpos<0)
- continue; //no add at the beginning
- */
- undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->key().track, newpos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
- }
-
- // 4-(undo) remove inserted keys
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- float newpos = E->get().pos + -from_t + motion;
- /*
- if (newpos<0)
- continue; //no remove what no inserted
- */
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newpos);
- }
-
- // 5-(undo) reinsert keys
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
- }
-
- // 6-(undo) reinsert overlapped keys
- for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
-
- _AnimMoveRestore &amr = E->get();
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
- }
-
- // 6-(undo) reinsert overlapped keys
- for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
-
- _AnimMoveRestore &amr = E->get();
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
- }
-
- undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
- undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
-
- // 7-reselect
-
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- float oldpos = E->get().pos;
- float newpos = oldpos - from_t + motion;
- //if (newpos>=0)
- undo_redo->add_do_method(this, "_select_at_anim", animation, E->key().track, newpos);
- undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, oldpos);
- }
-
- undo_redo->commit_action();
- _edit_if_single_selection();
-
- } break;
- default: {}
- }
-
- //button released
- click.click = ClickOver::CLICK_NONE;
- track_editor->update();
- }
- }
- }
-
- Ref<InputEventMouseMotion> mm = p_input;
-
- if (mm.is_valid()) {
-
- mouse_over.over = MouseOver::OVER_NONE;
- mouse_over.track = -1;
- te->update();
- track_editor->set_tooltip("");
-
- if (!track_editor->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field()))
- track_editor->call_deferred("grab_focus");
-
- if (click.click != ClickOver::CLICK_NONE) {
-
- switch (click.click) {
- case ClickOver::CLICK_RESIZE_NAMES: {
-
- float base = click.at.y;
- float clickp = click.at.x - ofs.x;
- float dif = base - clickp;
-
- float target = mm->get_position().x + dif - ofs.x;
-
- float ratio = target / settings_limit;
-
- if (ratio > 0.9)
- ratio = 0.9;
- else if (ratio < 0.2)
- ratio = 0.2;
-
- name_column_ratio = ratio;
-
- } break;
- case ClickOver::CLICK_DRAG_TIMELINE: {
-
- Point2 mpos = mm->get_position() - ofs;
- /*
- if (mpos.x<name_limit)
- mpos.x=name_limit;
- if (mpos.x>settings_limit)
- mpos.x=settings_limit;
- */
-
- //int zoomw = settings_limit-name_limit;
- float scale = _get_zoom_scale();
- float pos = h_scroll->get_value() + (mpos.x - name_limit) / scale;
- if (animation->get_step()) {
- pos = Math::stepify(pos, animation->get_step());
- }
- if (pos < 0)
- pos = 0;
- if (pos >= animation->get_length())
- pos = animation->get_length();
-
- if (pos < h_scroll->get_value()) {
- h_scroll->set_value(pos);
- } else if (pos > h_scroll->get_value() + (settings_limit - name_limit) / scale) {
- h_scroll->set_value(pos - (settings_limit - name_limit) / scale);
- }
-
- timeline_pos = pos;
- emit_signal("timeline_changed", pos, true);
-
- } break;
- case ClickOver::CLICK_SELECT_KEYS: {
-
- click.to = mm->get_position();
- if (click.to.y < h && click.at.y > h && mm->get_relative().y < 0) {
-
- float prev = v_scroll->get_value();
- v_scroll->set_value(v_scroll->get_value() - 1);
- if (prev != v_scroll->get_value())
- click.at.y += h;
- }
- if (click.to.y > size.height && click.at.y < size.height && mm->get_relative().y > 0) {
-
- float prev = v_scroll->get_value();
- v_scroll->set_value(v_scroll->get_value() + 1);
- if (prev != v_scroll->get_value())
- click.at.y -= h;
- }
-
- } break;
- case ClickOver::CLICK_MOVE_KEYS: {
-
- click.to = mm->get_position();
- } break;
- default: {}
- }
-
- return;
- } else if (mm->get_button_mask() & BUTTON_MASK_MIDDLE) {
-
- int rel = mm->get_relative().x;
- float relf = rel / _get_zoom_scale();
- h_scroll->set_value(h_scroll->get_value() - relf);
- }
-
- if (mm->get_button_mask() == 0) {
-
- Point2 mpos = mm->get_position() - ofs;
-
- if (mpos.y < h) {
- return;
- }
-
- mpos.y -= h;
-
- int idx = mpos.y / h;
- idx += v_scroll->get_value();
- if (idx < 0 || idx >= animation->get_track_count())
- return;
-
- mouse_over.track = idx;
-
- if (mpos.x < name_limit) {
- //name column
-
- mouse_over.over = MouseOver::OVER_NAME;
-
- } else if (mpos.x < settings_limit) {
-
- float pos = mpos.x - name_limit;
- pos /= _get_zoom_scale();
- pos += h_scroll->get_value();
- float w_time = (type_icon[0]->get_width() / _get_zoom_scale()) / 2.0;
-
- int kidx = animation->track_find_key(idx, pos);
- int kidx_n = kidx + 1;
-
- bool found = false;
-
- if (kidx >= 0 && kidx < animation->track_get_key_count(idx)) {
-
- float kpos = animation->track_get_key_time(idx, kidx);
- if (ABS(pos - kpos) <= w_time) {
-
- mouse_over.over = MouseOver::OVER_KEY;
- mouse_over.track = idx;
- mouse_over.over_key = kidx;
- found = true;
- }
- }
-
- if (!found && kidx_n >= 0 && kidx_n < animation->track_get_key_count(idx)) {
-
- float kpos = animation->track_get_key_time(idx, kidx_n);
- if (ABS(pos - kpos) <= w_time) {
-
- mouse_over.over = MouseOver::OVER_KEY;
- mouse_over.track = idx;
- mouse_over.over_key = kidx_n;
- found = true;
- }
- }
-
- if (found) {
-
- String text;
- text = "time: " + rtos(animation->track_get_key_time(idx, mouse_over.over_key)) + "\n";
-
- switch (animation->track_get_type(idx)) {
-
- case Animation::TYPE_TRANSFORM: {
-
- Dictionary d = animation->track_get_key_value(idx, mouse_over.over_key);
- if (d.has("location"))
- text += "location: " + String(d["location"]) + "\n";
- if (d.has("rotation"))
- text += "rot: " + String(d["rotation"]) + "\n";
- if (d.has("scale"))
- text += "scale: " + String(d["scale"]) + "\n";
- } break;
- case Animation::TYPE_VALUE: {
-
- Variant v = animation->track_get_key_value(idx, mouse_over.over_key);
- //text+="value: "+String(v)+"\n";
-
- bool prop_exists = false;
- Variant::Type valid_type = Variant::NIL;
- Object *obj = NULL;
-
- RES res;
- Vector<StringName> leftover_path;
- Node *node = root->get_node_and_resource(animation->track_get_path(idx), res, leftover_path);
-
- if (res.is_valid()) {
- obj = res.ptr();
- } else if (node) {
- obj = node;
- }
-
- if (obj) {
- valid_type = obj->get_static_property_type_indexed(leftover_path, &prop_exists);
- }
-
- text += "type: " + Variant::get_type_name(v.get_type()) + "\n";
- if (prop_exists && !Variant::can_convert(v.get_type(), valid_type)) {
- text += "value: " + String(v) + " (Invalid, expected type: " + Variant::get_type_name(valid_type) + ")\n";
- } else {
- text += "value: " + String(v) + "\n";
- }
-
- } break;
- case Animation::TYPE_METHOD: {
-
- Dictionary d = animation->track_get_key_value(idx, mouse_over.over_key);
- if (d.has("method"))
- text += String(d["method"]);
- text += "(";
- Vector<Variant> args;
- if (d.has("args"))
- args = d["args"];
- for (int i = 0; i < args.size(); i++) {
-
- if (i > 0)
- text += ", ";
- text += String(args[i]);
- }
- text += ")\n";
-
- } break;
- }
- text += "easing: " + rtos(animation->track_get_key_transition(idx, mouse_over.over_key));
-
- track_editor->set_tooltip(text);
- return;
- }
-
- } else {
- //button column
- int ofsx = size.width - mpos.x;
- if (ofsx < 0)
- return;
- /*
- if (ofsx < remove_icon->get_width()) {
-
- mouse_over.over=MouseOver::OVER_REMOVE;
-
- return;
- }
-
- ofsx-=hsep+remove_icon->get_width();
-
- if (ofsx < move_down_icon->get_width()) {
-
- mouse_over.over=MouseOver::OVER_DOWN;
- return;
- }
-
- ofsx-=hsep+move_down_icon->get_width();
-
- if (ofsx < move_up_icon->get_width()) {
-
- mouse_over.over=MouseOver::OVER_UP;
- return;
- }
-
- ofsx-=hsep*3+move_up_icon->get_width();
-
-*/
-
- if (ofsx < down_icon->get_width() + wrap_icon[0]->get_width() + hsep * 3) {
-
- mouse_over.over = MouseOver::OVER_WRAP;
- return;
- }
-
- ofsx -= hsep * 3 + wrap_icon[0]->get_width() + down_icon->get_width();
-
- if (ofsx < down_icon->get_width() + interp_icon[0]->get_width() + hsep * 3) {
-
- mouse_over.over = MouseOver::OVER_INTERP;
- return;
- }
-
- ofsx -= hsep * 2 + interp_icon[0]->get_width() + down_icon->get_width();
-
- if (ofsx < down_icon->get_width() + cont_icon[0]->get_width() + hsep * 3) {
-
- mouse_over.over = MouseOver::OVER_VALUE;
- return;
- }
-
- ofsx -= hsep * 3 + cont_icon[0]->get_width() + down_icon->get_width();
-
- if (ofsx < add_key_icon->get_width()) {
-
- mouse_over.over = MouseOver::OVER_ADD_KEY;
- return;
- }
- }
- }
- }
-
- Ref<InputEventMagnifyGesture> magnify_gesture = p_input;
- if (magnify_gesture.is_valid()) {
- zoom->set_value(zoom->get_value() * magnify_gesture->get_factor());
- }
-
- Ref<InputEventPanGesture> pan_gesture = p_input;
- if (pan_gesture.is_valid()) {
-
- h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8);
- v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8);
- }
-}
-
-void AnimationKeyEditor::_notification(int p_what) {
-
- switch (p_what) {
- case NOTIFICATION_VISIBILITY_CHANGED: {
-
- EditorNode::get_singleton()->update_keying();
- emit_signal("keying_changed");
- } break;
-
- case NOTIFICATION_ENTER_TREE: {
-
- key_editor->edit(key_edit);
-
- zoomicon->set_custom_minimum_size(Size2(24 * EDSCALE, 0));
- zoomicon->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
-
- menu_track->set_icon(get_icon("Tools", "EditorIcons"));
- menu_track->get_popup()->add_item(TTR("Scale Selection"), TRACK_MENU_SCALE);
- menu_track->get_popup()->add_item(TTR("Scale From Cursor"), TRACK_MENU_SCALE_PIVOT);
- menu_track->get_popup()->add_separator();
- menu_track->get_popup()->add_item(TTR("Duplicate Selection"), TRACK_MENU_DUPLICATE);
- menu_track->get_popup()->add_item(TTR("Duplicate Transposed"), TRACK_MENU_DUPLICATE_TRANSPOSE);
- menu_track->get_popup()->add_separator();
- menu_track->get_popup()->add_item(TTR("Goto Next Step"), TRACK_MENU_NEXT_STEP, KEY_MASK_CMD | KEY_RIGHT);
- menu_track->get_popup()->add_item(TTR("Goto Prev Step"), TRACK_MENU_PREV_STEP, KEY_MASK_CMD | KEY_LEFT);
- menu_track->get_popup()->add_separator();
- PopupMenu *tpp = memnew(PopupMenu);
- tpp->add_item(TTR("Linear"), TRACK_MENU_SET_ALL_TRANS_LINEAR);
- tpp->add_item(TTR("Constant"), TRACK_MENU_SET_ALL_TRANS_CONSTANT);
- tpp->add_item(TTR("In"), TRACK_MENU_SET_ALL_TRANS_IN);
- tpp->add_item(TTR("Out"), TRACK_MENU_SET_ALL_TRANS_OUT);
- tpp->add_item(TTR("In-Out"), TRACK_MENU_SET_ALL_TRANS_INOUT);
- tpp->add_item(TTR("Out-In"), TRACK_MENU_SET_ALL_TRANS_OUTIN);
- tpp->set_name(TTR("Transitions"));
- tpp->connect("id_pressed", this, "_menu_track");
- optimize_dialog->connect("confirmed", this, "_animation_optimize");
-
- menu_track->get_popup()->add_child(tpp);
-
- menu_track->get_popup()->add_item(TTR("Optimize Animation"), TRACK_MENU_OPTIMIZE);
- menu_track->get_popup()->add_item(TTR("Clean-Up Animation"), TRACK_MENU_CLEAN_UP);
-
- curve_linear->connect("pressed", this, "_menu_track", varray(CURVE_SET_LINEAR));
- curve_in->connect("pressed", this, "_menu_track", varray(CURVE_SET_IN));
- curve_out->connect("pressed", this, "_menu_track", varray(CURVE_SET_OUT));
- curve_inout->connect("pressed", this, "_menu_track", varray(CURVE_SET_INOUT));
- curve_outin->connect("pressed", this, "_menu_track", varray(CURVE_SET_OUTIN));
- curve_constant->connect("pressed", this, "_menu_track", varray(CURVE_SET_CONSTANT));
-
- edit_button->connect("pressed", this, "_toggle_edit_curves");
-
- curve_edit->connect("transition_changed", this, "_curve_transition_changed");
- call_select->connect("selected", this, "_add_call_track");
-
- _update_menu();
-
- } break;
-
- case NOTIFICATION_THEME_CHANGED: {
- zoomicon->set_texture(get_icon("Zoom", "EditorIcons"));
-
- menu_add_track->set_icon(get_icon("Add", "EditorIcons"));
-
- menu_track->set_icon(get_icon("Tools", "EditorIcons"));
-
- menu_add_track->get_popup()->set_item_icon(ADD_TRACK_MENU_ADD_VALUE_TRACK, get_icon("KeyValue", "EditorIcons"));
- menu_add_track->get_popup()->set_item_icon(ADD_TRACK_MENU_ADD_TRANSFORM_TRACK, get_icon("KeyXform", "EditorIcons"));
- menu_add_track->get_popup()->set_item_icon(ADD_TRACK_MENU_ADD_CALL_TRACK, get_icon("KeyCall", "EditorIcons"));
-
- curve_linear->set_icon(get_icon("CurveLinear", "EditorIcons"));
- curve_in->set_icon(get_icon("CurveIn", "EditorIcons"));
- curve_out->set_icon(get_icon("CurveOut", "EditorIcons"));
- curve_inout->set_icon(get_icon("CurveInOut", "EditorIcons"));
- curve_outin->set_icon(get_icon("CurveOutIn", "EditorIcons"));
- curve_constant->set_icon(get_icon("CurveConstant", "EditorIcons"));
-
- move_up_button->set_icon(get_icon("MoveUp", "EditorIcons"));
- move_down_button->set_icon(get_icon("MoveDown", "EditorIcons"));
- remove_button->set_icon(get_icon("Remove", "EditorIcons"));
- edit_button->set_icon(get_icon("EditKey", "EditorIcons"));
-
- loop->set_icon(get_icon("Loop", "EditorIcons"));
-
- {
-
- right_data_size_cache = 0;
- int hsep = get_constant("hseparation", "Tree");
- Ref<Texture> remove_icon = get_icon("Remove", "EditorIcons");
- Ref<Texture> move_up_icon = get_icon("MoveUp", "EditorIcons");
- Ref<Texture> move_down_icon = get_icon("MoveDown", "EditorIcons");
- Ref<Texture> down_icon = get_icon("select_arrow", "Tree");
- Ref<Texture> add_key_icon = get_icon("TrackAddKey", "EditorIcons");
- Ref<Texture> interp_icon[3] = {
- get_icon("InterpRaw", "EditorIcons"),
- get_icon("InterpLinear", "EditorIcons"),
- get_icon("InterpCubic", "EditorIcons")
- };
- Ref<Texture> cont_icon[3] = {
- get_icon("TrackContinuous", "EditorIcons"),
- get_icon("TrackDiscrete", "EditorIcons"),
- get_icon("TrackTrigger", "EditorIcons")
- };
-
- Ref<Texture> wrap_icon[2] = {
- get_icon("InterpWrapClamp", "EditorIcons"),
- get_icon("InterpWrapLoop", "EditorIcons"),
- };
- right_data_size_cache = down_icon->get_width() * 3 + add_key_icon->get_width() + interp_icon[0]->get_width() + cont_icon[0]->get_width() + wrap_icon[0]->get_width() + hsep * 9;
- }
- } break;
- }
-}
-
-void AnimationKeyEditor::_scroll_changed(double) {
-
- if (te_drawing)
- return;
-
- track_editor->update();
-}
-
-void AnimationKeyEditor::_update_paths() {
-
- if (animation.is_valid()) {
- //timeline->set_max(animation->get_length());
- //timeline->set_step(0.01);
- track_editor->update();
- length->set_value(animation->get_length());
- step->set_value(animation->get_step());
- }
-}
-
-void AnimationKeyEditor::_root_removed() {
-
- root = NULL;
-}
-
-void AnimationKeyEditor::_update_menu() {
-
- updating = true;
-
- if (animation.is_valid()) {
-
- length->set_value(animation->get_length());
- loop->set_pressed(animation->has_loop());
- step->set_value(animation->get_step());
- }
-
- track_editor->update();
- updating = false;
-}
-void AnimationKeyEditor::_clear_selection() {
-
- selection.clear();
- key_edit->animation = Ref<Animation>();
- key_edit->track = 0;
- key_edit->key_ofs = 0;
- key_edit->hint = PropertyInfo();
- key_edit->base = NodePath();
- key_edit->notify_change();
-}
-
-void AnimationKeyEditor::set_animation(const Ref<Animation> &p_anim) {
-
- if (animation.is_valid())
- animation->disconnect("changed", this, "_update_paths");
- animation = p_anim;
- if (animation.is_valid())
- animation->connect("changed", this, "_update_paths");
-
- timeline_pos = 0;
- _clear_selection();
-
- _update_menu();
- selected_track = -1;
- _edit_if_single_selection();
-
- EditorNode::get_singleton()->update_keying();
-}
-
-void AnimationKeyEditor::set_root(Node *p_root) {
-
- if (root)
- root->disconnect("tree_exiting", this, "_root_removed");
-
- root = p_root;
-
- if (root)
- root->connect("tree_exiting", this, "_root_removed", make_binds(), CONNECT_ONESHOT);
-}
-
-Node *AnimationKeyEditor::get_root() const {
-
- return root;
-}
-
-void AnimationKeyEditor::update_keying() {
-
- bool keying_enabled = is_visible_in_tree() && animation.is_valid();
-
- if (keying_enabled == keying)
- return;
-
- keying = keying_enabled;
- _update_menu();
- emit_signal("keying_changed");
-}
-
-bool AnimationKeyEditor::has_keying() const {
-
- return keying;
-}
-
-void AnimationKeyEditor::_query_insert(const InsertData &p_id) {
-
- if (insert_frame != Engine::get_singleton()->get_frames_drawn()) {
- //clear insert list for the frame if frame changed
- if (insert_confirm->is_visible_in_tree())
- return; //do nothing
- insert_data.clear();
- insert_query = false;
- }
- insert_frame = Engine::get_singleton()->get_frames_drawn();
-
- for (List<InsertData>::Element *E = insert_data.front(); E; E = E->next()) {
- //prevent insertion of multiple tracks
- if (E->get().path == p_id.path)
- return; //already inserted a track for this on this frame
- }
-
- insert_data.push_back(p_id);
-
- if (p_id.track_idx == -1) {
- if (bool(EDITOR_DEF("editors/animation/confirm_insert_track", true))) {
- //potential new key, does not exist
- if (insert_data.size() == 1)
- insert_confirm->set_text(vformat(TTR("Create NEW track for %s and insert key?"), p_id.query));
- else
- insert_confirm->set_text(vformat(TTR("Create %d NEW tracks and insert keys?"), insert_data.size()));
-
- insert_confirm->get_ok()->set_text(TTR("Create"));
- insert_confirm->popup_centered_minsize();
- insert_query = true;
- } else {
- call_deferred("_insert_delay");
- insert_queue = true;
- }
-
- } else {
- if (!insert_query && !insert_queue) {
- call_deferred("_insert_delay");
- insert_queue = true;
- }
- }
-}
-
-void AnimationKeyEditor::insert_transform_key(Spatial *p_node, const String &p_sub, const Transform &p_xform) {
-
- if (!keying)
- return;
- if (!animation.is_valid())
- return;
-
- ERR_FAIL_COND(!root);
- //let's build a node path
- String path = root->get_path_to(p_node);
- if (p_sub != "")
- path += ":" + p_sub;
-
- NodePath np = path;
-
- int track_idx = -1;
-
- for (int i = 0; i < animation->get_track_count(); i++) {
-
- if (animation->track_get_type(i) != Animation::TYPE_TRANSFORM)
- continue;
- if (animation->track_get_path(i) != np)
- continue;
-
- track_idx = i;
- break;
- }
-
- InsertData id;
- Dictionary val;
-
- id.path = np;
- id.track_idx = track_idx;
- id.value = p_xform;
- id.type = Animation::TYPE_TRANSFORM;
- id.query = "node '" + p_node->get_name() + "'";
- id.advance = false;
-
- //dialog insert
-
- _query_insert(id);
-}
-
-void AnimationKeyEditor::insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists) {
-
- ERR_FAIL_COND(!root);
- //let's build a node path
-
- Node *node = p_node;
-
- String path = root->get_path_to(node);
-
- for (int i = 1; i < history->get_path_size(); i++) {
-
- String prop = history->get_path_property(i);
- ERR_FAIL_COND(prop == "");
- path += ":" + prop;
- }
-
- path += ":" + p_property;
-
- NodePath np = path;
-
- //locate track
-
- int track_idx = -1;
-
- for (int i = 0; i < animation->get_track_count(); i++) {
-
- if (animation->track_get_type(i) != Animation::TYPE_VALUE)
- continue;
- if (animation->track_get_path(i) != np)
- continue;
-
- track_idx = i;
- break;
- }
-
- if (p_only_if_exists && track_idx == -1)
- return;
- InsertData id;
- id.path = np;
- id.track_idx = track_idx;
- id.value = p_value;
- id.type = Animation::TYPE_VALUE;
- id.query = "property '" + p_property + "'";
- id.advance = false;
- //dialog insert
- _query_insert(id);
-}
-
-void AnimationKeyEditor::insert_value_key(const String &p_property, const Variant &p_value, bool p_advance) {
-
- ERR_FAIL_COND(!root);
- //let's build a node path
- ERR_FAIL_COND(history->get_path_size() == 0);
- Object *obj = ObjectDB::get_instance(history->get_path_object(0));
- ERR_FAIL_COND(!Object::cast_to<Node>(obj));
-
- Node *node = Object::cast_to<Node>(obj);
-
- String path = root->get_path_to(node);
-
- for (int i = 1; i < history->get_path_size(); i++) {
-
- String prop = history->get_path_property(i);
- ERR_FAIL_COND(prop == "");
- path += ":" + prop;
- }
-
- path += ":" + p_property;
-
- NodePath np = path;
-
- //locate track
-
- int track_idx = -1;
-
- for (int i = 0; i < animation->get_track_count(); i++) {
-
- if (animation->track_get_type(i) != Animation::TYPE_VALUE)
- continue;
- if (animation->track_get_path(i) != np)
- continue;
-
- track_idx = i;
- break;
- }
-
- InsertData id;
- id.path = np;
- id.track_idx = track_idx;
- id.value = p_value;
- id.type = Animation::TYPE_VALUE;
- id.query = "property '" + p_property + "'";
- id.advance = p_advance;
- //dialog insert
- _query_insert(id);
-}
-
-void AnimationKeyEditor::_confirm_insert_list() {
-
- undo_redo->create_action(TTR("Anim Create & Insert"));
-
- int last_track = animation->get_track_count();
- while (insert_data.size()) {
-
- last_track = _confirm_insert(insert_data.front()->get(), last_track);
- insert_data.pop_front();
- }
-
- undo_redo->commit_action();
-}
-
-int AnimationKeyEditor::_confirm_insert(InsertData p_id, int p_last_track) {
-
- if (p_last_track == -1)
- p_last_track = animation->get_track_count();
-
- bool created = false;
- if (p_id.track_idx < 0) {
-
- created = true;
- undo_redo->create_action(TTR("Anim Insert Track & Key"));
- Animation::UpdateMode update_mode = Animation::UPDATE_DISCRETE;
-
- if (p_id.type == Animation::TYPE_VALUE) {
- //wants a new tack
-
- {
- //hack
- NodePath np;
- animation->add_track(p_id.type);
- animation->track_set_path(animation->get_track_count() - 1, p_id.path);
- PropertyInfo h = _find_hint_for_track(animation->get_track_count() - 1, np);
- animation->remove_track(animation->get_track_count() - 1); //hack
-
- if (h.type == Variant::REAL ||
- h.type == Variant::VECTOR2 ||
- h.type == Variant::RECT2 ||
- h.type == Variant::VECTOR3 ||
- h.type == Variant::AABB ||
- h.type == Variant::QUAT ||
- h.type == Variant::COLOR ||
- h.type == Variant::TRANSFORM) {
-
- update_mode = Animation::UPDATE_CONTINUOUS;
- }
-
- if (h.usage & PROPERTY_USAGE_ANIMATE_AS_TRIGGER) {
- update_mode = Animation::UPDATE_TRIGGER;
- }
- }
- }
-
- p_id.track_idx = p_last_track;
-
- undo_redo->add_do_method(animation.ptr(), "add_track", p_id.type);
- undo_redo->add_do_method(animation.ptr(), "track_set_path", p_id.track_idx, p_id.path);
- if (p_id.type == Animation::TYPE_VALUE)
- undo_redo->add_do_method(animation.ptr(), "value_track_set_update_mode", p_id.track_idx, update_mode);
-
- } else {
- undo_redo->create_action(TTR("Anim Insert Key"));
- }
-
- float time = timeline_pos;
- Variant value;
-
- switch (p_id.type) {
-
- case Animation::TYPE_VALUE: {
-
- value = p_id.value;
-
- } break;
- case Animation::TYPE_TRANSFORM: {
-
- Transform tr = p_id.value;
- Dictionary d;
- d["location"] = tr.origin;
- d["scale"] = tr.basis.get_scale();
- d["rotation"] = Quat(tr.basis); //.orthonormalized();
- value = d;
- } break;
- default: {}
- }
-
- undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_id.track_idx, time, value);
-
- if (created) {
-
- //just remove the track
- undo_redo->add_undo_method(animation.ptr(), "remove_track", p_last_track);
- p_last_track++;
- } else {
-
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_id.track_idx, time);
- int existing = animation->track_find_key(p_id.track_idx, time, true);
- if (existing != -1) {
- Variant v = animation->track_get_key_value(p_id.track_idx, existing);
- float trans = animation->track_get_key_transition(p_id.track_idx, existing);
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", p_id.track_idx, time, v, trans);
- }
- }
-
- undo_redo->add_do_method(this, "update");
- undo_redo->add_undo_method(this, "update");
- undo_redo->add_do_method(track_editor, "update");
- undo_redo->add_undo_method(track_editor, "update");
- undo_redo->add_do_method(track_pos, "update");
- undo_redo->add_undo_method(track_pos, "update");
-
- undo_redo->commit_action();
-
- return p_last_track;
-}
-
-Ref<Animation> AnimationKeyEditor::get_current_animation() const {
-
- return animation;
-}
-
-void AnimationKeyEditor::_animation_len_changed(float p_len) {
-
- if (updating)
- return;
-
- if (!animation.is_null()) {
-
- undo_redo->create_action(TTR("Change Anim Len"));
- undo_redo->add_do_method(animation.ptr(), "set_length", p_len);
- undo_redo->add_undo_method(animation.ptr(), "set_length", animation->get_length());
- undo_redo->add_do_method(this, "_animation_len_update");
- undo_redo->add_undo_method(this, "_animation_len_update");
- undo_redo->commit_action();
- }
-}
-
-void AnimationKeyEditor::_animation_len_update() {
-
- if (!animation.is_null())
- emit_signal(alc, animation->get_length());
-}
-
-void AnimationKeyEditor::_animation_changed() {
- if (updating)
- return;
- _update_menu();
-}
-
-void AnimationKeyEditor::_animation_loop_changed() {
-
- if (updating)
- return;
-
- if (!animation.is_null()) {
-
- undo_redo->create_action(TTR("Change Anim Loop"));
- undo_redo->add_do_method(animation.ptr(), "set_loop", loop->is_pressed());
- undo_redo->add_undo_method(animation.ptr(), "set_loop", !loop->is_pressed());
- undo_redo->commit_action();
- }
-}
-
-void AnimationKeyEditor::_create_value_item(int p_type) {
-
- undo_redo->create_action(TTR("Anim Create Typed Value Key"));
-
- Variant::CallError ce;
- Variant v = Variant::construct(Variant::Type(p_type), NULL, 0, ce);
- undo_redo->add_do_method(animation.ptr(), "track_insert_key", cvi_track, cvi_pos, v);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", cvi_track, cvi_pos);
-
- int existing = animation->track_find_key(cvi_track, cvi_pos, true);
-
- if (existing != -1) {
- Variant v = animation->track_get_key_value(cvi_track, existing);
- float trans = animation->track_get_key_transition(cvi_track, existing);
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", cvi_track, cvi_pos, v, trans);
- }
-
- undo_redo->commit_action();
-}
-
-void AnimationKeyEditor::set_anim_pos(float p_pos) {
-
- if (animation.is_null())
- return;
- timeline_pos = p_pos;
- update();
- track_pos->update();
- track_editor->update();
-}
-
-void AnimationKeyEditor::_pane_drag(const Point2 &p_delta) {
-
- Size2 ecs = ec->get_custom_minimum_size();
- ecs.y -= p_delta.y;
- if (ecs.y < 100)
- ecs.y = 100;
- ec->set_custom_minimum_size(ecs);
-}
-
-void AnimationKeyEditor::_insert_delay() {
-
- if (insert_query) {
- //discard since it's entered into query mode
- insert_queue = false;
- return;
- }
-
- undo_redo->create_action(TTR("Anim Insert"));
-
- int last_track = animation->get_track_count();
- bool advance = false;
- while (insert_data.size()) {
-
- if (insert_data.front()->get().advance)
- advance = true;
- last_track = _confirm_insert(insert_data.front()->get(), last_track);
- insert_data.pop_front();
- }
-
- undo_redo->commit_action();
-
- if (advance) {
- float step = animation->get_step();
- if (step == 0)
- step = 1;
-
- float pos = timeline_pos;
-
- pos = Math::stepify(pos + step, step);
- if (pos > animation->get_length())
- pos = animation->get_length();
- timeline_pos = pos;
- track_pos->update();
- emit_signal("timeline_changed", pos, true);
- }
- insert_queue = false;
-}
-
-void AnimationKeyEditor::_step_changed(float p_len) {
-
- updating = true;
- if (!animation.is_null()) {
- animation->set_step(p_len);
- emit_signal("animation_step_changed", animation->get_step());
- }
- updating = false;
-}
-
-void AnimationKeyEditor::_scale() {
-
- if (selection.empty())
- return;
-
- float from_t = 1e20;
- float to_t = -1e20;
- float len = -1e20;
- float pivot = 0;
-
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
- float t = animation->track_get_key_time(E->key().track, E->key().key);
- if (t < from_t)
- from_t = t;
- if (t > to_t)
- to_t = t;
- }
-
- len = to_t - from_t;
- if (last_menu_track_opt == TRACK_MENU_SCALE_PIVOT) {
- pivot = timeline_pos;
-
- } else {
-
- pivot = from_t;
- }
-
- float s = scale->get_value();
- if (s == 0) {
- ERR_PRINT("Can't scale to 0");
- }
-
- undo_redo->create_action(TTR("Anim Scale Keys"));
-
- List<_AnimMoveRestore> to_restore;
-
- // 1-remove the keys
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
- }
- // 2- remove overlapped keys
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- float newtime = (E->get().pos - from_t) * s + from_t;
- int idx = animation->track_find_key(E->key().track, newtime, true);
- if (idx == -1)
- continue;
- SelectedKey sk;
- sk.key = idx;
- sk.track = E->key().track;
- if (selection.has(sk))
- continue; //already in selection, don't save
-
- undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newtime);
- _AnimMoveRestore amr;
-
- amr.key = animation->track_get_key_value(E->key().track, idx);
- amr.track = E->key().track;
- amr.time = newtime;
- amr.transition = animation->track_get_key_transition(E->key().track, idx);
-
- to_restore.push_back(amr);
- }
-
-#define _NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * ABS(s) + from_t
- // 3-move the keys (re insert them)
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- float newpos = _NEW_POS(E->get().pos);
- undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->key().track, newpos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
- }
-
- // 4-(undo) remove inserted keys
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- float newpos = _NEW_POS(E->get().pos);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newpos);
- }
-
- // 5-(undo) reinsert keys
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
- }
-
- // 6-(undo) reinsert overlapped keys
- for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
-
- _AnimMoveRestore &amr = E->get();
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
- }
-
- // 6-(undo) reinsert overlapped keys
- for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
-
- _AnimMoveRestore &amr = E->get();
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
- }
-
- undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
- undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
-
- // 7-reselect
-
- for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
-
- float oldpos = E->get().pos;
- float newpos = _NEW_POS(oldpos);
- if (newpos >= 0)
- undo_redo->add_do_method(this, "_select_at_anim", animation, E->key().track, newpos);
- undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, oldpos);
- }
-#undef _NEW_POS
- undo_redo->commit_action();
-}
-
-void AnimationKeyEditor::_add_call_track(const NodePath &p_base) {
-
- Node *base = EditorNode::get_singleton()->get_edited_scene();
- if (!base)
- return;
- Node *from = base->get_node(p_base);
- if (!from || !root)
- return;
-
- NodePath path = root->get_path_to(from);
-
- //print_line("root: "+String(root->get_path()));
- //print_line("path: "+String(path));
-
- undo_redo->create_action(TTR("Anim Add Call Track"));
- undo_redo->add_do_method(animation.ptr(), "add_track", Animation::TYPE_METHOD);
- undo_redo->add_do_method(animation.ptr(), "track_set_path", animation->get_track_count(), path);
- undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
- undo_redo->commit_action();
-}
-
-void AnimationKeyEditor::cleanup() {
-
- set_animation(Ref<Animation>());
-}
-
-void AnimationKeyEditor::_bind_methods() {
-
- ClassDB::bind_method(D_METHOD("_root_removed"), &AnimationKeyEditor::_root_removed);
- ClassDB::bind_method(D_METHOD("_scale"), &AnimationKeyEditor::_scale);
- ClassDB::bind_method(D_METHOD("set_root"), &AnimationKeyEditor::set_root);
-
- //ClassDB::bind_method(D_METHOD("_confirm_insert"),&AnimationKeyEditor::_confirm_insert);
- ClassDB::bind_method(D_METHOD("_confirm_insert_list"), &AnimationKeyEditor::_confirm_insert_list);
-
- ClassDB::bind_method(D_METHOD("_update_paths"), &AnimationKeyEditor::_update_paths);
- ClassDB::bind_method(D_METHOD("_track_editor_draw"), &AnimationKeyEditor::_track_editor_draw);
-
- ClassDB::bind_method(D_METHOD("_animation_changed"), &AnimationKeyEditor::_animation_changed);
- ClassDB::bind_method(D_METHOD("_scroll_changed"), &AnimationKeyEditor::_scroll_changed);
- ClassDB::bind_method(D_METHOD("_track_editor_gui_input"), &AnimationKeyEditor::_track_editor_gui_input);
- ClassDB::bind_method(D_METHOD("_track_name_changed"), &AnimationKeyEditor::_track_name_changed);
- ClassDB::bind_method(D_METHOD("_track_menu_selected"), &AnimationKeyEditor::_track_menu_selected);
- ClassDB::bind_method(D_METHOD("_menu_add_track"), &AnimationKeyEditor::_menu_add_track);
- ClassDB::bind_method(D_METHOD("_menu_track"), &AnimationKeyEditor::_menu_track);
- ClassDB::bind_method(D_METHOD("_clear_selection_for_anim"), &AnimationKeyEditor::_clear_selection_for_anim);
- ClassDB::bind_method(D_METHOD("_select_at_anim"), &AnimationKeyEditor::_select_at_anim);
- ClassDB::bind_method(D_METHOD("_track_position_draw"), &AnimationKeyEditor::_track_position_draw);
- ClassDB::bind_method(D_METHOD("_insert_delay"), &AnimationKeyEditor::_insert_delay);
- ClassDB::bind_method(D_METHOD("_step_changed"), &AnimationKeyEditor::_step_changed);
-
- ClassDB::bind_method(D_METHOD("_animation_loop_changed"), &AnimationKeyEditor::_animation_loop_changed);
- ClassDB::bind_method(D_METHOD("_animation_len_changed"), &AnimationKeyEditor::_animation_len_changed);
- ClassDB::bind_method(D_METHOD("_create_value_item"), &AnimationKeyEditor::_create_value_item);
- ClassDB::bind_method(D_METHOD("_pane_drag"), &AnimationKeyEditor::_pane_drag);
-
- ClassDB::bind_method(D_METHOD("_animation_len_update"), &AnimationKeyEditor::_animation_len_update);
-
- ClassDB::bind_method(D_METHOD("set_animation"), &AnimationKeyEditor::set_animation);
- ClassDB::bind_method(D_METHOD("_animation_optimize"), &AnimationKeyEditor::_animation_optimize);
- ClassDB::bind_method(D_METHOD("_curve_transition_changed"), &AnimationKeyEditor::_curve_transition_changed);
- ClassDB::bind_method(D_METHOD("_toggle_edit_curves"), &AnimationKeyEditor::_toggle_edit_curves);
- ClassDB::bind_method(D_METHOD("_add_call_track"), &AnimationKeyEditor::_add_call_track);
-
- ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop")));
- ADD_SIGNAL(MethodInfo("keying_changed"));
- ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::REAL, "position"), PropertyInfo(Variant::BOOL, "drag")));
- ADD_SIGNAL(MethodInfo("animation_len_changed", PropertyInfo(Variant::REAL, "len")));
- ADD_SIGNAL(MethodInfo("animation_step_changed", PropertyInfo(Variant::REAL, "step")));
- ADD_SIGNAL(MethodInfo("key_edited", PropertyInfo(Variant::INT, "track"), PropertyInfo(Variant::INT, "key")));
-}
-
-AnimationKeyEditor::AnimationKeyEditor() {
-
- alc = "animation_len_changed";
- editor_selection = EditorNode::get_singleton()->get_editor_selection();
-
- selected_track = -1;
- updating = false;
- te_drawing = false;
- undo_redo = EditorNode::get_singleton()->get_undo_redo();
- history = EditorNode::get_singleton()->get_editor_history();
-
- ec = memnew(Control);
- ec->set_custom_minimum_size(Size2(0, 150) * EDSCALE);
- add_child(ec);
- ec->set_v_size_flags(SIZE_EXPAND_FILL);
-
- h_scroll = memnew(HScrollBar);
- h_scroll->connect("value_changed", this, "_scroll_changed");
- add_child(h_scroll);
- h_scroll->set_value(0);
-
- HBoxContainer *hb = memnew(HBoxContainer);
- add_child(hb);
-
- root = NULL;
- //menu = memnew( MenuButton );
- //menu->set_flat(true);
- //menu->set_position(Point2());
- //add_child(menu);
-
- zoomicon = memnew(TextureRect);
- hb->add_child(zoomicon);
- zoomicon->set_tooltip(TTR("Animation zoom."));
-
- zoom = memnew(HSlider);
- //hb->add_child(zoom);
- zoom->set_step(0.01);
- zoom->set_min(0.0);
- zoom->set_max(2.0);
- zoom->set_value(1.0);
- zoom->set_h_size_flags(SIZE_EXPAND_FILL);
- zoom->set_v_size_flags(SIZE_EXPAND_FILL);
- zoom->set_stretch_ratio(2);
- hb->add_child(zoom);
- zoom->connect("value_changed", this, "_scroll_changed");
- zoom->set_tooltip(TTR("Animation zoom."));
-
- hb->add_child(memnew(VSeparator));
-
- Label *l = memnew(Label);
- l->set_text(TTR("Length (s):"));
- hb->add_child(l);
-
- length = memnew(SpinBox);
- length->set_min(0.01);
- length->set_max(10000);
- length->set_step(0.01);
- length->set_h_size_flags(SIZE_EXPAND_FILL);
- length->set_stretch_ratio(1);
- length->set_tooltip(TTR("Animation length (in seconds)."));
- length->set_editable(false);
-
- hb->add_child(length);
- length->connect("value_changed", this, "_animation_len_changed");
-
- l = memnew(Label);
- l->set_text(TTR("Step (s):"));
- hb->add_child(l);
-
- step = memnew(SpinBox);
- step->set_min(0.00);
- step->set_max(128);
- step->set_step(0.01);
- step->set_value(0.0);
- step->set_h_size_flags(SIZE_EXPAND_FILL);
- step->set_stretch_ratio(1);
- step->set_tooltip(TTR("Cursor step snap (in seconds)."));
- step->set_editable(false);
-
- hb->add_child(step);
- step->connect("value_changed", this, "_step_changed");
-
- loop = memnew(ToolButton);
- loop->set_toggle_mode(true);
- loop->connect("pressed", this, "_animation_loop_changed");
- hb->add_child(loop);
- loop->set_tooltip(TTR("Enable/Disable looping in animation."));
- loop->set_disabled(true);
-
- hb->add_child(memnew(VSeparator));
-
- menu_add_track = memnew(MenuButton);
- hb->add_child(menu_add_track);
- menu_add_track->get_popup()->connect("id_pressed", this, "_menu_add_track");
- menu_add_track->set_tooltip(TTR("Add new tracks."));
- menu_add_track->get_popup()->add_icon_item(get_icon("KeyValue", "EditorIcons"), "Add Normal Track", ADD_TRACK_MENU_ADD_VALUE_TRACK);
- menu_add_track->get_popup()->add_icon_item(get_icon("KeyXform", "EditorIcons"), "Add Transform Track", ADD_TRACK_MENU_ADD_TRANSFORM_TRACK);
- menu_add_track->get_popup()->add_icon_item(get_icon("KeyCall", "EditorIcons"), "Add Call Func Track", ADD_TRACK_MENU_ADD_CALL_TRACK);
-
- move_up_button = memnew(ToolButton);
- hb->add_child(move_up_button);
- move_up_button->connect("pressed", this, "_menu_track", make_binds(TRACK_MENU_MOVE_UP));
- move_up_button->set_focus_mode(FOCUS_NONE);
- move_up_button->set_disabled(true);
- move_up_button->set_tooltip(TTR("Move current track up."));
-
- move_down_button = memnew(ToolButton);
- hb->add_child(move_down_button);
- move_down_button->connect("pressed", this, "_menu_track", make_binds(TRACK_MENU_MOVE_DOWN));
- move_down_button->set_focus_mode(FOCUS_NONE);
- move_down_button->set_disabled(true);
- move_down_button->set_tooltip(TTR("Move current track down."));
-
- remove_button = memnew(ToolButton);
- hb->add_child(remove_button);
- remove_button->connect("pressed", this, "_menu_track", make_binds(TRACK_MENU_REMOVE));
- remove_button->set_focus_mode(FOCUS_NONE);
- remove_button->set_disabled(true);
- remove_button->set_tooltip(TTR("Remove selected track."));
-
- hb->add_child(memnew(VSeparator));
-
- menu_track = memnew(MenuButton);
- hb->add_child(menu_track);
- menu_track->get_popup()->connect("id_pressed", this, "_menu_track");
- menu_track->set_tooltip(TTR("Track Tools"));
-
- edit_button = memnew(ToolButton);
- edit_button->set_toggle_mode(true);
- edit_button->set_focus_mode(FOCUS_NONE);
- edit_button->set_disabled(true);
-
- hb->add_child(edit_button);
- edit_button->set_tooltip(TTR("Enable editing of individual keys by clicking them."));
-
- optimize_dialog = memnew(ConfirmationDialog);
- add_child(optimize_dialog);
- optimize_dialog->set_title(TTR("Anim. Optimizer"));
- VBoxContainer *optimize_vb = memnew(VBoxContainer);
- optimize_dialog->add_child(optimize_vb);
-
- optimize_linear_error = memnew(SpinBox);
- optimize_linear_error->set_max(1.0);
- optimize_linear_error->set_min(0.001);
- optimize_linear_error->set_step(0.001);
- optimize_linear_error->set_value(0.05);
- optimize_vb->add_margin_child(TTR("Max. Linear Error:"), optimize_linear_error);
- optimize_angular_error = memnew(SpinBox);
- optimize_angular_error->set_max(1.0);
- optimize_angular_error->set_min(0.001);
- optimize_angular_error->set_step(0.001);
- optimize_angular_error->set_value(0.01);
-
- optimize_vb->add_margin_child(TTR("Max. Angular Error:"), optimize_angular_error);
- optimize_max_angle = memnew(SpinBox);
- optimize_vb->add_margin_child(TTR("Max Optimizable Angle:"), optimize_max_angle);
- optimize_max_angle->set_max(360.0);
- optimize_max_angle->set_min(0.0);
- optimize_max_angle->set_step(0.1);
- optimize_max_angle->set_value(22);
-
- optimize_dialog->get_ok()->set_text(TTR("Optimize"));
-
- /*keying = memnew( Button );
- keying->set_toggle_mode(true);
- //keying->set_text("Keys");
- keying->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,60);
- keying->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,10);
- keying->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,55);
- keying->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,10);
- //add_child(keying);
- keying->connect("pressed",this,"_keying_toggled");
- */
-
- /* l = memnew( Label );
- l->set_text("Base: ");
- l->set_position(Point2(0,3));
- //dr_panel->add_child(l);*/
-
- //menu->get_popup()->connect("id_pressed",this,"_menu_callback");
-
- hb = memnew(HBoxContainer);
- hb->set_anchors_and_margins_preset(Control::PRESET_WIDE);
- ec->add_child(hb);
- hb->set_v_size_flags(SIZE_EXPAND_FILL);
-
- track_editor = memnew(Control);
- track_editor->connect("draw", this, "_track_editor_draw");
- hb->add_child(track_editor);
- track_editor->connect("gui_input", this, "_track_editor_gui_input");
- track_editor->set_focus_mode(Control::FOCUS_ALL);
- track_editor->set_h_size_flags(SIZE_EXPAND_FILL);
-
- track_pos = memnew(Control);
- track_pos->set_anchors_and_margins_preset(Control::PRESET_WIDE);
- track_pos->set_mouse_filter(MOUSE_FILTER_IGNORE);
- track_editor->add_child(track_pos);
- track_pos->connect("draw", this, "_track_position_draw");
-
- select_anim_warning = memnew(Label);
- track_editor->add_child(select_anim_warning);
- select_anim_warning->set_anchors_and_margins_preset(Control::PRESET_WIDE);
- select_anim_warning->set_text(TTR("Select an AnimationPlayer from the Scene Tree to edit animations."));
- select_anim_warning->set_autowrap(true);
- select_anim_warning->set_align(Label::ALIGN_CENTER);
- select_anim_warning->set_valign(Label::VALIGN_CENTER);
-
- v_scroll = memnew(VScrollBar);
- hb->add_child(v_scroll);
- v_scroll->connect("value_changed", this, "_scroll_changed");
- v_scroll->set_value(0);
-
- key_editor_tab = memnew(TabContainer);
- key_editor_tab->set_tab_align(TabContainer::ALIGN_LEFT);
- hb->add_child(key_editor_tab);
- key_editor_tab->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
-
- key_editor = memnew(PropertyEditor);
- key_editor->hide_top_label();
- key_editor->set_name(TTR("Key"));
- key_editor_tab->add_child(key_editor);
-
- key_edit = memnew(AnimationKeyEdit);
- key_edit->undo_redo = undo_redo;
- //key_edit->ke_dialog=key_edit_dialog;
-
- type_menu = memnew(PopupMenu);
- type_menu->set_pass_on_modal_close_click(false);
- add_child(type_menu);
- for (int i = 0; i < Variant::VARIANT_MAX; i++)
- type_menu->add_item(Variant::get_type_name(Variant::Type(i)), i);
- type_menu->connect("id_pressed", this, "_create_value_item");
-
- VBoxContainer *curve_vb = memnew(VBoxContainer);
- curve_vb->set_name(TTR("Transition"));
- HBoxContainer *curve_hb = memnew(HBoxContainer);
- curve_vb->add_child(curve_hb);
-
- curve_linear = memnew(ToolButton);
- curve_linear->set_focus_mode(FOCUS_NONE);
- curve_hb->add_child(curve_linear);
- curve_in = memnew(ToolButton);
- curve_in->set_focus_mode(FOCUS_NONE);
- curve_hb->add_child(curve_in);
- curve_out = memnew(ToolButton);
- curve_out->set_focus_mode(FOCUS_NONE);
- curve_hb->add_child(curve_out);
- curve_inout = memnew(ToolButton);
- curve_inout->set_focus_mode(FOCUS_NONE);
- curve_hb->add_child(curve_inout);
- curve_outin = memnew(ToolButton);
- curve_outin->set_focus_mode(FOCUS_NONE);
- curve_hb->add_child(curve_outin);
- curve_constant = memnew(ToolButton);
- curve_constant->set_focus_mode(FOCUS_NONE);
- curve_hb->add_child(curve_constant);
-
- curve_edit = memnew(AnimationCurveEdit);
- curve_vb->add_child(curve_edit);
- curve_edit->set_v_size_flags(SIZE_EXPAND_FILL);
- key_editor_tab->add_child(curve_vb);
-
- track_name = memnew(LineEdit);
- track_name->set_as_toplevel(true);
- track_name->hide();
- add_child(track_name);
- track_name->connect("text_entered", this, "_track_name_changed");
- track_menu = memnew(PopupMenu);
- track_menu->set_pass_on_modal_close_click(false);
- add_child(track_menu);
- track_menu->connect("id_pressed", this, "_track_menu_selected");
-
- key_editor_tab->hide();
-
- last_idx = 1;
-
- _update_menu();
-
- insert_confirm = memnew(ConfirmationDialog);
- add_child(insert_confirm);
- insert_confirm->connect("confirmed", this, "_confirm_insert_list");
-
- click.click = ClickOver::CLICK_NONE;
-
- name_column_ratio = 0.3;
- timeline_pos = 0;
-
- keying = false;
- insert_frame = 0;
- insert_query = false;
- insert_queue = false;
-
- editor_selection->connect("selection_changed", track_editor, "update");
-
- scale_dialog = memnew(ConfirmationDialog);
- VBoxContainer *vbc = memnew(VBoxContainer);
- scale_dialog->add_child(vbc);
-
- scale = memnew(SpinBox);
- scale->set_min(-99999);
- scale->set_max(99999);
- scale->set_step(0.001);
- vbc->add_margin_child(TTR("Scale Ratio:"), scale);
- scale_dialog->connect("confirmed", this, "_scale");
- add_child(scale_dialog);
-
- call_select = memnew(SceneTreeDialog);
- add_child(call_select);
- call_select->set_title(TTR("Call Functions in Which Node?"));
-
- cleanup_dialog = memnew(ConfirmationDialog);
- add_child(cleanup_dialog);
- VBoxContainer *cleanup_vb = memnew(VBoxContainer);
- cleanup_dialog->add_child(cleanup_vb);
-
- cleanup_keys = memnew(CheckButton);
- cleanup_keys->set_text(TTR("Remove invalid keys"));
- cleanup_keys->set_pressed(true);
- cleanup_vb->add_child(cleanup_keys);
-
- cleanup_tracks = memnew(CheckButton);
- cleanup_tracks->set_text(TTR("Remove unresolved and empty tracks"));
- cleanup_tracks->set_pressed(true);
- cleanup_vb->add_child(cleanup_tracks);
-
- cleanup_all = memnew(CheckButton);
- cleanup_all->set_text(TTR("Clean-up all animations"));
- cleanup_vb->add_child(cleanup_all);
-
- cleanup_dialog->set_title(TTR("Clean-Up Animation(s) (NO UNDO!)"));
- cleanup_dialog->get_ok()->set_text(TTR("Clean-Up"));
-
- cleanup_dialog->connect("confirmed", this, "_menu_track", varray(TRACK_MENU_CLEAN_UP_CONFIRM));
-
- track_editor->set_clip_contents(true);
-}
-
-AnimationKeyEditor::~AnimationKeyEditor() {
-
- memdelete(key_edit);
-}
diff --git a/editor/animation_editor.h b/editor/animation_editor.h
deleted file mode 100644
index 1e593f237c..0000000000
--- a/editor/animation_editor.h
+++ /dev/null
@@ -1,348 +0,0 @@
-/*************************************************************************/
-/* animation_editor.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 ANIMATION_EDITOR_H
-#define ANIMATION_EDITOR_H
-
-#include "scene/gui/control.h"
-#include "scene/gui/file_dialog.h"
-#include "scene/gui/menu_button.h"
-#include "scene/gui/scroll_bar.h"
-#include "scene/gui/slider.h"
-#include "scene/gui/spin_box.h"
-#include "scene/gui/tab_container.h"
-#include "scene/gui/texture_rect.h"
-#include "scene/gui/tool_button.h"
-
-#include "editor_data.h"
-#include "property_editor.h"
-#include "scene/animation/animation_cache.h"
-#include "scene/resources/animation.h"
-#include "scene_tree_editor.h"
-
-class AnimationKeyEdit;
-class AnimationCurveEdit;
-
-class AnimationKeyEditor : public VBoxContainer {
-
- GDCLASS(AnimationKeyEditor, VBoxContainer);
-
- /*
- enum {
-
- MENU_NEW_ANIMATION,
- MENU_OPEN_ANIMATION,
- MENU_EDIT_ANIMATION,
- MENU_CLOSE_ANIMATION,
- MENU_KEYING_ACTIVE,
- MENU_SET_ROOT_NODE,
- MENU_SYNC_TO_PLAYER,
- MENU_ANIM_BASE=100,
- };
-
-*/
-
- enum {
-
- ADD_TRACK_MENU_ADD_VALUE_TRACK,
- ADD_TRACK_MENU_ADD_TRANSFORM_TRACK,
- ADD_TRACK_MENU_ADD_CALL_TRACK,
- TRACK_MENU_SCALE,
- TRACK_MENU_SCALE_PIVOT,
- TRACK_MENU_MOVE_UP,
- TRACK_MENU_MOVE_DOWN,
- TRACK_MENU_REMOVE,
- TRACK_MENU_DUPLICATE,
- TRACK_MENU_DUPLICATE_TRANSPOSE,
- TRACK_MENU_SET_ALL_TRANS_LINEAR,
- TRACK_MENU_SET_ALL_TRANS_CONSTANT,
- TRACK_MENU_SET_ALL_TRANS_OUT,
- TRACK_MENU_SET_ALL_TRANS_IN,
- TRACK_MENU_SET_ALL_TRANS_INOUT,
- TRACK_MENU_SET_ALL_TRANS_OUTIN,
- TRACK_MENU_NEXT_STEP,
- TRACK_MENU_PREV_STEP,
- TRACK_MENU_OPTIMIZE,
- TRACK_MENU_CLEAN_UP,
- TRACK_MENU_CLEAN_UP_CONFIRM,
- CURVE_SET_LINEAR,
- CURVE_SET_IN,
- CURVE_SET_OUT,
- CURVE_SET_INOUT,
- CURVE_SET_OUTIN,
- CURVE_SET_CONSTANT
- };
-
- enum {
- RIGHT_MENU_DUPLICATE,
- RIGHT_MENU_DUPLICATE_TRANSPOSE,
- RIGHT_MENU_REMOVE
- };
-
- struct MouseOver {
-
- enum Over {
- OVER_NONE,
- OVER_NAME,
- OVER_KEY,
- OVER_VALUE,
- OVER_INTERP,
- OVER_WRAP,
- OVER_UP,
- OVER_DOWN,
- OVER_REMOVE,
- OVER_ADD_KEY,
- };
-
- Over over;
- int track;
- int over_key;
-
- } mouse_over;
-
- struct SelectedKey {
-
- int track;
- int key;
- bool operator<(const SelectedKey &p_key) const { return track == p_key.track ? key < p_key.key : track < p_key.track; };
- };
-
- struct KeyInfo {
-
- float pos;
- };
-
- Map<SelectedKey, KeyInfo> selection;
-
- struct ClickOver {
-
- enum Click {
-
- CLICK_NONE,
- CLICK_RESIZE_NAMES,
- CLICK_DRAG_TIMELINE,
- CLICK_MOVE_KEYS,
- CLICK_SELECT_KEYS
-
- };
-
- SelectedKey selk;
- bool shift;
- Click click;
- Point2 at;
- Point2 to;
- } click;
-
- float timeline_pos;
-
- float name_column_ratio;
-
- int track_name_editing;
- int interp_editing;
- int cont_editing;
- int wrap_editing;
- int selected_track;
- int track_ofs[5];
-
- int last_menu_track_opt;
- LineEdit *track_name;
- PopupMenu *track_menu;
- PopupMenu *type_menu;
-
- Control *ec;
- TextureRect *zoomicon;
- HSlider *zoom;
- //MenuButton *menu;
- SpinBox *length;
- Button *loop;
- bool keying;
- ToolButton *edit_button;
- ToolButton *move_up_button;
- ToolButton *move_down_button;
- ToolButton *remove_button;
-
- ToolButton *curve_linear;
- ToolButton *curve_in;
- ToolButton *curve_out;
- ToolButton *curve_inout;
- ToolButton *curve_outin;
- ToolButton *curve_constant;
-
- ConfirmationDialog *optimize_dialog;
- SpinBox *optimize_linear_error;
- SpinBox *optimize_angular_error;
- SpinBox *optimize_max_angle;
-
- ConfirmationDialog *cleanup_dialog;
- CheckButton *cleanup_keys;
- CheckButton *cleanup_tracks;
- CheckButton *cleanup_all;
-
- SpinBox *step;
-
- MenuButton *menu_add_track;
- MenuButton *menu_track;
-
- HScrollBar *h_scroll;
- VScrollBar *v_scroll;
-
- Control *track_editor;
- Control *track_pos;
- TabContainer *key_editor_tab;
-
- ConfirmationDialog *scale_dialog;
- SpinBox *scale;
-
- PropertyEditor *key_editor;
-
- SceneTreeDialog *call_select;
-
- Ref<Animation> animation;
- void _update_paths();
-
- int last_idx;
-
- Node *root;
-
- UndoRedo *undo_redo;
- EditorHistory *history;
- ConfirmationDialog *insert_confirm;
-
- AnimationKeyEdit *key_edit;
- AnimationCurveEdit *curve_edit;
-
- bool inserting;
-
- bool updating;
- bool te_drawing;
-
- void _animation_len_changed(float p_len);
- void _animation_loop_changed();
- void _step_changed(float p_len);
-
- struct InsertData {
-
- Animation::TrackType type;
- NodePath path;
- int track_idx;
- Variant value;
- String query;
- bool advance;
- }; /* insert_data;*/
-
- bool insert_query;
- List<InsertData> insert_data;
- uint64_t insert_frame;
-
- int cvi_track;
- float cvi_pos;
-
- int right_data_size_cache;
-
- EditorSelection *editor_selection;
-
- Label *select_anim_warning;
-
- float _get_zoom_scale() const;
-
- void _track_editor_draw();
- void _track_editor_gui_input(const Ref<InputEvent> &p_input);
- void _track_position_draw();
-
- void _track_name_changed(const String &p_name);
- void _track_menu_selected(int p_idx);
- void _confirm_insert_list();
- int _confirm_insert(InsertData p_id, int p_last_track = -1);
- void _query_insert(const InsertData &p_id);
- void _update_menu();
- bool insert_queue;
- void _insert_delay();
- void _scale();
-
- void _clear_selection();
-
- //void _browse_path();
-
- StringName alc;
-
- void _animation_changed();
- void _animation_optimize();
- void _cleanup_animation(Ref<Animation> p_animation);
-
- void _scroll_changed(double);
-
- void _menu_add_track(int p_type);
- void _menu_track(int p_type);
-
- void _clear_selection_for_anim(const Ref<Animation> &p_anim);
- void _select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos);
- void _curve_transition_changed(float p_what);
-
- PropertyInfo _find_hint_for_track(int p_idx, NodePath &r_base_path);
-
- void _create_value_item(int p_type);
- void _pane_drag(const Point2 &p_delta);
- bool _edit_if_single_selection();
-
- void _toggle_edit_curves();
- void _animation_len_update();
-
- void _add_call_track(const NodePath &p_base);
-
- void _anim_duplicate_keys(bool transpose = false);
- void _anim_delete_keys();
-
- void _root_removed();
-
-protected:
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
- void set_animation(const Ref<Animation> &p_anim);
- Ref<Animation> get_current_animation() const;
- void set_root(Node *p_root);
- Node *get_root() const;
- void update_keying();
- bool has_keying() const;
-
- void cleanup();
-
- void set_anim_pos(float p_pos);
- void insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists = false);
- void insert_value_key(const String &p_property, const Variant &p_value, bool p_advance);
- void insert_transform_key(Spatial *p_node, const String &p_sub, const Transform &p_xform);
-
- void show_select_node_warning(bool p_show) { select_anim_warning->set_visible(p_show); }
- AnimationKeyEditor();
- ~AnimationKeyEditor();
-};
-
-#endif // ANIMATION_EDITOR_H
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
new file mode 100644
index 0000000000..42d5ea120e
--- /dev/null
+++ b/editor/animation_track_editor.cpp
@@ -0,0 +1,5076 @@
+/*************************************************************************/
+/* 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"
+#include "editor/plugins/animation_player_editor_plugin.h"
+#include "editor_node.h"
+#include "editor_scale.h"
+#include "os/keyboard.h"
+#include "scene/main/viewport.h"
+#include "servers/audio/audio_stream.h"
+
+class AnimationTrackKeyEdit : public Object {
+
+ GDCLASS(AnimationTrackKeyEdit, Object);
+
+public:
+ bool setting;
+ bool hidden;
+
+ bool _hide_script_from_inspector() {
+ return true;
+ }
+
+ static void _bind_methods() {
+
+ 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;
+
+ void _fix_node_path(Variant &value) {
+
+ NodePath np = value;
+
+ if (np == NodePath())
+ return;
+
+ Node *root = EditorNode::get_singleton()->get_tree()->get_root();
+
+ Node *np_node = root->get_node(np);
+ ERR_FAIL_COND(!np_node);
+
+ Node *edited_node = root->get_node(base);
+ ERR_FAIL_COND(!edited_node);
+
+ value = edited_node->get_path_to(np_node);
+ }
+
+ void _update_obj(const Ref<Animation> &p_anim) {
+ if (setting)
+ return;
+ if (hidden)
+ return;
+ if (!(animation == p_anim))
+ return;
+ notify_change();
+ }
+
+ void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) {
+ if (hidden)
+ return;
+ if (!(animation == p_anim))
+ return;
+ if (from != key_ofs)
+ return;
+ key_ofs = to;
+ if (setting)
+ return;
+ notify_change();
+ }
+
+ bool _set(const StringName &p_name, const Variant &p_value) {
+
+ int key = animation->track_find_key(track, key_ofs, true);
+ ERR_FAIL_COND_V(key == -1, false);
+
+ String name = p_name;
+ if (name == "time") {
+
+ float new_time = p_value;
+ if (new_time == key_ofs)
+ return true;
+
+ int existing = animation->track_find_key(track, new_time, true);
+
+ setting = true;
+ undo_redo->create_action(TTR("Anim Change Keyframe Time"), UndoRedo::MERGE_ENDS);
+
+ Variant val = animation->track_get_key_value(track, key);
+ float trans = animation->track_get_key_transition(track, key);
+
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key);
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans);
+ undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, new_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans);
+ undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs);
+
+ if (existing != -1) {
+ Variant v = animation->track_get_key_value(track, existing);
+ float trans = animation->track_get_key_transition(track, existing);
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, new_time, v, trans);
+ }
+
+ undo_redo->commit_action();
+ setting = false;
+
+ return true;
+ } else if (name == "easing") {
+
+ float val = p_value;
+ float prev_val = animation->track_get_key_transition(track, key);
+ setting = true;
+ undo_redo->create_action(TTR("Anim Change Transition"), UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", track, key, val);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", track, key, prev_val);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+ setting = false;
+ return true;
+ }
+
+ switch (animation->track_get_type(track)) {
+
+ case Animation::TYPE_TRANSFORM: {
+
+ Dictionary d_old = animation->track_get_key_value(track, key);
+ Dictionary d_new = d_old;
+ d_new[p_name] = p_value;
+ setting = true;
+ undo_redo->create_action(TTR("Anim Change Transform"));
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+ setting = false;
+ return true;
+
+ } break;
+ case Animation::TYPE_VALUE: {
+
+ if (name == "value") {
+
+ Variant value = p_value;
+
+ if (value.get_type() == Variant::NODE_PATH) {
+
+ _fix_node_path(value);
+ }
+
+ setting = true;
+ undo_redo->create_action(TTR("Anim Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ Variant prev = animation->track_get_key_value(track, key);
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, prev);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+ setting = false;
+ return true;
+ }
+
+ } break;
+ case Animation::TYPE_METHOD: {
+
+ Dictionary d_old = animation->track_get_key_value(track, key);
+ Dictionary d_new = d_old;
+
+ bool change_notify_deserved = false;
+ bool mergeable = false;
+
+ if (name == "name") {
+
+ d_new["method"] = p_value;
+ }
+
+ if (name == "arg_count") {
+
+ Vector<Variant> args = d_old["args"];
+ args.resize(p_value);
+ d_new["args"] = args;
+ change_notify_deserved = true;
+ }
+
+ if (name.begins_with("args/")) {
+
+ Vector<Variant> args = d_old["args"];
+ int idx = name.get_slice("/", 1).to_int();
+ ERR_FAIL_INDEX_V(idx, args.size(), false);
+
+ String what = name.get_slice("/", 2);
+ if (what == "type") {
+ Variant::Type t = Variant::Type(int(p_value));
+
+ if (t != args[idx].get_type()) {
+ Variant::CallError err;
+ if (Variant::can_convert(args[idx].get_type(), t)) {
+ Variant old = args[idx];
+ Variant *ptrs[1] = { &old };
+ args[idx] = Variant::construct(t, (const Variant **)ptrs, 1, err);
+ } else {
+
+ args[idx] = Variant::construct(t, NULL, 0, err);
+ }
+ change_notify_deserved = true;
+ d_new["args"] = args;
+ }
+ }
+ if (what == "value") {
+
+ Variant value = p_value;
+ if (value.get_type() == Variant::NODE_PATH) {
+
+ _fix_node_path(value);
+ }
+
+ args[idx] = value;
+ d_new["args"] = args;
+ mergeable = true;
+ }
+ }
+
+ if (mergeable)
+ undo_redo->create_action(TTR("Anim Change Call"), UndoRedo::MERGE_ENDS);
+ else
+ undo_redo->create_action(TTR("Anim Change Call"));
+
+ Variant prev = animation->track_get_key_value(track, key);
+ setting = true;
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+ setting = false;
+ if (change_notify_deserved)
+ notify_change();
+ return true;
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ if (name == "value") {
+
+ Variant value = p_value;
+
+ setting = true;
+ undo_redo->create_action(TTR("Anim Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ float prev = animation->bezier_track_get_key_value(track, key);
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_value", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_value", track, key, prev);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+ setting = false;
+ return true;
+ }
+ if (name == "in_handle") {
+
+ Variant value = p_value;
+
+ setting = true;
+ undo_redo->create_action(TTR("Anim Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ Vector2 prev = animation->bezier_track_get_key_in_handle(track, key);
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_in_handle", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_in_handle", track, key, prev);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+ setting = false;
+ return true;
+ }
+ if (name == "out_handle") {
+
+ Variant value = p_value;
+
+ setting = true;
+ undo_redo->create_action(TTR("Anim Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ Vector2 prev = animation->bezier_track_get_key_out_handle(track, key);
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_out_handle", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_out_handle", track, key, prev);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+ setting = false;
+ return true;
+ }
+
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ if (name == "stream") {
+
+ Ref<AudioStream> stream = p_value;
+
+ setting = true;
+ undo_redo->create_action(TTR("Anim Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ RES prev = animation->audio_track_get_key_stream(track, key);
+ undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_stream", track, key, stream);
+ undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_stream", track, key, prev);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+ setting = false;
+ return true;
+ }
+ if (name == "start_offset") {
+
+ float value = p_value;
+
+ setting = true;
+ undo_redo->create_action(TTR("Anim Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ float prev = animation->audio_track_get_key_start_offset(track, key);
+ undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, prev);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+ setting = false;
+ return true;
+ }
+ if (name == "end_offset") {
+
+ float value = p_value;
+
+ setting = true;
+ undo_redo->create_action(TTR("Anim Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ float prev = animation->audio_track_get_key_end_offset(track, key);
+ undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, prev);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+ setting = false;
+ return true;
+ }
+
+ } break;
+ case Animation::TYPE_ANIMATION: {
+
+ if (name == "animation") {
+
+ StringName name = p_value;
+
+ setting = true;
+ undo_redo->create_action(TTR("Anim Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ StringName prev = animation->animation_track_get_key_animation(track, key);
+ undo_redo->add_do_method(animation.ptr(), "animation_track_set_key_animation", track, key, name);
+ undo_redo->add_undo_method(animation.ptr(), "animation_track_set_key_animation", track, key, prev);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+ setting = false;
+ return true;
+ }
+
+ } break;
+ }
+
+ return false;
+ }
+
+ bool _get(const StringName &p_name, Variant &r_ret) const {
+
+ int key = animation->track_find_key(track, key_ofs, true);
+ ERR_FAIL_COND_V(key == -1, false);
+
+ String name = p_name;
+ if (name == "time") {
+ r_ret = key_ofs;
+ return true;
+ } else if (name == "easing") {
+ r_ret = animation->track_get_key_transition(track, key);
+ return true;
+ }
+
+ switch (animation->track_get_type(track)) {
+
+ case Animation::TYPE_TRANSFORM: {
+
+ Dictionary d = animation->track_get_key_value(track, key);
+ ERR_FAIL_COND_V(!d.has(name), false);
+ r_ret = d[p_name];
+ return true;
+
+ } break;
+ case Animation::TYPE_VALUE: {
+
+ if (name == "value") {
+ r_ret = animation->track_get_key_value(track, key);
+ return true;
+ }
+
+ } break;
+ case Animation::TYPE_METHOD: {
+
+ Dictionary d = animation->track_get_key_value(track, key);
+
+ if (name == "name") {
+
+ ERR_FAIL_COND_V(!d.has("method"), false);
+ r_ret = d["method"];
+ return true;
+ }
+
+ ERR_FAIL_COND_V(!d.has("args"), false);
+
+ Vector<Variant> args = d["args"];
+
+ if (name == "arg_count") {
+
+ r_ret = args.size();
+ return true;
+ }
+
+ if (name.begins_with("args/")) {
+
+ int idx = name.get_slice("/", 1).to_int();
+ ERR_FAIL_INDEX_V(idx, args.size(), false);
+
+ String what = name.get_slice("/", 2);
+ if (what == "type") {
+ r_ret = args[idx].get_type();
+ return true;
+ }
+ if (what == "value") {
+ r_ret = args[idx];
+ return true;
+ }
+ }
+
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ if (name == "value") {
+ r_ret = animation->bezier_track_get_key_value(track, key);
+ return true;
+ }
+ if (name == "in_handle") {
+ r_ret = animation->bezier_track_get_key_in_handle(track, key);
+ return true;
+ }
+ if (name == "out_handle") {
+ r_ret = animation->bezier_track_get_key_out_handle(track, key);
+ return true;
+ }
+
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ if (name == "stream") {
+ r_ret = animation->audio_track_get_key_stream(track, key);
+ return true;
+ }
+ if (name == "start_offset") {
+ r_ret = animation->audio_track_get_key_start_offset(track, key);
+ return true;
+ }
+ if (name == "end_offset") {
+ r_ret = animation->audio_track_get_key_end_offset(track, key);
+ return true;
+ }
+
+ } break;
+ case Animation::TYPE_ANIMATION: {
+
+ if (name == "animation") {
+ r_ret = animation->animation_track_get_key_animation(track, key);
+ return true;
+ }
+
+ } break;
+ }
+
+ return false;
+ }
+ void _get_property_list(List<PropertyInfo> *p_list) const {
+
+ if (animation.is_null())
+ return;
+
+ ERR_FAIL_INDEX(track, animation->get_track_count());
+ int key = animation->track_find_key(track, key_ofs, true);
+ ERR_FAIL_COND(key == -1);
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01"));
+
+ switch (animation->track_get_type(track)) {
+
+ case Animation::TYPE_TRANSFORM: {
+
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "location"));
+ p_list->push_back(PropertyInfo(Variant::QUAT, "rotation"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
+
+ } break;
+ case Animation::TYPE_VALUE: {
+
+ Variant v = animation->track_get_key_value(track, key);
+
+ if (hint.type != Variant::NIL) {
+
+ PropertyInfo pi = hint;
+ pi.name = "value";
+ p_list->push_back(pi);
+ } else {
+
+ PropertyHint hint = PROPERTY_HINT_NONE;
+ String hint_string;
+
+ if (v.get_type() == Variant::OBJECT) {
+ //could actually check the object property if exists..? yes i will!
+ Ref<Resource> res = v;
+ if (res.is_valid()) {
+
+ hint = PROPERTY_HINT_RESOURCE_TYPE;
+ hint_string = res->get_class();
+ }
+ }
+
+ if (v.get_type() != Variant::NIL)
+ p_list->push_back(PropertyInfo(v.get_type(), "value", hint, hint_string));
+ }
+
+ } break;
+ case Animation::TYPE_METHOD: {
+
+ p_list->push_back(PropertyInfo(Variant::STRING, "name"));
+ p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,5,1"));
+
+ Dictionary d = animation->track_get_key_value(track, key);
+ ERR_FAIL_COND(!d.has("args"));
+ Vector<Variant> args = d["args"];
+ String vtypes;
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+
+ if (i > 0)
+ vtypes += ",";
+ vtypes += Variant::get_type_name(Variant::Type(i));
+ }
+
+ for (int i = 0; i < args.size(); i++) {
+
+ p_list->push_back(PropertyInfo(Variant::INT, "args/" + itos(i) + "/type", PROPERTY_HINT_ENUM, vtypes));
+ if (args[i].get_type() != Variant::NIL)
+ p_list->push_back(PropertyInfo(args[i].get_type(), "args/" + itos(i) + "/value"));
+ }
+
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "value"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "in_handle"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "out_handle"));
+
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "start_offset", PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "end_offset", PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater"));
+
+ } break;
+ case Animation::TYPE_ANIMATION: {
+
+ String animations;
+
+ if (root_path && root_path->has_node(animation->track_get_path(track))) {
+
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node(animation->track_get_path(track)));
+ if (ap) {
+ List<StringName> anims;
+ ap->get_animation_list(&anims);
+ for (List<StringName>::Element *E = anims.front(); E; E = E->next()) {
+ if (animations != String()) {
+ animations += ",";
+ }
+
+ animations += String(E->get());
+ }
+ }
+ }
+
+ if (animations != String()) {
+ animations += ",";
+ }
+ animations += "[stop]";
+
+ p_list->push_back(PropertyInfo(Variant::STRING, "animation", PROPERTY_HINT_ENUM, animations));
+
+ } break;
+ }
+
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE) {
+ p_list->push_back(PropertyInfo(Variant::REAL, "easing", PROPERTY_HINT_EXP_EASING));
+ }
+ }
+
+ UndoRedo *undo_redo;
+ Ref<Animation> animation;
+ int track;
+ float key_ofs;
+ Node *root_path;
+
+ PropertyInfo hint;
+ NodePath base;
+
+ void notify_change() {
+
+ _change_notify();
+ }
+
+ Node *get_root_path() {
+ return root_path;
+ }
+
+ AnimationTrackKeyEdit() {
+ hidden = true;
+ key_ofs = 0;
+ track = -1;
+ setting = false;
+ root_path = NULL;
+ }
+};
+
+void AnimationTimelineEdit::_zoom_changed(double) {
+
+ update();
+ play_position->update();
+ emit_signal("zoom_changed");
+}
+
+float AnimationTimelineEdit::get_zoom_scale() const {
+
+ float zv = zoom->get_value();
+ if (zv < 1) {
+ zv = 1.0 - zv;
+ return Math::pow(1.0f + zv, 8.0f) * 100;
+ } else {
+ return 1.0 / Math::pow(zv, 8.0f) * 100;
+ }
+}
+
+void AnimationTimelineEdit::_anim_length_changed(double p_new_len) {
+
+ if (editing)
+ return;
+
+ p_new_len = MAX(0.001, p_new_len);
+
+ editing = true;
+ *block_animation_update_ptr = true;
+ undo_redo->create_action("Change animation length");
+ undo_redo->add_do_method(animation.ptr(), "set_length", p_new_len);
+ undo_redo->add_undo_method(animation.ptr(), "set_length", animation->get_length());
+ undo_redo->commit_action();
+ *block_animation_update_ptr = false;
+ editing = false;
+ update();
+
+ emit_signal("length_changed", p_new_len);
+}
+
+void AnimationTimelineEdit::_anim_loop_pressed() {
+
+ *block_animation_update_ptr = true;
+ undo_redo->create_action("Change animation loop");
+ undo_redo->add_do_method(animation.ptr(), "set_loop", loop->is_pressed());
+ undo_redo->add_undo_method(animation.ptr(), "set_loop", animation->has_loop());
+ undo_redo->commit_action();
+ *block_animation_update_ptr = false;
+}
+
+int AnimationTimelineEdit::get_buttons_width() const {
+
+ Ref<Texture> interp_mode = get_icon("TrackContinuous", "EditorIcons");
+ Ref<Texture> interp_type = get_icon("InterpRaw", "EditorIcons");
+ Ref<Texture> loop_type = get_icon("InterpWrapClamp", "EditorIcons");
+ Ref<Texture> remove_icon = get_icon("Remove", "EditorIcons");
+ Ref<Texture> down_icon = get_icon("select_arrow", "Tree");
+
+ int total_w = interp_mode->get_width() + interp_type->get_width() + loop_type->get_width() + remove_icon->get_width();
+ total_w += (down_icon->get_width() + 4 * EDSCALE) * 4;
+
+ return total_w;
+}
+
+int AnimationTimelineEdit::get_name_limit() const {
+
+ Ref<Texture> hsize_icon = get_icon("Hsize", "EditorIcons");
+
+ int limit = MAX(name_limit, add_track->get_minimum_size().width + hsize_icon->get_width());
+
+ limit = MIN(limit, get_size().width - get_buttons_width() - 1);
+
+ return limit;
+}
+
+void AnimationTimelineEdit::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ add_track->set_icon(get_icon("Add", "EditorIcons"));
+ loop->set_icon(get_icon("Loop", "EditorIcons"));
+ time_icon->set_texture(get_icon("Time", "EditorIcons"));
+
+ add_track->get_popup()->clear();
+ add_track->get_popup()->add_icon_item(get_icon("KeyValue", "EditorIcons"), TTR("Property Track"));
+ add_track->get_popup()->add_icon_item(get_icon("KeyXform", "EditorIcons"), TTR("3D Transform Track"));
+ add_track->get_popup()->add_icon_item(get_icon("KeyCall", "EditorIcons"), TTR("Call Method Track"));
+ add_track->get_popup()->add_icon_item(get_icon("KeyBezier", "EditorIcons"), TTR("Bezier Curve Track"));
+ add_track->get_popup()->add_icon_item(get_icon("KeyAudio", "EditorIcons"), TTR("Audio Playback Track"));
+ add_track->get_popup()->add_icon_item(get_icon("KeyAnimation", "EditorIcons"), TTR("Animation Playback Track"));
+ }
+
+ if (p_what == NOTIFICATION_RESIZED) {
+ 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();
+
+ if (!animation.is_valid())
+ return;
+
+ Ref<Font> font = get_font("font", "Label");
+ Color color = get_color("font_color", "Label");
+
+ int zoomw = key_range;
+ float scale = get_zoom_scale();
+ int h = get_size().height;
+
+ float l = animation->get_length();
+ if (l <= 0)
+ l = 0.001; //avoid crashor
+
+ Ref<Texture> hsize_icon = get_icon("Hsize", "EditorIcons");
+ hsize_rect = Rect2(get_name_limit() - hsize_icon->get_width() - 2 * EDSCALE, (get_size().height - hsize_icon->get_height()) / 2, hsize_icon->get_width(), hsize_icon->get_height());
+ draw_texture(hsize_icon, hsize_rect.position);
+
+ float keys_from = get_value();
+ float keys_to = keys_from + zoomw / scale;
+
+ {
+ float time_min = 0;
+ float time_max = animation->get_length();
+ for (int i = 0; i < animation->get_track_count(); i++) {
+
+ if (animation->track_get_key_count(i) > 0) {
+
+ float beg = animation->track_get_key_time(i, 0);
+ /*if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
+ beg += animation->bezier_track_get_key_in_handle(i, 0).x;
+ }* not worth it since they have no use */
+
+ if (beg < time_min)
+ time_min = beg;
+
+ float end = animation->track_get_key_time(i, animation->track_get_key_count(i) - 1);
+ /*if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
+ end += animation->bezier_track_get_key_out_handle(i, animation->track_get_key_count(i) - 1).x;
+ } not worth it since they have no use */
+
+ if (end > time_max)
+ time_max = end;
+ }
+ }
+
+ float extra = (zoomw / scale) * 0.5;
+
+ //if (time_min < -0.001)
+ // time_min -= extra;
+ time_max += extra;
+ set_min(time_min);
+ set_max(time_max);
+
+ if (zoomw / scale < (time_max - time_min)) {
+ hscroll->show();
+
+ } else {
+
+ hscroll->hide();
+ }
+ }
+
+ set_page(zoomw / scale);
+
+ int end_px = (l - get_value()) * scale;
+ int begin_px = -get_value() * scale;
+ Color notimecol = get_color("dark_color_2", "Editor");
+ Color timecolor = color;
+ timecolor.a = 0.2;
+ Color linecolor = color;
+ linecolor.a = 0.2;
+
+ {
+
+ draw_rect(Rect2(Point2(get_name_limit(), 0), Point2(zoomw - 1, h)), notimecol);
+
+ if (begin_px < zoomw && end_px > 0) {
+
+ if (begin_px < 0)
+ begin_px = 0;
+ if (end_px > zoomw)
+ end_px = zoomw;
+
+ draw_rect(Rect2(Point2(get_name_limit() + begin_px, 0), Point2(end_px - begin_px - 1, h)), timecolor);
+ }
+ }
+
+ Color color_time_sec = color;
+ Color color_time_dec = color;
+ color_time_dec.a *= 0.5;
+#define SC_ADJ 100
+ int min = 30;
+ int dec = 1;
+ int step = 1;
+ int decimals = 2;
+ bool step_found = false;
+
+ const int period_width = font->get_char_size('.').width;
+ int max_digit_width = font->get_char_size('0').width;
+ for (int i = 1; i <= 9; i++) {
+ const int digit_width = font->get_char_size('0' + i).width;
+ max_digit_width = MAX(digit_width, max_digit_width);
+ }
+ const int max_sc = int(Math::ceil(zoomw / scale));
+ const int max_sc_width = String::num(max_sc).length() * max_digit_width;
+
+ while (!step_found) {
+
+ min = max_sc_width;
+ if (decimals > 0)
+ min += period_width + max_digit_width * decimals;
+
+ static const int _multp[3] = { 1, 2, 5 };
+ for (int i = 0; i < 3; i++) {
+
+ step = (_multp[i] * dec);
+ if (step * scale / SC_ADJ > min) {
+ step_found = true;
+ break;
+ }
+ }
+ if (step_found)
+ break;
+ dec *= 10;
+ decimals--;
+ if (decimals < 0)
+ decimals = 0;
+ }
+
+ for (int i = 0; i < zoomw; i++) {
+
+ float pos = get_value() + double(i) / scale;
+ float prev = get_value() + (double(i) - 1.0) / scale;
+
+ int sc = int(Math::floor(pos * SC_ADJ));
+ int prev_sc = int(Math::floor(prev * SC_ADJ));
+ bool sub = (sc % SC_ADJ);
+
+ if ((sc / step) != (prev_sc / step) || (prev_sc < 0 && sc >= 0)) {
+
+ int scd = sc < 0 ? prev_sc : sc;
+ draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor);
+ draw_string(font, Point2(get_name_limit() + i + 3, (h - font->get_height()) / 2 + font->get_ascent()).floor(), String::num((scd - (scd % step)) / double(SC_ADJ), decimals), sub ? color_time_dec : color_time_sec, zoomw - i);
+ }
+ }
+
+ draw_line(Vector2(0, get_size().height), get_size(), linecolor);
+ }
+}
+
+void AnimationTimelineEdit::set_animation(const Ref<Animation> &p_animation) {
+ 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();
+}
+
+Size2 AnimationTimelineEdit::get_minimum_size() const {
+
+ Size2 ms = add_track->get_minimum_size();
+ Ref<Font> font = get_font("font", "Label");
+ ms.height = MAX(ms.height, font->get_height());
+ ms.width = get_buttons_width() + add_track->get_minimum_size().width + get_icon("Hsize", "EditorIcons")->get_width() + 2;
+ return ms;
+}
+
+void AnimationTimelineEdit::set_block_animation_update_ptr(bool *p_block_ptr) {
+ block_animation_update_ptr = p_block_ptr;
+}
+
+void AnimationTimelineEdit::set_undo_redo(UndoRedo *p_undo_redo) {
+ undo_redo = p_undo_redo;
+}
+
+void AnimationTimelineEdit::set_zoom(Range *p_zoom) {
+ zoom = p_zoom;
+ zoom->connect("value_changed", this, "_zoom_changed");
+}
+
+void AnimationTimelineEdit::set_play_position(float p_pos) {
+
+ play_position_pos = p_pos;
+ play_position->update();
+}
+
+float AnimationTimelineEdit::get_play_position() const {
+ return play_position_pos;
+}
+
+void AnimationTimelineEdit::update_play_position() {
+ play_position->update();
+}
+
+void AnimationTimelineEdit::update_values() {
+
+ if (!animation.is_valid() || editing)
+ return;
+
+ editing = true;
+ length->set_value(animation->get_length());
+ loop->set_pressed(animation->has_loop());
+ editing = false;
+}
+
+void AnimationTimelineEdit::_play_position_draw() {
+
+ if (!animation.is_valid() || play_position_pos < 0)
+ return;
+
+ float scale = get_zoom_scale();
+ int h = play_position->get_size().height;
+
+ int px = (-get_value() + play_position_pos) * scale + get_name_limit();
+
+ if (px >= get_name_limit() && px < (play_position->get_size().width - get_buttons_width())) {
+ Color color = get_color("accent_color", "Editor");
+ play_position->draw_line(Point2(px, 0), Point2(px, h), color);
+ }
+}
+
+void AnimationTimelineEdit::_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 && hsize_rect.has_point(mb->get_position())) {
+
+ dragging_hsize = true;
+ dragging_hsize_from = mb->get_position().x;
+ dragging_hsize_at = name_limit;
+ }
+
+ if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && dragging_hsize) {
+ dragging_hsize = false;
+ }
+ if (mb.is_valid() && mb->get_position().x > get_name_limit() && mb->get_position().x < (get_size().width - get_buttons_width())) {
+
+ if (!panning_timeline && mb->get_button_index() == BUTTON_LEFT) {
+ int x = mb->get_position().x - get_name_limit();
+
+ float ofs = x / get_zoom_scale() + get_value();
+ emit_signal("timeline_changed", ofs, false);
+ dragging_timeline = true;
+ }
+ if (!dragging_timeline && mb->get_button_index() == BUTTON_MIDDLE) {
+ int x = mb->get_position().x - get_name_limit();
+ panning_timeline_from = x / get_zoom_scale();
+ panning_timeline = true;
+ panning_timeline_at = get_value();
+ }
+ }
+
+ if (dragging_timeline && mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && !mb->is_pressed()) {
+ dragging_timeline = false;
+ }
+
+ if (panning_timeline && mb.is_valid() && mb->get_button_index() == BUTTON_MIDDLE && !mb->is_pressed()) {
+ panning_timeline = false;
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+
+ if (mm.is_valid()) {
+
+ if (dragging_hsize) {
+ int ofs = mm->get_position().x - dragging_hsize_from;
+ name_limit = dragging_hsize_at + ofs;
+ update();
+ emit_signal("name_limit_changed");
+ play_position->update();
+ }
+ if (dragging_timeline) {
+ int x = mm->get_position().x - get_name_limit();
+ float ofs = x / get_zoom_scale() + get_value();
+ emit_signal("timeline_changed", ofs, false);
+ }
+ if (panning_timeline) {
+ int x = mm->get_position().x - get_name_limit();
+ float ofs = x / get_zoom_scale();
+ float diff = ofs - panning_timeline_from;
+ set_value(panning_timeline_at - diff);
+ }
+ }
+}
+
+void AnimationTimelineEdit::set_hscroll(HScrollBar *p_hscroll) {
+
+ hscroll = p_hscroll;
+}
+
+void AnimationTimelineEdit::_track_added(int p_track) {
+ emit_signal("track_added", p_track);
+}
+
+void AnimationTimelineEdit::_bind_methods() {
+ ClassDB::bind_method("_zoom_changed", &AnimationTimelineEdit::_zoom_changed);
+ ClassDB::bind_method("_anim_length_changed", &AnimationTimelineEdit::_anim_length_changed);
+ ClassDB::bind_method("_anim_loop_pressed", &AnimationTimelineEdit::_anim_loop_pressed);
+ ClassDB::bind_method("_play_position_draw", &AnimationTimelineEdit::_play_position_draw);
+ ClassDB::bind_method("_gui_input", &AnimationTimelineEdit::_gui_input);
+ ClassDB::bind_method("_track_added", &AnimationTimelineEdit::_track_added);
+
+ ADD_SIGNAL(MethodInfo("zoom_changed"));
+ ADD_SIGNAL(MethodInfo("name_limit_changed"));
+ ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::REAL, "position"), PropertyInfo(Variant::BOOL, "drag")));
+ ADD_SIGNAL(MethodInfo("track_added", PropertyInfo(Variant::INT, "track")));
+ ADD_SIGNAL(MethodInfo("length_changed", PropertyInfo(Variant::REAL, "size")));
+}
+
+AnimationTimelineEdit::AnimationTimelineEdit() {
+
+ block_animation_update_ptr = NULL;
+ editing = false;
+ name_limit = 150;
+ zoom = NULL;
+
+ play_position_pos = 0;
+ play_position = memnew(Control);
+ play_position->set_mouse_filter(MOUSE_FILTER_PASS);
+ add_child(play_position);
+ play_position->set_anchors_and_margins_preset(PRESET_WIDE);
+ play_position->connect("draw", this, "_play_position_draw");
+
+ add_track = memnew(MenuButton);
+ add_track->set_position(Vector2(0, 0));
+ add_child(add_track);
+ add_track->set_text(TTR("Add Track"));
+
+ len_hb = memnew(HBoxContainer);
+
+ Control *expander = memnew(Control);
+ expander->set_h_size_flags(SIZE_EXPAND_FILL);
+ len_hb->add_child(expander);
+ time_icon = memnew(TextureRect);
+ time_icon->set_v_size_flags(SIZE_SHRINK_CENTER);
+ time_icon->set_tooltip(TTR("Animation Length Time (seconds)"));
+ len_hb->add_child(time_icon);
+ length = memnew(EditorSpinSlider);
+ length->set_min(0.001);
+ length->set_max(3600);
+ length->set_step(0.01);
+ length->set_allow_greater(true);
+ length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0));
+ length->set_hide_slider(true);
+ length->set_tooltip(TTR("Animation Length Time (seconds)"));
+ length->connect("value_changed", this, "_anim_length_changed");
+ len_hb->add_child(length);
+ loop = memnew(ToolButton);
+ loop->set_tooltip(TTR("Animation Looping"));
+ loop->connect("pressed", this, "_anim_loop_pressed");
+ loop->set_toggle_mode(true);
+ len_hb->add_child(loop);
+ add_child(len_hb);
+
+ add_track->hide();
+ add_track->get_popup()->connect("index_pressed", this, "_track_added");
+ len_hb->hide();
+
+ panning_timeline = false;
+ dragging_timeline = false;
+ dragging_hsize = false;
+}
+
+////////////////////////////////////
+
+void AnimationTrackEdit::_notification(int p_what) {
+ if (p_what == NOTIFICATION_DRAW) {
+ if (animation.is_null())
+ return;
+ ERR_FAIL_INDEX(track, animation->get_track_count());
+
+ int limit = timeline->get_name_limit();
+
+ if (has_focus()) {
+ Color accent = get_color("accent_color", "Editor");
+ accent.a *= 0.7;
+ draw_rect(Rect2(Point2(), get_size()), accent, false);
+ }
+
+ Ref<Font> font = get_font("font", "Label");
+ Color color = get_color("font_color", "Label");
+ Ref<Texture> type_icons[6] = {
+ get_icon("KeyValue", "EditorIcons"),
+ get_icon("KeyXform", "EditorIcons"),
+ get_icon("KeyCall", "EditorIcons"),
+ get_icon("KeyBezier", "EditorIcons"),
+ get_icon("KeyAudio", "EditorIcons"),
+ get_icon("KeyAnimation", "EditorIcons")
+ };
+ int hsep = get_constant("hseparation", "ItemList");
+ Color linecolor = color;
+ linecolor.a = 0.2;
+
+ // NAMES AND ICONS //
+
+ {
+
+ Ref<Texture> check = animation->track_is_enabled(track) ? get_icon("checked", "CheckBox") : get_icon("unchecked", "CheckBox");
+
+ int ofs = in_group ? check->get_width() : 0; //not the best reference for margin but..
+
+ check_rect = Rect2(Point2(ofs, int(get_size().height - check->get_height()) / 2), check->get_size());
+
+ draw_texture(check, check_rect.position);
+
+ ofs += check->get_width() + hsep;
+
+ Ref<Texture> type_icon = type_icons[animation->track_get_type(track)];
+
+ draw_texture(type_icon, Point2(ofs, int(get_size().height - type_icon->get_height()) / 2));
+ ofs += type_icon->get_width() + hsep;
+
+ NodePath path = animation->track_get_path(track);
+
+ Node *node = NULL;
+
+ if (root && root->has_node(path)) {
+ node = root->get_node(path);
+ }
+
+ String text;
+ Color text_color = color;
+ if (node && EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
+ text_color = get_color("accent_color", "Editor");
+ }
+
+ if (in_group) {
+
+ if (animation->track_get_type(track) == Animation::TYPE_METHOD) {
+ text = TTR("Functions:");
+ } else if (animation->track_get_type(track) == Animation::TYPE_AUDIO) {
+ text = TTR("Audio Clips:");
+ } else if (animation->track_get_type(track) == Animation::TYPE_ANIMATION) {
+ text = TTR("Anim Clips:");
+ } else {
+ Vector<StringName> sn = path.get_subnames();
+ for (int i = 0; i < sn.size(); i++) {
+ if (i > 0) {
+ text += ".";
+ }
+ text += sn[i];
+ }
+ }
+ text_color.a *= 0.7;
+ } else if (node) {
+ Ref<Texture> icon;
+ if (has_icon(node->get_class(), "EditorIcons")) {
+ icon = get_icon(node->get_class(), "EditorIcons");
+ } else {
+ icon = get_icon("Node", "EditorIcons");
+ }
+
+ draw_texture(icon, Point2(ofs, int(get_size().height - icon->get_height()) / 2));
+ icon_cache = icon;
+
+ text = node->get_name();
+ ofs += hsep;
+ ofs += icon->get_width();
+ Vector<StringName> sn = path.get_subnames();
+ for (int i = 0; i < sn.size(); i++) {
+ text += ".";
+ text += sn[i];
+ }
+ } else {
+ icon_cache = type_icon;
+
+ text = path;
+ }
+
+ path_cache = text;
+
+ path_rect = Rect2(ofs, 0, limit - ofs - hsep, get_size().height);
+
+ Vector2 string_pos = Point2(ofs, (get_size().height - font->get_height()) / 2 + font->get_ascent());
+ string_pos = string_pos.floor();
+ draw_string(font, string_pos, text, text_color, limit - ofs - hsep);
+
+ draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor);
+ }
+
+ // KEYFAMES //
+
+ draw_bg(limit, get_size().width - timeline->get_buttons_width());
+
+ {
+
+ float scale = timeline->get_zoom_scale();
+ int limit_end = get_size().width - timeline->get_buttons_width();
+
+ for (int i = 0; i < animation->track_get_key_count(track); i++) {
+
+ float offset = animation->track_get_key_time(track, i) - timeline->get_value();
+ if (editor->is_key_selected(track, i) && editor->is_moving_selection()) {
+ offset += editor->get_moving_selection_offset();
+ }
+ offset = offset * scale + limit;
+ if (i < animation->track_get_key_count(track) - 1) {
+
+ float offset_n = animation->track_get_key_time(track, i + 1) - timeline->get_value();
+ if (editor->is_key_selected(track, i + 1) && editor->is_moving_selection()) {
+ offset_n += editor->get_moving_selection_offset();
+ }
+ offset_n = offset_n * scale + limit;
+
+ draw_key_link(i, scale, int(offset), int(offset_n), limit, limit_end);
+ }
+
+ draw_key(i, scale, int(offset), editor->is_key_selected(track, i), limit, limit_end);
+ }
+ }
+
+ draw_fg(limit, get_size().width - timeline->get_buttons_width());
+
+ // BUTTONS //
+ {
+
+ Ref<Texture> wrap_icon[2] = {
+ get_icon("InterpWrapClamp", "EditorIcons"),
+ get_icon("InterpWrapLoop", "EditorIcons"),
+ };
+
+ Ref<Texture> interp_icon[3] = {
+ get_icon("InterpRaw", "EditorIcons"),
+ get_icon("InterpLinear", "EditorIcons"),
+ get_icon("InterpCubic", "EditorIcons")
+ };
+ Ref<Texture> cont_icon[4] = {
+ get_icon("TrackContinuous", "EditorIcons"),
+ get_icon("TrackDiscrete", "EditorIcons"),
+ get_icon("TrackTrigger", "EditorIcons"),
+ get_icon("TrackCapture", "EditorIcons")
+ };
+
+ int ofs = get_size().width - timeline->get_buttons_width();
+
+ Ref<Texture> down_icon = get_icon("select_arrow", "Tree");
+
+ draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor);
+
+ ofs += hsep;
+ {
+ //callmode
+
+ Animation::UpdateMode update_mode;
+
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE) {
+ update_mode = animation->value_track_get_update_mode(track);
+ } else {
+ update_mode = Animation::UPDATE_CONTINUOUS;
+ }
+
+ Ref<Texture> update_icon = cont_icon[update_mode];
+
+ update_mode_rect.position.x = ofs;
+ update_mode_rect.position.y = int(get_size().height - update_icon->get_height()) / 2;
+ update_mode_rect.size = update_icon->get_size();
+
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE) {
+ draw_texture(update_icon, update_mode_rect.position);
+ }
+ //make it easier to click
+ update_mode_rect.position.y = 0;
+ update_mode_rect.size.y = get_size().height;
+
+ ofs += update_icon->get_width() + hsep;
+ update_mode_rect.size.x += hsep;
+
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE) {
+ draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
+ update_mode_rect.size.x += down_icon->get_width();
+ bezier_edit_rect = Rect2();
+ } else if (animation->track_get_type(track) == Animation::TYPE_BEZIER) {
+ Ref<Texture> bezier_icon = get_icon("EditBezier", "EditorIcons");
+ update_mode_rect.size.x += down_icon->get_width();
+ bezier_edit_rect.position = update_mode_rect.position + (update_mode_rect.size - bezier_icon->get_size()) / 2;
+ bezier_edit_rect.size = bezier_icon->get_size();
+ draw_texture(bezier_icon, bezier_edit_rect.position);
+ update_mode_rect = Rect2();
+ } else {
+ update_mode_rect = Rect2();
+ bezier_edit_rect = Rect2();
+ }
+
+ ofs += down_icon->get_width();
+ draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor);
+ ofs += hsep;
+ }
+
+ {
+ //interp
+
+ Animation::InterpolationType interp_mode = animation->track_get_interpolation_type(track);
+
+ Ref<Texture> icon = interp_icon[interp_mode];
+
+ interp_mode_rect.position.x = ofs;
+ interp_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
+ interp_mode_rect.size = icon->get_size();
+
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM) {
+ draw_texture(icon, interp_mode_rect.position);
+ }
+ //make it easier to click
+ interp_mode_rect.position.y = 0;
+ interp_mode_rect.size.y = get_size().height;
+
+ ofs += icon->get_width() + hsep;
+ interp_mode_rect.size.x += hsep;
+
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM) {
+ draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
+ interp_mode_rect.size.x += down_icon->get_width();
+ } else {
+ interp_mode_rect = Rect2();
+ }
+
+ ofs += down_icon->get_width();
+ draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor);
+ ofs += hsep;
+ }
+
+ {
+ //loop
+
+ bool loop_wrap = animation->track_get_interpolation_loop_wrap(track);
+
+ Ref<Texture> icon = wrap_icon[loop_wrap ? 1 : 0];
+
+ loop_mode_rect.position.x = ofs;
+ loop_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
+ loop_mode_rect.size = icon->get_size();
+
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM) {
+ draw_texture(icon, loop_mode_rect.position);
+ }
+
+ loop_mode_rect.position.y = 0;
+ loop_mode_rect.size.y = get_size().height;
+
+ ofs += icon->get_width() + hsep;
+ loop_mode_rect.size.x += hsep;
+
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM) {
+ draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
+ loop_mode_rect.size.x += down_icon->get_width();
+ } else {
+ loop_mode_rect = Rect2();
+ }
+
+ ofs += down_icon->get_width();
+ draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor);
+ ofs += hsep;
+ }
+
+ {
+ //erase
+
+ Ref<Texture> icon = get_icon("Remove", "EditorIcons");
+
+ remove_rect.position.x = ofs + ((get_size().width - ofs) - icon->get_width()) / 2;
+ remove_rect.position.y = int(get_size().height - icon->get_height()) / 2;
+ remove_rect.size = icon->get_size();
+
+ draw_texture(icon, remove_rect.position);
+ }
+ }
+
+ if (in_group) {
+ draw_line(Vector2(timeline->get_name_limit(), get_size().height), get_size(), linecolor);
+ } else {
+ draw_line(Vector2(0, get_size().height), get_size(), linecolor);
+ }
+
+ if (dropping_at != 0) {
+ Color drop_color = get_color("accent_color", "Editor");
+ if (dropping_at < 0) {
+ draw_line(Vector2(0, 0), Vector2(get_size().width, 0), drop_color);
+ } else {
+ draw_line(Vector2(0, get_size().height), get_size(), drop_color);
+ }
+ }
+ }
+
+ if (p_what == NOTIFICATION_MOUSE_EXIT || p_what == NOTIFICATION_DRAG_END) {
+ cancel_drop();
+ }
+}
+
+int AnimationTrackEdit::get_key_height() const {
+ if (!animation.is_valid())
+ return 0;
+
+ return type_icon->get_height();
+}
+Rect2 AnimationTrackEdit::get_key_rect(int p_index, float p_pixels_sec) {
+
+ if (!animation.is_valid())
+ return Rect2();
+ Rect2 rect = Rect2(-type_icon->get_width() / 2, 0, type_icon->get_width(), get_size().height);
+
+ //make it a big easier to click
+ rect.position.x -= rect.size.x * 0.5;
+ rect.size.x *= 2;
+ return rect;
+}
+
+bool AnimationTrackEdit::is_key_selectable_by_distance() const {
+ return true;
+}
+
+void AnimationTrackEdit::draw_key_link(int p_index, float p_pixels_sec, int p_x, int p_next_x, int p_clip_left, int p_clip_right) {
+ if (p_next_x < p_clip_left)
+ return;
+ if (p_x > p_clip_right)
+ return;
+
+ Variant current = animation->track_get_key_value(get_track(), p_index);
+ Variant next = animation->track_get_key_value(get_track(), p_index + 1);
+ if (current != next)
+ return;
+
+ Color color = get_color("font_color", "Label");
+ color.a = 0.5;
+
+ int from_x = MAX(p_x, p_clip_left);
+ int to_x = MIN(p_next_x, p_clip_right);
+
+ draw_line(Point2(from_x + 1, get_size().height / 2), Point2(to_x, get_size().height / 2), color, 2);
+}
+
+void AnimationTrackEdit::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
+
+ if (!animation.is_valid())
+ return;
+
+ if (p_x < p_clip_left || p_x > p_clip_right)
+ return;
+
+ Vector2 ofs(p_x - type_icon->get_width() / 2, int(get_size().height - type_icon->get_height()) / 2);
+
+ if (animation->track_get_type(track) == Animation::TYPE_METHOD) {
+ Ref<Font> font = get_font("font", "Label");
+ Color color = get_color("font_color", "Label");
+ color.a = 0.5;
+
+ Dictionary d = animation->track_get_key_value(track, p_index);
+ String text;
+
+ if (d.has("method"))
+ text += String(d["method"]);
+ text += "(";
+ Vector<Variant> args;
+ if (d.has("args"))
+ args = d["args"];
+ for (int i = 0; i < args.size(); i++) {
+
+ if (i > 0)
+ text += ", ";
+ text += String(args[i]);
+ }
+ text += ")";
+
+ int limit = MAX(0, p_clip_right - p_x - type_icon->get_width());
+ if (limit > 0) {
+ draw_string(font, Vector2(p_x + type_icon->get_width(), int(get_size().height - font->get_height()) / 2 + font->get_ascent()), text, color, limit);
+ }
+ }
+ if (p_selected) {
+ draw_texture(selected_icon, ofs);
+ } else {
+ draw_texture(type_icon, ofs);
+ }
+}
+
+//helper
+void AnimationTrackEdit::draw_rect_clipped(const Rect2 &p_rect, const Color &p_color, bool p_filled) {
+
+ int clip_left = timeline->get_name_limit();
+ int clip_right = get_size().width - timeline->get_buttons_width();
+
+ if (p_rect.position.x > clip_right)
+ return;
+ if (p_rect.position.x + p_rect.size.x < clip_left)
+ return;
+ Rect2 clip = Rect2(clip_left, 0, clip_right - clip_left, get_size().height);
+ draw_rect(clip.clip(p_rect), p_color, p_filled);
+}
+
+void AnimationTrackEdit::draw_bg(int p_clip_left, int p_clip_right) {
+}
+
+void AnimationTrackEdit::draw_fg(int p_clip_left, int p_clip_right) {
+}
+
+void AnimationTrackEdit::draw_texture_clipped(const Ref<Texture> &p_texture, const Vector2 &p_pos) {
+
+ draw_texture_region_clipped(p_texture, Rect2(p_pos, p_texture->get_size()), Rect2(Point2(), p_texture->get_size()));
+}
+
+void AnimationTrackEdit::draw_texture_region_clipped(const Ref<Texture> &p_texture, const Rect2 &p_rect, const Rect2 &p_region) {
+
+ int clip_left = timeline->get_name_limit();
+ int clip_right = get_size().width - timeline->get_buttons_width();
+
+ //clip left and right
+ if (clip_left > p_rect.position.x + p_rect.size.x)
+ return;
+ if (clip_right < p_rect.position.x)
+ return;
+
+ Rect2 rect = p_rect;
+ Rect2 region = p_region;
+
+ if (clip_left > rect.position.x) {
+ int rect_pixels = (clip_left - rect.position.x);
+ int region_pixels = rect_pixels * region.size.x / rect.size.x;
+
+ rect.position.x += rect_pixels;
+ rect.size.x -= rect_pixels;
+
+ region.position.x += region_pixels;
+ region.size.x -= region_pixels;
+ }
+
+ if (clip_right < rect.position.x + rect.size.x) {
+
+ int rect_pixels = rect.position.x + rect.size.x - clip_right;
+ int region_pixels = rect_pixels * region.size.x / rect.size.x;
+
+ rect.size.x -= rect_pixels;
+ region.size.x -= region_pixels;
+ }
+
+ draw_texture_rect_region(p_texture, rect, region);
+}
+
+int AnimationTrackEdit::get_track() const {
+ return track;
+}
+
+Ref<Animation> AnimationTrackEdit::get_animation() const {
+ return animation;
+}
+
+void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animation, int p_track) {
+
+ animation = p_animation;
+ track = p_track;
+ update();
+
+ Ref<Texture> type_icons[6] = {
+ get_icon("KeyValue", "EditorIcons"),
+ get_icon("KeyXform", "EditorIcons"),
+ get_icon("KeyCall", "EditorIcons"),
+ get_icon("KeyBezier", "EditorIcons"),
+ get_icon("KeyAudio", "EditorIcons"),
+ get_icon("KeyAnimation", "EditorIcons")
+ };
+
+ ERR_FAIL_INDEX(track, animation->get_track_count());
+
+ type_icon = type_icons[animation->track_get_type(track)];
+ selected_icon = get_icon("KeySelected", "EditorIcons");
+}
+
+Size2 AnimationTrackEdit::get_minimum_size() const {
+
+ Ref<Texture> texture = get_icon("Object", "EditorIcons");
+ Ref<Font> font = get_font("font", "Label");
+ int separation = get_constant("vseparation", "ItemList");
+
+ int max_h = MAX(texture->get_height(), font->get_height());
+ max_h = MAX(max_h, get_key_height());
+
+ return Vector2(1, max_h + separation);
+}
+
+void AnimationTrackEdit::set_undo_redo(UndoRedo *p_undo_redo) {
+ undo_redo = p_undo_redo;
+}
+
+void AnimationTrackEdit::set_timeline(AnimationTimelineEdit *p_timeline) {
+ timeline = p_timeline;
+ timeline->connect("zoom_changed", this, "_zoom_changed");
+ timeline->connect("name_limit_changed", this, "_zoom_changed");
+}
+void AnimationTrackEdit::set_editor(AnimationTrackEditor *p_editor) {
+ editor = p_editor;
+}
+
+void AnimationTrackEdit::_play_position_draw() {
+
+ if (!animation.is_valid() || play_position_pos < 0)
+ return;
+
+ float scale = timeline->get_zoom_scale();
+ int h = get_size().height;
+
+ int px = (-timeline->get_value() + play_position_pos) * scale + timeline->get_name_limit();
+
+ if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) {
+ Color color = get_color("accent_color", "Editor");
+ play_position->draw_line(Point2(px, 0), Point2(px, h), color);
+ }
+}
+
+void AnimationTrackEdit::set_play_position(float p_pos) {
+
+ play_position_pos = p_pos;
+ play_position->update();
+}
+
+void AnimationTrackEdit::update_play_position() {
+ play_position->update();
+}
+
+void AnimationTrackEdit::set_root(Node *p_root) {
+ root = p_root;
+}
+void AnimationTrackEdit::_zoom_changed() {
+ update();
+ play_position->update();
+}
+
+void AnimationTrackEdit::_path_entered(const String &p_text) {
+
+ *block_animation_update_ptr = true;
+ undo_redo->create_action("Change Track Path");
+ undo_redo->add_do_method(animation.ptr(), "track_set_path", track, p_text);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_path", track, animation->track_get_path(track));
+ undo_redo->commit_action();
+ *block_animation_update_ptr = false;
+ update();
+ path->hide();
+}
+
+String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
+
+ if (check_rect.has_point(p_pos)) {
+ return TTR("Toggle this track on/off.");
+ }
+
+ if (path_rect.has_point(p_pos)) {
+ return animation->track_get_path(track);
+ }
+
+ if (update_mode_rect.has_point(p_pos)) {
+ return TTR("Update Mode (How this property is set)");
+ }
+
+ if (interp_mode_rect.has_point(p_pos)) {
+ return TTR("Interpolation Mode");
+ }
+
+ if (loop_mode_rect.has_point(p_pos)) {
+ return TTR("Loop Wrap Mode (Interpolate end with beginning on loop)");
+ }
+
+ if (remove_rect.has_point(p_pos)) {
+ return TTR("Remove this track.");
+ }
+
+ if (p_pos.x >= timeline->get_name_limit() && p_pos.x <= (get_size().width - timeline->get_buttons_width())) {
+
+ int key_idx = -1;
+ float key_distance = 1e20;
+
+ for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) { //select should happen in the opposite order of drawing for more accurate overlap select
+
+ Rect2 rect = const_cast<AnimationTrackEdit *>(this)->get_key_rect(i, timeline->get_zoom_scale());
+ float offset = animation->track_get_key_time(track, i) - timeline->get_value();
+ offset = offset * timeline->get_zoom_scale() + timeline->get_name_limit();
+ rect.position.x += offset;
+
+ if (rect.has_point(p_pos)) {
+
+ if (const_cast<AnimationTrackEdit *>(this)->is_key_selectable_by_distance()) {
+ float distance = ABS(offset - p_pos.x);
+ if (key_idx == -1 || distance < key_distance) {
+ key_idx = i;
+ key_distance = distance;
+ }
+ } else {
+ //first one does it
+ break;
+ }
+ }
+ }
+
+ if (key_idx != -1) {
+
+ String text = TTR("Time (s): ") + rtos(animation->track_get_key_time(track, key_idx)) + "\n";
+ switch (animation->track_get_type(track)) {
+
+ case Animation::TYPE_TRANSFORM: {
+
+ Dictionary d = animation->track_get_key_value(track, key_idx);
+ if (d.has("location"))
+ text += "Pos: " + String(d["location"]) + "\n";
+ if (d.has("rotation"))
+ text += "Rot: " + String(d["rotation"]) + "\n";
+ if (d.has("scale"))
+ text += "Scale: " + String(d["scale"]) + "\n";
+ } break;
+ case Animation::TYPE_VALUE: {
+
+ Variant v = animation->track_get_key_value(track, key_idx);
+ //text+="value: "+String(v)+"\n";
+
+ bool prop_exists = false;
+ Variant::Type valid_type = Variant::NIL;
+ Object *obj = NULL;
+
+ RES res;
+ Vector<StringName> leftover_path;
+ Node *node = root->get_node_and_resource(animation->track_get_path(track), res, leftover_path);
+
+ if (res.is_valid()) {
+ obj = res.ptr();
+ } else if (node) {
+ obj = node;
+ }
+
+ if (obj) {
+ valid_type = obj->get_static_property_type_indexed(leftover_path, &prop_exists);
+ }
+
+ text += "Type: " + Variant::get_type_name(v.get_type()) + "\n";
+ if (prop_exists && !Variant::can_convert(v.get_type(), valid_type)) {
+ text += "Value: " + String(v) + " (Invalid, expected type: " + Variant::get_type_name(valid_type) + ")\n";
+ } else {
+ text += "Value: " + String(v) + "\n";
+ }
+ text += "Easing: " + rtos(animation->track_get_key_transition(track, key_idx));
+
+ } break;
+ case Animation::TYPE_METHOD: {
+
+ Dictionary d = animation->track_get_key_value(track, key_idx);
+ if (d.has("method"))
+ text += String(d["method"]);
+ text += "(";
+ Vector<Variant> args;
+ if (d.has("args"))
+ args = d["args"];
+ for (int i = 0; i < args.size(); i++) {
+
+ if (i > 0)
+ text += ", ";
+ text += String(args[i]);
+ }
+ text += ")\n";
+
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ float h = animation->bezier_track_get_key_value(track, key_idx);
+ text += "Value: " + rtos(h) + "\n";
+ Vector2 ih = animation->bezier_track_get_key_in_handle(track, key_idx);
+ text += "In-Handle: " + ih + "\n";
+ Vector2 oh = animation->bezier_track_get_key_out_handle(track, key_idx);
+ text += "Out-Handle: " + oh + "\n";
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ String stream_name = "null";
+ RES stream = animation->audio_track_get_key_stream(track, key_idx);
+ if (stream.is_valid()) {
+ if (stream->get_path().is_resource_file()) {
+ stream_name = stream->get_path().get_file();
+ } else if (stream->get_name() != "") {
+ stream_name = stream->get_name();
+ } else {
+ stream_name = stream->get_class();
+ }
+ }
+
+ text += "Stream: " + stream_name + "\n";
+ float so = animation->audio_track_get_key_start_offset(track, key_idx);
+ text += "Start (s): " + rtos(so) + "\n";
+ float eo = animation->audio_track_get_key_end_offset(track, key_idx);
+ text += "End (s): " + rtos(eo) + "\n";
+ } break;
+ case Animation::TYPE_ANIMATION: {
+
+ String name = animation->animation_track_get_key_animation(track, key_idx);
+ text += "Animation Clip: " + name + "\n";
+ } break;
+ }
+ return text;
+ }
+ }
+
+ return Control::get_tooltip(p_pos);
+}
+
+void AnimationTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
+
+ if (p_event->is_pressed()) {
+ if (ED_GET_SHORTCUT("animation_editor/duplicate_selection")->is_shortcut(p_event)) {
+ emit_signal("duplicate_request");
+ accept_event();
+ }
+
+ if (ED_GET_SHORTCUT("animation_editor/duplicate_selection_transposed")->is_shortcut(p_event)) {
+ emit_signal("duplicate_transpose_request");
+ accept_event();
+ }
+
+ if (ED_GET_SHORTCUT("animation_editor/delete_selection")->is_shortcut(p_event)) {
+ emit_signal("delete_request");
+ accept_event();
+ }
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ Point2 pos = mb->get_position();
+
+ if (check_rect.has_point(pos)) {
+ *block_animation_update_ptr = true;
+ undo_redo->create_action("Toggle track enabled");
+ undo_redo->add_do_method(animation.ptr(), "track_set_enabled", track, !animation->track_is_enabled(track));
+ undo_redo->add_undo_method(animation.ptr(), "track_set_enabled", track, animation->track_is_enabled(track));
+ undo_redo->commit_action();
+ *block_animation_update_ptr = false;
+ update();
+ accept_event();
+ }
+ if (path_rect.has_point(pos)) {
+
+ clicking_on_name = true;
+ accept_event();
+ }
+
+ if (update_mode_rect.has_point(pos)) {
+ if (!menu) {
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect("id_pressed", this, "_menu_selected");
+ }
+ menu->clear();
+ menu->add_icon_item(get_icon("TrackContinuous", "EditorIcons"), TTR("Continuous"), MENU_CALL_MODE_CONTINUOUS);
+ menu->add_icon_item(get_icon("TrackDiscrete", "EditorIcons"), TTR("Discrete"), MENU_CALL_MODE_DISCRETE);
+ menu->add_icon_item(get_icon("TrackTrigger", "EditorIcons"), TTR("Trigger"), MENU_CALL_MODE_TRIGGER);
+ menu->add_icon_item(get_icon("TrackCapture", "EditorIcons"), TTR("Capture"), MENU_CALL_MODE_CAPTURE);
+ menu->set_as_minsize();
+
+ Vector2 popup_pos = get_global_position() + update_mode_rect.position + Vector2(0, update_mode_rect.size.height);
+ menu->set_global_position(popup_pos);
+ menu->popup();
+ accept_event();
+ }
+
+ if (interp_mode_rect.has_point(pos)) {
+ if (!menu) {
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect("id_pressed", this, "_menu_selected");
+ }
+ menu->clear();
+ menu->add_icon_item(get_icon("InterpRaw", "EditorIcons"), TTR("Nearest"), MENU_INTERPOLATION_NEAREST);
+ menu->add_icon_item(get_icon("InterpLinear", "EditorIcons"), TTR("Linear"), MENU_INTERPOLATION_LINEAR);
+ menu->add_icon_item(get_icon("InterpCubic", "EditorIcons"), TTR("Cubic"), MENU_INTERPOLATION_CUBIC);
+ menu->set_as_minsize();
+
+ Vector2 popup_pos = get_global_position() + interp_mode_rect.position + Vector2(0, interp_mode_rect.size.height);
+ menu->set_global_position(popup_pos);
+ menu->popup();
+ accept_event();
+ }
+
+ if (loop_mode_rect.has_point(pos)) {
+ if (!menu) {
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect("id_pressed", this, "_menu_selected");
+ }
+ menu->clear();
+ menu->add_icon_item(get_icon("InterpWrapClamp", "EditorIcons"), TTR("Clamp Loop Interp"), MENU_LOOP_CLAMP);
+ menu->add_icon_item(get_icon("InterpWrapLoop", "EditorIcons"), TTR("Wrap Loop Interp"), MENU_LOOP_WRAP);
+ menu->set_as_minsize();
+
+ Vector2 popup_pos = get_global_position() + loop_mode_rect.position + Vector2(0, loop_mode_rect.size.height);
+ menu->set_global_position(popup_pos);
+ menu->popup();
+ accept_event();
+ }
+
+ if (remove_rect.has_point(pos)) {
+ emit_signal("remove_request", track);
+ accept_event();
+ }
+
+ if (bezier_edit_rect.has_point(pos)) {
+ emit_signal("bezier_edit");
+ accept_event();
+ }
+
+ //check keyframes
+
+ float scale = timeline->get_zoom_scale();
+ int limit = timeline->get_name_limit();
+ int limit_end = get_size().width - timeline->get_buttons_width();
+
+ if (pos.x >= limit && pos.x <= limit_end) {
+
+ int key_idx = -1;
+ float key_distance = 1e20;
+
+ for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) { //select should happen in the opposite order of drawing for more accurate overlap select
+
+ Rect2 rect = get_key_rect(i, scale);
+ float offset = animation->track_get_key_time(track, i) - timeline->get_value();
+ offset = offset * scale + limit;
+ rect.position.x += offset;
+
+ if (rect.has_point(pos)) {
+
+ if (is_key_selectable_by_distance()) {
+ float distance = ABS(offset - pos.x);
+ if (key_idx == -1 || distance < key_distance) {
+ key_idx = i;
+ key_distance = distance;
+ }
+ } else {
+ //first one does it
+ key_idx = i;
+ break;
+ }
+ }
+ }
+
+ if (key_idx != -1) {
+ if (mb->get_command() || mb->get_shift()) {
+ if (editor->is_key_selected(track, key_idx)) {
+ emit_signal("deselect_key", key_idx);
+
+ } else {
+ emit_signal("select_key", key_idx, false);
+ moving_selection_attempt = true;
+ select_single_attempt = -1;
+ moving_selection_from_ofs = (mb->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale();
+ }
+ } else {
+ if (!editor->is_key_selected(track, key_idx)) {
+ emit_signal("select_key", key_idx, true);
+ select_single_attempt = -1;
+ } else {
+ select_single_attempt = key_idx;
+ }
+
+ moving_selection_attempt = true;
+ moving_selection_from_ofs = (mb->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale();
+ }
+ accept_event();
+ } else {
+ emit_signal("clear_selection");
+ }
+ }
+
+ /*using focus instead
+ * if (!selected && pos.x >= timeline->get_name_limit() && pos.x < (get_size().width - timeline->get_buttons_width())) {
+ set_selected(true);
+ emit_signal("selected");
+ }
+ */
+ }
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
+ Point2 pos = mb->get_position();
+ if (pos.x >= timeline->get_name_limit() && pos.x <= get_size().width - timeline->get_buttons_width()) {
+ //can do something with menu too! show insert key
+ float offset = (pos.x - timeline->get_name_limit()) / timeline->get_zoom_scale();
+ if (!menu) {
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect("id_pressed", this, "_menu_selected");
+ }
+
+ menu->clear();
+ menu->add_icon_item(get_icon("Key", "EditorIcons"), TTR("Insert Key"), MENU_KEY_INSERT);
+ if (editor->is_selection_active()) {
+ menu->add_separator();
+ menu->add_icon_item(get_icon("Duplicate", "EditorIcons"), TTR("Duplicate Key(s)"), MENU_KEY_DUPLICATE);
+ menu->add_separator();
+ menu->add_icon_item(get_icon("Remove", "EditorIcons"), TTR("Delete Key(s)"), MENU_KEY_DELETE);
+ }
+ menu->set_as_minsize();
+
+ Vector2 popup_pos = get_global_transform().xform(get_local_mouse_position());
+ menu->set_global_position(popup_pos);
+ menu->popup();
+
+ insert_at_pos = offset + timeline->get_value();
+ accept_event();
+ }
+ }
+
+ if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && clicking_on_name) {
+
+ if (!path) {
+ path = memnew(LineEdit);
+ add_child(path);
+ path->set_as_toplevel(true);
+ path->connect("text_entered", this, "_path_entered");
+ }
+
+ path->set_text(animation->track_get_path(track));
+ Vector2 theme_ofs = path->get_stylebox("normal", "LineEdit")->get_offset();
+ path->set_position(get_global_position() + path_rect.position - theme_ofs);
+ path->set_size(path_rect.size);
+ path->show_modal();
+ path->grab_focus();
+ path->set_cursor_position(path->get_text().length());
+ clicking_on_name = false;
+ }
+
+ if (mb.is_valid() && moving_selection_attempt) {
+
+ if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ moving_selection_attempt = false;
+ if (moving_selection) {
+ emit_signal("move_selection_commit");
+ } else if (select_single_attempt != -1) {
+ emit_signal("select_key", select_single_attempt, true);
+ }
+ moving_selection = false;
+ select_single_attempt = -1;
+ }
+
+ if (moving_selection && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
+
+ moving_selection_attempt = false;
+ moving_selection = false;
+ emit_signal("move_selection_cancel");
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT && moving_selection_attempt) {
+
+ if (!moving_selection) {
+ moving_selection = true;
+ emit_signal("move_selection_begin");
+ }
+
+ float new_ofs = (mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale();
+ emit_signal("move_selection", new_ofs - moving_selection_from_ofs);
+ }
+}
+
+Variant AnimationTrackEdit::get_drag_data(const Point2 &p_point) {
+
+ if (!clicking_on_name)
+ return Variant();
+
+ Dictionary drag_data;
+ drag_data["type"] = "animation_track";
+ drag_data["index"] = track;
+
+ ToolButton *tb = memnew(ToolButton);
+ tb->set_text(path_cache);
+ tb->set_icon(icon_cache);
+ set_drag_preview(tb);
+
+ clicking_on_name = false;
+
+ return drag_data;
+}
+
+bool AnimationTrackEdit::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+
+ Dictionary d = p_data;
+ if (!d.has("type")) {
+ return false;
+ }
+
+ String type = d["type"];
+ if (type != "animation_track")
+ return false;
+
+ if (p_point.y < get_size().height / 2) {
+ dropping_at = -1;
+ } else {
+ dropping_at = 1;
+ }
+
+ const_cast<AnimationTrackEdit *>(this)->update();
+ const_cast<AnimationTrackEdit *>(this)->emit_signal("drop_attempted", track);
+
+ return true;
+}
+void AnimationTrackEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
+
+ Dictionary d = p_data;
+ if (!d.has("type")) {
+ return;
+ }
+
+ String type = d["type"];
+ if (type != "animation_track")
+ return;
+
+ int from_track = d["index"];
+
+ if (dropping_at < 0) {
+ emit_signal("dropped", from_track, track);
+ } else {
+ emit_signal("dropped", from_track, track + 1);
+ }
+}
+
+void AnimationTrackEdit::_menu_selected(int p_index) {
+
+ switch (p_index) {
+ case MENU_CALL_MODE_CONTINUOUS:
+ case MENU_CALL_MODE_DISCRETE:
+ case MENU_CALL_MODE_TRIGGER:
+ case MENU_CALL_MODE_CAPTURE: {
+
+ Animation::UpdateMode update_mode = Animation::UpdateMode(p_index);
+ *block_animation_update_ptr = true;
+ undo_redo->create_action("Change animation update mode");
+ undo_redo->add_do_method(animation.ptr(), "value_track_set_update_mode", track, update_mode);
+ undo_redo->add_undo_method(animation.ptr(), "value_track_set_update_mode", track, animation->value_track_get_update_mode(track));
+ undo_redo->commit_action();
+ *block_animation_update_ptr = false;
+ update();
+
+ } break;
+ case MENU_INTERPOLATION_NEAREST:
+ case MENU_INTERPOLATION_LINEAR:
+ case MENU_INTERPOLATION_CUBIC: {
+
+ Animation::InterpolationType interp_mode = Animation::InterpolationType(p_index - MENU_INTERPOLATION_NEAREST);
+ *block_animation_update_ptr = true;
+ undo_redo->create_action("Change animation interpolation mode");
+ undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_type", track, interp_mode);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_type", track, animation->track_get_interpolation_type(track));
+ undo_redo->commit_action();
+ *block_animation_update_ptr = false;
+ update();
+ } break;
+ case MENU_LOOP_WRAP:
+ case MENU_LOOP_CLAMP: {
+
+ bool loop_wrap = p_index == MENU_LOOP_WRAP;
+ *block_animation_update_ptr = true;
+ undo_redo->create_action("Change animation loop mode");
+ undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_loop_wrap", track, loop_wrap);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_loop_wrap", track, animation->track_get_interpolation_loop_wrap(track));
+ undo_redo->commit_action();
+ *block_animation_update_ptr = false;
+ update();
+
+ } break;
+ case MENU_KEY_INSERT: {
+ emit_signal("insert_key", insert_at_pos);
+ } break;
+ case MENU_KEY_DUPLICATE: {
+ emit_signal("duplicate_request");
+
+ } break;
+ case MENU_KEY_DELETE: {
+ emit_signal("delete_request");
+
+ } break;
+ }
+}
+
+void AnimationTrackEdit::set_block_animation_update_ptr(bool *p_block_ptr) {
+ block_animation_update_ptr = p_block_ptr;
+}
+
+void AnimationTrackEdit::cancel_drop() {
+ if (dropping_at != 0) {
+ dropping_at = 0;
+ update();
+ }
+}
+void AnimationTrackEdit::set_in_group(bool p_enable) {
+ in_group = p_enable;
+ update();
+}
+
+void AnimationTrackEdit::append_to_selection(const Rect2 &p_box) {
+
+ Rect2 select_rect(timeline->get_name_limit(), 0, get_size().width - timeline->get_name_limit() - timeline->get_buttons_width(), get_size().height);
+ select_rect = select_rect.clip(p_box);
+
+ for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) { //select should happen in the opposite order of drawing for more accurate overlap select
+
+ Rect2 rect = const_cast<AnimationTrackEdit *>(this)->get_key_rect(i, timeline->get_zoom_scale());
+ float offset = animation->track_get_key_time(track, i) - timeline->get_value();
+ offset = offset * timeline->get_zoom_scale() + timeline->get_name_limit();
+ rect.position.x += offset;
+
+ if (select_rect.intersects(rect)) {
+ emit_signal("select_key", i, false);
+ }
+ }
+}
+
+void AnimationTrackEdit::_bind_methods() {
+
+ ClassDB::bind_method("_zoom_changed", &AnimationTrackEdit::_zoom_changed);
+ ClassDB::bind_method("_menu_selected", &AnimationTrackEdit::_menu_selected);
+ ClassDB::bind_method("_gui_input", &AnimationTrackEdit::_gui_input);
+ ClassDB::bind_method("_path_entered", &AnimationTrackEdit::_path_entered);
+ ClassDB::bind_method("_play_position_draw", &AnimationTrackEdit::_play_position_draw);
+
+ ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::REAL, "position"), PropertyInfo(Variant::BOOL, "drag")));
+ ADD_SIGNAL(MethodInfo("remove_request", PropertyInfo(Variant::INT, "track")));
+ ADD_SIGNAL(MethodInfo("dropped", PropertyInfo(Variant::INT, "from_track"), PropertyInfo(Variant::INT, "to_track")));
+ ADD_SIGNAL(MethodInfo("insert_key", PropertyInfo(Variant::REAL, "ofs")));
+ ADD_SIGNAL(MethodInfo("select_key", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "single")));
+ ADD_SIGNAL(MethodInfo("deselect_key", PropertyInfo(Variant::INT, "index")));
+ ADD_SIGNAL(MethodInfo("clear_selection"));
+ ADD_SIGNAL(MethodInfo("bezier_edit"));
+
+ ADD_SIGNAL(MethodInfo("move_selection_begin"));
+ ADD_SIGNAL(MethodInfo("move_selection", PropertyInfo(Variant::REAL, "ofs")));
+ ADD_SIGNAL(MethodInfo("move_selection_commit"));
+ ADD_SIGNAL(MethodInfo("move_selection_cancel"));
+
+ ADD_SIGNAL(MethodInfo("duplicate_request"));
+ ADD_SIGNAL(MethodInfo("duplicate_transpose_request"));
+ ADD_SIGNAL(MethodInfo("delete_request"));
+}
+
+AnimationTrackEdit::AnimationTrackEdit() {
+ undo_redo = NULL;
+ timeline = NULL;
+ root = NULL;
+ path = NULL;
+ menu = NULL;
+ block_animation_update_ptr = NULL;
+ clicking_on_name = false;
+ dropping_at = 0;
+
+ in_group = false;
+
+ moving_selection_attempt = false;
+ moving_selection = false;
+ select_single_attempt = -1;
+
+ play_position_pos = 0;
+ play_position = memnew(Control);
+ play_position->set_mouse_filter(MOUSE_FILTER_PASS);
+ add_child(play_position);
+ play_position->set_anchors_and_margins_preset(PRESET_WIDE);
+ play_position->connect("draw", this, "_play_position_draw");
+ set_focus_mode(FOCUS_CLICK);
+ set_mouse_filter(MOUSE_FILTER_PASS); //scroll has to work too for selection
+}
+
+//////////////////////////////////////
+
+AnimationTrackEdit *AnimationTrackEditPlugin::create_value_track_edit(Object *p_object, Variant::Type p_type, const String &p_property, PropertyHint p_hint, const String &p_hint_string, int p_usage) {
+ if (get_script_instance()) {
+ Variant args[6] = {
+ p_object,
+ p_type,
+ p_property,
+ p_hint,
+ p_hint_string,
+ p_usage
+ };
+
+ Variant *argptrs[6] = {
+ &args[0],
+ &args[1],
+ &args[2],
+ &args[3],
+ &args[4],
+ &args[5]
+ };
+
+ Variant::CallError ce;
+ return Object::cast_to<AnimationTrackEdit>(get_script_instance()->call("create_value_track_edit", (const Variant **)&argptrs, 6, ce).operator Object *());
+ }
+ return NULL;
+}
+
+AnimationTrackEdit *AnimationTrackEditPlugin::create_audio_track_edit() {
+
+ if (get_script_instance()) {
+ return Object::cast_to<AnimationTrackEdit>(get_script_instance()->call("create_audio_track_edit").operator Object *());
+ }
+ return NULL;
+}
+
+AnimationTrackEdit *AnimationTrackEditPlugin::create_animation_track_edit(Object *p_object) {
+ if (get_script_instance()) {
+ return Object::cast_to<AnimationTrackEdit>(get_script_instance()->call("create_animation_track_edit", p_object).operator Object *());
+ }
+ return NULL;
+}
+
+///////////////////////////////////////
+
+void AnimationTrackEditGroup::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_DRAW) {
+ Ref<Font> font = get_font("font", "Label");
+ int separation = get_constant("hseparation", "ItemList");
+ Color color = get_color("font_color", "Label");
+
+ if (root && root->has_node(node)) {
+ Node *n = root->get_node(node);
+ if (n && EditorNode::get_singleton()->get_editor_selection()->is_selected(n)) {
+ color = get_color("accent_color", "Editor");
+ }
+ }
+
+ Color bgcol = get_color("dark_color_2", "Editor");
+ bgcol.a *= 0.6;
+ draw_rect(Rect2(Point2(), get_size()), bgcol);
+ Color linecolor = color;
+ linecolor.a = 0.2;
+
+ draw_line(Point2(), Point2(get_size().width, 0), linecolor);
+ draw_line(Point2(timeline->get_name_limit(), 0), Point2(timeline->get_name_limit(), get_size().height), linecolor);
+ draw_line(Point2(get_size().width - timeline->get_buttons_width(), 0), Point2(get_size().width - timeline->get_buttons_width(), get_size().height), linecolor);
+
+ int ofs = 0;
+ draw_texture(icon, Point2(ofs, int(get_size().height - icon->get_height()) / 2));
+ ofs += separation + icon->get_width();
+ draw_string(font, Point2(ofs, int(get_size().height - font->get_height()) / 2 + font->get_ascent()), node_name, color, timeline->get_name_limit() - ofs);
+
+ int px = (-timeline->get_value() + timeline->get_play_position()) * timeline->get_zoom_scale() + timeline->get_name_limit();
+
+ if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) {
+ Color accent = get_color("accent_color", "Editor");
+ draw_line(Point2(px, 0), Point2(px, get_size().height), accent);
+ }
+ }
+}
+
+void AnimationTrackEditGroup::set_type_and_name(const Ref<Texture> &p_type, const String &p_name, const NodePath &p_node) {
+ icon = p_type;
+ node_name = p_name;
+ node = p_node;
+ update();
+ minimum_size_changed();
+}
+
+Size2 AnimationTrackEditGroup::get_minimum_size() const {
+
+ Ref<Font> font = get_font("font", "Label");
+ int separation = get_constant("vseparation", "ItemList");
+
+ return Vector2(0, MAX(font->get_height(), icon->get_height()) + separation);
+}
+
+void AnimationTrackEditGroup::set_timeline(AnimationTimelineEdit *p_timeline) {
+ timeline = p_timeline;
+ timeline->connect("zoom_changed", this, "_zoom_changed");
+ timeline->connect("name_limit_changed", this, "_zoom_changed");
+}
+
+void AnimationTrackEditGroup::set_root(Node *p_root) {
+ root = p_root;
+ update();
+}
+
+void AnimationTrackEditGroup::_zoom_changed() {
+ update();
+}
+
+void AnimationTrackEditGroup::_bind_methods() {
+ ClassDB::bind_method("_zoom_changed", &AnimationTrackEditGroup::_zoom_changed);
+}
+
+AnimationTrackEditGroup::AnimationTrackEditGroup() {
+ set_mouse_filter(MOUSE_FILTER_PASS);
+}
+
+//////////////////////////////////////
+
+void AnimationTrackEditor::add_track_edit_plugin(const Ref<AnimationTrackEditPlugin> &p_plugin) {
+
+ if (track_edit_plugins.find(p_plugin) != -1)
+ return;
+ track_edit_plugins.push_back(p_plugin);
+}
+
+void AnimationTrackEditor::remove_track_edit_plugin(const Ref<AnimationTrackEditPlugin> &p_plugin) {
+
+ track_edit_plugins.erase(p_plugin);
+}
+
+void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) {
+
+ if (animation != p_anim && _get_track_selected() >= 0) {
+ track_edits[_get_track_selected()]->release_focus();
+ }
+ if (animation.is_valid()) {
+ animation->disconnect("changed", this, "_animation_changed");
+ _clear_selection();
+ }
+ animation = p_anim;
+ timeline->set_animation(p_anim);
+
+ _cancel_bezier_edit();
+ _update_tracks();
+
+ 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);
+ step->set_read_only(true);
+ snap->set_disabled(true);
+ }
+}
+
+Ref<Animation> AnimationTrackEditor::get_current_animation() const {
+
+ return animation;
+}
+void AnimationTrackEditor::_root_removed(Node *p_root) {
+ root = NULL;
+}
+
+void AnimationTrackEditor::set_root(Node *p_root) {
+ if (root) {
+ root->disconnect("tree_exiting", this, "_root_removed");
+ }
+
+ root = p_root;
+
+ if (root) {
+ root->connect("tree_exiting", this, "_root_removed", make_binds(), CONNECT_ONESHOT);
+ }
+
+ _update_tracks();
+}
+
+Node *AnimationTrackEditor::get_root() const {
+
+ return root;
+}
+
+void AnimationTrackEditor::update_keying() {
+ bool keying_enabled = is_visible_in_tree() && animation.is_valid();
+
+ if (keying_enabled == keying)
+ return;
+
+ keying = keying_enabled;
+ //_update_menu();
+ emit_signal("keying_changed");
+}
+
+bool AnimationTrackEditor::has_keying() const {
+ return keying;
+}
+
+void AnimationTrackEditor::cleanup() {
+ set_animation(Ref<Animation>());
+}
+
+void AnimationTrackEditor::_name_limit_changed() {
+
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->update();
+ }
+}
+
+void AnimationTrackEditor::_timeline_changed(float p_new_pos, bool p_drag) {
+
+ emit_signal("timeline_changed", p_new_pos, p_drag);
+}
+
+void AnimationTrackEditor::_track_remove_request(int p_track) {
+
+ int idx = p_track;
+ if (idx >= 0 && idx < animation->get_track_count()) {
+ _clear_selection();
+ undo_redo->create_action(TTR("Remove Anim Track"));
+ undo_redo->add_do_method(animation.ptr(), "remove_track", idx);
+ undo_redo->add_undo_method(animation.ptr(), "add_track", animation->track_get_type(idx), idx);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_path", idx, animation->track_get_path(idx));
+ //todo interpolation
+ for (int i = 0; i < animation->track_get_key_count(idx); i++) {
+
+ Variant v = animation->track_get_key_value(idx, i);
+ float time = animation->track_get_key_time(idx, i);
+ float trans = animation->track_get_key_transition(idx, i);
+
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", idx, time, v);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", idx, i, trans);
+ }
+
+ undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_type", idx, animation->track_get_interpolation_type(idx));
+ if (animation->track_get_type(idx) == Animation::TYPE_VALUE) {
+ undo_redo->add_undo_method(animation.ptr(), "value_track_set_update_mode", idx, animation->value_track_get_update_mode(idx));
+ }
+
+ undo_redo->commit_action();
+ }
+}
+
+void AnimationTrackEditor::set_anim_pos(float p_pos) {
+
+ timeline->set_play_position(p_pos);
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->set_play_position(p_pos);
+ }
+ for (int i = 0; i < groups.size(); i++) {
+ groups[i]->update();
+ }
+ bezier_edit->set_play_position(p_pos);
+}
+
+void AnimationTrackEditor::_query_insert(const InsertData &p_id) {
+
+ if (insert_frame != Engine::get_singleton()->get_frames_drawn()) {
+ //clear insert list for the frame if frame changed
+ if (insert_confirm->is_visible_in_tree())
+ return; //do nothing
+ insert_data.clear();
+ insert_query = false;
+ }
+ insert_frame = Engine::get_singleton()->get_frames_drawn();
+
+ for (List<InsertData>::Element *E = insert_data.front(); E; E = E->next()) {
+ //prevent insertion of multiple tracks
+ if (E->get().path == p_id.path)
+ return; //already inserted a track for this on this frame
+ }
+
+ insert_data.push_back(p_id);
+
+ if (p_id.track_idx == -1) {
+ if (bool(EDITOR_DEF("editors/animation/confirm_insert_track", true))) {
+ //potential new key, does not exist
+ if (insert_data.size() == 1)
+ insert_confirm_text->set_text(vformat(TTR("Create NEW track for %s and insert key?"), p_id.query));
+ else
+ insert_confirm_text->set_text(vformat(TTR("Create %d NEW tracks and insert keys?"), insert_data.size()));
+
+ bool all_bezier = true;
+ for (int i = 0; i < insert_data.size(); i++) {
+ if (insert_data[i].type != Animation::TYPE_VALUE && insert_data[i].type != Animation::TYPE_BEZIER) {
+ all_bezier = false;
+ }
+
+ if (insert_data[i].type != Animation::TYPE_VALUE) {
+ continue;
+ }
+ switch (insert_data[i].value.get_type()) {
+ case Variant::INT:
+ case Variant::REAL:
+ case Variant::VECTOR2:
+ case Variant::VECTOR3:
+ case Variant::QUAT:
+ case Variant::PLANE:
+ case Variant::COLOR: {
+ //good
+ } break;
+ default: {
+ all_bezier = false;
+ }
+ }
+ }
+
+ insert_confirm_bezier->set_visible(all_bezier);
+ insert_confirm->get_ok()->set_text(TTR("Create"));
+ insert_confirm->popup_centered_minsize();
+ insert_query = true;
+ } else {
+ call_deferred("_insert_delay");
+ insert_queue = true;
+ }
+
+ } else {
+ if (!insert_query && !insert_queue) {
+ call_deferred("_insert_delay");
+ insert_queue = true;
+ }
+ }
+}
+
+void AnimationTrackEditor::_insert_delay() {
+
+ if (insert_query) {
+ //discard since it's entered into query mode
+ insert_queue = false;
+ return;
+ }
+
+ undo_redo->create_action(TTR("Anim Insert"));
+
+ int last_track = animation->get_track_count();
+ bool advance = false;
+ while (insert_data.size()) {
+
+ if (insert_data.front()->get().advance)
+ advance = true;
+ last_track = _confirm_insert(insert_data.front()->get(), last_track);
+ insert_data.pop_front();
+ }
+
+ undo_redo->commit_action();
+
+ if (advance) {
+ float step = animation->get_step();
+ if (step == 0)
+ step = 1;
+
+ float pos = timeline->get_play_position();
+
+ pos = Math::stepify(pos + step, step);
+ if (pos > animation->get_length())
+ pos = animation->get_length();
+ set_anim_pos(pos);
+ emit_signal("timeline_changed", pos, true);
+ }
+ insert_queue = false;
+}
+
+void AnimationTrackEditor::insert_transform_key(Spatial *p_node, const String &p_sub, const Transform &p_xform) {
+
+ if (!keying)
+ return;
+ if (!animation.is_valid())
+ return;
+
+ ERR_FAIL_COND(!root);
+ //let's build a node path
+ String path = root->get_path_to(p_node);
+ if (p_sub != "")
+ path += ":" + p_sub;
+
+ NodePath np = path;
+
+ int track_idx = -1;
+
+ for (int i = 0; i < animation->get_track_count(); i++) {
+
+ if (animation->track_get_type(i) != Animation::TYPE_TRANSFORM)
+ continue;
+ if (animation->track_get_path(i) != np)
+ continue;
+
+ track_idx = i;
+ break;
+ }
+
+ InsertData id;
+ Dictionary val;
+
+ id.path = np;
+ id.track_idx = track_idx;
+ id.value = p_xform;
+ id.type = Animation::TYPE_TRANSFORM;
+ id.query = "node '" + p_node->get_name() + "'";
+ id.advance = false;
+
+ //dialog insert
+
+ _query_insert(id);
+}
+
+void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant &p_value) {
+
+ String path = p_path;
+
+ //animation property is a special case, always creates an animation track
+ for (int i = 0; i < animation->get_track_count(); i++) {
+
+ String np = animation->track_get_path(i);
+
+ if (path == np && animation->track_get_type(i) == Animation::TYPE_ANIMATION) {
+ //exists
+ InsertData id;
+ id.path = path;
+ id.track_idx = i;
+ id.value = p_value;
+ id.type = Animation::TYPE_ANIMATION;
+ id.query = "animation";
+ id.advance = false;
+ //dialog insert
+ _query_insert(id);
+ return;
+ }
+ }
+
+ InsertData id;
+ id.path = path;
+ id.track_idx = -1;
+ id.value = p_value;
+ id.type = Animation::TYPE_ANIMATION;
+ id.query = "animation";
+ id.advance = false;
+ //dialog insert
+ _query_insert(id);
+}
+
+void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists) {
+
+ ERR_FAIL_COND(!root);
+ //let's build a node path
+
+ Node *node = p_node;
+
+ String path = root->get_path_to(node);
+
+ if (Object::cast_to<AnimationPlayer>(node) && p_property == "current_animation") {
+ if (node == AnimationPlayerEditor::singleton->get_player()) {
+ EditorNode::get_singleton()->show_warning(TTR("AnimationPlayer can't animate itself, only other players."));
+ return;
+ }
+ _insert_animation_key(path, p_value);
+ return;
+ }
+
+ EditorHistory *history = EditorNode::get_singleton()->get_editor_history();
+ for (int i = 1; i < history->get_path_size(); i++) {
+
+ String prop = history->get_path_property(i);
+ ERR_FAIL_COND(prop == "");
+ path += ":" + prop;
+ }
+
+ path += ":" + p_property;
+
+ NodePath np = path;
+
+ //locate track
+
+ bool inserted = false;
+
+ for (int i = 0; i < animation->get_track_count(); i++) {
+
+ if (animation->track_get_type(i) == Animation::TYPE_VALUE) {
+ if (animation->track_get_path(i) != np)
+ continue;
+
+ InsertData id;
+ id.path = np;
+ id.track_idx = i;
+ id.value = p_value;
+ id.type = Animation::TYPE_VALUE;
+ id.query = "property '" + p_property + "'";
+ id.advance = false;
+ //dialog insert
+ _query_insert(id);
+ inserted = true;
+ } else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
+
+ Variant value;
+ if (animation->track_get_path(i) == np) {
+ value = p_value; //all good
+ } else {
+ String path = animation->track_get_path(i);
+ if (NodePath(path.get_basename()) == np) {
+ String subindex = path.get_extension();
+ value = p_value.get(subindex);
+ } else {
+ continue;
+ }
+ }
+
+ InsertData id;
+ id.path = animation->track_get_path(i);
+ id.track_idx = i;
+ id.value = value;
+ id.type = Animation::TYPE_BEZIER;
+ id.query = "property '" + p_property + "'";
+ id.advance = false;
+ //dialog insert
+ _query_insert(id);
+ inserted = true;
+ }
+ }
+
+ if (inserted || p_only_if_exists)
+ return;
+ InsertData id;
+ id.path = np;
+ id.track_idx = -1;
+ id.value = p_value;
+ id.type = Animation::TYPE_VALUE;
+ id.query = "property '" + p_property + "'";
+ id.advance = false;
+ //dialog insert
+ _query_insert(id);
+}
+
+void AnimationTrackEditor::insert_value_key(const String &p_property, const Variant &p_value, bool p_advance) {
+
+ EditorHistory *history = EditorNode::get_singleton()->get_editor_history();
+
+ ERR_FAIL_COND(!root);
+ //let's build a node path
+ ERR_FAIL_COND(history->get_path_size() == 0);
+ Object *obj = ObjectDB::get_instance(history->get_path_object(0));
+ ERR_FAIL_COND(!Object::cast_to<Node>(obj));
+
+ Node *node = Object::cast_to<Node>(obj);
+
+ String path = root->get_path_to(node);
+
+ if (Object::cast_to<AnimationPlayer>(node) && p_property == "current_animation") {
+ if (node == AnimationPlayerEditor::singleton->get_player()) {
+ EditorNode::get_singleton()->show_warning(TTR("AnimationPlayer can't animate itself, only other players."));
+ return;
+ }
+ _insert_animation_key(path, p_value);
+ return;
+ }
+
+ for (int i = 1; i < history->get_path_size(); i++) {
+
+ String prop = history->get_path_property(i);
+ ERR_FAIL_COND(prop == "");
+ path += ":" + prop;
+ }
+
+ path += ":" + p_property;
+
+ NodePath np = path;
+
+ //locate track
+
+ bool inserted = false;
+
+ for (int i = 0; i < animation->get_track_count(); i++) {
+
+ if (animation->track_get_type(i) == Animation::TYPE_VALUE) {
+ if (animation->track_get_path(i) != np)
+ continue;
+
+ InsertData id;
+ id.path = np;
+ id.track_idx = i;
+ id.value = p_value;
+ id.type = Animation::TYPE_VALUE;
+ id.query = "property '" + p_property + "'";
+ id.advance = p_advance;
+ //dialog insert
+ _query_insert(id);
+ inserted = true;
+ } else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
+
+ Variant value;
+ if (animation->track_get_path(i) == np) {
+ value = p_value; //all good
+ } else {
+ String path = animation->track_get_path(i);
+ if (NodePath(path.get_basename()) == np) {
+ String subindex = path.get_extension();
+ value = p_value.get(subindex);
+ } else {
+ continue;
+ }
+ }
+
+ InsertData id;
+ id.path = animation->track_get_path(i);
+ id.track_idx = i;
+ id.value = value;
+ id.type = Animation::TYPE_BEZIER;
+ id.query = "property '" + p_property + "'";
+ id.advance = p_advance;
+ //dialog insert
+ _query_insert(id);
+ inserted = true;
+ }
+ }
+
+ if (!inserted) {
+ InsertData id;
+ id.path = np;
+ id.track_idx = -1;
+ id.value = p_value;
+ id.type = Animation::TYPE_VALUE;
+ id.query = "property '" + p_property + "'";
+ id.advance = p_advance;
+ //dialog insert
+ _query_insert(id);
+ }
+}
+
+void AnimationTrackEditor::_confirm_insert_list() {
+
+ undo_redo->create_action(TTR("Anim Create & Insert"));
+
+ int last_track = animation->get_track_count();
+ while (insert_data.size()) {
+
+ last_track = _confirm_insert(insert_data.front()->get(), last_track, insert_confirm_bezier->is_pressed());
+ insert_data.pop_front();
+ }
+
+ undo_redo->commit_action();
+}
+
+PropertyInfo AnimationTrackEditor::_find_hint_for_track(int p_idx, NodePath &r_base_path, Variant *r_current_val) {
+
+ r_base_path = NodePath();
+ ERR_FAIL_COND_V(!animation.is_valid(), PropertyInfo());
+ ERR_FAIL_INDEX_V(p_idx, animation->get_track_count(), PropertyInfo());
+
+ if (!root) {
+ return PropertyInfo();
+ }
+
+ NodePath path = animation->track_get_path(p_idx);
+
+ if (!root->has_node_and_resource(path)) {
+ return PropertyInfo();
+ }
+
+ RES res;
+ Vector<StringName> leftover_path;
+ Node *node = root->get_node_and_resource(path, res, leftover_path, true);
+
+ if (node) {
+ r_base_path = node->get_path();
+ }
+
+ if (leftover_path.empty()) {
+ if (r_current_val) {
+ if (res.is_valid()) {
+ *r_current_val = res;
+ } else if (node) {
+ *r_current_val = node;
+ }
+ }
+ return PropertyInfo();
+ }
+
+ Variant property_info_base;
+ if (res.is_valid()) {
+ property_info_base = res;
+ if (r_current_val) {
+ *r_current_val = res->get(leftover_path[leftover_path.size() - 1]);
+ }
+ } else if (node) {
+ property_info_base = node;
+ if (r_current_val) {
+ *r_current_val = node->get(leftover_path[leftover_path.size() - 1]);
+ }
+ }
+
+ for (int i = 0; i < leftover_path.size() - 1; i++) {
+ property_info_base = property_info_base.get_named(leftover_path[i]);
+ }
+
+ List<PropertyInfo> pinfo;
+ property_info_base.get_property_list(&pinfo);
+
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+
+ if (E->get().name == leftover_path[leftover_path.size() - 1]) {
+ return E->get();
+ }
+ }
+
+ return PropertyInfo();
+}
+
+static Vector<String> _get_bezier_subindices_for_type(Variant::Type p_type, bool *r_valid = NULL) {
+ Vector<String> subindices;
+ if (r_valid) {
+ *r_valid = true;
+ }
+ switch (p_type) {
+ case Variant::INT: {
+ subindices.push_back("");
+ } break;
+ case Variant::REAL: {
+ subindices.push_back("");
+ } break;
+ case Variant::VECTOR2: {
+ subindices.push_back(".x");
+ subindices.push_back(".y");
+ } break;
+ case Variant::VECTOR3: {
+ subindices.push_back(".x");
+ subindices.push_back(".y");
+ subindices.push_back(".z");
+ } break;
+ case Variant::QUAT: {
+ subindices.push_back(".x");
+ subindices.push_back(".y");
+ subindices.push_back(".z");
+ subindices.push_back(".w");
+ } break;
+ case Variant::COLOR: {
+ subindices.push_back(".r");
+ subindices.push_back(".g");
+ subindices.push_back(".b");
+ subindices.push_back(".a");
+ } break;
+ case Variant::PLANE: {
+ subindices.push_back(".x");
+ subindices.push_back(".y");
+ subindices.push_back(".z");
+ subindices.push_back(".d");
+ } break;
+ default: {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ }
+ }
+
+ return subindices;
+}
+
+int AnimationTrackEditor::_confirm_insert(InsertData p_id, int p_last_track, bool p_create_beziers) {
+
+ if (p_last_track == -1)
+ p_last_track = animation->get_track_count();
+
+ bool created = false;
+ if (p_id.track_idx < 0) {
+
+ if (p_create_beziers && (p_id.value.get_type() == Variant::VECTOR2 ||
+ p_id.value.get_type() == Variant::VECTOR3 ||
+ p_id.value.get_type() == Variant::QUAT ||
+ p_id.value.get_type() == Variant::COLOR ||
+ p_id.value.get_type() == Variant::PLANE)) {
+
+ Vector<String> subindices = _get_bezier_subindices_for_type(p_id.value.get_type());
+
+ for (int i = 0; i < subindices.size(); i++) {
+ InsertData id = p_id;
+ id.type = Animation::TYPE_BEZIER;
+ id.value = p_id.value.get(subindices[i].substr(1, subindices[i].length()));
+ id.path = String(p_id.path) + subindices[i];
+ _confirm_insert(id, p_last_track + i);
+ }
+
+ return p_last_track + subindices.size() - 1;
+ }
+ created = true;
+ undo_redo->create_action(TTR("Anim Insert Track & Key"));
+ Animation::UpdateMode update_mode = Animation::UPDATE_DISCRETE;
+
+ if (p_id.type == Animation::TYPE_VALUE || p_id.type == Animation::TYPE_BEZIER) {
+ //wants a new tack
+
+ {
+ //hack
+ NodePath np;
+ animation->add_track(p_id.type);
+ animation->track_set_path(animation->get_track_count() - 1, p_id.path);
+ PropertyInfo h = _find_hint_for_track(animation->get_track_count() - 1, np);
+ animation->remove_track(animation->get_track_count() - 1); //hack
+
+ if (h.type == Variant::REAL ||
+ h.type == Variant::VECTOR2 ||
+ h.type == Variant::RECT2 ||
+ h.type == Variant::VECTOR3 ||
+ h.type == Variant::AABB ||
+ h.type == Variant::QUAT ||
+ h.type == Variant::COLOR ||
+ h.type == Variant::PLANE ||
+ h.type == Variant::TRANSFORM2D ||
+ h.type == Variant::TRANSFORM) {
+
+ update_mode = Animation::UPDATE_CONTINUOUS;
+ }
+
+ if (h.usage & PROPERTY_USAGE_ANIMATE_AS_TRIGGER) {
+ update_mode = Animation::UPDATE_TRIGGER;
+ }
+ }
+ }
+
+ p_id.track_idx = p_last_track;
+
+ undo_redo->add_do_method(animation.ptr(), "add_track", p_id.type);
+ undo_redo->add_do_method(animation.ptr(), "track_set_path", p_id.track_idx, p_id.path);
+ if (p_id.type == Animation::TYPE_VALUE)
+ undo_redo->add_do_method(animation.ptr(), "value_track_set_update_mode", p_id.track_idx, update_mode);
+
+ } else {
+ undo_redo->create_action(TTR("Anim Insert Key"));
+ }
+
+ float time = timeline->get_play_position();
+ Variant value;
+
+ switch (p_id.type) {
+
+ case Animation::TYPE_VALUE: {
+
+ value = p_id.value;
+
+ } break;
+ case Animation::TYPE_TRANSFORM: {
+
+ Transform tr = p_id.value;
+ Dictionary d;
+ d["location"] = tr.origin;
+ d["scale"] = tr.basis.get_scale();
+ d["rotation"] = Quat(tr.basis); //.orthonormalized();
+ value = d;
+ } break;
+ case Animation::TYPE_BEZIER: {
+ Array array;
+ array.resize(5);
+ array[0] = p_id.value;
+ array[1] = -0.25;
+ array[2] = 0;
+ array[3] = 0.25;
+ array[4] = 0;
+ value = array;
+
+ } break;
+ case Animation::TYPE_ANIMATION: {
+ value = p_id.value;
+ } break;
+ default: {}
+ }
+
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_id.track_idx, time, value);
+
+ if (created) {
+
+ //just remove the track
+ undo_redo->add_undo_method(animation.ptr(), "remove_track", p_last_track);
+ p_last_track++;
+ } else {
+
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_id.track_idx, time);
+ int existing = animation->track_find_key(p_id.track_idx, time, true);
+ if (existing != -1) {
+ Variant v = animation->track_get_key_value(p_id.track_idx, existing);
+ float trans = animation->track_get_key_transition(p_id.track_idx, existing);
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", p_id.track_idx, time, v, trans);
+ }
+ }
+
+ /*
+ undo_redo->add_do_method(this, "update_tracks");
+ undo_redo->add_undo_method(this, "update");
+ undo_redo->add_do_method(track_editor, "update");
+ undo_redo->add_undo_method(track_editor, "update");
+ undo_redo->add_do_method(track_pos, "update");
+ undo_redo->add_undo_method(track_pos, "update");
+*/
+ undo_redo->commit_action();
+
+ return p_last_track;
+}
+
+void AnimationTrackEditor::show_select_node_warning(bool p_show) {
+}
+
+bool AnimationTrackEditor::is_key_selected(int p_track, int p_key) const {
+
+ SelectedKey sk;
+ sk.key = p_key;
+ sk.track = p_track;
+
+ return selection.has(sk);
+}
+
+bool AnimationTrackEditor::is_selection_active() const {
+ return selection.size();
+}
+
+void AnimationTrackEditor::_update_tracks() {
+
+ int selected = _get_track_selected();
+
+ while (track_vbox->get_child_count()) {
+ memdelete(track_vbox->get_child(0));
+ }
+
+ track_edits.clear();
+ groups.clear();
+
+ if (animation.is_null())
+ return;
+
+ Map<String, VBoxContainer *> group_sort;
+
+ bool use_grouping = !view_group->is_pressed();
+ bool use_filter = selected_filter->is_pressed();
+
+ for (int i = 0; i < animation->get_track_count(); i++) {
+ AnimationTrackEdit *track_edit = NULL;
+
+ //find hint and info for plugin
+
+ if (use_filter) {
+ NodePath path = animation->track_get_path(i);
+
+ if (root && root->has_node(path)) {
+ Node *node = root->get_node(path);
+ if (!node) {
+ continue; // no node, no filter
+ }
+ if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
+ continue; //skip track due to not selected
+ }
+ }
+ }
+
+ if (animation->track_get_type(i) == Animation::TYPE_VALUE) {
+
+ NodePath path = animation->track_get_path(i);
+
+ if (root && root->has_node_and_resource(path)) {
+ RES res;
+ Vector<StringName> leftover_path;
+ Node *node = root->get_node_and_resource(path, res, leftover_path, true);
+
+ Object *object = node;
+ if (res.is_valid()) {
+ object = res.ptr();
+ } else {
+ object = node;
+ }
+
+ if (object && !leftover_path.empty()) {
+ //not a property (value track?)
+ PropertyInfo pinfo;
+ pinfo.name = leftover_path[leftover_path.size() - 1];
+ //now let's see if we can get more info about it
+
+ List<PropertyInfo> plist;
+ object->get_property_list(&plist);
+
+ for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+
+ if (E->get().name == leftover_path[leftover_path.size() - 1]) {
+ pinfo = E->get();
+ break;
+ }
+ }
+
+ for (int j = 0; j < track_edit_plugins.size(); j++) {
+ track_edit = track_edit_plugins[j]->create_value_track_edit(object, pinfo.type, pinfo.name, pinfo.hint, pinfo.hint_string, pinfo.usage);
+ if (track_edit) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (animation->track_get_type(i) == Animation::TYPE_AUDIO) {
+
+ for (int j = 0; j < track_edit_plugins.size(); j++) {
+ track_edit = track_edit_plugins[j]->create_audio_track_edit();
+ if (track_edit) {
+ break;
+ }
+ }
+ }
+
+ if (animation->track_get_type(i) == Animation::TYPE_ANIMATION) {
+ NodePath path = animation->track_get_path(i);
+
+ Node *node = NULL;
+ if (root && root->has_node(path)) {
+ node = root->get_node(path);
+ }
+
+ if (node && Object::cast_to<AnimationPlayer>(node)) {
+ for (int j = 0; j < track_edit_plugins.size(); j++) {
+ track_edit = track_edit_plugins[j]->create_animation_track_edit(node);
+ if (track_edit) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (track_edit == NULL) {
+ //no valid plugin_found
+ track_edit = memnew(AnimationTrackEdit);
+ }
+
+ track_edits.push_back(track_edit);
+
+ if (use_grouping) {
+ String base_path = animation->track_get_path(i);
+ base_path = base_path.get_slice(":", 0); // remove subpath
+
+ if (!group_sort.has(base_path)) {
+ AnimationTrackEditGroup *g = memnew(AnimationTrackEditGroup);
+ Ref<Texture> icon = get_icon("Node", "EditorIcons");
+ String name = base_path;
+ String tooltip;
+ if (root) {
+ Node *n = root->get_node(base_path);
+ if (n) {
+ if (has_icon(n->get_class(), "EditorIcons")) {
+ icon = get_icon(n->get_class(), "EditorIcons");
+ }
+ name = n->get_name();
+ tooltip = root->get_path_to(n);
+ }
+ }
+
+ g->set_type_and_name(icon, name, animation->track_get_path(i));
+ g->set_root(root);
+ g->set_tooltip(tooltip);
+ g->set_timeline(timeline);
+ groups.push_back(g);
+ VBoxContainer *vb = memnew(VBoxContainer);
+ vb->add_constant_override("separation", 0);
+ vb->add_child(g);
+ track_vbox->add_child(vb);
+ group_sort[base_path] = vb;
+ }
+
+ track_edit->set_in_group(true);
+ group_sort[base_path]->add_child(track_edit);
+
+ } else {
+ track_edit->set_in_group(false);
+ track_vbox->add_child(track_edit);
+ }
+
+ track_edit->set_undo_redo(undo_redo);
+ track_edit->set_timeline(timeline);
+ track_edit->set_block_animation_update_ptr(&block_animation_update);
+ track_edit->set_root(root);
+ track_edit->set_animation_and_track(animation, i);
+ track_edit->set_play_position(timeline->get_play_position());
+ track_edit->set_editor(this);
+
+ if (selected == i) {
+ track_edit->grab_focus();
+ }
+
+ track_edit->connect("timeline_changed", this, "_timeline_changed");
+ track_edit->connect("remove_request", this, "_track_remove_request", varray(), CONNECT_DEFERRED);
+ track_edit->connect("dropped", this, "_dropped_track", varray(), CONNECT_DEFERRED);
+ track_edit->connect("insert_key", this, "_insert_key_from_track", varray(i), CONNECT_DEFERRED);
+ track_edit->connect("select_key", this, "_key_selected", varray(i), CONNECT_DEFERRED);
+ track_edit->connect("deselect_key", this, "_key_deselected", varray(i), CONNECT_DEFERRED);
+ track_edit->connect("bezier_edit", this, "_bezier_edit", varray(i), CONNECT_DEFERRED);
+ track_edit->connect("clear_selection", this, "_clear_selection");
+ track_edit->connect("move_selection_begin", this, "_move_selection_begin");
+ track_edit->connect("move_selection", this, "_move_selection");
+ track_edit->connect("move_selection_commit", this, "_move_selection_commit");
+ track_edit->connect("move_selection_cancel", this, "_move_selection_cancel");
+
+ track_edit->connect("duplicate_request", this, "_edit_menu_pressed", varray(EDIT_DUPLICATE_SELECTION), CONNECT_DEFERRED);
+ track_edit->connect("duplicate_transpose_request", this, "_edit_menu_pressed", varray(EDIT_DUPLICATE_TRANSPOSED), CONNECT_DEFERRED);
+ track_edit->connect("delete_request", this, "_edit_menu_pressed", varray(EDIT_DELETE_SELECTION), CONNECT_DEFERRED);
+ }
+}
+
+void AnimationTrackEditor::_animation_changed() {
+
+ timeline->update();
+ timeline->update_values();
+ if (block_animation_update) {
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->update();
+ }
+ for (int i = 0; i < groups.size(); i++) {
+ groups[i]->update();
+ }
+ } else {
+ _update_tracks();
+ }
+
+ bezier_edit->update();
+
+ step->set_block_signals(true);
+ step->set_value(animation->get_step());
+ step->set_block_signals(false);
+}
+
+MenuButton *AnimationTrackEditor::get_edit_menu() {
+ return edit;
+}
+
+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"));
+ selected_filter->set_icon(get_icon("AnimationFilter", "EditorIcons"));
+ main_panel->add_style_override("panel", get_stylebox("bg", "Tree"));
+ }
+
+ if (p_what == NOTIFICATION_READY) {
+ EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", this, "_selection_changed");
+ }
+
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ update_keying();
+ EditorNode::get_singleton()->update_keying();
+ emit_signal("keying_changed");
+ }
+}
+
+void AnimationTrackEditor::_update_scroll(double) {
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->update();
+ }
+ for (int i = 0; i < groups.size(); i++) {
+ groups[i]->update();
+ }
+}
+
+void AnimationTrackEditor::_update_step(double p_new_step) {
+
+ undo_redo->create_action("Change animation step");
+ undo_redo->add_do_method(animation.ptr(), "set_step", p_new_step);
+ undo_redo->add_undo_method(animation.ptr(), "set_step", animation->get_step());
+ step->set_block_signals(true);
+ undo_redo->commit_action();
+ step->set_block_signals(false);
+ emit_signal("animation_step_changed", p_new_step);
+}
+
+void AnimationTrackEditor::_update_length(double p_new_len) {
+
+ emit_signal("animation_len_changed", p_new_len);
+}
+
+void AnimationTrackEditor::_dropped_track(int p_from_track, int p_to_track) {
+ if (p_to_track >= track_edits.size()) {
+ p_to_track = track_edits.size() - 1;
+ }
+
+ if (p_from_track == p_to_track)
+ return;
+
+ _clear_selection();
+ undo_redo->create_action("Rearrange tracks");
+ undo_redo->add_do_method(animation.ptr(), "track_swap", p_from_track, p_to_track);
+ undo_redo->add_undo_method(animation.ptr(), "track_swap", p_to_track, p_from_track);
+ undo_redo->commit_action();
+}
+
+void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
+
+ ERR_FAIL_COND(!root);
+ Node *node = get_node(p_path);
+ ERR_FAIL_COND(!node);
+ NodePath path_to = root->get_path_to(node);
+
+ if (adding_track_type == Animation::TYPE_TRANSFORM && !node->is_class("Spatial")) {
+ EditorNode::get_singleton()->show_warning(TTR("Transform tracks only apply to Spatial-based nodes."));
+ return;
+ }
+
+ switch (adding_track_type) {
+ case Animation::TYPE_VALUE: {
+ adding_track_path = path_to;
+ prop_selector->set_type_filter(Vector<Variant::Type>());
+ prop_selector->select_property_from_instance(node);
+ } break;
+ case Animation::TYPE_TRANSFORM:
+ case Animation::TYPE_METHOD: {
+
+ undo_redo->create_action("Add Track");
+ undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
+ undo_redo->add_do_method(animation.ptr(), "track_set_path", animation->get_track_count(), path_to);
+ undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
+ undo_redo->commit_action();
+
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ Vector<Variant::Type> filter;
+ filter.push_back(Variant::INT);
+ filter.push_back(Variant::REAL);
+ filter.push_back(Variant::VECTOR2);
+ filter.push_back(Variant::VECTOR3);
+ filter.push_back(Variant::QUAT);
+ filter.push_back(Variant::PLANE);
+ filter.push_back(Variant::COLOR);
+
+ adding_track_path = path_to;
+ prop_selector->set_type_filter(filter);
+ prop_selector->select_property_from_instance(node);
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ if (!node->is_class("AudioStreamPlayer") && !node->is_class("AudioStreamPlayer2D") && !node->is_class("AudioStreamPlayer3D")) {
+ EditorNode::get_singleton()->show_warning(TTR("Audio tracks can only point to nodes of type:\n-AudioStreamPlayer\n-AudioStreamPlayer2D\n-AudioStreamPlayer3D"));
+ return;
+ }
+
+ undo_redo->create_action("Add Track");
+ undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
+ undo_redo->add_do_method(animation.ptr(), "track_set_path", animation->get_track_count(), path_to);
+ undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
+ undo_redo->commit_action();
+
+ } break;
+ case Animation::TYPE_ANIMATION: {
+
+ if (!node->is_class("AnimationPlayer")) {
+ EditorNode::get_singleton()->show_warning(TTR("Animation tracks can only point to AnimationPlayer nodes."));
+ return;
+ }
+
+ if (node == AnimationPlayerEditor::singleton->get_player()) {
+ EditorNode::get_singleton()->show_warning(TTR("An animation player can't animate itself, only other players."));
+ return;
+ }
+
+ undo_redo->create_action("Add Track");
+ undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
+ undo_redo->add_do_method(animation.ptr(), "track_set_path", animation->get_track_count(), path_to);
+ undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
+ undo_redo->commit_action();
+
+ } break;
+ }
+}
+
+void AnimationTrackEditor::_add_track(int p_type) {
+ if (!root) {
+ EditorNode::get_singleton()->show_warning(TTR("Not possible to add a new track without a root"));
+ return;
+ }
+ adding_track_type = p_type;
+ pick_track->popup_centered_ratio();
+}
+
+void AnimationTrackEditor::_new_track_property_selected(String p_name) {
+
+ String full_path = String(adding_track_path) + ":" + p_name;
+
+ if (adding_track_type == Animation::TYPE_VALUE) {
+
+ Animation::UpdateMode update_mode = Animation::UPDATE_DISCRETE;
+ {
+ //hack
+ NodePath np;
+ animation->add_track(Animation::TYPE_VALUE);
+ animation->track_set_path(animation->get_track_count() - 1, full_path);
+ PropertyInfo h = _find_hint_for_track(animation->get_track_count() - 1, np);
+ animation->remove_track(animation->get_track_count() - 1); //hack
+ if (h.type == Variant::REAL ||
+ h.type == Variant::VECTOR2 ||
+ h.type == Variant::RECT2 ||
+ h.type == Variant::VECTOR3 ||
+ h.type == Variant::AABB ||
+ h.type == Variant::QUAT ||
+ h.type == Variant::COLOR ||
+ h.type == Variant::PLANE ||
+ h.type == Variant::TRANSFORM2D ||
+ h.type == Variant::TRANSFORM) {
+
+ update_mode = Animation::UPDATE_CONTINUOUS;
+ }
+
+ if (h.usage & PROPERTY_USAGE_ANIMATE_AS_TRIGGER) {
+ update_mode = Animation::UPDATE_TRIGGER;
+ }
+ }
+
+ undo_redo->create_action("Add Track");
+ undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
+ undo_redo->add_do_method(animation.ptr(), "track_set_path", animation->get_track_count(), full_path);
+ undo_redo->add_do_method(animation.ptr(), "value_track_set_update_mode", animation->get_track_count(), update_mode);
+ undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
+ undo_redo->commit_action();
+ } else {
+ Vector<String> subindices;
+ {
+ //hack
+ NodePath np;
+ animation->add_track(Animation::TYPE_VALUE);
+ animation->track_set_path(animation->get_track_count() - 1, full_path);
+ PropertyInfo h = _find_hint_for_track(animation->get_track_count() - 1, np);
+ animation->remove_track(animation->get_track_count() - 1); //hack
+ bool valid;
+ subindices = _get_bezier_subindices_for_type(h.type, &valid);
+ if (!valid) {
+ EditorNode::get_singleton()->show_warning("Invalid track for Bezier (no suitable sub-properties)");
+ return;
+ }
+ }
+
+ undo_redo->create_action("Add Bezier Track");
+ int base_track = animation->get_track_count();
+ for (int i = 0; i < subindices.size(); i++) {
+ undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
+ undo_redo->add_do_method(animation.ptr(), "track_set_path", base_track + i, full_path + subindices[i]);
+ undo_redo->add_undo_method(animation.ptr(), "remove_track", base_track + i);
+ }
+ undo_redo->commit_action();
+ }
+}
+
+void AnimationTrackEditor::_timeline_value_changed(double) {
+
+ timeline->update_play_position();
+
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->update();
+ track_edits[i]->update_play_position();
+ }
+
+ for (int i = 0; i < groups.size(); i++) {
+ groups[i]->update();
+ }
+
+ bezier_edit->update();
+ bezier_edit->update_play_position();
+}
+
+int AnimationTrackEditor::_get_track_selected() {
+
+ for (int i = 0; i < track_edits.size(); i++) {
+ if (track_edits[i]->has_focus())
+ return i;
+ }
+
+ return -1;
+}
+
+void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
+
+ ERR_FAIL_INDEX(p_track, animation->get_track_count());
+
+ if (snap->is_pressed() && step->get_value() != 0) {
+ p_ofs = Math::stepify(p_ofs, step->get_value());
+ }
+ while (animation->track_find_key(p_track, p_ofs, true) != -1) { //make sure insertion point is valid
+ p_ofs += 0.001;
+ }
+
+ switch (animation->track_get_type(p_track)) {
+ case Animation::TYPE_TRANSFORM: {
+ if (!root->has_node(animation->track_get_path(p_track))) {
+ EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
+ return;
+ }
+ Spatial *base = Object::cast_to<Spatial>(root->get_node(animation->track_get_path(p_track)));
+
+ if (!base) {
+ EditorNode::get_singleton()->show_warning(TTR("Track is not of type Spatial, can't insert key"));
+ return;
+ }
+
+ Transform xf = base->get_transform();
+
+ Vector3 loc = xf.get_origin();
+ Vector3 scale = xf.basis.get_scale_local();
+ Quat rot = xf.basis;
+
+ undo_redo->create_action("Add Transform Track Key");
+ undo_redo->add_do_method(animation.ptr(), "transform_track_insert_key", p_track, p_ofs, loc, rot, scale);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->commit_action();
+
+ } break;
+ case Animation::TYPE_VALUE: {
+
+ NodePath bp;
+ Variant value;
+ _find_hint_for_track(p_track, bp, &value);
+
+ undo_redo->create_action("Add Track Key");
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, value);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->commit_action();
+
+ } break;
+ case Animation::TYPE_METHOD: {
+ if (!root->has_node(animation->track_get_path(p_track))) {
+ EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a method key."));
+ return;
+ }
+ Node *base = root->get_node(animation->track_get_path(p_track));
+
+ method_selector->select_method_from_instance(base);
+
+ insert_key_from_track_call_ofs = p_ofs;
+ insert_key_from_track_call_track = p_track;
+
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ NodePath bp;
+ Variant value;
+ _find_hint_for_track(p_track, bp, &value);
+ Array arr;
+ arr.resize(5);
+ arr[0] = value;
+ arr[1] = -0.25;
+ arr[2] = 0;
+ arr[3] = 0.25;
+ arr[4] = 0;
+
+ undo_redo->create_action("Add Track Key");
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, arr);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->commit_action();
+
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ Dictionary ak;
+ ak["stream"] = RES();
+ ak["start_offset"] = 0;
+ ak["end_offset"] = 0;
+
+ undo_redo->create_action("Add Track Key");
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, ak);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->commit_action();
+ } break;
+ case Animation::TYPE_ANIMATION: {
+
+ StringName anim = "[stop]";
+
+ undo_redo->create_action("Add Track Key");
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, anim);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->commit_action();
+ } break;
+ }
+}
+
+void AnimationTrackEditor::_add_method_key(const String &p_method) {
+
+ if (!root->has_node(animation->track_get_path(insert_key_from_track_call_track))) {
+ EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a method key."));
+ return;
+ }
+ Node *base = root->get_node(animation->track_get_path(insert_key_from_track_call_track));
+
+ List<MethodInfo> minfo;
+ base->get_method_list(&minfo);
+
+ for (List<MethodInfo>::Element *E = minfo.front(); E; E = E->next()) {
+ if (E->get().name == p_method) {
+
+ Dictionary d;
+ d["method"] = p_method;
+ Array params;
+ int first_defarg = E->get().arguments.size() - E->get().default_arguments.size();
+
+ for (int i = 0; i < E->get().arguments.size(); i++) {
+
+ if (i >= first_defarg) {
+ Variant arg = E->get().default_arguments[i - first_defarg];
+ params.push_back(arg);
+ } else {
+ Variant::CallError ce;
+ Variant arg = Variant::construct(E->get().arguments[i].type, NULL, 0, ce);
+ params.push_back(arg);
+ }
+ }
+ d["args"] = params;
+
+ undo_redo->create_action("Add Method Track Key");
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", insert_key_from_track_call_track, insert_key_from_track_call_ofs, d);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", insert_key_from_track_call_track, insert_key_from_track_call_ofs);
+ undo_redo->commit_action();
+
+ return;
+ }
+ }
+
+ EditorNode::get_singleton()->show_warning(TTR("Method not found in object: ") + p_method);
+}
+
+void AnimationTrackEditor::_key_selected(int p_key, bool p_single, int p_track) {
+
+ ERR_FAIL_INDEX(p_track, animation->get_track_count());
+ ERR_FAIL_INDEX(p_key, animation->track_get_key_count(p_track));
+
+ SelectedKey sk;
+ sk.key = p_key;
+ sk.track = p_track;
+
+ if (p_single) {
+ _clear_selection();
+ }
+
+ KeyInfo ki;
+ ki.pos = animation->track_get_key_time(p_track, p_key);
+ selection[sk] = ki;
+
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->update();
+ }
+
+ _update_key_edit();
+}
+
+void AnimationTrackEditor::_key_deselected(int p_key, int p_track) {
+
+ ERR_FAIL_INDEX(p_track, animation->get_track_count());
+ ERR_FAIL_INDEX(p_key, animation->track_get_key_count(p_track));
+
+ SelectedKey sk;
+ sk.key = p_key;
+ sk.track = p_track;
+
+ selection.erase(sk);
+
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->update();
+ }
+
+ _update_key_edit();
+}
+
+void AnimationTrackEditor::_move_selection_begin() {
+ moving_selection = true;
+ moving_selection_offset = 0;
+}
+
+void AnimationTrackEditor::_move_selection(float p_offset) {
+ moving_selection_offset = p_offset;
+ if (snap->is_pressed() && step->get_value() != 0) {
+ moving_selection_offset = Math::stepify(moving_selection_offset, step->get_value());
+ }
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->update();
+ }
+}
+
+struct _AnimMoveRestore {
+
+ int track;
+ float time;
+ Variant key;
+ float transition;
+};
+//used for undo/redo
+
+void AnimationTrackEditor::_clear_key_edit() {
+ if (key_edit) {
+
+#if 0
+ // going back seems like the most comfortable thing to do, but it results
+ // in weird behaviors and crashes, because going back to animation editor
+ // triggers the editor setting up again itself
+
+ bool go_back = false;
+ if (EditorNode::get_singleton()->get_inspector()->get_edited_object() == key_edit) {
+ EditorNode::get_singleton()->push_item(NULL);
+ go_back = true;
+ }
+
+ memdelete(key_edit);
+ key_edit = NULL;
+
+ if (go_back) {
+ EditorNode::get_singleton()->get_inspector_dock()->go_back();
+ }
+#else
+ //if key edit is the object being inspected, remove it first
+ if (EditorNode::get_singleton()->get_inspector()->get_edited_object() == key_edit) {
+ EditorNode::get_singleton()->push_item(NULL);
+ }
+ //then actually delete it
+ memdelete(key_edit);
+ key_edit = NULL;
+#endif
+ }
+}
+
+void AnimationTrackEditor::_clear_selection() {
+ selection.clear();
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->update();
+ }
+ _clear_key_edit();
+}
+
+void AnimationTrackEditor::_update_key_edit() {
+
+ _clear_key_edit();
+ if (!animation.is_valid())
+ return;
+ if (selection.size() != 1) {
+ return;
+ }
+
+ key_edit = memnew(AnimationTrackKeyEdit);
+ key_edit->animation = animation;
+ key_edit->track = selection.front()->key().track;
+
+ float ofs = animation->track_get_key_time(key_edit->track, selection.front()->key().key);
+ key_edit->key_ofs = ofs;
+ key_edit->root_path = root;
+
+ NodePath np;
+ key_edit->hint = _find_hint_for_track(key_edit->track, np);
+ key_edit->undo_redo = undo_redo;
+ key_edit->base = np;
+
+ EditorNode::get_singleton()->push_item(key_edit);
+}
+
+void AnimationTrackEditor::_clear_selection_for_anim(const Ref<Animation> &p_anim) {
+
+ if (!(animation == p_anim))
+ return;
+ //selection.clear();
+ _clear_selection();
+}
+
+void AnimationTrackEditor::_select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos) {
+
+ if (!(animation == p_anim))
+ return;
+
+ int idx = animation->track_find_key(p_track, p_pos, true);
+ ERR_FAIL_COND(idx < 0);
+
+ SelectedKey sk;
+ sk.track = p_track;
+ sk.key = idx;
+ KeyInfo ki;
+ ki.pos = p_pos;
+
+ selection.insert(sk, ki);
+}
+
+void AnimationTrackEditor::_move_selection_commit() {
+
+ undo_redo->create_action(TTR("Anim Move Keys"));
+
+ List<_AnimMoveRestore> to_restore;
+
+ float motion = moving_selection_offset;
+ // 1-remove the keys
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
+ }
+ // 2- remove overlapped keys
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float newtime = E->get().pos + motion;
+ int idx = animation->track_find_key(E->key().track, newtime, true);
+ if (idx == -1)
+ continue;
+ SelectedKey sk;
+ sk.key = idx;
+ sk.track = E->key().track;
+ if (selection.has(sk))
+ continue; //already in selection, don't save
+
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newtime);
+ _AnimMoveRestore amr;
+
+ amr.key = animation->track_get_key_value(E->key().track, idx);
+ amr.track = E->key().track;
+ amr.time = newtime;
+ amr.transition = animation->track_get_key_transition(E->key().track, idx);
+
+ to_restore.push_back(amr);
+ }
+
+ // 3-move the keys (re insert them)
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float newpos = E->get().pos + motion;
+ /*
+ if (newpos<0)
+ continue; //no add at the beginning
+ */
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->key().track, newpos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
+ }
+
+ // 4-(undo) remove inserted keys
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float newpos = E->get().pos + motion;
+ /*
+ if (newpos<0)
+ continue; //no remove what no inserted
+ */
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newpos);
+ }
+
+ // 5-(undo) reinsert keys
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
+ }
+
+ // 6-(undo) reinsert overlapped keys
+ for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
+
+ _AnimMoveRestore &amr = E->get();
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
+ }
+
+ // 6-(undo) reinsert overlapped keys
+ for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
+
+ _AnimMoveRestore &amr = E->get();
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
+ }
+
+ undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
+ undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
+
+ // 7-reselect
+
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float oldpos = E->get().pos;
+ float newpos = oldpos + motion;
+ //if (newpos>=0)
+ undo_redo->add_do_method(this, "_select_at_anim", animation, E->key().track, newpos);
+ undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, oldpos);
+ }
+
+ undo_redo->commit_action();
+
+ moving_selection = false;
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->update();
+ }
+}
+void AnimationTrackEditor::_move_selection_cancel() {
+
+ moving_selection = false;
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->update();
+ }
+}
+
+bool AnimationTrackEditor::is_moving_selection() const {
+ return moving_selection;
+}
+float AnimationTrackEditor::get_moving_selection_offset() const {
+ return moving_selection_offset;
+}
+
+void AnimationTrackEditor::_box_selection_draw() {
+
+ Color color = get_color("accent_color", "Editor");
+ color.a = 0.2;
+ Rect2 rect = Rect2(Point2(), box_selection->get_size());
+ box_selection->draw_rect(rect, color);
+}
+
+void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_command() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
+
+ timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05);
+ scroll->accept_event();
+ }
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_command() && mb->get_button_index() == BUTTON_WHEEL_UP) {
+
+ timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05);
+ scroll->accept_event();
+ }
+
+ if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) {
+ if (mb->is_pressed()) {
+ box_selecting = true;
+ box_selecting_from = scroll->get_global_transform().xform(mb->get_position());
+ box_select_rect = Rect2();
+ } else if (box_selecting) {
+
+ if (box_selection->is_visible_in_tree()) {
+ //only if moved
+ for (int i = 0; i < track_edits.size(); i++) {
+
+ Rect2 local_rect = box_select_rect;
+ local_rect.position -= track_edits[i]->get_global_position();
+ track_edits[i]->append_to_selection(local_rect);
+ }
+
+ if (_get_track_selected() == -1 && track_edits.size() > 0) { //minimal hack to make shortcuts work
+ track_edits[track_edits.size() - 1]->grab_focus();
+ }
+ } else {
+ _clear_selection(); //clear it
+ }
+
+ box_selection->hide();
+ box_selecting = false;
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+
+ if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_MIDDLE) {
+
+ timeline->set_value(timeline->get_value() - mm->get_relative().x / timeline->get_zoom_scale());
+ }
+
+ if (mm.is_valid() && box_selecting) {
+
+ if (!(mm->get_button_mask() & BUTTON_MASK_LEFT)) {
+ //no longer
+ box_selection->hide();
+ box_selecting = false;
+ return;
+ }
+
+ if (!box_selection->is_visible_in_tree()) {
+ if (!mm->get_shift()) {
+ _clear_selection(); //only append if shift is pressed
+ }
+ box_selection->show();
+ }
+
+ Vector2 from = box_selecting_from;
+ Vector2 to = scroll->get_global_transform().xform(mm->get_position());
+
+ if (from.x > to.x) {
+ SWAP(from.x, to.x);
+ }
+
+ if (from.y > to.y) {
+ SWAP(from.y, to.y);
+ }
+
+ Rect2 rect(from, to - from);
+ Rect2 scroll_rect = Rect2(scroll->get_global_position(), scroll->get_size());
+ rect = scroll_rect.clip(rect);
+ box_selection->set_position(rect.position);
+ box_selection->set_size(rect.size);
+
+ box_select_rect = rect;
+
+ if (get_local_mouse_position().y < 0) {
+ //avoid box selection from going up and lose focus to viewport
+ warp_mouse(Vector2(mm->get_position().x, 0));
+ }
+ }
+}
+
+void AnimationTrackEditor::_cancel_bezier_edit() {
+ bezier_edit->hide();
+ scroll->show();
+}
+
+void AnimationTrackEditor::_bezier_edit(int p_for_track) {
+
+ _clear_selection(); //bezier probably wants to use a separate selection mode
+ bezier_edit->set_root(root);
+ bezier_edit->set_animation_and_track(animation, p_for_track);
+ scroll->hide();
+ bezier_edit->show();
+ //search everything within the track and curve- edit it
+}
+
+void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) {
+ //duplicait!
+ if (selection.size() && animation.is_valid() && (!transpose || (_get_track_selected() >= 0 && _get_track_selected() < animation->get_track_count()))) {
+
+ int top_track = 0x7FFFFFFF;
+ float top_time = 1e10;
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ const SelectedKey &sk = E->key();
+
+ float t = animation->track_get_key_time(sk.track, sk.key);
+ if (t < top_time)
+ top_time = t;
+ if (sk.track < top_track)
+ top_track = sk.track;
+ }
+ ERR_FAIL_COND(top_track == 0x7FFFFFFF || top_time == 1e10);
+
+ //
+
+ int start_track = transpose ? _get_track_selected() : top_track;
+
+ undo_redo->create_action(TTR("Anim Duplicate Keys"));
+
+ List<Pair<int, float> > new_selection_values;
+
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ const SelectedKey &sk = E->key();
+
+ float t = animation->track_get_key_time(sk.track, sk.key);
+
+ float dst_time = t + (timeline->get_play_position() - top_time);
+ int dst_track = sk.track + (start_track - top_track);
+
+ if (dst_track < 0 || dst_track >= animation->get_track_count())
+ continue;
+
+ if (animation->track_get_type(dst_track) != animation->track_get_type(sk.track))
+ continue;
+
+ int existing_idx = animation->track_find_key(dst_track, dst_time, true);
+
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", dst_track, dst_time, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", dst_track, dst_time);
+
+ Pair<int, float> p;
+ p.first = dst_track;
+ p.second = dst_time;
+ new_selection_values.push_back(p);
+
+ if (existing_idx != -1) {
+
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", dst_track, dst_time, animation->track_get_key_value(dst_track, existing_idx), animation->track_get_key_transition(dst_track, existing_idx));
+ }
+ }
+
+ undo_redo->commit_action();
+
+ //reselect duplicated
+
+ Map<SelectedKey, KeyInfo> new_selection;
+ for (List<Pair<int, float> >::Element *E = new_selection_values.front(); E; E = E->next()) {
+
+ int track = E->get().first;
+ float time = E->get().second;
+
+ int existing_idx = animation->track_find_key(track, time, true);
+
+ if (existing_idx == -1)
+ continue;
+ SelectedKey sk2;
+ sk2.track = track;
+ sk2.key = existing_idx;
+
+ KeyInfo ki;
+ ki.pos = time;
+
+ new_selection[sk2] = ki;
+ }
+
+ selection = new_selection;
+ _update_tracks();
+ _update_key_edit();
+ }
+}
+void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
+
+ last_menu_track_opt = p_option;
+ switch (p_option) {
+ case EDIT_COPY_TRACKS: {
+ track_copy_select->clear();
+ TreeItem *troot = track_copy_select->create_item();
+
+ for (int i = 0; i < animation->get_track_count(); i++) {
+
+ NodePath path = animation->track_get_path(i);
+ Node *node = NULL;
+
+ if (root && root->has_node(path)) {
+ node = root->get_node(path);
+ }
+
+ String text;
+ Ref<Texture> icon = get_icon("Node", "EditorIcons");
+ if (node) {
+ if (has_icon(node->get_class(), "EditorIcons")) {
+ icon = get_icon(node->get_class(), "EditorIcons");
+ }
+
+ text = node->get_name();
+ Vector<StringName> sn = path.get_subnames();
+ for (int i = 0; i < sn.size(); i++) {
+ text += ".";
+ text += sn[i];
+ }
+
+ path = NodePath(node->get_path().get_names(), path.get_subnames(), true); //store full path instead for copying
+ } else {
+ text = path;
+ int sep = text.find(":");
+ if (sep != -1) {
+ text = text.substr(sep + 1, text.length());
+ }
+ }
+
+ switch (animation->track_get_type(i)) {
+ case Animation::TYPE_TRANSFORM: text += " (Transform)"; break;
+ case Animation::TYPE_METHOD: text += " (Methods)"; break;
+ case Animation::TYPE_BEZIER: text += " (Bezier)"; break;
+ case Animation::TYPE_AUDIO: text += " (Audio)"; break;
+ default: {};
+ }
+
+ TreeItem *it = track_copy_select->create_item(troot);
+ it->set_editable(0, true);
+ it->set_selectable(0, true);
+ it->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ it->set_icon(0, icon);
+ it->set_text(0, text);
+ Dictionary md;
+ md["track_idx"] = i;
+ md["path"] = path;
+ it->set_metadata(0, md);
+ }
+
+ track_copy_dialog->popup_centered_minsize(Size2(300, 500) * EDSCALE);
+ } break;
+ case EDIT_COPY_TRACKS_CONFIRM: {
+
+ track_clipboard.clear();
+ TreeItem *root = track_copy_select->get_root();
+ if (root) {
+
+ TreeItem *it = root->get_children();
+ while (it) {
+ Dictionary md = it->get_metadata(0);
+ int idx = md["track_idx"];
+ if (it->is_checked(0) && idx >= 0 && idx < animation->get_track_count()) {
+ TrackClipboard tc;
+ tc.base_path = animation->track_get_path(idx);
+ tc.full_path = md["path"];
+ tc.track_type = animation->track_get_type(idx);
+ tc.interp_type = animation->track_get_interpolation_type(idx);
+ if (tc.track_type == Animation::TYPE_VALUE) {
+ tc.update_mode = animation->value_track_get_update_mode(idx);
+ }
+ tc.loop_wrap = animation->track_get_interpolation_loop_wrap(idx);
+ tc.enabled = animation->track_is_enabled(idx);
+ for (int i = 0; i < animation->track_get_key_count(idx); i++) {
+ TrackClipboard::Key k;
+ k.time = animation->track_get_key_time(idx, i);
+ k.value = animation->track_get_key_value(idx, i);
+ k.transition = animation->track_get_key_transition(idx, i);
+ tc.keys.push_back(k);
+ }
+ track_clipboard.push_back(tc);
+ }
+ it = it->get_next();
+ }
+ }
+ } break;
+ case EDIT_PASTE_TRACKS: {
+
+ if (track_clipboard.size() == 0) {
+ EditorNode::get_singleton()->show_warning(TTR("Clipboard is empty"));
+ break;
+ }
+
+ int base_track = animation->get_track_count();
+ undo_redo->create_action("Paste Tracks");
+ for (int i = 0; i < track_clipboard.size(); i++) {
+ undo_redo->add_do_method(animation.ptr(), "add_track", track_clipboard[i].track_type);
+ Node *exists = NULL;
+ NodePath path = track_clipboard[i].base_path;
+
+ if (root) {
+ NodePath np = track_clipboard[i].full_path;
+ exists = root->get_node(np);
+ if (exists) {
+ path = NodePath(root->get_path_to(exists).get_names(), track_clipboard[i].full_path.get_subnames(), false);
+ }
+ }
+
+ undo_redo->add_do_method(animation.ptr(), "track_set_path", base_track, path);
+ undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_type", base_track, track_clipboard[i].interp_type);
+ undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_loop_wrap", base_track, track_clipboard[i].loop_wrap);
+ undo_redo->add_do_method(animation.ptr(), "track_set_enabled", base_track, track_clipboard[i].enabled);
+ if (track_clipboard[i].track_type == Animation::TYPE_VALUE) {
+ undo_redo->add_do_method(animation.ptr(), "value_track_set_update_mode", base_track, track_clipboard[i].update_mode);
+ }
+
+ for (int j = 0; j < track_clipboard[i].keys.size(); j++) {
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", base_track, track_clipboard[i].keys[j].time, track_clipboard[i].keys[j].value, track_clipboard[i].keys[j].transition);
+ }
+
+ undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
+
+ base_track++;
+ }
+
+ undo_redo->commit_action();
+ } break;
+
+ case EDIT_SCALE_SELECTION:
+ case EDIT_SCALE_FROM_CURSOR: {
+ scale_dialog->popup_centered(Size2(200, 100) * EDSCALE);
+ } break;
+ case EDIT_SCALE_CONFIRM: {
+ if (selection.empty())
+ return;
+
+ float from_t = 1e20;
+ float to_t = -1e20;
+ float len = -1e20;
+ float pivot = 0;
+
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
+ float t = animation->track_get_key_time(E->key().track, E->key().key);
+ if (t < from_t)
+ from_t = t;
+ if (t > to_t)
+ to_t = t;
+ }
+
+ len = to_t - from_t;
+ if (last_menu_track_opt == EDIT_SCALE_FROM_CURSOR) {
+ pivot = timeline->get_play_position();
+
+ } else {
+
+ pivot = from_t;
+ }
+
+ float s = scale->get_value();
+ if (s == 0) {
+ ERR_PRINT("Can't scale to 0");
+ }
+
+ undo_redo->create_action(TTR("Anim Scale Keys"));
+
+ List<_AnimMoveRestore> to_restore;
+
+ // 1-remove the keys
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
+ }
+ // 2- remove overlapped keys
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float newtime = (E->get().pos - from_t) * s + from_t;
+ int idx = animation->track_find_key(E->key().track, newtime, true);
+ if (idx == -1)
+ continue;
+ SelectedKey sk;
+ sk.key = idx;
+ sk.track = E->key().track;
+ if (selection.has(sk))
+ continue; //already in selection, don't save
+
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newtime);
+ _AnimMoveRestore amr;
+
+ amr.key = animation->track_get_key_value(E->key().track, idx);
+ amr.track = E->key().track;
+ amr.time = newtime;
+ amr.transition = animation->track_get_key_transition(E->key().track, idx);
+
+ to_restore.push_back(amr);
+ }
+
+#define _NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * ABS(s) + from_t
+ // 3-move the keys (re insert them)
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float newpos = _NEW_POS(E->get().pos);
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->key().track, newpos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
+ }
+
+ // 4-(undo) remove inserted keys
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float newpos = _NEW_POS(E->get().pos);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newpos);
+ }
+
+ // 5-(undo) reinsert keys
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
+ }
+
+ // 6-(undo) reinsert overlapped keys
+ for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
+
+ _AnimMoveRestore &amr = E->get();
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
+ }
+
+ // 6-(undo) reinsert overlapped keys
+ for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
+
+ _AnimMoveRestore &amr = E->get();
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
+ }
+
+ undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
+ undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
+
+ // 7-reselect
+
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ float oldpos = E->get().pos;
+ float newpos = _NEW_POS(oldpos);
+ if (newpos >= 0)
+ undo_redo->add_do_method(this, "_select_at_anim", animation, E->key().track, newpos);
+ undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, oldpos);
+ }
+#undef _NEW_POS
+ undo_redo->commit_action();
+ } break;
+ case EDIT_DUPLICATE_SELECTION: {
+
+ if (bezier_edit->is_visible()) {
+ bezier_edit->duplicate_selection();
+ break;
+ }
+ _anim_duplicate_keys(false);
+ } break;
+ case EDIT_DUPLICATE_TRANSPOSED: {
+ if (bezier_edit->is_visible()) {
+ EditorNode::get_singleton()->show_warning(TTR("This option does not work for Bezier editing, as it's only a single track."));
+ break;
+ }
+ _anim_duplicate_keys(true);
+ } break;
+ case EDIT_DELETE_SELECTION: {
+
+ if (bezier_edit->is_visible()) {
+ bezier_edit->delete_selection();
+ break;
+ }
+
+ if (selection.size()) {
+ undo_redo->create_action(TTR("Anim Delete Keys"));
+
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
+ }
+ undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
+ undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
+ undo_redo->commit_action();
+ //selection.clear();
+ _update_key_edit();
+ }
+ } break;
+ case EDIT_GOTO_NEXT_STEP: {
+
+ if (animation.is_null())
+ break;
+ float step = animation->get_step();
+ if (step == 0)
+ step = 1;
+
+ float pos = timeline->get_play_position();
+
+ pos = Math::stepify(pos + step, step);
+ if (pos > animation->get_length())
+ pos = animation->get_length();
+ set_anim_pos(pos);
+
+ emit_signal("timeline_changed", pos, true);
+
+ } break;
+ case EDIT_GOTO_PREV_STEP: {
+ if (animation.is_null())
+ break;
+ float step = animation->get_step();
+ if (step == 0)
+ step = 1;
+
+ float pos = timeline->get_play_position();
+ pos = Math::stepify(pos - step, step);
+ if (pos < 0)
+ pos = 0;
+ set_anim_pos(pos);
+ emit_signal("timeline_changed", pos, true);
+
+ } break;
+ case EDIT_OPTIMIZE_ANIMATION: {
+ optimize_dialog->popup_centered(Size2(250, 180) * EDSCALE);
+
+ } break;
+ case EDIT_OPTIMIZE_ANIMATION_CONFIRM: {
+ animation->optimize(optimize_linear_error->get_value(), optimize_angular_error->get_value(), optimize_max_angle->get_value());
+ _update_tracks();
+ undo_redo->clear_history();
+
+ } break;
+ case EDIT_CLEAN_UP_ANIMATION: {
+ cleanup_dialog->popup_centered_minsize(Size2(300, 0) * EDSCALE);
+
+ } break;
+ case EDIT_CLEAN_UP_ANIMATION_CONFIRM: {
+ if (cleanup_all->is_pressed()) {
+ List<StringName> names;
+ AnimationPlayerEditor::singleton->get_player()->get_animation_list(&names);
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ _cleanup_animation(AnimationPlayerEditor::singleton->get_player()->get_animation(E->get()));
+ }
+ } else {
+ _cleanup_animation(animation);
+ }
+
+ } break;
+ }
+}
+
+void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) {
+
+ for (int i = 0; i < p_animation->get_track_count(); i++) {
+
+ bool prop_exists = false;
+ Variant::Type valid_type = Variant::NIL;
+ Object *obj = NULL;
+
+ RES res;
+ Vector<StringName> leftover_path;
+
+ Node *node = root->get_node_and_resource(p_animation->track_get_path(i), res, leftover_path);
+
+ if (res.is_valid()) {
+ obj = res.ptr();
+ } else if (node) {
+ obj = node;
+ }
+
+ if (obj && p_animation->track_get_type(i) == Animation::TYPE_VALUE) {
+ valid_type = obj->get_static_property_type_indexed(leftover_path, &prop_exists);
+ }
+
+ if (!obj && cleanup_tracks->is_pressed()) {
+
+ p_animation->remove_track(i);
+ i--;
+ continue;
+ }
+
+ if (!prop_exists || p_animation->track_get_type(i) != Animation::TYPE_VALUE || cleanup_keys->is_pressed() == false)
+ continue;
+
+ for (int j = 0; j < p_animation->track_get_key_count(i); j++) {
+
+ Variant v = p_animation->track_get_key_value(i, j);
+
+ if (!Variant::can_convert(v.get_type(), valid_type)) {
+ p_animation->track_remove_key(i, j);
+ j--;
+ }
+ }
+
+ if (p_animation->track_get_key_count(i) == 0 && cleanup_tracks->is_pressed()) {
+ p_animation->remove_track(i);
+ i--;
+ }
+ }
+
+ undo_redo->clear_history();
+ _update_tracks();
+}
+
+void AnimationTrackEditor::_view_group_toggle() {
+ _update_tracks();
+ view_group->set_icon(get_icon(view_group->is_pressed() ? "AnimationTrackList" : "AnimationTrackGroup", "EditorIcons"));
+}
+
+void AnimationTrackEditor::_selection_changed() {
+
+ if (selected_filter->is_pressed()) {
+ _update_tracks(); //needs updatin
+ } else {
+ for (int i = 0; i < track_edits.size(); i++) {
+ track_edits[i]->update();
+ }
+
+ for (int i = 0; i < groups.size(); i++) {
+ groups[i]->update();
+ }
+ }
+}
+
+float AnimationTrackEditor::snap_time(float p_value) {
+
+ if (snap->is_pressed()) {
+ p_value = Math::stepify(p_value, step->get_value());
+ }
+
+ return p_value;
+}
+
+void AnimationTrackEditor::_bind_methods() {
+
+ ClassDB::bind_method("_animation_changed", &AnimationTrackEditor::_animation_changed);
+ ClassDB::bind_method("_timeline_changed", &AnimationTrackEditor::_timeline_changed);
+ ClassDB::bind_method("_track_remove_request", &AnimationTrackEditor::_track_remove_request);
+ ClassDB::bind_method("_name_limit_changed", &AnimationTrackEditor::_name_limit_changed);
+ ClassDB::bind_method("_update_scroll", &AnimationTrackEditor::_update_scroll);
+ ClassDB::bind_method("_update_step", &AnimationTrackEditor::_update_step);
+ ClassDB::bind_method("_update_length", &AnimationTrackEditor::_update_length);
+ ClassDB::bind_method("_dropped_track", &AnimationTrackEditor::_dropped_track);
+ ClassDB::bind_method("_add_track", &AnimationTrackEditor::_add_track);
+ ClassDB::bind_method("_new_track_node_selected", &AnimationTrackEditor::_new_track_node_selected);
+ ClassDB::bind_method("_new_track_property_selected", &AnimationTrackEditor::_new_track_property_selected);
+ ClassDB::bind_method("_root_removed", &AnimationTrackEditor::_root_removed);
+ ClassDB::bind_method("_confirm_insert_list", &AnimationTrackEditor::_confirm_insert_list);
+ ClassDB::bind_method("_insert_delay", &AnimationTrackEditor::_insert_delay);
+ ClassDB::bind_method("_timeline_value_changed", &AnimationTrackEditor::_timeline_value_changed);
+ ClassDB::bind_method("_insert_key_from_track", &AnimationTrackEditor::_insert_key_from_track);
+ ClassDB::bind_method("_add_method_key", &AnimationTrackEditor::_add_method_key);
+ ClassDB::bind_method("_key_selected", &AnimationTrackEditor::_key_selected);
+ ClassDB::bind_method("_key_deselected", &AnimationTrackEditor::_key_deselected);
+ ClassDB::bind_method("_clear_selection", &AnimationTrackEditor::_clear_selection);
+ ClassDB::bind_method("_move_selection_begin", &AnimationTrackEditor::_move_selection_begin);
+ ClassDB::bind_method("_move_selection", &AnimationTrackEditor::_move_selection);
+ ClassDB::bind_method("_move_selection_commit", &AnimationTrackEditor::_move_selection_commit);
+ ClassDB::bind_method("_move_selection_cancel", &AnimationTrackEditor::_move_selection_cancel);
+ ClassDB::bind_method("_clear_selection_for_anim", &AnimationTrackEditor::_clear_selection_for_anim);
+ ClassDB::bind_method("_select_at_anim", &AnimationTrackEditor::_select_at_anim);
+ ClassDB::bind_method("_scroll_input", &AnimationTrackEditor::_scroll_input);
+ ClassDB::bind_method("_box_selection_draw", &AnimationTrackEditor::_box_selection_draw);
+ ClassDB::bind_method("_bezier_edit", &AnimationTrackEditor::_bezier_edit);
+ ClassDB::bind_method("_cancel_bezier_edit", &AnimationTrackEditor::_cancel_bezier_edit);
+ ClassDB::bind_method("_edit_menu_pressed", &AnimationTrackEditor::_edit_menu_pressed);
+ ClassDB::bind_method("_view_group_toggle", &AnimationTrackEditor::_view_group_toggle);
+ ClassDB::bind_method("_selection_changed", &AnimationTrackEditor::_selection_changed);
+
+ ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::REAL, "position"), PropertyInfo(Variant::BOOL, "drag")));
+ ADD_SIGNAL(MethodInfo("keying_changed"));
+ ADD_SIGNAL(MethodInfo("animation_len_changed", PropertyInfo(Variant::REAL, "len")));
+ ADD_SIGNAL(MethodInfo("animation_step_changed", PropertyInfo(Variant::REAL, "step")));
+}
+
+AnimationTrackEditor::AnimationTrackEditor() {
+ root = NULL;
+ block_animation_update = false;
+
+ undo_redo = EditorNode::get_singleton()->get_undo_redo();
+
+ main_panel = memnew(PanelContainer);
+ add_child(main_panel);
+ main_panel->set_v_size_flags(SIZE_EXPAND_FILL);
+ HBoxContainer *timeline_scroll = memnew(HBoxContainer);
+ main_panel->add_child(timeline_scroll);
+ timeline_scroll->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ VBoxContainer *timeline_vbox = memnew(VBoxContainer);
+ timeline_scroll->add_child(timeline_vbox);
+ timeline_vbox->set_v_size_flags(SIZE_EXPAND_FILL);
+ timeline_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
+ timeline_vbox->add_constant_override("separation", 0);
+
+ timeline = memnew(AnimationTimelineEdit);
+ timeline->set_block_animation_update_ptr(&block_animation_update);
+ timeline->set_undo_redo(undo_redo);
+ timeline_vbox->add_child(timeline);
+ timeline->connect("timeline_changed", this, "_timeline_changed");
+ timeline->connect("name_limit_changed", this, "_name_limit_changed");
+ timeline->connect("track_added", this, "_add_track");
+ timeline->connect("value_changed", this, "_timeline_value_changed");
+ timeline->connect("length_changed", this, "_update_length");
+
+ scroll = memnew(ScrollContainer);
+ timeline_vbox->add_child(scroll);
+ scroll->set_v_size_flags(SIZE_EXPAND_FILL);
+ VScrollBar *sb = scroll->get_v_scrollbar();
+ scroll->remove_child(sb);
+ timeline_scroll->add_child(sb); //move here so timeline and tracks are always aligned
+ scroll->connect("gui_input", this, "_scroll_input");
+
+ bezier_edit = memnew(AnimationBezierTrackEdit);
+ timeline_vbox->add_child(bezier_edit);
+ bezier_edit->set_block_animation_update_ptr(&block_animation_update);
+ bezier_edit->set_undo_redo(undo_redo);
+ bezier_edit->set_editor(this);
+ bezier_edit->set_timeline(timeline);
+ bezier_edit->hide();
+ bezier_edit->set_v_size_flags(SIZE_EXPAND_FILL);
+ bezier_edit->connect("close_request", this, "_cancel_bezier_edit");
+
+ timeline_vbox->set_custom_minimum_size(Size2(0, 150) * EDSCALE);
+
+ hscroll = memnew(HScrollBar);
+ 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);
+ scroll->add_child(track_vbox);
+ track_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
+ scroll->set_enable_h_scroll(false);
+ scroll->set_enable_v_scroll(true);
+ track_vbox->add_constant_override("separation", 0);
+
+ //timeline_vbox->add_child(memnew(HSeparator));
+ HBoxContainer *bottom_hb = memnew(HBoxContainer);
+ add_child(bottom_hb);
+ bottom_hb->add_spacer();
+
+ selected_filter = memnew(ToolButton);
+ selected_filter->connect("pressed", this, "_view_group_toggle"); //same function works the same
+ selected_filter->set_toggle_mode(true);
+ selected_filter->set_tooltip(TTR("Only show tracks from nodes selected in tree."));
+
+ bottom_hb->add_child(selected_filter);
+
+ view_group = memnew(ToolButton);
+ view_group->connect("pressed", this, "_view_group_toggle");
+ view_group->set_toggle_mode(true);
+ view_group->set_tooltip(TTR("Group tracks by node or display them as plain list."));
+
+ bottom_hb->add_child(view_group);
+ bottom_hb->add_child(memnew(VSeparator));
+
+ snap = memnew(ToolButton);
+ snap->set_text(TTR("Snap (s): "));
+ bottom_hb->add_child(snap);
+ snap->set_disabled(true);
+ snap->set_toggle_mode(true);
+ snap->set_pressed(true);
+
+ step = memnew(EditorSpinSlider);
+ step->set_min(0);
+ step->set_max(1000);
+ 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);
+
+ bottom_hb->add_child(memnew(VSeparator));
+
+ zoom_icon = memnew(TextureRect);
+ zoom_icon->set_v_size_flags(SIZE_SHRINK_CENTER);
+ bottom_hb->add_child(zoom_icon);
+ zoom = memnew(HSlider);
+ zoom->set_step(0.01);
+ zoom->set_min(0.0);
+ zoom->set_max(2.0);
+ zoom->set_value(1.0);
+ zoom->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
+ zoom->set_v_size_flags(SIZE_SHRINK_CENTER);
+ bottom_hb->add_child(zoom);
+ timeline->set_zoom(zoom);
+
+ 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();
+ edit->get_popup()->add_item(TTR("Scale Selection"), EDIT_SCALE_SELECTION);
+ edit->get_popup()->add_item(TTR("Scale From Cursor"), EDIT_SCALE_FROM_CURSOR);
+ edit->get_popup()->add_separator();
+ edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_CMD | KEY_D), EDIT_DUPLICATE_SELECTION);
+ edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/duplicate_selection_transposed", TTR("Duplicate Transposed"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_D), EDIT_DUPLICATE_TRANSPOSED);
+ edit->get_popup()->set_item_shortcut_disabled(edit->get_popup()->get_item_index(EDIT_DUPLICATE_SELECTION), true);
+ edit->get_popup()->set_item_shortcut_disabled(edit->get_popup()->get_item_index(EDIT_DUPLICATE_TRANSPOSED), true);
+ edit->get_popup()->add_separator();
+ edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/delete_selection", TTR("Delete Selection"), KEY_DELETE), EDIT_DELETE_SELECTION);
+ edit->get_popup()->set_item_shortcut_disabled(edit->get_popup()->get_item_index(EDIT_DELETE_SELECTION), true);
+ //this shortcut will be checked from the track itself. so no need to enable it here (will conflict with scenetree dock)
+
+ edit->get_popup()->add_separator();
+ edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_next_step", TTR("Goto Next Step"), KEY_MASK_CMD | KEY_RIGHT), EDIT_GOTO_NEXT_STEP);
+ edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_prev_step", TTR("Goto Prev Step"), KEY_MASK_CMD | KEY_LEFT), EDIT_GOTO_PREV_STEP);
+ edit->get_popup()->add_separator();
+ edit->get_popup()->add_item(TTR("Optimize Animation"), EDIT_OPTIMIZE_ANIMATION);
+ edit->get_popup()->add_item(TTR("Clean-Up Animation"), EDIT_CLEAN_UP_ANIMATION);
+
+ edit->get_popup()->connect("id_pressed", this, "_edit_menu_pressed");
+
+ pick_track = memnew(SceneTreeDialog);
+ add_child(pick_track);
+ pick_track->set_title(TTR("Pick the node that will be animated:"));
+ pick_track->connect("selected", this, "_new_track_node_selected");
+ prop_selector = memnew(PropertySelector);
+ add_child(prop_selector);
+ prop_selector->connect("selected", this, "_new_track_property_selected");
+
+ method_selector = memnew(PropertySelector);
+ add_child(method_selector);
+ method_selector->connect("selected", this, "_add_method_key");
+
+ inserting = false;
+ insert_query = false;
+ insert_frame = 0;
+ insert_queue = false;
+
+ insert_confirm = memnew(ConfirmationDialog);
+ add_child(insert_confirm);
+ insert_confirm->connect("confirmed", this, "_confirm_insert_list");
+ VBoxContainer *icvb = memnew(VBoxContainer);
+ insert_confirm->add_child(icvb);
+ insert_confirm_text = memnew(Label);
+ icvb->add_child(insert_confirm_text);
+ insert_confirm_bezier = memnew(CheckBox);
+ insert_confirm_bezier->set_text(TTR("Use Bezier Curves"));
+ icvb->add_child(insert_confirm_bezier);
+ keying = false;
+ moving_selection = 0;
+ key_edit = NULL;
+
+ box_selection = memnew(Control);
+ add_child(box_selection);
+ box_selection->set_as_toplevel(true);
+ box_selection->set_mouse_filter(MOUSE_FILTER_IGNORE);
+ box_selection->hide();
+ box_selection->connect("draw", this, "_box_selection_draw");
+ box_selecting = false;
+
+ //default plugins
+
+ Ref<AnimationTrackEditDefaultPlugin> def_plugin;
+ def_plugin.instance();
+ add_track_edit_plugin(def_plugin);
+
+ //dialogs
+
+ optimize_dialog = memnew(ConfirmationDialog);
+ add_child(optimize_dialog);
+ optimize_dialog->set_title(TTR("Anim. Optimizer"));
+ VBoxContainer *optimize_vb = memnew(VBoxContainer);
+ optimize_dialog->add_child(optimize_vb);
+
+ optimize_linear_error = memnew(SpinBox);
+ optimize_linear_error->set_max(1.0);
+ optimize_linear_error->set_min(0.001);
+ optimize_linear_error->set_step(0.001);
+ optimize_linear_error->set_value(0.05);
+ optimize_vb->add_margin_child(TTR("Max. Linear Error:"), optimize_linear_error);
+ optimize_angular_error = memnew(SpinBox);
+ optimize_angular_error->set_max(1.0);
+ optimize_angular_error->set_min(0.001);
+ optimize_angular_error->set_step(0.001);
+ optimize_angular_error->set_value(0.01);
+
+ optimize_vb->add_margin_child(TTR("Max. Angular Error:"), optimize_angular_error);
+ optimize_max_angle = memnew(SpinBox);
+ optimize_vb->add_margin_child(TTR("Max Optimizable Angle:"), optimize_max_angle);
+ optimize_max_angle->set_max(360.0);
+ optimize_max_angle->set_min(0.0);
+ optimize_max_angle->set_step(0.1);
+ optimize_max_angle->set_value(22);
+
+ optimize_dialog->get_ok()->set_text(TTR("Optimize"));
+ optimize_dialog->connect("confirmed", this, "_edit_menu_pressed", varray(EDIT_CLEAN_UP_ANIMATION_CONFIRM));
+
+ //
+
+ cleanup_dialog = memnew(ConfirmationDialog);
+ add_child(cleanup_dialog);
+ VBoxContainer *cleanup_vb = memnew(VBoxContainer);
+ cleanup_dialog->add_child(cleanup_vb);
+
+ cleanup_keys = memnew(CheckButton);
+ cleanup_keys->set_text(TTR("Remove invalid keys"));
+ cleanup_keys->set_pressed(true);
+ cleanup_vb->add_child(cleanup_keys);
+
+ cleanup_tracks = memnew(CheckButton);
+ cleanup_tracks->set_text(TTR("Remove unresolved and empty tracks"));
+ cleanup_tracks->set_pressed(true);
+ cleanup_vb->add_child(cleanup_tracks);
+
+ cleanup_all = memnew(CheckButton);
+ cleanup_all->set_text(TTR("Clean-up all animations"));
+ cleanup_vb->add_child(cleanup_all);
+
+ cleanup_dialog->set_title(TTR("Clean-Up Animation(s) (NO UNDO!)"));
+ cleanup_dialog->get_ok()->set_text(TTR("Clean-Up"));
+
+ cleanup_dialog->connect("confirmed", this, "_edit_menu_pressed", varray(EDIT_CLEAN_UP_ANIMATION_CONFIRM));
+
+ //
+ scale_dialog = memnew(ConfirmationDialog);
+ VBoxContainer *vbc = memnew(VBoxContainer);
+ scale_dialog->add_child(vbc);
+
+ scale = memnew(SpinBox);
+ scale->set_min(-99999);
+ scale->set_max(99999);
+ scale->set_step(0.001);
+ vbc->add_margin_child(TTR("Scale Ratio:"), scale);
+ scale_dialog->connect("confirmed", this, "_edit_menu_pressed", varray(EDIT_SCALE_CONFIRM));
+ add_child(scale_dialog);
+
+ track_copy_dialog = memnew(ConfirmationDialog);
+ add_child(track_copy_dialog);
+ track_copy_dialog->set_title(TTR("Select tracks to copy:"));
+ track_copy_dialog->get_ok()->set_text(TTR("Copy"));
+
+ track_copy_select = memnew(Tree);
+ track_copy_select->set_hide_root(true);
+ track_copy_dialog->add_child(track_copy_select);
+ track_copy_dialog->connect("confirmed", this, "_edit_menu_pressed", varray(EDIT_COPY_TRACKS_CONFIRM));
+}
+
+AnimationTrackEditor::~AnimationTrackEditor() {
+ if (key_edit) {
+ memdelete(key_edit);
+ }
+}
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
new file mode 100644
index 0000000000..0692c88bea
--- /dev/null
+++ b/editor/animation_track_editor.h
@@ -0,0 +1,484 @@
+#ifndef ANIMATION_TRACK_EDITOR_H
+#define ANIMATION_TRACK_EDITOR_H
+
+#include "scene/gui/control.h"
+#include "scene/gui/file_dialog.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/scroll_bar.h"
+#include "scene/gui/slider.h"
+#include "scene/gui/spin_box.h"
+#include "scene/gui/tab_container.h"
+#include "scene/gui/texture_rect.h"
+#include "scene/gui/tool_button.h"
+
+#include "editor/property_selector.h"
+#include "editor_data.h"
+#include "editor_spin_slider.h"
+#include "property_editor.h"
+#include "scene/animation/animation_cache.h"
+#include "scene/resources/animation.h"
+#include "scene_tree_editor.h"
+
+class AnimationTimelineEdit : public Range {
+ GDCLASS(AnimationTimelineEdit, Range)
+
+ Ref<Animation> animation;
+ int name_limit;
+ Range *zoom;
+ Range *h_scroll;
+ float play_position_pos;
+
+ HBoxContainer *len_hb;
+ EditorSpinSlider *length;
+ ToolButton *loop;
+ TextureRect *time_icon;
+
+ MenuButton *add_track;
+ Control *play_position; //separate control used to draw so updates for only position changed are much faster
+ HScrollBar *hscroll;
+
+ void _zoom_changed(double);
+ void _anim_length_changed(double p_new_len);
+ void _anim_loop_pressed();
+
+ void _play_position_draw();
+ UndoRedo *undo_redo;
+ Rect2 hsize_rect;
+
+ bool editing;
+ bool *block_animation_update_ptr; //used to block all tracks re-gen (speed up)
+
+ bool panning_timeline;
+ float panning_timeline_from;
+ float panning_timeline_at;
+ bool dragging_timeline;
+ bool dragging_hsize;
+ float dragging_hsize_from;
+ float dragging_hsize_at;
+
+ void _gui_input(const Ref<InputEvent> &p_event);
+ void _track_added(int p_track);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ int get_name_limit() const;
+ int get_buttons_width() const;
+
+ float get_zoom_scale() const;
+
+ virtual Size2 get_minimum_size() const;
+ void set_animation(const Ref<Animation> &p_animation);
+ void set_zoom(Range *p_zoom);
+ Range *get_zoom() const { return zoom; }
+ void set_undo_redo(UndoRedo *p_undo_redo);
+ void set_block_animation_update_ptr(bool *p_block_ptr);
+
+ void set_play_position(float p_pos);
+ float get_play_position() const;
+ void update_play_position();
+
+ void update_values();
+
+ void set_hscroll(HScrollBar *p_hscroll);
+
+ AnimationTimelineEdit();
+};
+
+class AnimationTrackEditor;
+
+class AnimationTrackEdit : public Control {
+
+ GDCLASS(AnimationTrackEdit, Control)
+
+ enum {
+ MENU_CALL_MODE_CONTINUOUS,
+ MENU_CALL_MODE_DISCRETE,
+ MENU_CALL_MODE_TRIGGER,
+ MENU_CALL_MODE_CAPTURE,
+ MENU_INTERPOLATION_NEAREST,
+ MENU_INTERPOLATION_LINEAR,
+ MENU_INTERPOLATION_CUBIC,
+ MENU_LOOP_WRAP,
+ MENU_LOOP_CLAMP,
+ MENU_KEY_INSERT,
+ MENU_KEY_DUPLICATE,
+ MENU_KEY_DELETE
+ };
+ AnimationTimelineEdit *timeline;
+ UndoRedo *undo_redo;
+ LineEdit *path;
+ Node *root;
+ Control *play_position; //separate control used to draw so updates for only position changed are much faster
+ float play_position_pos;
+
+ Ref<Animation> animation;
+ int track;
+
+ Rect2 check_rect;
+ Rect2 path_rect;
+
+ Rect2 update_mode_rect;
+ Rect2 interp_mode_rect;
+ Rect2 loop_mode_rect;
+ Rect2 remove_rect;
+ Rect2 bezier_edit_rect;
+
+ Ref<Texture> type_icon;
+ Ref<Texture> selected_icon;
+
+ PopupMenu *menu;
+
+ bool clicking_on_name;
+
+ void _zoom_changed();
+
+ Ref<Texture> icon_cache;
+ String path_cache;
+
+ void _menu_selected(int p_index);
+
+ bool *block_animation_update_ptr; //used to block all tracks re-gen (speed up)
+
+ void _path_entered(const String &p_text);
+ void _play_position_draw();
+ mutable int dropping_at;
+
+ float insert_at_pos;
+ bool moving_selection_attempt;
+ int select_single_attempt;
+ bool moving_selection;
+ float moving_selection_from_ofs;
+
+ bool in_group;
+ AnimationTrackEditor *editor;
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+ virtual void _gui_input(const Ref<InputEvent> &p_event);
+
+public:
+ virtual Variant get_drag_data(const Point2 &p_point);
+ virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
+ virtual void drop_data(const Point2 &p_point, const Variant &p_data);
+
+ virtual String get_tooltip(const Point2 &p_pos) const;
+
+ virtual int get_key_height() const;
+ virtual Rect2 get_key_rect(int p_index, float p_pixels_sec);
+ virtual bool is_key_selectable_by_distance() const;
+ virtual void draw_key_link(int p_index, float p_pixels_sec, int p_x, int p_next_x, int p_clip_left, int p_clip_right);
+ virtual void draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right);
+ virtual void draw_bg(int p_clip_left, int p_clip_right);
+ virtual void draw_fg(int p_clip_left, int p_clip_right);
+
+ //helper
+ void draw_texture_clipped(const Ref<Texture> &p_texture, const Vector2 &p_pos);
+ void draw_texture_region_clipped(const Ref<Texture> &p_texture, const Rect2 &p_rect, const Rect2 &p_region);
+ void draw_rect_clipped(const Rect2 &p_rect, const Color &p_color, bool p_filled = true);
+
+ int get_track() const;
+ Ref<Animation> get_animation() const;
+ AnimationTimelineEdit *get_timeline() const { return timeline; }
+ AnimationTrackEditor *get_editor() const { return editor; }
+ UndoRedo *get_undo_redo() const { return undo_redo; }
+ bool *get_block_animation_update_ptr() { return block_animation_update_ptr; }
+
+ void set_animation_and_track(const Ref<Animation> &p_animation, int p_track);
+ virtual Size2 get_minimum_size() const;
+
+ void set_undo_redo(UndoRedo *p_undo_redo);
+ void set_timeline(AnimationTimelineEdit *p_timeline);
+ void set_editor(AnimationTrackEditor *p_editor);
+ void set_root(Node *p_root);
+
+ void set_block_animation_update_ptr(bool *p_block_ptr);
+
+ void set_play_position(float p_pos);
+ void update_play_position();
+ void cancel_drop();
+
+ void set_in_group(bool p_enable);
+ void append_to_selection(const Rect2 &p_box);
+
+ AnimationTrackEdit();
+};
+
+class AnimationTrackEditPlugin : public Reference {
+ GDCLASS(AnimationTrackEditPlugin, Reference)
+public:
+ virtual AnimationTrackEdit *create_value_track_edit(Object *p_object, Variant::Type p_type, const String &p_property, PropertyHint p_hint, const String &p_hint_string, int p_usage);
+ virtual AnimationTrackEdit *create_audio_track_edit();
+ virtual AnimationTrackEdit *create_animation_track_edit(Object *p_object);
+};
+
+class AnimationTrackKeyEdit;
+class AnimationBezierTrackEdit;
+
+class AnimationTrackEditGroup : public Control {
+ GDCLASS(AnimationTrackEditGroup, Control)
+ Ref<Texture> icon;
+ String node_name;
+ NodePath node;
+ Node *root;
+ AnimationTimelineEdit *timeline;
+
+ void _zoom_changed();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ void set_type_and_name(const Ref<Texture> &p_type, const String &p_name, const NodePath &p_node);
+ virtual Size2 get_minimum_size() const;
+ void set_timeline(AnimationTimelineEdit *p_timeline);
+ void set_root(Node *p_root);
+
+ AnimationTrackEditGroup();
+};
+
+class AnimationTrackEditor : public VBoxContainer {
+ GDCLASS(AnimationTrackEditor, VBoxContainer)
+
+ enum {
+ EDIT_COPY_TRACKS,
+ EDIT_COPY_TRACKS_CONFIRM,
+ EDIT_PASTE_TRACKS,
+ EDIT_SCALE_SELECTION,
+ EDIT_SCALE_FROM_CURSOR,
+ EDIT_SCALE_CONFIRM,
+ EDIT_DUPLICATE_SELECTION,
+ EDIT_DUPLICATE_TRANSPOSED,
+ EDIT_DELETE_SELECTION,
+ EDIT_GOTO_NEXT_STEP,
+ EDIT_GOTO_PREV_STEP,
+ EDIT_OPTIMIZE_ANIMATION,
+ EDIT_OPTIMIZE_ANIMATION_CONFIRM,
+ EDIT_CLEAN_UP_ANIMATION,
+ EDIT_CLEAN_UP_ANIMATION_CONFIRM
+ };
+
+ Ref<Animation> animation;
+ Node *root;
+
+ MenuButton *edit;
+
+ PanelContainer *main_panel;
+ HScrollBar *hscroll;
+ ScrollContainer *scroll;
+ VBoxContainer *track_vbox;
+ AnimationBezierTrackEdit *bezier_edit;
+
+ AnimationTimelineEdit *timeline;
+ HSlider *zoom;
+ EditorSpinSlider *step;
+ TextureRect *zoom_icon;
+ ToolButton *snap;
+
+ Vector<AnimationTrackEdit *> track_edits;
+ Vector<AnimationTrackEditGroup *> groups;
+
+ bool block_animation_update;
+
+ int _get_track_selected();
+ void _animation_changed();
+ void _update_tracks();
+
+ void _name_limit_changed();
+ void _timeline_changed(float p_new_pos, bool p_drag);
+ void _track_remove_request(int p_track);
+
+ UndoRedo *undo_redo;
+
+ void _update_scroll(double);
+ void _update_step(double p_new_step);
+ void _update_length(double p_new_step);
+ void _dropped_track(int p_from_track, int p_to_track);
+
+ void _add_track(int p_type);
+ void _new_track_node_selected(NodePath p_path);
+ void _new_track_property_selected(String p_name);
+
+ PropertySelector *prop_selector;
+ PropertySelector *method_selector;
+ SceneTreeDialog *pick_track;
+ int adding_track_type;
+ NodePath adding_track_path;
+
+ bool keying;
+
+ struct InsertData {
+
+ Animation::TrackType type;
+ NodePath path;
+ int track_idx;
+ Variant value;
+ String query;
+ bool advance;
+ }; /* insert_data;*/
+
+ Label *insert_confirm_text;
+ CheckBox *insert_confirm_bezier;
+ ConfirmationDialog *insert_confirm;
+ bool insert_queue;
+ bool inserting;
+ bool insert_query;
+ List<InsertData> insert_data;
+ uint64_t insert_frame;
+
+ void _query_insert(const InsertData &p_id);
+ void _confirm_insert_list();
+ int _confirm_insert(InsertData p_id, int p_last_track, bool p_create_beziers = false);
+ void _insert_delay();
+
+ void _root_removed(Node *p_root);
+
+ PropertyInfo _find_hint_for_track(int p_idx, NodePath &r_base_path, Variant *r_current_val = NULL);
+
+ void _timeline_value_changed(double);
+
+ float insert_key_from_track_call_ofs;
+ int insert_key_from_track_call_track;
+ void _insert_key_from_track(float p_ofs, int p_track);
+ void _add_method_key(const String &p_method);
+
+ void _clear_selection();
+ void _clear_selection_for_anim(const Ref<Animation> &p_anim);
+ void _select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos);
+
+ //selection
+
+ struct SelectedKey {
+
+ int track;
+ int key;
+ bool operator<(const SelectedKey &p_key) const { return track == p_key.track ? key < p_key.key : track < p_key.track; };
+ };
+
+ struct KeyInfo {
+
+ float pos;
+ };
+
+ Map<SelectedKey, KeyInfo> selection;
+
+ void _key_selected(int p_key, bool p_single, int p_track);
+ void _key_deselected(int p_key, int p_track);
+
+ bool moving_selection;
+ float moving_selection_offset;
+ void _move_selection_begin();
+ void _move_selection(float p_offset);
+ void _move_selection_commit();
+ void _move_selection_cancel();
+
+ AnimationTrackKeyEdit *key_edit;
+ void _update_key_edit();
+
+ void _clear_key_edit();
+
+ Control *box_selection;
+ void _box_selection_draw();
+ bool box_selecting;
+ Vector2 box_selecting_from;
+ Rect2 box_select_rect;
+ void _scroll_input(const Ref<InputEvent> &p_event);
+
+ Vector<Ref<AnimationTrackEditPlugin> > track_edit_plugins;
+
+ void _cancel_bezier_edit();
+ void _bezier_edit(int p_for_track);
+
+ ////////////// edit menu stuff
+
+ ConfirmationDialog *optimize_dialog;
+ SpinBox *optimize_linear_error;
+ SpinBox *optimize_angular_error;
+ SpinBox *optimize_max_angle;
+
+ ConfirmationDialog *cleanup_dialog;
+ CheckButton *cleanup_keys;
+ CheckButton *cleanup_tracks;
+ CheckButton *cleanup_all;
+
+ ConfirmationDialog *scale_dialog;
+ SpinBox *scale;
+
+ void _edit_menu_pressed(int p_option);
+ int last_menu_track_opt;
+
+ void _cleanup_animation(Ref<Animation> p_animation);
+
+ void _anim_duplicate_keys(bool transpose);
+
+ void _view_group_toggle();
+ ToolButton *view_group;
+ ToolButton *selected_filter;
+
+ void _selection_changed();
+
+ ConfirmationDialog *track_copy_dialog;
+ Tree *track_copy_select;
+ struct TrackClipboard {
+ NodePath full_path;
+ NodePath base_path;
+ Animation::TrackType track_type;
+ Animation::InterpolationType interp_type;
+ Animation::UpdateMode update_mode;
+ bool loop_wrap;
+ bool enabled;
+
+ struct Key {
+ float time;
+ float transition;
+ Variant value;
+ };
+ Vector<Key> keys;
+ };
+
+ Vector<TrackClipboard> track_clipboard;
+
+ void _insert_animation_key(NodePath p_path, const Variant &p_value);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ void add_track_edit_plugin(const Ref<AnimationTrackEditPlugin> &p_plugin);
+ void remove_track_edit_plugin(const Ref<AnimationTrackEditPlugin> &p_plugin);
+
+ void set_animation(const Ref<Animation> &p_anim);
+ Ref<Animation> get_current_animation() const;
+ void set_root(Node *p_root);
+ Node *get_root() const;
+ void update_keying();
+ bool has_keying() const;
+
+ void cleanup();
+
+ void set_anim_pos(float p_pos);
+ void insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists = false);
+ void insert_value_key(const String &p_property, const Variant &p_value, bool p_advance);
+ void insert_transform_key(Spatial *p_node, const String &p_sub, const Transform &p_xform);
+
+ void show_select_node_warning(bool p_show);
+
+ bool is_key_selected(int p_track, int p_key) const;
+ bool is_selection_active() const;
+ bool is_moving_selection() const;
+ float get_moving_selection_offset() const;
+ bool is_snap_enabled();
+ float snap_time(float p_value);
+
+ MenuButton *get_edit_menu();
+ AnimationTrackEditor();
+ ~AnimationTrackEditor();
+};
+
+#endif // ANIMATION_TRACK_EDITOR_H
diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp
new file mode 100644
index 0000000000..d0c91f10d9
--- /dev/null
+++ b/editor/animation_track_editor_plugins.cpp
@@ -0,0 +1,1317 @@
+#include "animation_track_editor_plugins.h"
+#include "editor/audio_stream_preview.h"
+#include "editor_resource_preview.h"
+#include "editor_scale.h"
+#include "scene/2d/animated_sprite.h"
+#include "scene/2d/sprite.h"
+#include "scene/3d/sprite_3d.h"
+#include "scene/animation/animation_player.h"
+#include "servers/audio/audio_stream.h"
+/// BOOL ///
+int AnimationTrackEditBool::get_key_height() const {
+
+ Ref<Texture> checked = get_icon("checked", "CheckBox");
+ return checked->get_height();
+}
+Rect2 AnimationTrackEditBool::get_key_rect(int p_index, float p_pixels_sec) {
+
+ Ref<Texture> checked = get_icon("checked", "CheckBox");
+ return Rect2(0, 0, checked->get_width(), get_size().height);
+}
+
+bool AnimationTrackEditBool::is_key_selectable_by_distance() const {
+
+ return false;
+}
+void AnimationTrackEditBool::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
+
+ Ref<Texture> icon;
+ bool checked = get_animation()->track_get_key_value(get_track(), p_index);
+
+ if (checked)
+ icon = get_icon("checked", "CheckBox");
+ else
+ icon = get_icon("unchecked", "CheckBox");
+
+ Vector2 ofs(p_x, int(get_size().height - icon->get_height()) / 2);
+
+ draw_texture_clipped(icon, ofs);
+
+ if (p_selected) {
+ Color color = get_color("accent_color", "Editor");
+ draw_rect_clipped(Rect2(ofs, icon->get_size()), color, false);
+ }
+}
+
+/// COLOR ///
+
+int AnimationTrackEditColor::get_key_height() const {
+
+ Ref<Font> font = get_font("font", "Label");
+ return font->get_height() * 0.8;
+}
+Rect2 AnimationTrackEditColor::get_key_rect(int p_index, float p_pixels_sec) {
+
+ Ref<Font> font = get_font("font", "Label");
+ int fh = font->get_height() * 0.8;
+ return Rect2(0, 0, fh, get_size().height);
+}
+
+bool AnimationTrackEditColor::is_key_selectable_by_distance() const {
+
+ return false;
+}
+
+void AnimationTrackEditColor::draw_key_link(int p_index, float p_pixels_sec, int p_x, int p_next_x, int p_clip_left, int p_clip_right) {
+
+ int x_from = p_x;
+ int x_to = p_next_x;
+
+ Ref<Font> font = get_font("font", "Label");
+ int fh = (font->get_height() * 0.8);
+
+ x_from += fh - 1;
+ x_to += 1;
+ fh /= 3;
+
+ if (x_from > p_clip_right)
+ return;
+
+ if (x_to < p_clip_left)
+ return;
+
+ Color color = get_animation()->track_get_key_value(get_track(), p_index);
+ Color color_next = get_animation()->track_get_key_value(get_track(), p_index + 1);
+
+ if (x_from < p_clip_left) {
+ float c = float(p_clip_left - x_from) / (x_to - x_from);
+ color = color.linear_interpolate(color_next, c);
+ x_from = p_clip_left;
+ }
+
+ if (x_to > p_clip_right) {
+ float c = float(p_clip_right - x_from) / (x_to - x_from);
+ color_next = color.linear_interpolate(color_next, c);
+ x_to = p_clip_right;
+ }
+
+ int y_from = (get_size().height - fh) / 2;
+
+ Vector<Vector2> points;
+ Vector<Color> colors;
+
+ points.push_back(Vector2(x_from, y_from));
+ colors.push_back(color);
+
+ points.push_back(Vector2(x_to, y_from));
+ colors.push_back(color_next);
+
+ points.push_back(Vector2(x_to, y_from + fh));
+ colors.push_back(color_next);
+
+ points.push_back(Vector2(x_from, y_from + fh));
+ colors.push_back(color);
+
+ draw_primitive(points, colors, Vector<Vector2>());
+}
+
+void AnimationTrackEditColor::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
+
+ Color color = get_animation()->track_get_key_value(get_track(), p_index);
+
+ Ref<Font> font = get_font("font", "Label");
+ int fh = font->get_height() * 0.8;
+
+ Rect2 rect(Vector2(p_x, int(get_size().height - fh) / 2), Size2(fh, fh));
+
+ draw_rect_clipped(Rect2(rect.position, rect.size / 2), Color(0.4, 0.4, 0.4));
+ draw_rect_clipped(Rect2(rect.position + rect.size / 2, rect.size / 2), Color(0.4, 0.4, 0.4));
+ draw_rect_clipped(Rect2(rect.position + Vector2(rect.size.x / 2, 0), rect.size / 2), Color(0.6, 0.6, 0.6));
+ draw_rect_clipped(Rect2(rect.position + Vector2(0, rect.size.y / 2), rect.size / 2), Color(0.6, 0.6, 0.6));
+ draw_rect_clipped(rect, color);
+
+ if (p_selected) {
+ Color accent = get_color("accent_color", "Editor");
+ draw_rect_clipped(rect, accent, false);
+ }
+}
+
+/// AUDIO ///
+
+void AnimationTrackEditAudio::_preview_changed(ObjectID p_which) {
+
+ Object *object = ObjectDB::get_instance(id);
+
+ if (!object)
+ return;
+
+ Ref<AudioStream> stream = object->call("get_stream");
+
+ if (stream.is_valid() && stream->get_instance_id() == p_which) {
+ update();
+ }
+}
+
+int AnimationTrackEditAudio::get_key_height() const {
+
+ if (!ObjectDB::get_instance(id)) {
+ return AnimationTrackEdit::get_key_height();
+ }
+
+ Ref<Font> font = get_font("font", "Label");
+ return int(font->get_height() * 1.5);
+}
+Rect2 AnimationTrackEditAudio::get_key_rect(int p_index, float p_pixels_sec) {
+
+ Object *object = ObjectDB::get_instance(id);
+
+ if (!object) {
+ return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
+ }
+
+ Ref<AudioStream> stream = object->call("get_stream");
+
+ if (!stream.is_valid()) {
+ return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
+ }
+
+ bool play = get_animation()->track_get_key_value(get_track(), p_index);
+ if (play) {
+ float len = stream->get_length();
+
+ if (len == 0) {
+
+ Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
+ len = preview->get_length();
+ }
+
+ if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
+ len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
+ }
+
+ return Rect2(0, 0, len * p_pixels_sec, get_size().height);
+ } else {
+ Ref<Font> font = get_font("font", "Label");
+ int fh = font->get_height() * 0.8;
+ return Rect2(0, 0, fh, get_size().height);
+ }
+}
+
+bool AnimationTrackEditAudio::is_key_selectable_by_distance() const {
+
+ return false;
+}
+void AnimationTrackEditAudio::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
+
+ Object *object = ObjectDB::get_instance(id);
+
+ if (!object) {
+ AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
+ return;
+ }
+
+ Ref<AudioStream> stream = object->call("get_stream");
+
+ if (!stream.is_valid()) {
+ AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
+ return;
+ }
+
+ Ref<Font> font = get_font("font", "Label");
+ float fh = int(font->get_height() * 1.5);
+
+ bool play = get_animation()->track_get_key_value(get_track(), p_index);
+ if (play) {
+ float len = stream->get_length();
+
+ Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
+
+ float preview_len = preview->get_length();
+
+ if (len == 0) {
+ len = preview_len;
+ }
+
+ int pixel_len = len * p_pixels_sec;
+
+ int pixel_begin = p_x;
+ int pixel_end = p_x + pixel_len;
+
+ if (pixel_end < p_clip_left)
+ return;
+
+ if (pixel_begin > p_clip_right)
+ return;
+
+ int from_x = MAX(pixel_begin, p_clip_left);
+ int to_x = MIN(pixel_end, p_clip_right);
+
+ if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
+ float limit = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
+ int limit_x = pixel_begin + limit * p_pixels_sec;
+ to_x = MIN(limit_x, to_x);
+ }
+
+ if (to_x <= from_x)
+ return;
+
+ int h = get_size().height;
+ Rect2 rect = Rect2(from_x, (h - fh) / 2, to_x - from_x, fh);
+ draw_rect(rect, Color(0.25, 0.25, 0.25));
+
+ Vector<Vector2> lines;
+ lines.resize((to_x - from_x + 1) * 2);
+ preview_len = preview->get_length();
+
+ for (int i = from_x; i < to_x; i++) {
+
+ float ofs = (i - pixel_begin) * preview_len / pixel_len;
+ float ofs_n = ((i + 1) - pixel_begin) * preview_len / pixel_len;
+ 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 - from_x;
+ lines[idx * 2 + 0] = Vector2(i, rect.position.y + min * rect.size.y);
+ lines[idx * 2 + 1] = Vector2(i, rect.position.y + max * rect.size.y);
+ }
+
+ Vector<Color> color;
+ color.push_back(Color(0.75, 0.75, 0.75));
+
+ VS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), lines, color);
+
+ if (p_selected) {
+ Color accent = get_color("accent_color", "Editor");
+ draw_rect(rect, accent, false);
+ }
+ } else {
+ Ref<Font> font = get_font("font", "Label");
+ int fh = font->get_height() * 0.8;
+ Rect2 rect(Vector2(p_x, int(get_size().height - fh) / 2), Size2(fh, fh));
+
+ Color color = get_color("font_color", "Label");
+ draw_rect(rect, color);
+
+ if (p_selected) {
+ Color accent = get_color("accent_color", "Editor");
+ draw_rect(rect, accent, false);
+ }
+ }
+}
+
+void AnimationTrackEditAudio::set_node(Object *p_object) {
+
+ id = p_object->get_instance_id();
+}
+
+void AnimationTrackEditAudio::_bind_methods() {
+ ClassDB::bind_method("_preview_changed", &AnimationTrackEditAudio::_preview_changed);
+}
+
+AnimationTrackEditAudio::AnimationTrackEditAudio() {
+ AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", this, "_preview_changed");
+}
+
+/// SPRITE FRAME ///
+
+int AnimationTrackEditSpriteFrame::get_key_height() const {
+
+ if (!ObjectDB::get_instance(id)) {
+ return AnimationTrackEdit::get_key_height();
+ }
+
+ Ref<Font> font = get_font("font", "Label");
+ return int(font->get_height() * 2);
+}
+Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_sec) {
+
+ Object *object = ObjectDB::get_instance(id);
+
+ if (!object) {
+ return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
+ }
+
+ Size2 size;
+
+ if (Object::cast_to<Sprite>(object) || Object::cast_to<Sprite3D>(object)) {
+
+ Ref<Texture> texture = object->call("get_texture");
+ if (!texture.is_valid()) {
+ return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
+ }
+
+ size = texture->get_size();
+
+ if (bool(object->call("is_region"))) {
+ size = Rect2(object->call("get_region_rect")).size;
+ }
+
+ int hframes = object->call("get_hframes");
+ int vframes = object->call("get_vframes");
+
+ if (hframes > 1) {
+ size.x /= hframes;
+ }
+ if (vframes > 1) {
+ size.y /= vframes;
+ }
+ } else if (Object::cast_to<AnimatedSprite>(object) || Object::cast_to<AnimatedSprite3D>(object)) {
+
+ Ref<SpriteFrames> sf = object->call("get_sprite_frames");
+ if (sf.is_null()) {
+ return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
+ }
+
+ List<StringName> animations;
+ sf->get_animation_list(&animations);
+
+ int frame = get_animation()->track_get_key_value(get_track(), p_index);
+ String animation;
+ if (animations.size() == 1) {
+ animation = animations.front()->get();
+ } else {
+ // Go through other track to find if animation is set
+ String animation_path = get_animation()->track_get_path(get_track());
+ animation_path = animation_path.replace(":frame", ":animation");
+ int animation_track = get_animation()->find_track(animation_path);
+ float track_time = get_animation()->track_get_key_time(get_track(), p_index);
+ int animaiton_index = get_animation()->track_find_key(animation_track, track_time);
+ animation = get_animation()->track_get_key_value(animation_track, animaiton_index);
+ }
+
+ Ref<Texture> texture = sf->get_frame(animation, frame);
+ if (!texture.is_valid()) {
+ return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
+ }
+
+ size = texture->get_size();
+ }
+
+ size = size.floor();
+
+ Ref<Font> font = get_font("font", "Label");
+ int height = int(font->get_height() * 2);
+ int width = height * size.width / size.height;
+
+ return Rect2(0, 0, width, get_size().height);
+}
+
+bool AnimationTrackEditSpriteFrame::is_key_selectable_by_distance() const {
+
+ return false;
+}
+void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
+
+ Object *object = ObjectDB::get_instance(id);
+
+ if (!object) {
+ AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
+ return;
+ }
+
+ int frame = get_animation()->track_get_key_value(get_track(), p_index);
+
+ Ref<Texture> texture;
+ Rect2 region;
+
+ if (Object::cast_to<Sprite>(object) || Object::cast_to<Sprite3D>(object)) {
+
+ texture = object->call("get_texture");
+ if (!texture.is_valid()) {
+ AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
+ return;
+ }
+
+ region.size = texture->get_size();
+
+ if (bool(object->call("is_region"))) {
+
+ region = Rect2(object->call("get_region_rect"));
+ }
+
+ int hframes = object->call("get_hframes");
+ int vframes = object->call("get_vframes");
+
+ if (hframes > 1) {
+ region.size.x /= hframes;
+ }
+ if (vframes > 1) {
+ region.size.y /= vframes;
+ }
+
+ region.position.x += region.size.x * (frame % hframes);
+ region.position.y += region.size.y * (frame / hframes);
+
+ } else if (Object::cast_to<AnimatedSprite>(object) || Object::cast_to<AnimatedSprite3D>(object)) {
+
+ Ref<SpriteFrames> sf = object->call("get_sprite_frames");
+ if (sf.is_null()) {
+ AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
+ return;
+ }
+
+ List<StringName> animations;
+ sf->get_animation_list(&animations);
+
+ int frame = get_animation()->track_get_key_value(get_track(), p_index);
+ String animation;
+ if (animations.size() == 1) {
+ animation = animations.front()->get();
+ } else {
+ // Go through other track to find if animation is set
+ String animation_path = get_animation()->track_get_path(get_track());
+ animation_path = animation_path.replace(":frame", ":animation");
+ int animation_track = get_animation()->find_track(animation_path);
+ float track_time = get_animation()->track_get_key_time(get_track(), p_index);
+ int animaiton_index = get_animation()->track_find_key(animation_track, track_time);
+ animation = get_animation()->track_get_key_value(animation_track, animaiton_index);
+ }
+
+ texture = sf->get_frame(animation, frame);
+ if (!texture.is_valid()) {
+ AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
+ return;
+ }
+
+ region.size = texture->get_size();
+ }
+
+ Ref<Font> font = get_font("font", "Label");
+ int height = int(font->get_height() * 2);
+
+ int width = height * region.size.width / region.size.height;
+
+ Rect2 rect(p_x, int(get_size().height - height) / 2, width, height);
+
+ if (rect.position.x + rect.size.x < p_clip_left)
+ return;
+
+ if (rect.position.x > p_clip_right)
+ return;
+
+ Color accent = get_color("accent_color", "Editor");
+ Color bg = accent;
+ bg.a = 0.15;
+
+ draw_rect_clipped(rect, bg);
+
+ draw_texture_region_clipped(texture, rect, region);
+
+ if (p_selected) {
+ draw_rect_clipped(rect, accent, false);
+ }
+}
+
+void AnimationTrackEditSpriteFrame::set_node(Object *p_object) {
+
+ id = p_object->get_instance_id();
+}
+
+/// SUB ANIMATION ///
+
+int AnimationTrackEditSubAnim::get_key_height() const {
+
+ if (!ObjectDB::get_instance(id)) {
+ return AnimationTrackEdit::get_key_height();
+ }
+
+ Ref<Font> font = get_font("font", "Label");
+ return int(font->get_height() * 1.5);
+}
+Rect2 AnimationTrackEditSubAnim::get_key_rect(int p_index, float p_pixels_sec) {
+
+ Object *object = ObjectDB::get_instance(id);
+
+ if (!object) {
+ return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
+ }
+
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);
+
+ if (!ap) {
+ return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
+ }
+
+ String anim = get_animation()->track_get_key_value(get_track(), p_index);
+
+ if (anim != "[stop]" && ap->has_animation(anim)) {
+
+ float len = ap->get_animation(anim)->get_length();
+
+ if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
+ len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
+ }
+
+ return Rect2(0, 0, len * p_pixels_sec, get_size().height);
+ } else {
+ Ref<Font> font = get_font("font", "Label");
+ int fh = font->get_height() * 0.8;
+ return Rect2(0, 0, fh, get_size().height);
+ }
+}
+
+bool AnimationTrackEditSubAnim::is_key_selectable_by_distance() const {
+
+ return false;
+}
+void AnimationTrackEditSubAnim::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
+
+ Object *object = ObjectDB::get_instance(id);
+
+ if (!object) {
+ AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
+ return;
+ }
+
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);
+
+ if (!ap) {
+ AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
+ return;
+ }
+
+ String anim = get_animation()->track_get_key_value(get_track(), p_index);
+
+ if (anim != "[stop]" && ap->has_animation(anim)) {
+
+ float len = ap->get_animation(anim)->get_length();
+
+ if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
+ len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
+ }
+
+ int pixel_len = len * p_pixels_sec;
+
+ int pixel_begin = p_x;
+ int pixel_end = p_x + pixel_len;
+
+ if (pixel_end < p_clip_left)
+ return;
+
+ if (pixel_begin > p_clip_right)
+ return;
+
+ int from_x = MAX(pixel_begin, p_clip_left);
+ int to_x = MIN(pixel_end, p_clip_right);
+
+ if (to_x <= from_x)
+ return;
+
+ Ref<Font> font = get_font("font", "Label");
+ int fh = font->get_height() * 1.5;
+
+ Rect2 rect(from_x, int(get_size().height - fh) / 2, to_x - from_x, fh);
+
+ Color color = get_color("font_color", "Label");
+ Color bg = color;
+ bg.r = 1 - color.r;
+ bg.g = 1 - color.g;
+ bg.b = 1 - color.b;
+ draw_rect(rect, bg);
+
+ Vector<Vector2> lines;
+ Vector<Color> colorv;
+ {
+ Ref<Animation> animation = ap->get_animation(anim);
+
+ for (int i = 0; i < animation->get_track_count(); i++) {
+
+ float h = (rect.size.height - 2) / animation->get_track_count();
+
+ int y = 2 + h * i + h / 2;
+
+ for (int j = 0; j < animation->track_get_key_count(i); j++) {
+
+ float ofs = animation->track_get_key_time(i, j);
+ int x = p_x + ofs * p_pixels_sec + 2;
+
+ if (x < from_x || x >= (to_x - 4))
+ continue;
+
+ lines.push_back(Point2(x, y));
+ lines.push_back(Point2(x + 1, y));
+ }
+ }
+
+ colorv.push_back(color);
+ }
+
+ if (lines.size() > 2) {
+ VS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), lines, colorv);
+ }
+
+ int limit = to_x - from_x - 4;
+ if (limit > 0) {
+ draw_string(font, Point2(from_x + 2, int(get_size().height - font->get_height()) / 2 + font->get_ascent()), anim, color);
+ }
+
+ if (p_selected) {
+ Color accent = get_color("accent_color", "Editor");
+ draw_rect(rect, accent, false);
+ }
+ } else {
+ Ref<Font> font = get_font("font", "Label");
+ int fh = font->get_height() * 0.8;
+ Rect2 rect(Vector2(p_x, int(get_size().height - fh) / 2), Size2(fh, fh));
+
+ Color color = get_color("font_color", "Label");
+ draw_rect(rect, color);
+
+ if (p_selected) {
+ Color accent = get_color("accent_color", "Editor");
+ draw_rect(rect, accent, false);
+ }
+ }
+}
+
+void AnimationTrackEditSubAnim::set_node(Object *p_object) {
+
+ id = p_object->get_instance_id();
+}
+
+//// VOLUME DB ////
+
+int AnimationTrackEditVolumeDB::get_key_height() const {
+
+ Ref<Texture> volume_texture = get_icon("ColorTrackVu", "EditorIcons");
+ return volume_texture->get_height() * 1.2;
+}
+
+void AnimationTrackEditVolumeDB::draw_bg(int p_clip_left, int p_clip_right) {
+
+ Ref<Texture> volume_texture = get_icon("ColorTrackVu", "EditorIcons");
+ int tex_h = volume_texture->get_height();
+
+ int y_from = (get_size().height - tex_h) / 2;
+ int y_size = tex_h;
+
+ Color color(1, 1, 1, 0.3);
+ draw_texture_rect(volume_texture, Rect2(p_clip_left, y_from, p_clip_right - p_clip_left, y_from + y_size), false, color);
+}
+
+void AnimationTrackEditVolumeDB::draw_fg(int p_clip_left, int p_clip_right) {
+
+ Ref<Texture> volume_texture = get_icon("ColorTrackVu", "EditorIcons");
+ int tex_h = volume_texture->get_height();
+ int y_from = (get_size().height - tex_h) / 2;
+ int db0 = y_from + (24 / 80.0) * tex_h;
+
+ draw_line(Vector2(p_clip_left, db0), Vector2(p_clip_right, db0), Color(1, 1, 1, 0.3));
+}
+
+void AnimationTrackEditVolumeDB::draw_key_link(int p_index, float p_pixels_sec, int p_x, int p_next_x, int p_clip_left, int p_clip_right) {
+
+ if (p_x > p_clip_right || p_next_x < p_clip_left)
+ return;
+
+ float db = get_animation()->track_get_key_value(get_track(), p_index);
+ float db_n = get_animation()->track_get_key_value(get_track(), p_index + 1);
+
+ db = CLAMP(db, -60, 24);
+ db_n = CLAMP(db_n, -60, 24);
+
+ float h = 1.0 - ((db + 60) / 84.0);
+ float h_n = 1.0 - ((db_n + 60) / 84.0);
+
+ int from_x = p_x;
+ int to_x = p_next_x;
+
+ if (from_x < p_clip_left) {
+ h = Math::lerp(h, h_n, float(p_clip_left - from_x) / float(to_x - from_x));
+ from_x = p_clip_left;
+ }
+
+ if (to_x > p_clip_right) {
+ h_n = Math::lerp(h, h_n, float(p_clip_right - from_x) / float(to_x - from_x));
+ to_x = p_clip_right;
+ }
+
+ Ref<Texture> volume_texture = get_icon("ColorTrackVu", "EditorIcons");
+ int tex_h = volume_texture->get_height();
+
+ int y_from = (get_size().height - tex_h) / 2;
+
+ Color color = get_color("font_color", "Label");
+ color.a *= 0.7;
+
+ draw_line(Point2(from_x, y_from + h * tex_h), Point2(to_x, y_from + h_n * tex_h), color, 2);
+}
+
+////////////////////////
+
+/// AUDIO ///
+
+void AnimationTrackEditTypeAudio::_preview_changed(ObjectID p_which) {
+
+ for (int i = 0; i < get_animation()->track_get_key_count(get_track()); i++) {
+ Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), i);
+ if (stream.is_valid() && stream->get_instance_id() == p_which) {
+ update();
+ return;
+ }
+ }
+}
+
+int AnimationTrackEditTypeAudio::get_key_height() const {
+
+ Ref<Font> font = get_font("font", "Label");
+ return int(font->get_height() * 1.5);
+}
+Rect2 AnimationTrackEditTypeAudio::get_key_rect(int p_index, float p_pixels_sec) {
+
+ Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), p_index);
+
+ if (!stream.is_valid()) {
+ return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
+ }
+
+ float start_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), p_index);
+ float end_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), p_index);
+
+ float len = stream->get_length();
+
+ if (len == 0) {
+
+ Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
+ len = preview->get_length();
+ }
+
+ len -= end_ofs;
+ len -= start_ofs;
+ if (len <= 0.001) {
+ len = 0.001;
+ }
+
+ if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
+ len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
+ }
+
+ return Rect2(0, 0, len * p_pixels_sec, get_size().height);
+}
+
+bool AnimationTrackEditTypeAudio::is_key_selectable_by_distance() const {
+
+ return false;
+}
+void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
+
+ Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), p_index);
+
+ if (!stream.is_valid()) {
+ AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
+ return;
+ }
+
+ float start_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), p_index);
+ float end_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), p_index);
+
+ if (len_resizing && p_index == len_resizing_index) {
+ float ofs_local = -len_resizing_rel / get_timeline()->get_zoom_scale();
+ if (len_resizing_start) {
+ start_ofs += ofs_local;
+ if (start_ofs < 0)
+ start_ofs = 0;
+ } else {
+ end_ofs += ofs_local;
+ if (end_ofs < 0)
+ end_ofs = 0;
+ }
+ }
+
+ Ref<Font> font = get_font("font", "Label");
+ float fh = int(font->get_height() * 1.5);
+
+ float len = stream->get_length();
+
+ Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
+
+ float preview_len = preview->get_length();
+
+ if (len == 0) {
+ len = preview_len;
+ }
+
+ int pixel_total_len = len * p_pixels_sec;
+
+ len -= end_ofs;
+ len -= start_ofs;
+
+ if (len <= 0.001) {
+ len = 0.001;
+ }
+
+ int pixel_len = len * p_pixels_sec;
+
+ int pixel_begin = p_x;
+ int pixel_end = p_x + pixel_len;
+
+ if (pixel_end < p_clip_left)
+ return;
+
+ if (pixel_begin > p_clip_right)
+ return;
+
+ int from_x = MAX(pixel_begin, p_clip_left);
+ int to_x = MIN(pixel_end, p_clip_right);
+
+ if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
+ float limit = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
+ int limit_x = pixel_begin + limit * p_pixels_sec;
+ to_x = MIN(limit_x, to_x);
+ }
+
+ if (to_x <= from_x) {
+ to_x = from_x + 1;
+ }
+
+ int h = get_size().height;
+ Rect2 rect = Rect2(from_x, (h - fh) / 2, to_x - from_x, fh);
+ draw_rect(rect, Color(0.25, 0.25, 0.25));
+
+ Vector<Vector2> lines;
+ lines.resize((to_x - from_x + 1) * 2);
+ preview_len = preview->get_length();
+
+ for (int i = from_x; i < to_x; i++) {
+
+ float ofs = (i - pixel_begin) * preview_len / pixel_total_len;
+ float ofs_n = ((i + 1) - pixel_begin) * preview_len / pixel_total_len;
+ ofs += start_ofs;
+ ofs_n += start_ofs;
+
+ 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 - from_x;
+ lines[idx * 2 + 0] = Vector2(i, rect.position.y + min * rect.size.y);
+ lines[idx * 2 + 1] = Vector2(i, rect.position.y + max * rect.size.y);
+ }
+
+ Vector<Color> color;
+ color.push_back(Color(0.75, 0.75, 0.75));
+
+ VS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), lines, color);
+
+ Color cut_color = get_color("accent_color", "Editor");
+ cut_color.a = 0.7;
+ if (start_ofs > 0 && pixel_begin > p_clip_left) {
+ draw_rect(Rect2(pixel_begin, rect.position.y, 1, rect.size.y), cut_color);
+ }
+ if (end_ofs > 0 && pixel_end < p_clip_right) {
+ draw_rect(Rect2(pixel_end, rect.position.y, 1, rect.size.y), cut_color);
+ }
+
+ if (p_selected) {
+ Color accent = get_color("accent_color", "Editor");
+ draw_rect(rect, accent, false);
+ }
+}
+
+void AnimationTrackEditTypeAudio::_bind_methods() {
+ ClassDB::bind_method("_preview_changed", &AnimationTrackEditTypeAudio::_preview_changed);
+}
+
+AnimationTrackEditTypeAudio::AnimationTrackEditTypeAudio() {
+ AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", this, "_preview_changed");
+ len_resizing = false;
+}
+
+bool AnimationTrackEditTypeAudio::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+
+ if (p_point.x > get_timeline()->get_name_limit() && p_point.x < get_size().width - get_timeline()->get_buttons_width()) {
+
+ Dictionary drag_data = p_data;
+ if (drag_data.has("type") && String(drag_data["type"]) == "resource") {
+ Ref<AudioStream> res = drag_data["resource"];
+ if (res.is_valid()) {
+ return true;
+ }
+ }
+
+ if (drag_data.has("type") && String(drag_data["type"]) == "files") {
+
+ Vector<String> files = drag_data["files"];
+
+ if (files.size() == 1) {
+ String file = files[0];
+ Ref<AudioStream> res = ResourceLoader::load(file);
+ if (res.is_valid()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return AnimationTrackEdit::can_drop_data(p_point, p_data);
+}
+void AnimationTrackEditTypeAudio::drop_data(const Point2 &p_point, const Variant &p_data) {
+
+ if (p_point.x > get_timeline()->get_name_limit() && p_point.x < get_size().width - get_timeline()->get_buttons_width()) {
+
+ Ref<AudioStream> stream;
+ Dictionary drag_data = p_data;
+ if (drag_data.has("type") && String(drag_data["type"]) == "resource") {
+ stream = drag_data["resource"];
+ } else if (drag_data.has("type") && String(drag_data["type"]) == "files") {
+
+ Vector<String> files = drag_data["files"];
+
+ if (files.size() == 1) {
+ String file = files[0];
+ stream = ResourceLoader::load(file);
+ }
+ }
+
+ if (stream.is_valid()) {
+
+ int x = p_point.x - get_timeline()->get_name_limit();
+ float ofs = x / get_timeline()->get_zoom_scale();
+ ofs += get_timeline()->get_value();
+
+ ofs = get_editor()->snap_time(ofs);
+
+ while (get_animation()->track_find_key(get_track(), ofs, true) != -1) { //make sure insertion point is valid
+ ofs += 0.001;
+ }
+
+ print_line("inserting");
+
+ *get_block_animation_update_ptr() = true;
+ get_undo_redo()->create_action("Add Audio Track Clip");
+ get_undo_redo()->add_do_method(get_animation().ptr(), "audio_track_insert_key", get_track(), ofs, stream);
+ get_undo_redo()->add_undo_method(get_animation().ptr(), "track_remove_key_at_position", get_track(), ofs);
+ get_undo_redo()->commit_action();
+ *get_block_animation_update_ptr() = false;
+
+ update();
+ return;
+ }
+ }
+
+ return AnimationTrackEdit::drop_data(p_point, p_data);
+}
+
+void AnimationTrackEditTypeAudio::_gui_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (!len_resizing && mm.is_valid()) {
+ bool use_hsize_cursor = false;
+ for (int i = 0; i < get_animation()->track_get_key_count(get_track()); i++) {
+
+ Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), i);
+
+ if (!stream.is_valid()) {
+ continue;
+ }
+
+ float start_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), i);
+ float end_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), i);
+ float len = stream->get_length();
+
+ if (len == 0) {
+ Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
+ float preview_len = preview->get_length();
+ len = preview_len;
+ }
+
+ len -= end_ofs;
+ len -= start_ofs;
+ if (len <= 0.001) {
+ len = 0.001;
+ }
+
+ if (get_animation()->track_get_key_count(get_track()) > i + 1) {
+ len = MIN(len, get_animation()->track_get_key_time(get_track(), i + 1) - get_animation()->track_get_key_time(get_track(), i));
+ }
+
+ float ofs = get_animation()->track_get_key_time(get_track(), i);
+
+ ofs -= get_timeline()->get_value();
+ ofs *= get_timeline()->get_zoom_scale();
+ ofs += get_timeline()->get_name_limit();
+
+ int end = ofs + len * get_timeline()->get_zoom_scale();
+
+ if (end >= get_timeline()->get_name_limit() && end <= get_size().width - get_timeline()->get_buttons_width() && ABS(mm->get_position().x - end) < 5 * EDSCALE) {
+ use_hsize_cursor = true;
+ len_resizing_index = i;
+ }
+ }
+
+ if (use_hsize_cursor) {
+ set_default_cursor_shape(CURSOR_HSIZE);
+ } else {
+ set_default_cursor_shape(CURSOR_ARROW);
+ }
+ }
+
+ if (len_resizing && mm.is_valid()) {
+ len_resizing_rel += mm->get_relative().x;
+ len_resizing_start = mm->get_shift();
+ update();
+ accept_event();
+ return;
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && get_default_cursor_shape() == CURSOR_HSIZE) {
+
+ len_resizing = true;
+ len_resizing_start = mb->get_shift();
+ len_resizing_from_px = mb->get_position().x;
+ len_resizing_rel = 0;
+ update();
+ accept_event();
+ return;
+ }
+
+ if (len_resizing && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ float ofs_local = -len_resizing_rel / get_timeline()->get_zoom_scale();
+ if (len_resizing_start) {
+ float prev_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), len_resizing_index);
+ *get_block_animation_update_ptr() = true;
+ get_undo_redo()->create_action("Change Audio Track Clip Start Offset");
+ get_undo_redo()->add_do_method(get_animation().ptr(), "audio_track_set_key_start_offset", get_track(), len_resizing_index, prev_ofs + ofs_local);
+ get_undo_redo()->add_undo_method(get_animation().ptr(), "audio_track_set_key_start_offset", get_track(), len_resizing_index, prev_ofs);
+ get_undo_redo()->commit_action();
+ *get_block_animation_update_ptr() = false;
+
+ } else {
+ float prev_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), len_resizing_index);
+ *get_block_animation_update_ptr() = true;
+ get_undo_redo()->create_action("Change Audio Track Clip End Offset");
+ get_undo_redo()->add_do_method(get_animation().ptr(), "audio_track_set_key_end_offset", get_track(), len_resizing_index, prev_ofs + ofs_local);
+ get_undo_redo()->add_undo_method(get_animation().ptr(), "audio_track_set_key_end_offset", get_track(), len_resizing_index, prev_ofs);
+ get_undo_redo()->commit_action();
+ *get_block_animation_update_ptr() = false;
+ }
+
+ len_resizing = false;
+ len_resizing_index = -1;
+ update();
+ accept_event();
+ return;
+ }
+
+ AnimationTrackEdit::_gui_input(p_event);
+}
+
+////////////////////
+/// SUB ANIMATION ///
+
+int AnimationTrackEditTypeAnimation::get_key_height() const {
+
+ if (!ObjectDB::get_instance(id)) {
+ return AnimationTrackEdit::get_key_height();
+ }
+
+ Ref<Font> font = get_font("font", "Label");
+ return int(font->get_height() * 1.5);
+}
+Rect2 AnimationTrackEditTypeAnimation::get_key_rect(int p_index, float p_pixels_sec) {
+
+ Object *object = ObjectDB::get_instance(id);
+
+ if (!object) {
+ return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
+ }
+
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);
+
+ if (!ap) {
+ return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
+ }
+
+ String anim = get_animation()->animation_track_get_key_animation(get_track(), p_index);
+ print_line("anim " + anim + " has " + itos(ap->has_animation(anim)));
+
+ if (anim != "[stop]" && ap->has_animation(anim)) {
+
+ float len = ap->get_animation(anim)->get_length();
+
+ if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
+ len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
+ }
+
+ return Rect2(0, 0, len * p_pixels_sec, get_size().height);
+ } else {
+ Ref<Font> font = get_font("font", "Label");
+ int fh = font->get_height() * 0.8;
+ return Rect2(0, 0, fh, get_size().height);
+ }
+}
+
+bool AnimationTrackEditTypeAnimation::is_key_selectable_by_distance() const {
+
+ return false;
+}
+void AnimationTrackEditTypeAnimation::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
+
+ Object *object = ObjectDB::get_instance(id);
+
+ if (!object) {
+ AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
+ return;
+ }
+
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);
+
+ if (!ap) {
+ AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
+ return;
+ }
+
+ String anim = get_animation()->animation_track_get_key_animation(get_track(), p_index);
+
+ if (anim != "[stop]" && ap->has_animation(anim)) {
+
+ float len = ap->get_animation(anim)->get_length();
+
+ if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
+ len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
+ }
+
+ int pixel_len = len * p_pixels_sec;
+
+ int pixel_begin = p_x;
+ int pixel_end = p_x + pixel_len;
+
+ if (pixel_end < p_clip_left)
+ return;
+
+ if (pixel_begin > p_clip_right)
+ return;
+
+ int from_x = MAX(pixel_begin, p_clip_left);
+ int to_x = MIN(pixel_end, p_clip_right);
+
+ if (to_x <= from_x)
+ return;
+
+ Ref<Font> font = get_font("font", "Label");
+ int fh = font->get_height() * 1.5;
+
+ Rect2 rect(from_x, int(get_size().height - fh) / 2, to_x - from_x, fh);
+
+ Color color = get_color("font_color", "Label");
+ Color bg = color;
+ bg.r = 1 - color.r;
+ bg.g = 1 - color.g;
+ bg.b = 1 - color.b;
+ draw_rect(rect, bg);
+
+ Vector<Vector2> lines;
+ Vector<Color> colorv;
+ {
+ Ref<Animation> animation = ap->get_animation(anim);
+
+ for (int i = 0; i < animation->get_track_count(); i++) {
+
+ float h = (rect.size.height - 2) / animation->get_track_count();
+
+ int y = 2 + h * i + h / 2;
+
+ for (int j = 0; j < animation->track_get_key_count(i); j++) {
+
+ float ofs = animation->track_get_key_time(i, j);
+ int x = p_x + ofs * p_pixels_sec + 2;
+
+ if (x < from_x || x >= (to_x - 4))
+ continue;
+
+ lines.push_back(Point2(x, y));
+ lines.push_back(Point2(x + 1, y));
+ }
+ }
+
+ colorv.push_back(color);
+ }
+
+ if (lines.size() > 2) {
+ VS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), lines, colorv);
+ }
+
+ int limit = to_x - from_x - 4;
+ if (limit > 0) {
+ draw_string(font, Point2(from_x + 2, int(get_size().height - font->get_height()) / 2 + font->get_ascent()), anim, color);
+ }
+
+ if (p_selected) {
+ Color accent = get_color("accent_color", "Editor");
+ draw_rect(rect, accent, false);
+ }
+ } else {
+ Ref<Font> font = get_font("font", "Label");
+ int fh = font->get_height() * 0.8;
+ Rect2 rect(Vector2(p_x, int(get_size().height - fh) / 2), Size2(fh, fh));
+
+ Color color = get_color("font_color", "Label");
+ draw_rect(rect, color);
+
+ if (p_selected) {
+ Color accent = get_color("accent_color", "Editor");
+ draw_rect(rect, accent, false);
+ }
+ }
+}
+
+void AnimationTrackEditTypeAnimation::set_node(Object *p_object) {
+
+ id = p_object->get_instance_id();
+}
+
+AnimationTrackEditTypeAnimation::AnimationTrackEditTypeAnimation() {
+}
+
+/////////
+AnimationTrackEdit *AnimationTrackEditDefaultPlugin::create_value_track_edit(Object *p_object, Variant::Type p_type, const String &p_property, PropertyHint p_hint, const String &p_hint_string, int p_usage) {
+
+ if (p_property == "playing" && (p_object->is_class("AudioStreamPlayer") || p_object->is_class("AudioStreamPlayer2D") || p_object->is_class("AudioStreamPlayer3D"))) {
+
+ AnimationTrackEditAudio *audio = memnew(AnimationTrackEditAudio);
+ audio->set_node(p_object);
+ return audio;
+ }
+
+ if (p_property == "frame" && (p_object->is_class("Sprite") || p_object->is_class("Sprite3D") || p_object->is_class("AnimatedSprite") || p_object->is_class("AnimatedSprite3D"))) {
+
+ AnimationTrackEditSpriteFrame *sprite = memnew(AnimationTrackEditSpriteFrame);
+ sprite->set_node(p_object);
+ return sprite;
+ }
+
+ if (p_property == "current_animation" && (p_object->is_class("AnimationPlayer"))) {
+
+ AnimationTrackEditSubAnim *player = memnew(AnimationTrackEditSubAnim);
+ player->set_node(p_object);
+ return player;
+ }
+
+ if (p_property == "volume_db") {
+
+ AnimationTrackEditVolumeDB *vu = memnew(AnimationTrackEditVolumeDB);
+ return vu;
+ }
+
+ if (p_type == Variant::BOOL) {
+ return memnew(AnimationTrackEditBool);
+ }
+ if (p_type == Variant::COLOR) {
+ return memnew(AnimationTrackEditColor);
+ }
+
+ return NULL;
+}
+
+AnimationTrackEdit *AnimationTrackEditDefaultPlugin::create_audio_track_edit() {
+
+ return memnew(AnimationTrackEditTypeAudio);
+}
+
+AnimationTrackEdit *AnimationTrackEditDefaultPlugin::create_animation_track_edit(Object *p_object) {
+
+ AnimationTrackEditTypeAnimation *an = memnew(AnimationTrackEditTypeAnimation);
+ an->set_node(p_object);
+ return an;
+}
diff --git a/editor/animation_track_editor_plugins.h b/editor/animation_track_editor_plugins.h
new file mode 100644
index 0000000000..59604412d9
--- /dev/null
+++ b/editor/animation_track_editor_plugins.h
@@ -0,0 +1,139 @@
+#ifndef ANIMATION_TRACK_EDITOR_PLUGINS_H
+#define ANIMATION_TRACK_EDITOR_PLUGINS_H
+
+#include "editor/animation_track_editor.h"
+
+class AnimationTrackEditBool : public AnimationTrackEdit {
+ GDCLASS(AnimationTrackEditBool, AnimationTrackEdit)
+ Ref<Texture> icon_checked;
+ Ref<Texture> icon_unchecked;
+
+public:
+ virtual int get_key_height() const;
+ virtual Rect2 get_key_rect(int p_index, float p_pixels_sec);
+ virtual bool is_key_selectable_by_distance() const;
+ virtual void draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right);
+};
+
+class AnimationTrackEditColor : public AnimationTrackEdit {
+ GDCLASS(AnimationTrackEditColor, AnimationTrackEdit)
+
+public:
+ virtual int get_key_height() const;
+ virtual Rect2 get_key_rect(int p_index, float p_pixels_sec);
+ virtual bool is_key_selectable_by_distance() const;
+ virtual void draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right);
+ virtual void draw_key_link(int p_index, float p_pixels_sec, int p_x, int p_next_x, int p_clip_left, int p_clip_right);
+};
+
+class AnimationTrackEditAudio : public AnimationTrackEdit {
+ GDCLASS(AnimationTrackEditAudio, AnimationTrackEdit)
+
+ ObjectID id;
+
+ void _preview_changed(ObjectID p_which);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual int get_key_height() const;
+ virtual Rect2 get_key_rect(int p_index, float p_pixels_sec);
+ virtual bool is_key_selectable_by_distance() const;
+ virtual void draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right);
+
+ void set_node(Object *p_object);
+
+ AnimationTrackEditAudio();
+};
+
+class AnimationTrackEditSpriteFrame : public AnimationTrackEdit {
+ GDCLASS(AnimationTrackEditSpriteFrame, AnimationTrackEdit)
+
+ ObjectID id;
+
+public:
+ virtual int get_key_height() const;
+ virtual Rect2 get_key_rect(int p_index, float p_pixels_sec);
+ virtual bool is_key_selectable_by_distance() const;
+ virtual void draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right);
+
+ void set_node(Object *p_object);
+};
+
+class AnimationTrackEditSubAnim : public AnimationTrackEdit {
+ GDCLASS(AnimationTrackEditSubAnim, AnimationTrackEdit)
+
+ ObjectID id;
+
+public:
+ virtual int get_key_height() const;
+ virtual Rect2 get_key_rect(int p_index, float p_pixels_sec);
+ virtual bool is_key_selectable_by_distance() const;
+ virtual void draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right);
+
+ void set_node(Object *p_object);
+};
+
+class AnimationTrackEditTypeAudio : public AnimationTrackEdit {
+ GDCLASS(AnimationTrackEditTypeAudio, AnimationTrackEdit)
+
+ void _preview_changed(ObjectID p_which);
+
+ bool len_resizing;
+ bool len_resizing_start;
+ int len_resizing_index;
+ float len_resizing_from_px;
+ float len_resizing_rel;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void _gui_input(const Ref<InputEvent> &p_event);
+
+ virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
+ virtual void drop_data(const Point2 &p_point, const Variant &p_data);
+
+ virtual int get_key_height() const;
+ virtual Rect2 get_key_rect(int p_index, float p_pixels_sec);
+ virtual bool is_key_selectable_by_distance() const;
+ virtual void draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right);
+
+ AnimationTrackEditTypeAudio();
+};
+
+class AnimationTrackEditTypeAnimation : public AnimationTrackEdit {
+ GDCLASS(AnimationTrackEditTypeAnimation, AnimationTrackEdit)
+
+ ObjectID id;
+
+public:
+ virtual int get_key_height() const;
+ virtual Rect2 get_key_rect(int p_index, float p_pixels_sec);
+ virtual bool is_key_selectable_by_distance() const;
+ virtual void draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right);
+
+ void set_node(Object *p_object);
+ AnimationTrackEditTypeAnimation();
+};
+
+class AnimationTrackEditVolumeDB : public AnimationTrackEdit {
+ GDCLASS(AnimationTrackEditVolumeDB, AnimationTrackEdit)
+
+public:
+ virtual void draw_bg(int p_clip_left, int p_clip_right);
+ virtual void draw_fg(int p_clip_left, int p_clip_right);
+ virtual int get_key_height() const;
+ virtual void draw_key_link(int p_index, float p_pixels_sec, int p_x, int p_next_x, int p_clip_left, int p_clip_right);
+};
+
+class AnimationTrackEditDefaultPlugin : public AnimationTrackEditPlugin {
+ GDCLASS(AnimationTrackEditDefaultPlugin, AnimationTrackEditPlugin)
+public:
+ virtual AnimationTrackEdit *create_value_track_edit(Object *p_object, Variant::Type p_type, const String &p_property, PropertyHint p_hint, const String &p_hint_string, int p_usage);
+ virtual AnimationTrackEdit *create_audio_track_edit();
+ virtual AnimationTrackEdit *create_animation_track_edit(Object *p_object);
+};
+
+#endif // ANIMATION_TRACK_EDITOR_PLUGINS_H
diff --git a/editor/audio_stream_preview.cpp b/editor/audio_stream_preview.cpp
new file mode 100644
index 0000000000..6ee4d7f4b0
--- /dev/null
+++ b/editor/audio_stream_preview.cpp
@@ -0,0 +1,211 @@
+#include "audio_stream_preview.h"
+
+/////////////////////
+
+float AudioStreamPreview::get_length() const {
+ return length;
+}
+float AudioStreamPreview::get_max(float p_time, float p_time_next) const {
+
+ if (length == 0)
+ return 0;
+
+ int max = preview.size() / 2;
+ int time_from = p_time / length * max;
+ int time_to = p_time_next / length * max;
+ time_from = CLAMP(time_from, 0, max - 1);
+ time_to = CLAMP(time_to, 0, max - 1);
+
+ if (time_to <= time_from) {
+ time_to = time_from + 1;
+ }
+
+ uint8_t vmax;
+
+ for (int i = time_from; i < time_to; i++) {
+
+ uint8_t v = preview[i * 2 + 1];
+ if (i == 0 || v > vmax) {
+ vmax = v;
+ }
+ }
+
+ return (vmax / 255.0) * 2.0 - 1.0;
+}
+float AudioStreamPreview::get_min(float p_time, float p_time_next) const {
+
+ if (length == 0)
+ return 0;
+
+ int max = preview.size() / 2;
+ int time_from = p_time / length * max;
+ int time_to = p_time_next / length * max;
+ time_from = CLAMP(time_from, 0, max - 1);
+ time_to = CLAMP(time_to, 0, max - 1);
+
+ if (time_to <= time_from) {
+ time_to = time_from + 1;
+ }
+
+ uint8_t vmin;
+
+ for (int i = time_from; i < time_to; i++) {
+
+ uint8_t v = preview[i * 2];
+ if (i == 0 || v < vmin) {
+ vmin = v;
+ }
+ }
+
+ return (vmin / 255.0) * 2.0 - 1.0;
+}
+
+AudioStreamPreview::AudioStreamPreview() {
+ length = 0;
+}
+
+////
+
+void AudioStreamPreviewGenerator::_update_emit(ObjectID p_id) {
+ emit_signal("preview_updated", p_id);
+}
+
+void AudioStreamPreviewGenerator::_preview_thread(void *p_preview) {
+
+ Preview *preview = (Preview *)p_preview;
+
+ float muxbuff_chunk_s = 0.25;
+
+ int mixbuff_chunk_frames = AudioServer::get_singleton()->get_mix_rate() * muxbuff_chunk_s;
+
+ Vector<AudioFrame> mix_chunk;
+ mix_chunk.resize(mixbuff_chunk_frames);
+
+ int frames_total = AudioServer::get_singleton()->get_mix_rate() * preview->preview->length;
+ int frames_todo = frames_total;
+
+ preview->playback->start();
+
+ while (frames_todo) {
+
+ int ofs_write = uint64_t(frames_total - frames_todo) * uint64_t(preview->preview->preview.size() / 2) / uint64_t(frames_total);
+ int to_read = MIN(frames_todo, mixbuff_chunk_frames);
+ int to_write = uint64_t(to_read) * uint64_t(preview->preview->preview.size() / 2) / uint64_t(frames_total);
+ to_write = MIN(to_write, (preview->preview->preview.size() / 2) - ofs_write);
+
+ preview->playback->mix(mix_chunk.ptrw(), 1.0, to_read);
+
+ for (int i = 0; i < to_write; i++) {
+ float max = -1000;
+ float min = 1000;
+ int from = uint64_t(i) * to_read / to_write;
+ int to = uint64_t(i + 1) * to_read / to_write;
+ to = MIN(to, to_read);
+ from = MIN(from, to_read - 1);
+ if (to == from) {
+ to = from + 1;
+ }
+
+ for (int j = from; j < to; j++) {
+
+ max = MAX(max, mix_chunk[j].l);
+ max = MAX(max, mix_chunk[j].r);
+
+ min = MIN(min, mix_chunk[j].l);
+ min = MIN(min, mix_chunk[j].r);
+ }
+
+ uint8_t pfrom = CLAMP((min * 0.5 + 0.5) * 255, 0, 255);
+ uint8_t pto = CLAMP((max * 0.5 + 0.5) * 255, 0, 255);
+
+ preview->preview->preview[(ofs_write + i) * 2 + 0] = pfrom;
+ preview->preview->preview[(ofs_write + i) * 2 + 1] = pto;
+ }
+
+ frames_todo -= to_read;
+ singleton->call_deferred("_update_emit", preview->id);
+ }
+
+ preview->playback->stop();
+
+ preview->generating = false;
+}
+
+Ref<AudioStreamPreview> AudioStreamPreviewGenerator::generate_preview(const Ref<AudioStream> &p_stream) {
+ ERR_FAIL_COND_V(p_stream.is_null(), Ref<AudioStreamPreview>());
+
+ if (previews.has(p_stream->get_instance_id())) {
+ return previews[p_stream->get_instance_id()].preview;
+ }
+
+ //no preview exists
+
+ previews[p_stream->get_instance_id()] = Preview();
+
+ Preview *preview = &previews[p_stream->get_instance_id()];
+ preview->base_stream = p_stream;
+ preview->playback = preview->base_stream->instance_playback();
+ preview->generating = true;
+ preview->id = p_stream->get_instance_id();
+
+ float len_s = preview->base_stream->get_length();
+ if (len_s == 0) {
+ len_s = 60 * 5; //five minutes
+ }
+
+ int frames = AudioServer::get_singleton()->get_mix_rate() * len_s;
+
+ Vector<uint8_t> maxmin;
+ int pw = frames / 20;
+ maxmin.resize(pw * 2);
+ {
+ uint8_t *ptr = maxmin.ptrw();
+ for (int i = 0; i < pw * 2; i++) {
+ ptr[i] = 127;
+ }
+ }
+
+ preview->preview.instance();
+ preview->preview->preview = maxmin;
+ preview->preview->length = len_s;
+
+ preview->thread = Thread::create(_preview_thread, preview);
+
+ return preview->preview;
+}
+
+void AudioStreamPreviewGenerator::_bind_methods() {
+ ClassDB::bind_method("_update_emit", &AudioStreamPreviewGenerator::_update_emit);
+ ClassDB::bind_method(D_METHOD("generate_preview", "stream"), &AudioStreamPreviewGenerator::generate_preview);
+
+ ADD_SIGNAL(MethodInfo("preview_updated", PropertyInfo(Variant::INT, "obj_id")));
+}
+
+AudioStreamPreviewGenerator *AudioStreamPreviewGenerator::singleton = NULL;
+
+void AudioStreamPreviewGenerator::_notification(int p_what) {
+ if (p_what == NOTIFICATION_PROCESS) {
+ List<ObjectID> to_erase;
+ for (Map<ObjectID, Preview>::Element *E = previews.front(); E; E = E->next()) {
+ if (!E->get().generating) {
+ if (E->get().thread) {
+ Thread::wait_to_finish(E->get().thread);
+ E->get().thread = NULL;
+ }
+ if (!ObjectDB::get_instance(E->key())) { //no longer in use, get rid of preview
+ to_erase.push_back(E->key());
+ }
+ }
+ }
+
+ while (to_erase.front()) {
+ previews.erase(to_erase.front()->get());
+ to_erase.pop_front();
+ }
+ }
+}
+
+AudioStreamPreviewGenerator::AudioStreamPreviewGenerator() {
+ singleton = this;
+ set_process(true);
+}
diff --git a/editor/audio_stream_preview.h b/editor/audio_stream_preview.h
new file mode 100644
index 0000000000..cfe1667e9d
--- /dev/null
+++ b/editor/audio_stream_preview.h
@@ -0,0 +1,56 @@
+#ifndef AUDIO_STREAM_PREVIEW_H
+#define AUDIO_STREAM_PREVIEW_H
+
+#include "os/thread.h"
+#include "scene/main/node.h"
+#include "servers/audio/audio_stream.h"
+
+class AudioStreamPreview : public Reference {
+ GDCLASS(AudioStreamPreview, Reference)
+ friend class AudioStream;
+ Vector<uint8_t> preview;
+ float length;
+
+ friend class AudioStreamPreviewGenerator;
+
+public:
+ float get_length() const;
+ float get_max(float p_time, float p_time_next) const;
+ float get_min(float p_time, float p_time_next) const;
+
+ AudioStreamPreview();
+};
+
+class AudioStreamPreviewGenerator : public Node {
+ GDCLASS(AudioStreamPreviewGenerator, Node)
+
+ static AudioStreamPreviewGenerator *singleton;
+
+ struct Preview {
+ Ref<AudioStreamPreview> preview;
+ Ref<AudioStream> base_stream;
+ Ref<AudioStreamPlayback> playback;
+ volatile bool generating;
+ ObjectID id;
+ Thread *thread;
+ };
+
+ Map<ObjectID, Preview> previews;
+
+ static void _preview_thread(void *p_preview);
+
+ void _update_emit(ObjectID p_id);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ static AudioStreamPreviewGenerator *get_singleton() { return singleton; }
+
+ Ref<AudioStreamPreview> generate_preview(const Ref<AudioStream> &p_preview);
+
+ AudioStreamPreviewGenerator();
+};
+
+#endif // AUDIO_STREAM_PREVIEW_H
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 24e86770bf..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) {
@@ -373,7 +373,6 @@ void FindReplaceBar::_hide_bar() {
void FindReplaceBar::_show_search() {
- hide(); // to update size correctly
show();
search_text->grab_focus();
@@ -481,7 +480,7 @@ void FindReplaceBar::set_text_edit(TextEdit *p_text_edit) {
void FindReplaceBar::_update_size() {
- container->set_custom_minimum_size(Size2(0, hbc->get_size().height));
+ container->set_size(Size2(hbc->get_size().width, 1));
}
void FindReplaceBar::_bind_methods() {
@@ -507,7 +506,8 @@ void FindReplaceBar::_bind_methods() {
FindReplaceBar::FindReplaceBar() {
- container = memnew(Control);
+ container = memnew(MarginContainer);
+ container->add_constant_override("margin_bottom", 5 * EDSCALE);
add_child(container);
container->set_clip_contents(true);
container->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -592,8 +592,7 @@ FindReplaceBar::FindReplaceBar() {
add_child(hide_button);
hide_button->set_focus_mode(FOCUS_NONE);
hide_button->connect("pressed", this, "_hide_pressed");
- hide_button->set_expand(true);
- hide_button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
+ hide_button->set_v_size_flags(SIZE_SHRINK_CENTER);
}
/*** CODE EDITOR ****/
@@ -776,9 +775,10 @@ 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"));
text_editor->cursor_set_block_mode(EditorSettings::get_singleton()->get("text_editor/cursor/block_caret"));
text_editor->set_smooth_scroll_enabled(EditorSettings::get_singleton()->get("text_editor/open_scripts/smooth_scrolling"));
text_editor->set_v_scroll_speed(EditorSettings::get_singleton()->get("text_editor/open_scripts/v_scroll_speed"));
diff --git a/editor/code_editor.h b/editor/code_editor.h
index a860ad24e2..2a3bb1ba76 100644
--- a/editor/code_editor.h
+++ b/editor/code_editor.h
@@ -63,7 +63,7 @@ class FindReplaceBar : public HBoxContainer {
GDCLASS(FindReplaceBar, HBoxContainer);
- Control *container;
+ MarginContainer *container;
LineEdit *search_text;
ToolButton *find_prev;
ToolButton *find_next;
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 7f93917744..8933fd7fe8 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();
}
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/dependency_editor.cpp b/editor/dependency_editor.cpp
index 953d787322..c4a17d5402 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -504,25 +504,25 @@ void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector<
void DependencyRemoveDialog::ok_pressed() {
+ for (int i = 0; i < files_to_delete.size(); ++i) {
+ if (ResourceCache::has(files_to_delete[i])) {
+ Resource *res = ResourceCache::get(files_to_delete[i]);
+ res->set_path("");
+ }
+ String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/");
+ print_line("Moving to trash: " + path);
+ Error err = OS::get_singleton()->move_to_trash(path);
+ if (err != OK) {
+ EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + files_to_delete[i] + "\n");
+ }
+ }
+
if (dirs_to_delete.size() == 0) {
//If we only deleted files we should only need to tell the file system about the files we touched.
for (int i = 0; i < files_to_delete.size(); ++i)
EditorFileSystem::get_singleton()->update_file(files_to_delete[i]);
} else {
- for (int i = 0; i < files_to_delete.size(); ++i) {
- if (ResourceCache::has(files_to_delete[i])) {
- Resource *res = ResourceCache::get(files_to_delete[i]);
- res->set_path("");
- }
- String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/");
- print_line("Moving to trash: " + path);
- Error err = OS::get_singleton()->move_to_trash(path);
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + files_to_delete[i] + "\n");
- }
- }
-
for (int i = 0; i < dirs_to_delete.size(); ++i) {
String path = OS::get_singleton()->get_resource_dir() + dirs_to_delete[i].replace_first("res://", "/");
print_line("Moving to trash: " + path);
diff --git a/editor/doc/doc_data.cpp b/editor/doc/doc_data.cpp
index 3434aa33f9..542dca74e0 100644
--- a/editor/doc/doc_data.cpp
+++ b/editor/doc/doc_data.cpp
@@ -233,7 +233,12 @@ void DocData::generate(bool p_basic_types) {
c.category = ClassDB::get_category(name);
List<PropertyInfo> properties;
- ClassDB::get_property_list(name, &properties, true);
+ if (name == "ProjectSettings") {
+ //special case for project settings, so settings can be documented
+ ProjectSettings::get_singleton()->get_property_list(&properties);
+ } else {
+ ClassDB::get_property_list(name, &properties, true);
+ }
for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
if (E->get().usage & PROPERTY_USAGE_GROUP || E->get().usage & PROPERTY_USAGE_CATEGORY || E->get().usage & PROPERTY_USAGE_INTERNAL)
@@ -567,6 +572,9 @@ void DocData::generate(bool p_basic_types) {
PropertyDoc pd;
Engine::Singleton &s = E->get();
+ if (!s.ptr) {
+ continue;
+ }
pd.name = s.name;
pd.type = s.ptr->get_class();
while (String(ClassDB::get_parent_class(pd.type)) != "Object")
@@ -807,9 +815,24 @@ Error DocData::_load(Ref<XMLParser> parser) {
if (parser->get_node_type() == XMLParser::NODE_TEXT)
c.description = parser->get_node_data();
} else if (name == "tutorials") {
- parser->read();
- if (parser->get_node_type() == XMLParser::NODE_TEXT)
- c.tutorials = parser->get_node_data();
+ while (parser->read() == OK) {
+
+ if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
+
+ String name = parser->get_node_name();
+
+ if (name == "link") {
+
+ parser->read();
+ if (parser->get_node_type() == XMLParser::NODE_TEXT)
+ c.tutorials.push_back(parser->get_node_data().strip_edges());
+ } else {
+ ERR_EXPLAIN("Invalid tag in doc file: " + name);
+ ERR_FAIL_V(ERR_FILE_CORRUPT);
+ }
+ } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "tutorials")
+ break; //end of <tutorials>
+ }
} else if (name == "demos") {
parser->read();
if (parser->get_node_type() == XMLParser::NODE_TEXT)
@@ -984,7 +1007,9 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri
_write_string(f, 2, c.description.strip_edges().xml_escape());
_write_string(f, 1, "</description>");
_write_string(f, 1, "<tutorials>");
- _write_string(f, 2, c.tutorials.strip_edges().xml_escape());
+ for (int i = 0; i < c.tutorials.size(); i++) {
+ _write_string(f, 2, "<link>" + c.tutorials.get(i).xml_escape() + "</link>");
+ }
_write_string(f, 1, "</tutorials>");
_write_string(f, 1, "<demos>");
_write_string(f, 2, c.demos.strip_edges().xml_escape());
diff --git a/editor/doc/doc_data.h b/editor/doc/doc_data.h
index 0461133f9f..c7b70b5fb9 100644
--- a/editor/doc/doc_data.h
+++ b/editor/doc/doc_data.h
@@ -85,7 +85,7 @@ public:
String category;
String brief_description;
String description;
- String tutorials;
+ Vector<String> tutorials;
String demos;
Vector<MethodDoc> methods;
Vector<MethodDoc> signals;
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index 360ed620f6..4b09db0a9e 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -31,11 +31,11 @@
#include "editor_about.h"
#include "editor_node.h"
-#include "authors.gen.h"
-#include "donors.gen.h"
-#include "license.gen.h"
-#include "version.h"
-#include "version_hash.gen.h"
+#include "core/authors.gen.h"
+#include "core/donors.gen.h"
+#include "core/license.gen.h"
+#include "core/version.h"
+#include "core/version_hash.gen.h"
void EditorAbout::_notification(int p_what) {
@@ -69,7 +69,7 @@ TextureRect *EditorAbout::get_logo() const {
return _logo;
}
-ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<String> &p_sections, const char **p_src[], const int p_flag_single_column) {
+ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<String> &p_sections, const char *const *const p_src[], const int p_flag_single_column) {
ScrollContainer *sc = memnew(ScrollContainer);
sc->set_name(p_name);
@@ -82,7 +82,7 @@ ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<St
for (int i = 0; i < p_sections.size(); i++) {
bool single_column = p_flag_single_column & 1 << i;
- const char **names_ptr = p_src[i];
+ const char *const *names_ptr = p_src[i];
if (*names_ptr) {
Label *lbl = memnew(Label);
@@ -151,7 +151,8 @@ EditorAbout::EditorAbout() {
dev_sections.push_back(TTR("Lead Developer"));
dev_sections.push_back(TTR("Project Manager ")); // " " appended to distinguish between 'project supervisor' and 'project list'
dev_sections.push_back(TTR("Developers"));
- const char **dev_src[] = { dev_founders, dev_lead, dev_manager, dev_names };
+ const char *const *dev_src[] = { AUTHORS_FOUNDERS, AUTHORS_LEAD_DEVELOPERS,
+ AUTHORS_PROJECT_MANAGERS, AUTHORS_DEVELOPERS };
tc->add_child(_populate_list(TTR("Authors"), dev_sections, dev_src, 1));
// Donors
@@ -163,7 +164,8 @@ EditorAbout::EditorAbout() {
donor_sections.push_back(TTR("Gold Donors"));
donor_sections.push_back(TTR("Silver Donors"));
donor_sections.push_back(TTR("Bronze Donors"));
- const char **donor_src[] = { donor_s_plat, donor_s_gold, donor_s_mini, donor_gold, donor_silver, donor_bronze };
+ const char *const *donor_src[] = { DONORS_SPONSOR_PLAT, DONORS_SPONSOR_GOLD,
+ DONORS_SPONSOR_MINI, DONORS_GOLD, DONORS_SILVER, DONORS_BRONZE };
tc->add_child(_populate_list(TTR("Donors"), donor_sections, donor_src, 3));
// License
@@ -172,7 +174,7 @@ EditorAbout::EditorAbout() {
_license_text->set_name(TTR("License"));
_license_text->set_h_size_flags(Control::SIZE_EXPAND_FILL);
_license_text->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- _license_text->set_text(String::utf8(about_license));
+ _license_text->set_text(String::utf8(GODOT_LICENSE_TEXT));
tc->add_child(_license_text);
// Thirdparty License
@@ -186,6 +188,7 @@ EditorAbout::EditorAbout() {
tpl_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
tpl_label->set_autowrap(true);
tpl_label->set_text(TTR("Godot Engine relies on a number of thirdparty free and open source libraries, all compatible with the terms of its MIT license. The following is an exhaustive list of all such thirdparty components with their respective copyright statements and license terms."));
+ tpl_label->set_size(Size2(630, 1) * EDSCALE);
license_thirdparty->add_child(tpl_label);
HSplitContainer *tpl_hbc = memnew(HSplitContainer);
@@ -207,20 +210,27 @@ EditorAbout::EditorAbout() {
tpl_ti_lc->set_selectable(0, false);
int read_idx = 0;
String long_text = "";
- for (int i = 0; i < THIRDPARTY_COUNT; i++) {
+ for (int component_index = 0; component_index < COPYRIGHT_INFO_COUNT; component_index++) {
+ const ComponentCopyright &component = COPYRIGHT_INFO[component_index];
TreeItem *ti = _tpl_tree->create_item(tpl_ti_tp);
- String thirdparty = String(about_thirdparty[i]);
- ti->set_text(0, thirdparty);
- String text = thirdparty + "\n";
- long_text += "- " + thirdparty + "\n\n";
- for (int j = 0; j < about_tp_copyright_count[i]; j++) {
-
- text += "\n Files:\n " + String(about_tp_file[read_idx]).replace("\n", "\n ") + "\n";
- String copyright = String::utf8(" \xc2\xa9 ") + String::utf8(about_tp_copyright[read_idx]).replace("\n", String::utf8("\n \xc2\xa9 "));
+ String component_name = component.name;
+ ti->set_text(0, component_name);
+ String text = component_name + "\n";
+ long_text += "- " + component_name + "\n";
+ for (int part_index = 0; part_index < component.part_count; part_index++) {
+ const ComponentCopyrightPart &part = component.parts[part_index];
+ text += "\n Files:";
+ for (int file_num = 0; file_num < part.file_count; file_num++) {
+ text += "\n " + String(part.files[file_num]);
+ }
+ String copyright;
+ for (int copyright_index = 0; copyright_index < part.copyright_count; copyright_index++) {
+ copyright += String::utf8("\n \xc2\xa9 ") + String::utf8(part.copyright_statements[copyright_index]);
+ }
text += copyright;
long_text += copyright;
- String license = "\n License: " + String(about_tp_license[read_idx]) + "\n";
+ String license = "\n License: " + String(part.license) + "\n";
text += license;
long_text += license + "\n";
read_idx++;
@@ -230,10 +240,10 @@ EditorAbout::EditorAbout() {
for (int i = 0; i < LICENSE_COUNT; i++) {
TreeItem *ti = _tpl_tree->create_item(tpl_ti_lc);
- String licensename = String(about_license_name[i]);
+ String licensename = String(LICENSE_NAMES[i]);
ti->set_text(0, licensename);
long_text += "- " + licensename + "\n\n";
- String licensebody = String(about_license_body[i]);
+ String licensebody = String(LICENSE_BODIES[i]);
ti->set_metadata(0, licensebody);
long_text += " " + licensebody.replace("\n", "\n ") + "\n\n";
}
diff --git a/editor/editor_about.h b/editor/editor_about.h
index b32fdf6567..71d1c95188 100644
--- a/editor/editor_about.h
+++ b/editor/editor_about.h
@@ -53,7 +53,7 @@ class EditorAbout : public AcceptDialog {
private:
void _license_tree_selected();
- ScrollContainer *_populate_list(const String &p_name, const List<String> &p_sections, const char **p_src[], const int p_flag_single_column = 0);
+ ScrollContainer *_populate_list(const String &p_name, const List<String> &p_sections, const char *const *const p_src[], const int p_flag_single_column = 0);
Tree *_tpl_tree;
RichTextLabel *_license_text;
diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp
index a2f5c1aa1a..2f0982e5d9 100644
--- a/editor/editor_autoload_settings.cpp
+++ b/editor/editor_autoload_settings.cpp
@@ -33,6 +33,8 @@
#include "editor_node.h"
#include "global_constants.h"
#include "project_settings.h"
+#include "scene/main/viewport.h"
+#include "scene/resources/packed_scene.h"
#define PREVIEW_LIST_MAX_SIZE 10
@@ -50,6 +52,13 @@ void EditorAutoloadSettings::_notification(int p_what) {
file_dialog->add_filter("*." + E->get());
}
+
+ for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) {
+ AutoLoadInfo &info = E->get();
+ if (info.node && info.in_editor) {
+ get_tree()->get_root()->call_deferred("add_child", info.node);
+ }
+ }
}
}
@@ -155,8 +164,8 @@ void EditorAutoloadSettings::_autoload_edited() {
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", selected_autoload, order);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", name);
- undo_redo->add_do_method(this, "update_autoload");
- undo_redo->add_undo_method(this, "update_autoload");
+ undo_redo->add_do_method(this, "call_deferred", "update_autoload");
+ undo_redo->add_undo_method(this, "call_deferred", "update_autoload");
undo_redo->add_do_method(this, "emit_signal", autoload_changed);
undo_redo->add_undo_method(this, "emit_signal", autoload_changed);
@@ -187,8 +196,8 @@ void EditorAutoloadSettings::_autoload_edited() {
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", base, order);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", base, order);
- undo_redo->add_do_method(this, "update_autoload");
- undo_redo->add_undo_method(this, "update_autoload");
+ undo_redo->add_do_method(this, "call_deferred", "update_autoload");
+ undo_redo->add_undo_method(this, "call_deferred", "update_autoload");
undo_redo->add_do_method(this, "emit_signal", autoload_changed);
undo_redo->add_undo_method(this, "emit_signal", autoload_changed);
@@ -289,6 +298,36 @@ void EditorAutoloadSettings::_autoload_file_callback(const String &p_path) {
autoload_add_name->set_text(p_path.get_file().get_basename());
}
+Node *EditorAutoloadSettings::_create_autoload(const String &p_path) {
+ RES res = ResourceLoader::load(p_path);
+ ERR_EXPLAIN("Can't autoload: " + p_path);
+ ERR_FAIL_COND_V(res.is_null(), NULL);
+ Node *n = NULL;
+ if (res->is_class("PackedScene")) {
+ Ref<PackedScene> ps = res;
+ n = ps->instance();
+ } else if (res->is_class("Script")) {
+ Ref<Script> s = res;
+ StringName ibt = s->get_instance_base_type();
+ bool valid_type = ClassDB::is_parent_class(ibt, "Node");
+ ERR_EXPLAIN("Script does not inherit a Node: " + p_path);
+ ERR_FAIL_COND_V(!valid_type, NULL);
+
+ Object *obj = ClassDB::instance(ibt);
+
+ ERR_EXPLAIN("Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt));
+ ERR_FAIL_COND_V(obj == NULL, NULL);
+
+ n = Object::cast_to<Node>(obj);
+ n->set_script(s.get_ref_ptr());
+ }
+
+ ERR_EXPLAIN("Path in autoload not a node or script: " + p_path);
+ ERR_FAIL_COND_V(!n, NULL);
+
+ return n;
+}
+
void EditorAutoloadSettings::update_autoload() {
if (updating_autoload)
@@ -296,6 +335,14 @@ void EditorAutoloadSettings::update_autoload() {
updating_autoload = true;
+ Map<String, AutoLoadInfo> to_remove;
+ List<AutoLoadInfo *> to_add;
+
+ for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) {
+ AutoLoadInfo &info = E->get();
+ to_remove.insert(info.name, info);
+ }
+
autoload_cache.clear();
tree->clear();
@@ -318,16 +365,39 @@ void EditorAutoloadSettings::update_autoload() {
continue;
AutoLoadInfo info;
- info.name = pi.name;
+ info.is_singleton = path.begins_with("*");
+
+ if (info.is_singleton) {
+ path = path.substr(1, path.length());
+ }
+
+ info.name = name;
+ info.path = path;
info.order = ProjectSettings::get_singleton()->get_order(pi.name);
- autoload_cache.push_back(info);
+ bool need_to_add = true;
+ if (to_remove.has(name)) {
+ AutoLoadInfo &old_info = to_remove[name];
+ if (old_info.path == info.path) {
+ // Still the same resource, check status
+ info.node = old_info.node;
+ if (info.node) {
+ Ref<Script> scr = info.node->get_script();
+ info.in_editor = scr.is_valid() && scr->is_tool();
+ if (info.is_singleton == old_info.is_singleton && info.in_editor == old_info.in_editor) {
+ to_remove.erase(name);
+ need_to_add = false;
+ } else {
+ info.node = NULL;
+ }
+ }
+ }
+ }
- bool global = false;
+ autoload_cache.push_back(info);
- if (path.begins_with("*")) {
- global = true;
- path = path.substr(1, path.length());
+ if (need_to_add) {
+ to_add.push_back(&(autoload_cache.back()->get()));
}
TreeItem *item = tree->create_item(root);
@@ -340,7 +410,7 @@ void EditorAutoloadSettings::update_autoload() {
item->set_cell_mode(2, TreeItem::CELL_MODE_CHECK);
item->set_editable(2, true);
item->set_text(2, TTR("Enable"));
- item->set_checked(2, global);
+ item->set_checked(2, info.is_singleton);
item->add_button(3, get_icon("FileList", "EditorIcons"), BUTTON_OPEN);
item->add_button(3, get_icon("MoveUp", "EditorIcons"), BUTTON_MOVE_UP);
item->add_button(3, get_icon("MoveDown", "EditorIcons"), BUTTON_MOVE_DOWN);
@@ -348,6 +418,60 @@ void EditorAutoloadSettings::update_autoload() {
item->set_selectable(3, false);
}
+ // Remove deleted/changed autoloads
+ for (Map<String, AutoLoadInfo>::Element *E = to_remove.front(); E; E = E->next()) {
+ AutoLoadInfo &info = E->get();
+ if (info.is_singleton) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->remove_named_global_constant(info.name);
+ }
+ }
+ if (info.in_editor) {
+ ERR_CONTINUE(!info.node);
+ get_tree()->get_root()->remove_child(info.node);
+ }
+
+ if (info.node) {
+ memdelete(info.node);
+ info.node = NULL;
+ }
+ }
+
+ // Load new/changed autoloads
+ List<Node *> nodes_to_add;
+ for (List<AutoLoadInfo *>::Element *E = to_add.front(); E; E = E->next()) {
+ AutoLoadInfo *info = E->get();
+
+ info->node = _create_autoload(info->path);
+
+ ERR_CONTINUE(!info->node);
+ info->node->set_name(info->name);
+
+ Ref<Script> scr = info->node->get_script();
+ info->in_editor = scr.is_valid() && scr->is_tool();
+
+ if (info->in_editor) {
+ //defer so references are all valid on _ready()
+ nodes_to_add.push_back(info->node);
+ }
+
+ if (info->is_singleton) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_named_global_constant(info->name, info->node);
+ }
+ }
+
+ if (!info->in_editor && !info->is_singleton) {
+ // No reason to keep this node
+ memdelete(info->node);
+ info->node = NULL;
+ }
+ }
+
+ for (List<Node *>::Element *E = nodes_to_add.front(); E; E = E->next()) {
+ get_tree()->get_root()->add_child(E->get());
+ }
+
updating_autoload = false;
}
@@ -486,8 +610,8 @@ void EditorAutoloadSettings::drop_data_fw(const Point2 &p_point, const Variant &
i = 0;
for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) {
- undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", E->get().name, orders[i++]);
- undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", E->get().name, E->get().order);
+ undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", "autoload/" + E->get().name, orders[i++]);
+ undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", "autoload/" + E->get().name, E->get().order);
}
orders.clear();
@@ -592,6 +716,66 @@ void EditorAutoloadSettings::_bind_methods() {
EditorAutoloadSettings::EditorAutoloadSettings() {
+ // Make first cache
+ List<PropertyInfo> props;
+ ProjectSettings::get_singleton()->get_property_list(&props);
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+
+ const PropertyInfo &pi = E->get();
+
+ if (!pi.name.begins_with("autoload/"))
+ continue;
+
+ String name = pi.name.get_slice("/", 1);
+ String path = ProjectSettings::get_singleton()->get(pi.name);
+
+ if (name.empty())
+ continue;
+
+ AutoLoadInfo info;
+ info.is_singleton = path.begins_with("*");
+
+ if (info.is_singleton) {
+ path = path.substr(1, path.length());
+ }
+
+ info.name = name;
+ info.path = path;
+ info.order = ProjectSettings::get_singleton()->get_order(pi.name);
+
+ if (info.is_singleton) {
+ // Make sure name references work before parsing scripts
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_named_global_constant(info.name, Variant());
+ }
+ }
+
+ autoload_cache.push_back(info);
+ }
+
+ for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) {
+ AutoLoadInfo &info = E->get();
+
+ info.node = _create_autoload(info.path);
+
+ if (info.node) {
+ Ref<Script> scr = info.node->get_script();
+ info.in_editor = scr.is_valid() && scr->is_tool();
+ info.node->set_name(info.name);
+ }
+
+ if (info.is_singleton) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_named_global_constant(info.name, info.node);
+ }
+ }
+
+ if (!info.is_singleton && !info.in_editor) {
+ memdelete(info.node);
+ info.node = NULL;
+ }
+ }
+
autoload_changed = "autoload_changed";
updating_autoload = false;
@@ -656,3 +840,12 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
add_child(tree, true);
}
+
+EditorAutoloadSettings::~EditorAutoloadSettings() {
+ for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) {
+ AutoLoadInfo &info = E->get();
+ if (info.node && !info.in_editor) {
+ memdelete(info.node);
+ }
+ }
+}
diff --git a/editor/editor_autoload_settings.h b/editor/editor_autoload_settings.h
index 6f622de6d5..0b75faa009 100644
--- a/editor/editor_autoload_settings.h
+++ b/editor/editor_autoload_settings.h
@@ -50,11 +50,21 @@ class EditorAutoloadSettings : public VBoxContainer {
struct AutoLoadInfo {
String name;
+ String path;
+ bool is_singleton;
+ bool in_editor;
int order;
+ Node *node;
bool operator==(const AutoLoadInfo &p_info) {
return order == p_info.order;
}
+
+ AutoLoadInfo() {
+ is_singleton = false;
+ in_editor = false;
+ node = NULL;
+ }
};
List<AutoLoadInfo> autoload_cache;
@@ -76,6 +86,7 @@ class EditorAutoloadSettings : public VBoxContainer {
void _autoload_activated();
void _autoload_open(const String &fpath);
void _autoload_file_callback(const String &p_path);
+ Node *_create_autoload(const String &p_path);
Variant get_drag_data_fw(const Point2 &p_point, Control *p_control);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_control) const;
@@ -91,6 +102,7 @@ public:
void autoload_remove(const String &p_name);
EditorAutoloadSettings();
+ ~EditorAutoloadSettings();
};
#endif
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 4ac3e33cb9..4dde893c6d 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -78,7 +78,7 @@ void EditorHistory::cleanup_history() {
current = history.size() - 1;
}
-void EditorHistory::_add_object(ObjectID p_object, const String &p_property, int p_level_change) {
+void EditorHistory::_add_object(ObjectID p_object, const String &p_property, int p_level_change, bool p_inspector_only) {
Object *obj = ObjectDB::get_instance(p_object);
ERR_FAIL_COND(!obj);
@@ -88,6 +88,7 @@ void EditorHistory::_add_object(ObjectID p_object, const String &p_property, int
o.ref = REF(r);
o.object = p_object;
o.property = p_property;
+ o.inspector_only = p_inspector_only;
History h;
@@ -120,6 +121,11 @@ void EditorHistory::_add_object(ObjectID p_object, const String &p_property, int
current++;
}
+void EditorHistory::add_object_inspector_only(ObjectID p_object) {
+
+ _add_object(p_object, "", -1, true);
+}
+
void EditorHistory::add_object(ObjectID p_object) {
_add_object(p_object, "", -1);
@@ -142,6 +148,13 @@ int EditorHistory::get_history_pos() {
return current;
}
+bool EditorHistory::is_history_obj_inspector_only(int p_obj) const {
+
+ ERR_FAIL_INDEX_V(p_obj, history.size(), false);
+ ERR_FAIL_INDEX_V(history[p_obj].level, history[p_obj].path.size(), false);
+ return history[p_obj].path[history[p_obj].level].inspector_only;
+}
+
ObjectID EditorHistory::get_history_obj(int p_obj) const {
ERR_FAIL_INDEX_V(p_obj, history.size(), 0);
ERR_FAIL_INDEX_V(history[p_obj].level, history[p_obj].path.size(), 0);
@@ -180,6 +193,14 @@ bool EditorHistory::previous() {
return true;
}
+bool EditorHistory::is_current_inspector_only() const {
+
+ if (current < 0 || current >= history.size())
+ return false;
+
+ const History &h = history[current];
+ return h.path[h.level].inspector_only;
+}
ObjectID EditorHistory::get_current() {
if (current < 0 || current >= history.size())
@@ -420,6 +441,18 @@ void EditorData::paste_object_params(Object *p_object) {
}
}
+bool EditorData::call_build() {
+
+ bool result = true;
+
+ for (int i = 0; i < editor_plugins.size() && result; i++) {
+
+ result &= editor_plugins[i]->build();
+ }
+
+ return result;
+}
+
UndoRedo &EditorData::get_undo_redo() {
return undo_redo;
diff --git a/editor/editor_data.h b/editor/editor_data.h
index f020d07ea7..0ecef8ae31 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -50,6 +50,7 @@ class EditorHistory {
REF ref;
ObjectID object;
String property;
+ bool inspector_only;
};
struct History {
@@ -70,7 +71,7 @@ class EditorHistory {
Variant value;
};
- void _add_object(ObjectID p_object, const String &p_property, int p_level_change);
+ void _add_object(ObjectID p_object, const String &p_property, int p_level_change, bool p_inspector_only = false);
public:
void cleanup_history();
@@ -78,6 +79,7 @@ public:
bool is_at_beginning() const;
bool is_at_end() const;
+ void add_object_inspector_only(ObjectID p_object);
void add_object(ObjectID p_object);
void add_object(ObjectID p_object, const String &p_subprop);
void add_object(ObjectID p_object, int p_relevel);
@@ -85,10 +87,12 @@ public:
int get_history_len();
int get_history_pos();
ObjectID get_history_obj(int p_obj) const;
+ bool is_history_obj_inspector_only(int p_obj) const;
bool next();
bool previous();
ObjectID get_current();
+ bool is_current_inspector_only() const;
int get_path_size() const;
ObjectID get_path_object(int p_index) const;
@@ -197,6 +201,7 @@ public:
NodePath get_edited_scene_live_edit_root();
bool check_and_update_scene(int p_idx);
void move_edited_scene_to_index(int p_idx);
+ bool call_build();
void set_plugin_window_layout(Ref<ConfigFile> p_layout);
void get_plugin_window_layout(Ref<ConfigFile> p_layout);
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 f3be02a8c7..65e50560bc 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -526,18 +526,7 @@ void EditorHelp::_unhandled_key_input(const Ref<InputEvent> &p_ev) {
void EditorHelp::_search(const String &) {
- if (search->get_text() == "")
- return;
-
- String stext = search->get_text();
- bool keep = prev_search == stext;
-
- bool ret = class_desc->search(stext, keep);
- if (!ret) {
- class_desc->search(stext, false);
- }
-
- prev_search = stext;
+ find_bar->search_next();
}
void EditorHelp::_class_list_select(const String &p_select) {
@@ -598,14 +587,6 @@ void EditorHelp::_class_desc_select(const String &p_select) {
}
void EditorHelp::_class_desc_input(const Ref<InputEvent> &p_input) {
-
- Ref<InputEventMouseButton> mb = p_input;
-
- if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == 1 && !mb->is_doubleclick()) {
- class_desc->set_selection_enabled(false);
- class_desc->set_selection_enabled(true);
- }
- set_focused();
}
void EditorHelp::_add_type(const String &p_type, const String &p_enum) {
@@ -1299,11 +1280,10 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) {
class_desc->add_newline();
// class_desc->add_newline();
- Vector<String> tutorials = cd.tutorials.split_spaces();
- if (tutorials.size() != 0) {
+ if (cd.tutorials.size() != 0) {
- for (int i = 0; i < tutorials.size(); i++) {
- String link = tutorials[i];
+ for (int i = 0; i < cd.tutorials.size(); i++) {
+ String link = cd.tutorials[i];
String linktxt = link;
int seppos = linktxt.find("//");
if (seppos != -1) {
@@ -1816,13 +1796,7 @@ void EditorHelp::scroll_to_section(int p_section_index) {
void EditorHelp::popup_search() {
- search_dialog->popup_centered(Size2(250, 80) * EDSCALE);
- search->grab_focus();
-}
-
-void EditorHelp::_search_cbk() {
-
- _search(search->get_text());
+ find_bar->popup_search();
}
String EditorHelp::get_class() {
@@ -1851,7 +1825,6 @@ void EditorHelp::_bind_methods() {
ClassDB::bind_method("_request_help", &EditorHelp::_request_help);
ClassDB::bind_method("_unhandled_key_input", &EditorHelp::_unhandled_key_input);
ClassDB::bind_method("_search", &EditorHelp::_search);
- ClassDB::bind_method("_search_cbk", &EditorHelp::_search_cbk);
ClassDB::bind_method("_help_callback", &EditorHelp::_help_callback);
ADD_SIGNAL(MethodInfo("go_to_help"));
@@ -1863,6 +1836,10 @@ EditorHelp::EditorHelp() {
EDITOR_DEF("text_editor/help/sort_functions_alphabetically", true);
+ find_bar = memnew(FindBar);
+ add_child(find_bar);
+ find_bar->hide();
+
class_desc = memnew(RichTextLabel);
add_child(class_desc);
class_desc->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -1870,24 +1847,14 @@ EditorHelp::EditorHelp() {
class_desc->connect("meta_clicked", this, "_class_desc_select");
class_desc->connect("gui_input", this, "_class_desc_input");
+ find_bar->set_rich_text_label(class_desc);
+
class_desc->set_selection_enabled(true);
scroll_locked = false;
select_locked = false;
- set_process_unhandled_key_input(true);
+ //set_process_unhandled_key_input(true);
class_desc->hide();
-
- search_dialog = memnew(ConfirmationDialog);
- add_child(search_dialog);
- VBoxContainer *search_vb = memnew(VBoxContainer);
- search_dialog->add_child(search_vb);
-
- search = memnew(LineEdit);
- search_dialog->register_text_enter(search);
- search_vb->add_margin_child(TTR("Search Text"), search);
- search_dialog->get_ok()->set_text(TTR("Find"));
- search_dialog->connect("confirmed", this, "_search_cbk");
- search_dialog->set_hide_on_ok(false);
}
EditorHelp::~EditorHelp() {
@@ -1964,3 +1931,197 @@ EditorHelpBit::EditorHelpBit() {
rich_text->set_override_selected_font_color(false);
set_custom_minimum_size(Size2(0, 70 * EDSCALE));
}
+
+FindBar::FindBar() {
+
+ container = memnew(Control);
+ add_child(container);
+
+ container->set_clip_contents(true);
+ container->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ hbc = memnew(HBoxContainer);
+ container->add_child(hbc);
+
+ vbc_search_text = memnew(VBoxContainer);
+ hbc->add_child(vbc_search_text);
+ vbc_search_text->set_h_size_flags(SIZE_EXPAND_FILL);
+ hbc->set_anchor_and_margin(MARGIN_RIGHT, 1, 0);
+
+ search_text = memnew(LineEdit);
+ vbc_search_text->add_child(search_text);
+ search_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ search_text->connect("text_changed", this, "_search_text_changed");
+ search_text->connect("text_entered", this, "_search_text_entered");
+
+ find_prev = memnew(ToolButton);
+ hbc->add_child(find_prev);
+ find_prev->set_focus_mode(FOCUS_NONE);
+ find_prev->connect("pressed", this, "_search_prev");
+
+ find_next = memnew(ToolButton);
+ hbc->add_child(find_next);
+ find_next->set_focus_mode(FOCUS_NONE);
+ find_next->connect("pressed", this, "_search_next");
+
+ error_label = memnew(Label);
+ hbc->add_child(error_label);
+ error_label->add_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor"));
+
+ hide_button = memnew(TextureButton);
+ add_child(hide_button);
+ hide_button->set_focus_mode(FOCUS_NONE);
+ hide_button->set_expand(true);
+ hide_button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
+ hide_button->connect("pressed", this, "_hide_pressed");
+}
+
+void FindBar::popup_search() {
+
+ show();
+ bool grabbed_focus = false;
+ if (!search_text->has_focus()) {
+ search_text->grab_focus();
+ grabbed_focus = true;
+ }
+
+ if (!search_text->get_text().empty()) {
+ search_text->select_all();
+ search_text->set_cursor_position(search_text->get_text().length());
+ if (grabbed_focus) {
+ _search();
+ }
+ }
+
+ call_deferred("_update_size");
+}
+
+void FindBar::_update_size() {
+
+ container->set_custom_minimum_size(Size2(0, hbc->get_size().height));
+}
+
+void FindBar::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_READY) {
+
+ find_prev->set_icon(get_icon("MoveUp", "EditorIcons"));
+ find_next->set_icon(get_icon("MoveDown", "EditorIcons"));
+ hide_button->set_normal_texture(get_icon("Close", "EditorIcons"));
+ hide_button->set_hover_texture(get_icon("Close", "EditorIcons"));
+ hide_button->set_pressed_texture(get_icon("Close", "EditorIcons"));
+ hide_button->set_custom_minimum_size(hide_button->get_normal_texture()->get_size());
+ } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+
+ set_process_unhandled_input(is_visible_in_tree());
+ } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
+
+ find_prev->set_icon(get_icon("MoveUp", "EditorIcons"));
+ find_next->set_icon(get_icon("MoveDown", "EditorIcons"));
+ hide_button->set_normal_texture(get_icon("Close", "EditorIcons"));
+ hide_button->set_hover_texture(get_icon("Close", "EditorIcons"));
+ hide_button->set_pressed_texture(get_icon("Close", "EditorIcons"));
+ hide_button->set_custom_minimum_size(hide_button->get_normal_texture()->get_size());
+ }
+}
+
+void FindBar::_bind_methods() {
+
+ ClassDB::bind_method("_unhandled_input", &FindBar::_unhandled_input);
+
+ ClassDB::bind_method("_search_text_changed", &FindBar::_search_text_changed);
+ ClassDB::bind_method("_search_text_entered", &FindBar::_search_text_entered);
+ ClassDB::bind_method("_search_next", &FindBar::search_next);
+ ClassDB::bind_method("_search_prev", &FindBar::search_prev);
+ ClassDB::bind_method("_hide_pressed", &FindBar::_hide_bar);
+ ClassDB::bind_method("_update_size", &FindBar::_update_size);
+
+ ADD_SIGNAL(MethodInfo("search"));
+}
+
+void FindBar::set_rich_text_label(RichTextLabel *p_rich_text_label) {
+
+ rich_text_label = p_rich_text_label;
+}
+
+bool FindBar::search_next() {
+
+ return _search();
+}
+
+bool FindBar::search_prev() {
+
+ return _search(true);
+}
+
+bool FindBar::_search(bool p_search_previous) {
+
+ String stext = search_text->get_text();
+ bool keep = prev_search == stext;
+
+ bool ret = rich_text_label->search(stext, keep, p_search_previous);
+ if (!ret) {
+ ret = rich_text_label->search(stext, false, p_search_previous);
+ }
+
+ prev_search = stext;
+
+ if (ret) {
+ set_error("");
+ } else {
+ set_error(stext.empty() ? "" : TTR("No Matches"));
+ }
+
+ return ret;
+}
+
+void FindBar::set_error(const String &p_label) {
+
+ error_label->set_text(p_label);
+}
+
+void FindBar::_hide_bar() {
+
+ if (search_text->has_focus())
+ rich_text_label->grab_focus();
+
+ hide();
+}
+
+void FindBar::_unhandled_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid()) {
+
+ if (k->is_pressed() && (rich_text_label->has_focus() || hbc->is_a_parent_of(get_focus_owner()))) {
+
+ bool accepted = true;
+
+ switch (k->get_scancode()) {
+
+ case KEY_ESCAPE: {
+
+ _hide_bar();
+ } break;
+ default: {
+
+ accepted = false;
+ } break;
+ }
+
+ if (accepted) {
+ accept_event();
+ }
+ }
+ }
+}
+
+void FindBar::_search_text_changed(const String &p_text) {
+
+ search_next();
+}
+
+void FindBar::_search_text_entered(const String &p_text) {
+
+ search_next();
+}
diff --git a/editor/editor_help.h b/editor/editor_help.h
index 0f93e1b55b..514169dc19 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -125,6 +125,52 @@ public:
EditorHelpIndex();
};
+class FindBar : public HBoxContainer {
+
+ GDCLASS(FindBar, HBoxContainer);
+
+ LineEdit *search_text;
+ ToolButton *find_prev;
+ ToolButton *find_next;
+ Label *error_label;
+ TextureButton *hide_button;
+ String prev_search;
+
+ Control *container;
+ HBoxContainer *hbc;
+ VBoxContainer *vbc_search_text;
+
+ RichTextLabel *rich_text_label;
+
+ void _show_search();
+ void _hide_bar();
+
+ void _search_text_changed(const String &p_text);
+ void _search_text_entered(const String &p_text);
+
+ void _update_size();
+
+protected:
+ void _notification(int p_what);
+ void _unhandled_input(const Ref<InputEvent> &p_event);
+
+ bool _search(bool p_search_previous = false);
+
+ static void _bind_methods();
+
+public:
+ void set_error(const String &p_label);
+
+ void set_rich_text_label(RichTextLabel *p_rich_text_label);
+
+ void popup_search();
+
+ bool search_prev();
+ bool search_next();
+
+ FindBar();
+};
+
class EditorHelp : public VBoxContainer {
GDCLASS(EditorHelp, VBoxContainer);
@@ -161,6 +207,7 @@ class EditorHelp : public VBoxContainer {
ConfirmationDialog *search_dialog;
LineEdit *search;
+ FindBar *find_bar;
String base_path;
@@ -194,7 +241,6 @@ class EditorHelp : public VBoxContainer {
void _request_help(const String &p_string);
void _search(const String &p_str);
- void _search_cbk();
void _unhandled_key_input(const Ref<InputEvent> &p_ev);
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
new file mode 100644
index 0000000000..17f383c8ae
--- /dev/null
+++ b/editor/editor_inspector.cpp
@@ -0,0 +1,2050 @@
+/*************************************************************************/
+/* editor_inspector.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 "editor_inspector.h"
+#include "array_property_edit.h"
+#include "dictionary_property_edit.h"
+#include "editor_node.h"
+#include "editor_scale.h"
+#include "multi_node_edit.h"
+#include "scene/resources/packed_scene.h"
+
+// TODO:
+// arrays and dictionary
+// replace property editor in sectionedpropertyeditor
+
+Size2 EditorProperty::get_minimum_size() const {
+
+ Size2 ms;
+ for (int i = 0; i < get_child_count(); i++) {
+
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c)
+ continue;
+ if (c->is_set_as_toplevel())
+ continue;
+ if (!c->is_visible())
+ continue;
+ if (c == bottom_editor)
+ continue;
+
+ Size2 minsize = c->get_combined_minimum_size();
+ ms.width = MAX(ms.width, minsize.width);
+ ms.height = MAX(ms.height, minsize.height);
+ }
+
+ if (keying) {
+ Ref<Texture> key = get_icon("Key", "EditorIcons");
+ ms.width += key->get_width() + get_constant("hseparator", "Tree");
+ }
+
+ if (checkable) {
+ Ref<Texture> check = get_icon("checked", "CheckBox");
+ ms.width += check->get_width() + get_constant("hseparator", "Tree");
+ }
+
+ if (bottom_editor != NULL) {
+ Ref<Font> font = get_font("font", "Tree");
+ ms.height += font->get_height();
+ ms.height += get_constant("vseparation", "Tree");
+ Size2 bems = bottom_editor->get_combined_minimum_size();
+ bems.width += get_constant("item_margin", "Tree");
+ ms.height += bems.height;
+ ms.width = MAX(ms.width, bems.width);
+ }
+
+ return ms;
+}
+
+void EditorProperty::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_SORT_CHILDREN) {
+
+ Size2 size = get_size();
+ Rect2 rect;
+ Rect2 bottom_rect;
+
+ {
+ int child_room = size.width * (1.0 - split_ratio);
+ Ref<Font> font = get_font("font", "Tree");
+ int height = font->get_height();
+
+ //compute room needed
+ for (int i = 0; i < get_child_count(); i++) {
+
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c)
+ continue;
+ if (c->is_set_as_toplevel())
+ continue;
+ if (c == bottom_editor)
+ continue;
+
+ Size2 minsize = c->get_combined_minimum_size();
+ child_room = MAX(child_room, minsize.width);
+ height = MAX(height, minsize.height);
+ }
+
+ text_size = MAX(0, size.width - child_room + 4 * EDSCALE);
+
+ rect = Rect2(text_size, 0, size.width - text_size, height);
+
+ if (bottom_editor) {
+
+ int m = 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);
+ }
+ }
+
+ if (keying) {
+ Ref<Texture> key;
+
+ if (use_keying_next()) {
+ key = get_icon("KeyNext", "EditorIcons");
+ } else {
+ key = get_icon("Key", "EditorIcons");
+ }
+
+ rect.size.x -= key->get_width() + get_constant("hseparator", "Tree");
+ }
+
+ //set children
+ for (int i = 0; i < get_child_count(); i++) {
+
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c)
+ continue;
+ if (c->is_set_as_toplevel())
+ continue;
+ if (c == bottom_editor)
+ continue;
+
+ fit_child_in_rect(c, rect);
+ }
+
+ if (bottom_editor) {
+ fit_child_in_rect(bottom_editor, bottom_rect);
+ }
+
+ update(); //need to redraw text
+ }
+
+ if (p_what == NOTIFICATION_DRAW) {
+ Ref<Font> font = get_font("font", "Tree");
+
+ Size2 size = get_size();
+ if (bottom_editor) {
+ size.height = bottom_editor->get_margin(MARGIN_TOP);
+ } else if (label_reference) {
+ size.height = label_reference->get_size().height;
+ }
+
+ if (selected) {
+ Ref<StyleBox> sb = get_stylebox("selected", "Tree");
+ draw_style_box(sb, Rect2(Vector2(), size));
+ }
+
+ Color color;
+ if (draw_red) {
+ color = get_color("error_color", "Editor");
+ } else {
+ color = get_color("font_color", "Tree");
+ }
+ if (label.find(".") != -1) {
+ color.a = 0.5; //this should be un-hacked honestly, as it's used for editor overrides
+ }
+
+ int ofs = 0;
+ if (checkable) {
+ Ref<Texture> checkbox;
+ if (checked)
+ checkbox = get_icon("checked", "CheckBox");
+ else
+ checkbox = get_icon("unchecked", "CheckBox");
+
+ Color color(1, 1, 1);
+ if (check_hover) {
+ color.r *= 1.2;
+ color.g *= 1.2;
+ color.b *= 1.2;
+ }
+ check_rect = Rect2(ofs, ((size.height - checkbox->get_height()) / 2), checkbox->get_width(), checkbox->get_height());
+ draw_texture(checkbox, check_rect.position, color);
+ ofs += get_constant("hseparator", "Tree");
+ ofs += checkbox->get_width();
+ } else {
+ check_rect = Rect2();
+ }
+
+ int text_limit = text_size;
+
+ if (can_revert) {
+ Ref<Texture> reload_icon = get_icon("ReloadSmall", "EditorIcons");
+ text_limit -= reload_icon->get_width() + get_constant("hseparator", "Tree") * 2;
+ revert_rect = Rect2(text_limit + get_constant("hseparator", "Tree"), (size.height - reload_icon->get_height()) / 2, reload_icon->get_width(), reload_icon->get_height());
+
+ Color color(1, 1, 1);
+ if (revert_hover) {
+ color.r *= 1.2;
+ color.g *= 1.2;
+ color.b *= 1.2;
+ }
+
+ draw_texture(reload_icon, revert_rect.position, color);
+ } else {
+ revert_rect = Rect2();
+ }
+
+ int v_ofs = (size.height - font->get_height()) / 2;
+ draw_string(font, Point2(ofs, v_ofs + font->get_ascent()), label, color, text_limit);
+
+ if (keying) {
+ Ref<Texture> key;
+
+ if (use_keying_next()) {
+ key = get_icon("KeyNext", "EditorIcons");
+ } else {
+ key = get_icon("Key", "EditorIcons");
+ }
+
+ ofs = size.width - key->get_width() - get_constant("hseparator", "Tree");
+
+ Color color(1, 1, 1);
+ if (keying_hover) {
+ color.r *= 1.2;
+ color.g *= 1.2;
+ color.b *= 1.2;
+ }
+ keying_rect = Rect2(ofs, ((size.height - key->get_height()) / 2), key->get_width(), key->get_height());
+ draw_texture(key, keying_rect.position, color);
+ } else {
+ keying_rect = Rect2();
+ }
+
+ //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);
+ }
+}
+
+void EditorProperty::set_label(const String &p_label) {
+ label = p_label;
+ update();
+}
+
+String EditorProperty::get_label() const {
+ return label;
+}
+
+Object *EditorProperty::get_edited_object() {
+ return object;
+}
+
+StringName EditorProperty::get_edited_property() {
+ return property;
+}
+
+void EditorProperty::update_property() {
+ if (get_script_instance())
+ get_script_instance()->call("update_property");
+}
+
+void EditorProperty::set_read_only(bool p_read_only) {
+ read_only = p_read_only;
+}
+
+bool EditorProperty::is_read_only() const {
+ return read_only;
+}
+
+bool EditorProperty::_might_be_in_instance() {
+
+ if (!object)
+ return false;
+
+ Node *node = Object::cast_to<Node>(object);
+
+ Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+
+ bool might_be = false;
+
+ while (node) {
+
+ if (node->get_scene_instance_state().is_valid()) {
+ might_be = true;
+ break;
+ }
+ if (node == edited_scene) {
+ if (node->get_scene_inherited_state().is_valid()) {
+ might_be = true;
+ break;
+ }
+ might_be = false;
+ break;
+ }
+ node = node->get_owner();
+ }
+
+ return might_be; // or might not be
+}
+
+bool EditorProperty::_get_instanced_node_original_property(const StringName &p_prop, Variant &value) {
+
+ Node *node = Object::cast_to<Node>(object);
+
+ if (!node)
+ return false;
+
+ Node *orig = node;
+
+ Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+
+ bool found = false;
+
+ while (node) {
+
+ Ref<SceneState> ss;
+
+ if (node == edited_scene) {
+ ss = node->get_scene_inherited_state();
+
+ } else {
+ ss = node->get_scene_instance_state();
+ }
+
+ if (ss.is_valid()) {
+
+ NodePath np = node->get_path_to(orig);
+ int node_idx = ss->find_node_by_path(np);
+ if (node_idx >= 0) {
+ bool lfound = false;
+ Variant lvar;
+ lvar = ss->get_property_value(node_idx, p_prop, lfound);
+ if (lfound) {
+
+ found = true;
+ value = lvar;
+ }
+ }
+ }
+ if (node == edited_scene) {
+ //just in case
+ break;
+ }
+ node = node->get_owner();
+ }
+
+ return found;
+}
+
+bool EditorProperty::_is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage) {
+
+ // this is a pretty difficult function, because a property may not be saved but may have
+ // the flag to not save if one or if zero
+
+ {
+ Node *node = Object::cast_to<Node>(object);
+ if (!node)
+ return false;
+
+ Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+ bool found_state = false;
+
+ while (node) {
+
+ Ref<SceneState> ss;
+
+ if (node == edited_scene) {
+ ss = node->get_scene_inherited_state();
+
+ } else {
+ ss = node->get_scene_instance_state();
+ }
+
+ if (ss.is_valid()) {
+ found_state = true;
+ }
+ if (node == edited_scene) {
+ //just in case
+ break;
+ }
+ node = node->get_owner();
+ }
+
+ if (!found_state)
+ return false; //pointless to check if we are not comparing against anything.
+ }
+
+ if (p_orig.get_type() == Variant::NIL) {
+ // not found (was not saved)
+ // check if it was not saved due to being zero or one
+ if (p_current.is_zero() && property_usage & PROPERTY_USAGE_STORE_IF_NONZERO)
+ return false;
+ if (p_current.is_one() && property_usage & PROPERTY_USAGE_STORE_IF_NONONE)
+ return false;
+ }
+
+ if (p_current.get_type() == Variant::REAL && p_orig.get_type() == Variant::REAL) {
+ float a = p_current;
+ float b = p_orig;
+
+ return Math::abs(a - b) > CMP_EPSILON; //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
+ }
+
+ return bool(Variant::evaluate(Variant::OP_NOT_EQUAL, p_current, p_orig));
+}
+
+bool EditorProperty::_is_instanced_node_with_original_property_different() {
+
+ bool mbi = _might_be_in_instance();
+ if (mbi) {
+ Variant vorig;
+ int usage = property_usage & (PROPERTY_USAGE_STORE_IF_NONONE | PROPERTY_USAGE_STORE_IF_NONZERO);
+ if (_get_instanced_node_original_property(property, vorig) || usage) {
+ Variant v = object->get(property);
+
+ if (_is_property_different(v, vorig, usage)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void EditorProperty::update_reload_status() {
+
+ if (property == StringName())
+ return; //no property, so nothing to do
+
+ bool has_reload = false;
+
+ if (_is_instanced_node_with_original_property_different()) {
+ has_reload = true;
+ }
+
+ if (object->call("property_can_revert", property).operator bool()) {
+
+ has_reload = true;
+ }
+
+ if (!has_reload && !object->get_script().is_null()) {
+ Ref<Script> scr = object->get_script();
+ Variant orig_value;
+ if (scr->get_property_default_value(property, orig_value)) {
+ if (orig_value != object->get(property)) {
+ has_reload = true;
+ }
+ }
+ }
+
+ if (has_reload != can_revert) {
+ can_revert = has_reload;
+ update();
+ }
+}
+
+bool EditorProperty::use_keying_next() const {
+ return false;
+}
+void EditorProperty::set_checkable(bool p_checkable) {
+
+ checkable = p_checkable;
+ update();
+ queue_sort();
+}
+
+bool EditorProperty::is_checkable() const {
+
+ return checkable;
+}
+
+void EditorProperty::set_checked(bool p_checked) {
+
+ checked = p_checked;
+ update();
+}
+
+bool EditorProperty::is_checked() const {
+
+ return checked;
+}
+
+void EditorProperty::set_draw_red(bool p_draw_red) {
+
+ draw_red = p_draw_red;
+ update();
+}
+
+void EditorProperty::set_keying(bool p_keying) {
+ keying = p_keying;
+ update();
+ queue_sort();
+}
+
+bool EditorProperty::is_keying() const {
+ return keying;
+}
+
+bool EditorProperty::is_draw_red() const {
+
+ return draw_red;
+}
+
+void EditorProperty::_focusable_focused(int p_index) {
+
+ if (!selectable)
+ return;
+ bool already_selected = selected;
+ selected = true;
+ selected_focusable = p_index;
+ update();
+ if (!already_selected && selected) {
+ emit_signal("selected", property, selected_focusable);
+ }
+}
+
+void EditorProperty::add_focusable(Control *p_control) {
+
+ p_control->connect("focus_entered", this, "_focusable_focused", varray(focusables.size()));
+ focusables.push_back(p_control);
+}
+
+void EditorProperty::select(int p_focusable) {
+
+ bool already_selected = selected;
+
+ if (p_focusable >= 0) {
+ ERR_FAIL_INDEX(p_focusable, focusables.size());
+ focusables[p_focusable]->grab_focus();
+ } else {
+ selected = true;
+ update();
+ }
+
+ if (!already_selected && selected) {
+ emit_signal("selected", property, selected_focusable);
+ }
+}
+
+void EditorProperty::deselect() {
+ selected = false;
+ selected_focusable = -1;
+ update();
+}
+
+bool EditorProperty::is_selected() const {
+ return selected;
+}
+
+void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) {
+
+ if (property == StringName())
+ return;
+
+ Ref<InputEventMouse> me = p_event;
+
+ if (me.is_valid()) {
+
+ bool button_left = me->get_button_mask() & BUTTON_MASK_LEFT;
+
+ bool new_keying_hover = keying_rect.has_point(me->get_position()) && !button_left;
+ if (new_keying_hover != keying_hover) {
+ keying_hover = new_keying_hover;
+ update();
+ }
+
+ bool new_revert_hover = revert_rect.has_point(me->get_position()) && !button_left;
+ if (new_revert_hover != revert_hover) {
+ revert_hover = new_revert_hover;
+ update();
+ }
+
+ bool new_check_hover = check_rect.has_point(me->get_position()) && !button_left;
+ if (new_check_hover != check_hover) {
+ check_hover = new_check_hover;
+ update();
+ }
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ if (!selected && selectable) {
+ selected = true;
+ emit_signal("selected", property, -1);
+ update();
+ }
+
+ if (keying_rect.has_point(mb->get_position())) {
+ emit_signal("property_keyed", property);
+ }
+
+ if (revert_rect.has_point(mb->get_position())) {
+
+ Variant vorig;
+
+ if (_might_be_in_instance() && _get_instanced_node_original_property(property, vorig)) {
+
+ emit_signal("property_changed", property, vorig.duplicate(true));
+ update_property();
+ return;
+ }
+
+ if (object->call("property_can_revert", property).operator bool()) {
+ Variant rev = object->call("property_get_revert", property);
+ emit_signal("property_changed", property, rev);
+ update_property();
+ }
+
+ if (!object->get_script().is_null()) {
+ Ref<Script> scr = object->get_script();
+ Variant orig_value;
+ if (scr->get_property_default_value(property, orig_value)) {
+ emit_signal("property_changed", property, orig_value);
+ update_property();
+ }
+ }
+ }
+ if (check_rect.has_point(mb->get_position())) {
+ checked = !checked;
+ update();
+ emit_signal("property_checked", property, checked);
+ }
+ }
+}
+
+void EditorProperty::set_label_reference(Control *p_control) {
+
+ label_reference = p_control;
+}
+void EditorProperty::set_bottom_editor(Control *p_control) {
+
+ bottom_editor = p_control;
+}
+Variant EditorProperty::get_drag_data(const Point2 &p_point) {
+
+ if (property == StringName())
+ return Variant();
+
+ Dictionary dp;
+ dp["type"] = "obj_property";
+ dp["object"] = object;
+ dp["property"] = property;
+ dp["value"] = object->get(property);
+
+ Label *label = memnew(Label);
+ label->set_text(property);
+ set_drag_preview(label);
+ return dp;
+}
+
+void EditorProperty::set_use_folding(bool p_use_folding) {
+
+ use_folding = p_use_folding;
+}
+
+bool EditorProperty::is_using_folding() const {
+
+ return use_folding;
+}
+
+void EditorProperty::expand_all_folding() {
+}
+
+void EditorProperty::collapse_all_folding() {
+}
+
+void EditorProperty::set_selectable(bool p_selectable) {
+ selectable = p_selectable;
+}
+
+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;
+}
+
+void EditorProperty::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_label", "text"), &EditorProperty::set_label);
+ ClassDB::bind_method(D_METHOD("get_label"), &EditorProperty::get_label);
+
+ ClassDB::bind_method(D_METHOD("set_read_only", "read_only"), &EditorProperty::set_read_only);
+ ClassDB::bind_method(D_METHOD("is_read_only"), &EditorProperty::is_read_only);
+
+ ClassDB::bind_method(D_METHOD("set_checkable", "checkable"), &EditorProperty::set_checkable);
+ ClassDB::bind_method(D_METHOD("is_checkable"), &EditorProperty::is_checkable);
+
+ ClassDB::bind_method(D_METHOD("set_checked", "checked"), &EditorProperty::set_checked);
+ ClassDB::bind_method(D_METHOD("is_checked"), &EditorProperty::is_checked);
+
+ ClassDB::bind_method(D_METHOD("set_draw_red", "draw_red"), &EditorProperty::set_draw_red);
+ ClassDB::bind_method(D_METHOD("is_draw_red"), &EditorProperty::is_draw_red);
+
+ ClassDB::bind_method(D_METHOD("set_keying", "keying"), &EditorProperty::set_keying);
+ ClassDB::bind_method(D_METHOD("is_keying"), &EditorProperty::is_keying);
+
+ ClassDB::bind_method(D_METHOD("get_edited_property"), &EditorProperty::get_edited_property);
+ ClassDB::bind_method(D_METHOD("get_edited_object"), &EditorProperty::get_edited_object);
+
+ ClassDB::bind_method(D_METHOD("_gui_input"), &EditorProperty::_gui_input);
+ ClassDB::bind_method(D_METHOD("_focusable_focused"), &EditorProperty::_focusable_focused);
+
+ 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");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "checked"), "set_checked", "is_checked");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_red"), "set_draw_red", "is_draw_red");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keying"), "set_keying", "is_keying");
+ ADD_SIGNAL(MethodInfo("property_changed", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
+ ADD_SIGNAL(MethodInfo("multiple_properties_changed", PropertyInfo(Variant::POOL_STRING_ARRAY, "properties"), PropertyInfo(Variant::ARRAY, "value")));
+ ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property")));
+ ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
+ ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::STRING, "bool")));
+ ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
+ ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "focusable_idx")));
+
+ MethodInfo vm;
+ vm.name = "update_property";
+ BIND_VMETHOD(vm);
+}
+
+EditorProperty::EditorProperty() {
+
+ split_ratio = 0.5;
+ selectable = true;
+ text_size = 0;
+ read_only = false;
+ checkable = false;
+ checked = false;
+ draw_red = false;
+ keying = false;
+ keying_hover = false;
+ revert_hover = false;
+ check_hover = false;
+ can_revert = false;
+ use_folding = false;
+ property_usage = 0;
+ selected = false;
+ selected_focusable = -1;
+ label_reference = NULL;
+ bottom_editor = NULL;
+}
+////////////////////////////////////////////////
+////////////////////////////////////////////////
+
+void EditorInspectorPlugin::add_custom_control(Control *control) {
+
+ AddedEditor ae;
+ ae.property_editor = control;
+ added_editors.push_back(ae);
+}
+
+void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop) {
+
+ ERR_FAIL_COND(Object::cast_to<EditorProperty>(p_prop) == NULL);
+
+ AddedEditor ae;
+ ae.properties.push_back(p_for_property);
+ ae.property_editor = p_prop;
+ added_editors.push_back(ae);
+}
+
+void EditorInspectorPlugin::add_property_editor_for_multiple_properties(const String &p_label, const Vector<String> &p_properties, Control *p_prop) {
+
+ AddedEditor ae;
+ ae.properties = p_properties;
+ ae.property_editor = p_prop;
+ ae.label = p_label;
+ added_editors.push_back(ae);
+}
+
+bool EditorInspectorPlugin::can_handle(Object *p_object) {
+
+ if (get_script_instance()) {
+ return get_script_instance()->call("can_handle", p_object);
+ }
+ return false;
+}
+void EditorInspectorPlugin::parse_begin(Object *p_object) {
+
+ if (get_script_instance()) {
+ get_script_instance()->call("parse_begin", p_object);
+ }
+}
+
+void EditorInspectorPlugin::parse_category(Object *p_object, const String &p_parse_category) {
+
+ if (get_script_instance()) {
+ get_script_instance()->call("parse_category", p_object, p_parse_category);
+ }
+}
+
+bool EditorInspectorPlugin::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 (get_script_instance()) {
+ Variant arg[6] = {
+ p_object, p_type, p_path, p_hint, p_hint_text, p_usage
+ };
+ const Variant *argptr[6] = {
+ &arg[0], &arg[1], &arg[2], &arg[3], &arg[4], &arg[5]
+ };
+
+ Variant::CallError err;
+ return get_script_instance()->call("parse_property", (const Variant **)&argptr, 6, err);
+ }
+ return false;
+}
+void EditorInspectorPlugin::parse_end() {
+
+ if (get_script_instance()) {
+ get_script_instance()->call("parse_end");
+ }
+}
+
+void EditorInspectorPlugin::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("add_custom_control", "control"), &EditorInspectorPlugin::add_custom_control);
+ ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor"), &EditorInspectorPlugin::add_property_editor);
+ ClassDB::bind_method(D_METHOD("add_property_editor_for_multiple_properties", "label", "properties", "editor"), &EditorInspectorPlugin::add_property_editor_for_multiple_properties);
+
+ MethodInfo vm;
+ vm.name = "can_handle";
+ vm.return_val.type = Variant::BOOL;
+ vm.arguments.push_back(PropertyInfo(Variant::OBJECT, "object"));
+ BIND_VMETHOD(vm);
+ vm.name = "parse_begin";
+ vm.return_val.type = Variant::NIL;
+ BIND_VMETHOD(vm);
+ vm.name = "parse_category";
+ vm.arguments.push_back(PropertyInfo(Variant::STRING, "category"));
+ BIND_VMETHOD(vm);
+ vm.arguments.pop_back();
+ vm.name = "parse_property";
+ vm.return_val.type = Variant::BOOL;
+ vm.arguments.push_back(PropertyInfo(Variant::INT, "type"));
+ vm.arguments.push_back(PropertyInfo(Variant::STRING, "path"));
+ vm.arguments.push_back(PropertyInfo(Variant::INT, "hint"));
+ vm.arguments.push_back(PropertyInfo(Variant::STRING, "hint_text"));
+ vm.arguments.push_back(PropertyInfo(Variant::INT, "usage"));
+ BIND_VMETHOD(vm);
+ vm.arguments.clear();
+ vm.name = "parse_end";
+ vm.return_val.type = Variant::NIL;
+ BIND_VMETHOD(vm);
+}
+
+////////////////////////////////////////////////
+////////////////////////////////////////////////
+
+void EditorInspectorCategory::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_DRAW) {
+
+ draw_rect(Rect2(Vector2(), get_size()), bg_color);
+ Ref<Font> font = get_font("font", "Tree");
+
+ int hs = get_constant("hseparation", "Tree");
+
+ int w = font->get_string_size(label).width;
+ if (icon.is_valid()) {
+ w += hs + icon->get_width();
+ }
+
+ int ofs = (get_size().width - w) / 2;
+
+ if (icon.is_valid()) {
+ draw_texture(icon, Point2(ofs, (get_size().height - icon->get_height()) / 2).floor());
+ ofs += hs + icon->get_width();
+ }
+
+ Color color = get_color("font_color", "Tree");
+ draw_string(font, Point2(ofs, font->get_ascent() + (get_size().height - font->get_height()) / 2).floor(), label, color, get_size().width);
+ }
+}
+
+Size2 EditorInspectorCategory::get_minimum_size() const {
+
+ Ref<Font> font = get_font("font", "Tree");
+
+ Size2 ms;
+ ms.width = 1;
+ ms.height = font->get_height();
+ if (icon.is_valid()) {
+ ms.height = MAX(icon->get_height(), ms.height);
+ }
+ ms.height += get_constant("vseparation", "Tree");
+
+ return ms;
+}
+
+EditorInspectorCategory::EditorInspectorCategory() {
+}
+
+////////////////////////////////////////////////
+////////////////////////////////////////////////
+
+void EditorInspectorSection::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_SORT_CHILDREN) {
+
+ Ref<Font> font = get_font("font", "Tree");
+ Ref<Texture> arrow;
+
+#ifdef TOOLS_ENABLED
+ if (foldable) {
+ if (object->editor_is_section_unfolded(section)) {
+ arrow = get_icon("arrow", "Tree");
+ } else {
+ arrow = get_icon("arrow_collapsed", "Tree");
+ }
+ }
+#endif
+
+ Size2 size = get_size();
+ Point2 offset;
+ offset.y = font->get_height();
+ if (arrow.is_valid()) {
+ offset.y = MAX(offset.y, arrow->get_height());
+ }
+
+ offset.y += get_constant("vseparation", "Tree");
+ offset.x += get_constant("item_margin", "Tree");
+
+ Rect2 rect(offset, size - offset);
+
+ //set children
+ for (int i = 0; i < get_child_count(); i++) {
+
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c)
+ continue;
+ if (c->is_set_as_toplevel())
+ continue;
+ if (!c->is_visible_in_tree())
+ continue;
+
+ fit_child_in_rect(c, rect);
+ }
+
+ update(); //need to redraw text
+ }
+
+ if (p_what == NOTIFICATION_DRAW) {
+
+ Ref<Texture> arrow;
+
+#ifdef TOOLS_ENABLED
+ if (foldable) {
+ if (object->editor_is_section_unfolded(section)) {
+ arrow = get_icon("arrow", "Tree");
+ } else {
+ arrow = get_icon("arrow_collapsed", "Tree");
+ }
+ }
+#endif
+
+ Ref<Font> font = get_font("font", "Tree");
+
+ int h = font->get_height();
+ if (arrow.is_valid()) {
+ h = MAX(h, arrow->get_height());
+ }
+ h += get_constant("vseparation", "Tree");
+
+ draw_rect(Rect2(Vector2(), Vector2(get_size().width, h)), bg_color);
+
+ int hs = get_constant("hseparation", "Tree");
+
+ int ofs = 0;
+ if (arrow.is_valid()) {
+ draw_texture(arrow, Point2(ofs, (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);
+ }
+}
+
+Size2 EditorInspectorSection::get_minimum_size() const {
+
+ Size2 ms;
+ for (int i = 0; i < get_child_count(); i++) {
+
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c)
+ continue;
+ if (c->is_set_as_toplevel())
+ continue;
+ if (!c->is_visible())
+ continue;
+ Size2 minsize = c->get_combined_minimum_size();
+ ms.width = MAX(ms.width, minsize.width);
+ ms.height = MAX(ms.height, minsize.height);
+ }
+
+ Ref<Font> font = get_font("font", "Tree");
+ ms.height += font->get_ascent() + get_constant("vseparation", "Tree");
+ ms.width += get_constant("item_margin", "Tree");
+
+ return ms;
+}
+
+void EditorInspectorSection::setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable) {
+
+ section = p_section;
+ label = p_label;
+ object = p_object;
+ bg_color = p_bg_color;
+ foldable = p_foldable;
+
+#ifdef TOOLS_ENABLED
+ if (foldable) {
+ 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
+}
+
+void EditorInspectorSection::_gui_input(const Ref<InputEvent> &p_event) {
+
+ if (!foldable)
+ return;
+
+#ifdef TOOLS_ENABLED
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ bool unfold = !object->editor_is_section_unfolded(section);
+ object->editor_set_section_unfold(section, unfold);
+ if (unfold) {
+ vbox->show();
+ } else {
+ vbox->hide();
+ }
+ }
+#endif
+}
+
+VBoxContainer *EditorInspectorSection::get_vbox() {
+ return vbox;
+}
+
+void EditorInspectorSection::unfold() {
+
+ if (!foldable)
+ return;
+#ifdef TOOLS_ENABLED
+
+ object->editor_set_section_unfold(section, true);
+ vbox->show();
+ update();
+#endif
+}
+
+void EditorInspectorSection::fold() {
+ if (!foldable)
+ return;
+
+#ifdef TOOLS_ENABLED
+
+ object->editor_set_section_unfold(section, false);
+ vbox->hide();
+ update();
+#endif
+}
+
+void EditorInspectorSection::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("setup", "section", "label", "object", "bg_color", "foldable"), &EditorInspectorSection::setup);
+ ClassDB::bind_method(D_METHOD("get_vbox"), &EditorInspectorSection::get_vbox);
+ ClassDB::bind_method(D_METHOD("unfold"), &EditorInspectorSection::unfold);
+ ClassDB::bind_method(D_METHOD("fold"), &EditorInspectorSection::fold);
+ ClassDB::bind_method(D_METHOD("_gui_input"), &EditorInspectorSection::_gui_input);
+}
+
+EditorInspectorSection::EditorInspectorSection() {
+ object = NULL;
+ foldable = false;
+ vbox = memnew(VBoxContainer);
+ add_child(vbox);
+}
+
+////////////////////////////////////////////////
+////////////////////////////////////////////////
+
+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);
+
+ for (int i = 0; i < inspector_plugin_count; i++) {
+ if (inspector_plugins[i] == p_plugin)
+ return; //already exists
+ }
+ inspector_plugins[inspector_plugin_count++] = p_plugin;
+}
+
+void EditorInspector::remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) {
+
+ ERR_FAIL_COND(inspector_plugin_count == MAX_PLUGINS);
+
+ int idx = -1;
+ for (int i = 0; i < inspector_plugin_count; i++) {
+ if (inspector_plugins[i] == p_plugin) {
+ idx = i;
+ break;
+ }
+ }
+
+ for (int i = idx; i < inspector_plugin_count - 1; i++) {
+ inspector_plugins[i] = inspector_plugins[i + 1];
+ }
+ inspector_plugin_count--;
+}
+
+void EditorInspector::cleanup_plugins() {
+ for (int i = 0; i < inspector_plugin_count; i++) {
+ inspector_plugins[i].unref();
+ }
+ inspector_plugin_count = 0;
+}
+
+void EditorInspector::set_undo_redo(UndoRedo *p_undo_redo) {
+ undo_redo = p_undo_redo;
+}
+
+String EditorInspector::get_selected_path() const {
+
+ return property_selected;
+}
+
+void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<EditorInspectorPlugin> ped) {
+
+ for (List<EditorInspectorPlugin::AddedEditor>::Element *F = ped->added_editors.front(); F; F = F->next()) {
+
+ EditorProperty *ep = Object::cast_to<EditorProperty>(F->get().property_editor);
+ current_vbox->add_child(F->get().property_editor);
+
+ if (ep) {
+
+ ep->object = object;
+ ep->connect("property_changed", this, "_property_changed");
+ ep->connect("property_keyed", this, "_property_keyed");
+ ep->connect("property_keyed_with_value", this, "_property_keyed_with_value");
+ ep->connect("property_checked", this, "_property_checked");
+ ep->connect("selected", this, "_property_selected");
+ ep->connect("multiple_properties_changed", this, "_multiple_properties_changed");
+ ep->connect("resource_selected", this, "_resource_selected", varray(), CONNECT_DEFERRED);
+ ep->connect("object_id_selected", this, "_object_id_selected", varray(), CONNECT_DEFERRED);
+
+ if (F->get().properties.size()) {
+
+ if (F->get().properties.size() == 1) {
+ //since it's one, associate:
+ ep->property = F->get().properties[0];
+ ep->property_usage = 0;
+ }
+
+ if (F->get().label != String()) {
+ ep->set_label(F->get().label);
+ }
+
+ for (int i = 0; i < F->get().properties.size(); i++) {
+ String prop = F->get().properties[i];
+
+ if (!editor_property_map.has(prop)) {
+ editor_property_map[prop] = List<EditorProperty *>();
+ }
+ editor_property_map[prop].push_back(ep);
+ }
+ }
+
+ ep->set_read_only(read_only);
+ ep->update_property();
+ ep->update_reload_status();
+ }
+ }
+ ped->added_editors.clear();
+}
+
+void EditorInspector::update_tree() {
+
+ //to update properly if all is refreshed
+ StringName current_selected = property_selected;
+ int current_focusable = property_focusable;
+
+ _clear();
+
+ if (!object)
+ return;
+
+ List<Ref<EditorInspectorPlugin> > valid_plugins;
+
+ for (int i = inspector_plugin_count - 1; i >= 0; i--) { //start by last, so lastly added can override newly added
+ if (!inspector_plugins[i]->can_handle(object))
+ continue;
+ valid_plugins.push_back(inspector_plugins[i]);
+ }
+
+ bool draw_red = false;
+
+ {
+ Node *nod = Object::cast_to<Node>(object);
+ Node *es = EditorNode::get_singleton()->get_edited_scene();
+ if (nod && es != nod && nod->get_owner() != es) {
+ draw_red = true;
+ }
+ }
+
+ // TreeItem *current_category = NULL;
+
+ String filter = search_box ? search_box->get_text() : "";
+ String group;
+ String group_base;
+
+ List<PropertyInfo> plist;
+ object->get_property_list(&plist, true);
+
+ HashMap<String, VBoxContainer *> item_path;
+ item_path[""] = main_vbox;
+
+ Color sscolor = get_color("prop_subsection", "Editor");
+
+ for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) {
+ Ref<EditorInspectorPlugin> ped = E->get();
+ ped->parse_begin(object);
+ _parse_added_editors(main_vbox, ped);
+ }
+
+ for (List<PropertyInfo>::Element *I = plist.front(); I; I = I->next()) {
+
+ PropertyInfo &p = I->get();
+
+ //make sure the property can be edited
+
+ if (p.usage & PROPERTY_USAGE_GROUP) {
+
+ group = p.name;
+ group_base = p.hint_string;
+
+ continue;
+
+ } else if (p.usage & PROPERTY_USAGE_CATEGORY) {
+
+ group = "";
+ group_base = "";
+
+ if (!show_categories)
+ continue;
+
+ List<PropertyInfo>::Element *N = I->next();
+ bool valid = true;
+ //if no properties in category, skip
+ while (N) {
+ if (N->get().usage & PROPERTY_USAGE_EDITOR)
+ break;
+ if (N->get().usage & PROPERTY_USAGE_CATEGORY) {
+ valid = false;
+ break;
+ }
+ N = N->next();
+ }
+ if (!valid)
+ continue; //empty, ignore
+
+ EditorInspectorCategory *category = memnew(EditorInspectorCategory);
+ main_vbox->add_child(category);
+
+ String type = p.name;
+ if (has_icon(type, "EditorIcons"))
+ category->icon = get_icon(type, "EditorIcons");
+ else
+ category->icon = get_icon("Object", "EditorIcons");
+ category->label = type;
+
+ category->bg_color = get_color("prop_category", "Editor");
+ if (use_doc_hints) {
+ StringName type = p.name;
+ if (!class_descr_cache.has(type)) {
+
+ String descr;
+ DocData *dd = EditorHelp::get_doc_data();
+ Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(type);
+ if (E) {
+ descr = E->get().brief_description;
+ }
+ 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]));
+ }
+
+ for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) {
+ Ref<EditorInspectorPlugin> ped = E->get();
+ ped->parse_category(object, p.name);
+ _parse_added_editors(main_vbox, ped);
+ }
+
+ continue;
+
+ } else if (!(p.usage & PROPERTY_USAGE_EDITOR))
+ continue;
+
+ if (p.name == "script" && (hide_script || bool(object->call("_hide_script_from_inspector")))) {
+ continue;
+ }
+
+ String basename = p.name;
+ if (group != "") {
+ if (group_base != "") {
+ if (basename.begins_with(group_base)) {
+ basename = basename.replace_first(group_base, "");
+ } else if (group_base.begins_with(basename)) {
+ //keep it, this is used pretty often
+ } else {
+ group = ""; //no longer using group base, clear
+ }
+ }
+ }
+
+ if (group != "") {
+ basename = group + "/" + basename;
+ }
+
+ String name = (basename.find("/") != -1) ? basename.right(basename.find_last("/") + 1) : basename;
+
+ if (capitalize_paths) {
+ int dot = name.find(".");
+ if (dot != -1) {
+ String ov = name.right(dot);
+ name = name.substr(0, dot);
+ name = name.camelcase_to_underscore().capitalize();
+ name += ov;
+
+ } else {
+ name = name.camelcase_to_underscore().capitalize();
+ }
+ }
+
+ String path = basename.left(basename.find_last("/"));
+
+ if (use_filter && filter != "") {
+
+ String cat = path;
+
+ if (capitalize_paths)
+ cat = cat.capitalize();
+
+ if (!filter.is_subsequence_ofi(cat) && !filter.is_subsequence_ofi(name))
+ continue;
+ }
+
+ VBoxContainer *current_vbox = main_vbox;
+
+ {
+
+ String acc_path = "";
+ int level = 1;
+ for (int i = 0; i < path.get_slice_count("/"); i++) {
+ String path_name = path.get_slice("/", i);
+ if (i > 0)
+ acc_path += "/";
+ acc_path += path_name;
+ if (!item_path.has(acc_path)) {
+ EditorInspectorSection *section = memnew(EditorInspectorSection);
+ current_vbox->add_child(section);
+ sections.push_back(section);
+
+ if (capitalize_paths)
+ path_name = path_name.capitalize();
+ Color c = sscolor;
+ c.a /= level;
+ section->setup(path_name, acc_path, object, c, use_folding);
+
+ item_path[acc_path] = section->get_vbox();
+ }
+ current_vbox = item_path[acc_path];
+ level = (MIN(level + 1, 4));
+ }
+ }
+
+ bool checkable = false;
+ bool checked = false;
+ if (p.usage & PROPERTY_USAGE_CHECKABLE) {
+ checkable = true;
+ checked = p.usage & PROPERTY_USAGE_CHECKED;
+ }
+
+ String doc_hint;
+
+ if (use_doc_hints) {
+
+ StringName classname = object->get_class_name();
+ StringName propname = p.name;
+ String descr;
+ bool found = false;
+
+ Map<StringName, Map<StringName, String> >::Element *E = descr_cache.find(classname);
+ if (E) {
+ Map<StringName, String>::Element *F = E->get().find(propname);
+ if (F) {
+ found = true;
+ descr = F->get();
+ }
+ }
+
+ if (!found) {
+ DocData *dd = EditorHelp::get_doc_data();
+ Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(classname);
+ while (E && descr == String()) {
+ for (int i = 0; i < E->get().properties.size(); i++) {
+ if (E->get().properties[i].name == propname.operator String()) {
+ descr = E->get().properties[i].description.strip_edges().word_wrap(80);
+ break;
+ }
+ }
+ if (!E->get().inherits.empty()) {
+ E = dd->class_list.find(E->get().inherits);
+ } else {
+ break;
+ }
+ }
+ descr_cache[classname][propname] = descr;
+ }
+
+ doc_hint = descr;
+ }
+
+#if 0
+ if (p.name == selected_property) {
+
+ item->select(1);
+ }
+#endif
+ for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) {
+ Ref<EditorInspectorPlugin> ped = E->get();
+ 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();
+
+ for (List<EditorInspectorPlugin::AddedEditor>::Element *F = editors.front(); F; F = F->next()) {
+
+ EditorProperty *ep = Object::cast_to<EditorProperty>(F->get().property_editor);
+ current_vbox->add_child(F->get().property_editor);
+
+ if (ep) {
+
+ ep->object = object;
+ ep->connect("property_changed", this, "_property_changed");
+ if (p.usage & PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED) {
+ ep->connect("property_changed", this, "_property_changed_update_all", varray(), CONNECT_DEFERRED);
+ }
+ ep->connect("property_keyed", this, "_property_keyed");
+ ep->connect("property_keyed_with_value", this, "_property_keyed_with_value");
+ ep->connect("property_checked", this, "_property_checked");
+ ep->connect("selected", this, "_property_selected");
+ ep->connect("multiple_properties_changed", this, "_multiple_properties_changed");
+ 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);
+ } else {
+ ep->set_tooltip(TTR("Property: ") + p.name);
+ }
+ ep->set_draw_red(draw_red);
+ ep->set_use_folding(use_folding);
+ ep->set_checkable(checkable);
+ ep->set_checked(checked);
+ ep->set_keying(keying);
+
+ if (F->get().properties.size()) {
+
+ if (F->get().properties.size() == 1) {
+ //since it's one, associate:
+ ep->property = F->get().properties[0];
+ ep->property_usage = p.usage;
+ //and set label?
+ }
+
+ if (F->get().label != String()) {
+ ep->set_label(F->get().label);
+ } else {
+ //use existin one
+ ep->set_label(name);
+ }
+ for (int i = 0; i < F->get().properties.size(); i++) {
+ String prop = F->get().properties[i];
+
+ if (!editor_property_map.has(prop)) {
+ editor_property_map[prop] = List<EditorProperty *>();
+ }
+ editor_property_map[prop].push_back(ep);
+ }
+ }
+
+ ep->set_read_only(read_only);
+ ep->update_property();
+ ep->update_reload_status();
+
+ if (current_selected && ep->property == current_selected) {
+ ep->select(current_focusable);
+ }
+ }
+ }
+
+ if (exclusive) {
+ break;
+ }
+ }
+ }
+
+ for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) {
+ Ref<EditorInspectorPlugin> ped = E->get();
+ ped->parse_end();
+ _parse_added_editors(main_vbox, ped);
+ }
+
+ //see if this property exists and should be kept
+}
+void EditorInspector::update_property(const String &p_prop) {
+ if (!editor_property_map.has(p_prop))
+ return;
+
+ for (List<EditorProperty *>::Element *E = editor_property_map[p_prop].front(); E; E = E->next()) {
+ E->get()->update_property();
+ E->get()->update_reload_status();
+ }
+}
+
+void EditorInspector::_clear() {
+
+ while (main_vbox->get_child_count()) {
+ memdelete(main_vbox->get_child(0));
+ }
+ property_selected = StringName();
+ property_focusable = -1;
+ editor_property_map.clear();
+ sections.clear();
+ pending.clear();
+}
+
+void EditorInspector::refresh() {
+
+ if (refresh_countdown > 0 || changing)
+ return;
+ refresh_countdown = EditorSettings::get_singleton()->get("docks/property_editor/auto_refresh_interval");
+}
+
+Object *EditorInspector::get_edited_object() {
+ return object;
+}
+
+void EditorInspector::edit(Object *p_object) {
+ if (object == p_object)
+ return;
+ if (object) {
+
+ _clear();
+ object->remove_change_receptor(this);
+ }
+
+ object = p_object;
+
+ if (object) {
+ object->add_change_receptor(this);
+ update_tree();
+ }
+}
+
+void EditorInspector::set_keying(bool p_active) {
+ if (keying == p_active)
+ return;
+ keying = p_active;
+ update_tree();
+}
+void EditorInspector::set_read_only(bool p_read_only) {
+ read_only = p_read_only;
+ update_tree();
+}
+
+bool EditorInspector::is_capitalize_paths_enabled() const {
+
+ return capitalize_paths;
+}
+void EditorInspector::set_enable_capitalize_paths(bool p_capitalize) {
+ capitalize_paths = p_capitalize;
+ update_tree();
+}
+
+void EditorInspector::set_autoclear(bool p_enable) {
+ autoclear = p_enable;
+}
+
+void EditorInspector::set_show_categories(bool p_show) {
+ show_categories = p_show;
+ update_tree();
+}
+
+void EditorInspector::set_use_doc_hints(bool p_enable) {
+ use_doc_hints = p_enable;
+ update_tree();
+}
+void EditorInspector::set_hide_script(bool p_hide) {
+ hide_script = p_hide;
+ update_tree();
+}
+void EditorInspector::set_use_filter(bool p_use) {
+ use_filter = p_use;
+ update_tree();
+}
+void EditorInspector::register_text_enter(Node *p_line_edit) {
+ search_box = Object::cast_to<LineEdit>(p_line_edit);
+ if (search_box)
+ search_box->connect("text_changed", this, "_filter_changed");
+}
+
+void EditorInspector::_filter_changed(const String &p_text) {
+
+ _clear();
+ update_tree();
+}
+
+void EditorInspector::set_subsection_selectable(bool p_selectable) {
+}
+
+void EditorInspector::set_property_selectable(bool p_selectable) {
+}
+
+void EditorInspector::set_use_folding(bool p_enable) {
+ use_folding = p_enable;
+ update_tree();
+}
+
+bool EditorInspector::is_using_folding() {
+ return use_folding;
+}
+
+void EditorInspector::collapse_all_folding() {
+
+ for (List<EditorInspectorSection *>::Element *E = sections.front(); E; E = E->next()) {
+ E->get()->fold();
+ }
+
+ for (Map<StringName, List<EditorProperty *> >::Element *F = editor_property_map.front(); F; F = F->next()) {
+ for (List<EditorProperty *>::Element *E = F->get().front(); E; E = E->next()) {
+ E->get()->collapse_all_folding();
+ }
+ }
+}
+
+void EditorInspector::expand_all_folding() {
+ for (List<EditorInspectorSection *>::Element *E = sections.front(); E; E = E->next()) {
+ E->get()->unfold();
+ }
+ for (Map<StringName, List<EditorProperty *> >::Element *F = editor_property_map.front(); F; F = F->next()) {
+ for (List<EditorProperty *>::Element *E = F->get().front(); E; E = E->next()) {
+ E->get()->expand_all_folding();
+ }
+ }
+}
+
+void EditorInspector::set_scroll_offset(int p_offset) {
+ set_v_scroll(p_offset);
+}
+
+int EditorInspector::get_scroll_offset() const {
+ return get_v_scroll();
+}
+
+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
+ return;
+
+ if (changing)
+ return;
+
+ if (p_property == String())
+ update_tree_pending = true;
+ else {
+ pending.insert(p_property);
+ }
+}
+
+void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field) {
+
+ if (autoclear && editor_property_map.has(p_name)) {
+ for (List<EditorProperty *>::Element *E = editor_property_map[p_name].front(); E; E = E->next()) {
+ if (E->get()->is_checkable()) {
+ E->get()->set_checked(true);
+ }
+ }
+ }
+
+ if (!undo_redo || Object::cast_to<ArrayPropertyEdit>(object) || Object::cast_to<DictionaryPropertyEdit>(object)) { //kind of hacky
+
+ object->set(p_name, p_value);
+ if (p_refresh_all)
+ _edit_request_change(object, "");
+ else
+ _edit_request_change(object, p_name);
+
+ emit_signal(_prop_edited, p_name);
+
+ } else if (Object::cast_to<MultiNodeEdit>(object)) {
+
+ Object::cast_to<MultiNodeEdit>(object)->set_property_field(p_name, p_value, p_changed_field);
+ _edit_request_change(object, p_name);
+ emit_signal(_prop_edited, p_name);
+ } else {
+
+ undo_redo->create_action(TTR("Set") + " " + p_name, UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_property(object, p_name, p_value);
+ undo_redo->add_undo_property(object, p_name, object->get(p_name));
+
+ if (p_refresh_all) {
+ undo_redo->add_do_method(this, "_edit_request_change", object, "");
+ undo_redo->add_undo_method(this, "_edit_request_change", object, "");
+ } else {
+
+ undo_redo->add_do_method(this, "_edit_request_change", object, p_name);
+ undo_redo->add_undo_method(this, "_edit_request_change", object, p_name);
+ }
+
+ Resource *r = Object::cast_to<Resource>(object);
+ if (r) {
+ if (!r->is_edited() && String(p_name) != "resource/edited") {
+ undo_redo->add_do_method(r, "set_edited", true);
+ undo_redo->add_undo_method(r, "set_edited", false);
+ }
+
+ if (String(p_name) == "resource_local_to_scene") {
+ bool prev = object->get(p_name);
+ bool next = p_value;
+ if (next) {
+ undo_redo->add_do_method(r, "setup_local_to_scene");
+ }
+ if (prev) {
+ undo_redo->add_undo_method(r, "setup_local_to_scene");
+ }
+ }
+ }
+ undo_redo->add_do_method(this, "emit_signal", _prop_edited, p_name);
+ undo_redo->add_undo_method(this, "emit_signal", _prop_edited, p_name);
+ undo_redo->commit_action();
+ }
+
+ if (editor_property_map.has(p_name)) {
+ for (List<EditorProperty *>::Element *E = editor_property_map[p_name].front(); E; E = E->next()) {
+ E->get()->update_reload_status();
+ }
+ }
+}
+
+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--;
+}
+
+void EditorInspector::_property_changed_update_all(const String &p_path, const Variant &p_value) {
+ update_tree();
+}
+
+void EditorInspector::_multiple_properties_changed(Vector<String> p_paths, Array p_values) {
+
+ ERR_FAIL_COND(p_paths.size() == 0 || p_values.size() == 0);
+ ERR_FAIL_COND(p_paths.size() != p_values.size());
+ String names;
+ for (int i = 0; i < p_paths.size(); i++) {
+ if (i > 0)
+ names += ",";
+ names += p_paths[i];
+ }
+ 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, "");
+ }
+ changing++;
+ undo_redo->commit_action();
+ changing--;
+}
+
+void EditorInspector::_property_keyed(const String &p_path) {
+
+ if (!object)
+ return;
+
+ emit_signal("property_keyed", p_path, object->get(p_path), false); //second param is deprecated
+}
+
+void EditorInspector::_property_keyed_with_value(const String &p_path, const Variant &p_value) {
+
+ if (!object)
+ return;
+
+ emit_signal("property_keyed", p_path, p_value, false); //second param is deprecated
+}
+
+void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
+
+ if (!object)
+ return;
+
+ //property checked
+ if (autoclear) {
+
+ if (!p_checked) {
+ object->set(p_path, Variant());
+ } else {
+
+ Variant to_create;
+ List<PropertyInfo> pinfo;
+ object->get_property_list(&pinfo);
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ if (E->get().name == p_path) {
+ Variant::CallError ce;
+ to_create = Variant::construct(E->get().type, NULL, 0, ce);
+ break;
+ }
+ }
+ object->set(p_path, to_create);
+ }
+
+ if (editor_property_map.has(p_path)) {
+ for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) {
+ E->get()->update_property();
+ E->get()->update_reload_status();
+ }
+ }
+
+ } else {
+ emit_signal("property_toggled", p_path, p_checked);
+ }
+}
+
+void EditorInspector::_property_selected(const String &p_path, int p_focusable) {
+
+ property_selected = p_path;
+ property_focusable = p_focusable;
+ //deselect the others
+ for (Map<StringName, List<EditorProperty *> >::Element *F = editor_property_map.front(); F; F = F->next()) {
+ if (F->key() == property_selected)
+ continue;
+ for (List<EditorProperty *>::Element *E = F->get().front(); E; E = E->next()) {
+ if (E->get()->is_selected())
+ E->get()->deselect();
+ }
+ }
+}
+
+void EditorInspector::_object_id_selected(const String &p_path, ObjectID p_id) {
+
+ emit_signal("object_id_selected", p_id);
+}
+
+void EditorInspector::_resource_selected(const String &p_path, RES p_resource) {
+ emit_signal("resource_selected", p_resource, p_path);
+}
+
+void EditorInspector::_node_removed(Node *p_node) {
+
+ if (p_node == object) {
+ edit(NULL);
+ }
+}
+
+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 (p_what == NOTIFICATION_EXIT_TREE) {
+
+ get_tree()->disconnect("node_removed", this, "_node_removed");
+ edit(NULL);
+ }
+
+ if (p_what == NOTIFICATION_PROCESS) {
+
+ if (refresh_countdown > 0) {
+ refresh_countdown -= get_process_delta_time();
+ if (refresh_countdown <= 0) {
+ for (Map<StringName, List<EditorProperty *> >::Element *F = editor_property_map.front(); F; F = F->next()) {
+ for (List<EditorProperty *>::Element *E = F->get().front(); E; E = E->next()) {
+ E->get()->update_property();
+ E->get()->update_reload_status();
+ }
+ }
+ }
+ }
+
+ changing++;
+
+ if (update_tree_pending) {
+
+ update_tree();
+ update_tree_pending = false;
+ pending.clear();
+
+ } else {
+
+ while (pending.size()) {
+ StringName prop = pending.front()->get();
+ if (editor_property_map.has(prop)) {
+ for (List<EditorProperty *>::Element *E = editor_property_map[prop].front(); E; E = E->next()) {
+ E->get()->update_property();
+ E->get()->update_reload_status();
+ }
+ }
+ pending.erase(pending.front());
+ }
+ }
+
+ changing--;
+ }
+
+ if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
+ update_tree();
+ }
+}
+
+void EditorInspector::_changed_callback(Object *p_changed, const char *p_prop) {
+ //this is called when property change is notified via _change_notify()
+ _edit_request_change(p_changed, p_prop);
+}
+
+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_update_all", &EditorInspector::_property_changed_update_all);
+
+ ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change);
+ ClassDB::bind_method("_node_removed", &EditorInspector::_node_removed);
+ ClassDB::bind_method("_filter_changed", &EditorInspector::_filter_changed);
+ ClassDB::bind_method("_property_keyed", &EditorInspector::_property_keyed);
+ ClassDB::bind_method("_property_keyed_with_value", &EditorInspector::_property_keyed_with_value);
+ ClassDB::bind_method("_property_checked", &EditorInspector::_property_checked);
+ 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("refresh", &EditorInspector::refresh);
+
+ 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")));
+}
+
+EditorInspector::EditorInspector() {
+ object = NULL;
+ undo_redo = NULL;
+ main_vbox = memnew(VBoxContainer);
+ main_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_child(main_vbox);
+ set_enable_h_scroll(false);
+ set_enable_v_scroll(true);
+
+ show_categories = false;
+ hide_script = true;
+ use_doc_hints = false;
+ capitalize_paths = false;
+ use_filter = false;
+ autoclear = false;
+ changing = 0;
+ use_folding = false;
+ update_all_pending = false;
+ update_tree_pending = false;
+ refresh_countdown = 0;
+ read_only = false;
+ search_box = NULL;
+ keying = false;
+ _prop_edited = "property_edited";
+ set_process(true);
+ property_focusable = -1;
+}
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
new file mode 100644
index 0000000000..b7a492f114
--- /dev/null
+++ b/editor/editor_inspector.h
@@ -0,0 +1,336 @@
+/*************************************************************************/
+/* editor_inspector.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 EDITOR_INSPECTOR_H
+#define EDITOR_INSPECTOR_H
+
+#include "scene/gui/box_container.h"
+#include "scene/gui/line_edit.h"
+#include "scene/gui/scroll_container.h"
+
+class UndoRedo;
+
+class EditorProperty : public Container {
+
+ GDCLASS(EditorProperty, Container)
+private:
+ String label;
+ int text_size;
+ friend class EditorInspector;
+ Object *object;
+ StringName property;
+
+ int property_usage;
+
+ bool read_only;
+ bool checkable;
+ bool checked;
+ bool draw_red;
+ bool keying;
+
+ Rect2 keying_rect;
+ bool keying_hover;
+ Rect2 revert_rect;
+ bool revert_hover;
+ Rect2 check_rect;
+ bool check_hover;
+
+ bool can_revert;
+
+ bool use_folding;
+
+ bool _might_be_in_instance();
+ bool _is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage);
+ bool _is_instanced_node_with_original_property_different();
+ bool _get_instanced_node_original_property(const StringName &p_prop, Variant &value);
+ void _focusable_focused(int p_index);
+
+ bool selectable;
+ bool selected;
+ int selected_focusable;
+
+ float split_ratio;
+
+ Vector<Control *> focusables;
+ Control *label_reference;
+ Control *bottom_editor;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ void _gui_input(const Ref<InputEvent> &p_event);
+
+public:
+ virtual Size2 get_minimum_size() const;
+
+ void set_label(const String &p_label);
+ String get_label() const;
+
+ void set_read_only(bool p_read_only);
+ bool is_read_only() const;
+
+ Object *get_edited_object();
+ StringName get_edited_property();
+
+ virtual void update_property();
+ void update_reload_status();
+
+ virtual bool use_keying_next() const;
+
+ void set_checkable(bool p_checkable);
+ bool is_checkable() const;
+
+ void set_checked(bool p_checked);
+ bool is_checked() const;
+
+ void set_draw_red(bool p_draw_red);
+ bool is_draw_red() const;
+
+ void set_keying(bool p_keying);
+ bool is_keying() const;
+
+ void add_focusable(Control *p_control);
+ void select(int p_focusable = -1);
+ void deselect();
+ bool is_selected() const;
+
+ void set_label_reference(Control *p_control);
+ void set_bottom_editor(Control *p_editor);
+
+ void set_use_folding(bool p_use_folding);
+ bool is_using_folding() const;
+
+ virtual void expand_all_folding();
+ virtual void collapse_all_folding();
+
+ virtual Variant get_drag_data(const Point2 &p_point);
+
+ 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);
+ EditorProperty();
+};
+
+class EditorInspectorPlugin : public Reference {
+ GDCLASS(EditorInspectorPlugin, Reference)
+
+ friend class EditorInspector;
+ struct AddedEditor {
+ Control *property_editor;
+ Vector<String> properties;
+ String label;
+ };
+
+ List<AddedEditor> added_editors;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void add_custom_control(Control *control);
+ void add_property_editor(const String &p_for_property, Control *p_prop);
+ void add_property_editor_for_multiple_properties(const String &p_label, const Vector<String> &p_properties, Control *p_prop);
+
+ virtual bool can_handle(Object *p_object);
+ virtual void parse_begin(Object *p_object);
+ virtual void parse_category(Object *p_object, const String &p_parse_category);
+ 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 EditorInspectorCategory : public Control {
+ GDCLASS(EditorInspectorCategory, Control);
+
+ friend class EditorInspector;
+ Ref<Texture> icon;
+ String label;
+ Color bg_color;
+
+protected:
+ void _notification(int p_what);
+
+public:
+ virtual Size2 get_minimum_size() const;
+
+ EditorInspectorCategory();
+};
+
+class EditorInspectorSection : public Container {
+ GDCLASS(EditorInspectorSection, Container);
+
+ String label;
+ String section;
+ Object *object;
+ VBoxContainer *vbox;
+ Color bg_color;
+ bool foldable;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+ void _gui_input(const Ref<InputEvent> &p_event);
+
+public:
+ virtual Size2 get_minimum_size() const;
+
+ void setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable);
+ VBoxContainer *get_vbox();
+ void unfold();
+ void fold();
+
+ Object *get_edited_object();
+
+ EditorInspectorSection();
+};
+
+class EditorInspector : public ScrollContainer {
+ GDCLASS(EditorInspector, ScrollContainer);
+
+ UndoRedo *undo_redo;
+ enum {
+ MAX_PLUGINS = 1024
+ };
+ static Ref<EditorInspectorPlugin> inspector_plugins[MAX_PLUGINS];
+ static int inspector_plugin_count;
+
+ VBoxContainer *main_vbox;
+
+ //map use to cache the instanced editors
+ Map<StringName, List<EditorProperty *> > editor_property_map;
+ List<EditorInspectorSection *> sections;
+ Set<StringName> pending;
+
+ void _clear();
+ Object *object;
+
+ //
+
+ LineEdit *search_box;
+ bool show_categories;
+ bool hide_script;
+ bool use_doc_hints;
+ bool capitalize_paths;
+ bool use_filter;
+ bool autoclear;
+ bool use_folding;
+ int changing;
+ bool update_all_pending;
+ bool read_only;
+ bool keying;
+
+ float refresh_countdown;
+ bool update_tree_pending;
+ StringName _prop_edited;
+ StringName property_selected;
+ int property_focusable;
+
+ Map<StringName, Map<StringName, String> > descr_cache;
+ Map<StringName, String> class_descr_cache;
+
+ 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, 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);
+ void _property_keyed_with_value(const String &p_path, const Variant &p_value);
+
+ void _property_checked(const String &p_path, bool p_checked);
+
+ void _resource_selected(const String &p_path, RES p_resource);
+ void _property_selected(const String &p_path, int p_focusable);
+ void _object_id_selected(const String &p_path, ObjectID p_id);
+
+ void _node_removed(Node *p_node);
+
+ void _changed_callback(Object *p_changed, const char *p_prop);
+ void _edit_request_change(Object *p_changed, const String &p_prop);
+
+ void _filter_changed(const String &p_text);
+ void _parse_added_editors(VBoxContainer *current_vbox, Ref<EditorInspectorPlugin> ped);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ static void add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
+ 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;
+
+ void update_tree();
+ void update_property(const String &p_prop);
+
+ void refresh();
+
+ void edit(Object *p_object);
+ Object *get_edited_object();
+
+ void set_keying(bool p_active);
+ void set_read_only(bool p_read_only);
+
+ bool is_capitalize_paths_enabled() const;
+ void set_enable_capitalize_paths(bool p_capitalize);
+ void set_autoclear(bool p_enable);
+
+ void set_show_categories(bool p_show);
+ void set_use_doc_hints(bool p_enable);
+ void set_hide_script(bool p_hide);
+
+ void set_use_filter(bool p_use);
+ void register_text_enter(Node *p_line_edit);
+
+ void set_subsection_selectable(bool p_selectable);
+ void set_property_selectable(bool p_selectable);
+
+ void set_use_folding(bool p_enable);
+ bool is_using_folding();
+
+ void collapse_all_folding();
+ void expand_all_folding();
+
+ void set_scroll_offset(int p_offset);
+ int get_scroll_offset() const;
+
+ EditorInspector();
+};
+
+#endif // INSPECTOR_H
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index fd5a6dffc9..70bc090bc4 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -51,11 +51,11 @@
#include "scene/resources/packed_scene.h"
#include "servers/physics_2d_server.h"
-#include "editor/animation_editor.h"
#include "editor/editor_audio_buses.h"
#include "editor/editor_file_system.h"
#include "editor/editor_help.h"
#include "editor/editor_initialize_ssl.h"
+#include "editor/editor_properties.h"
#include "editor/editor_settings.h"
#include "editor/editor_themes.h"
#include "editor/import/editor_import_collada.h"
@@ -66,7 +66,11 @@
#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_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/baked_lightmap_editor_plugin.h"
@@ -75,6 +79,7 @@
#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"
@@ -87,7 +92,6 @@
#include "editor/plugins/mesh_editor_plugin.h"
#include "editor/plugins/mesh_instance_editor_plugin.h"
#include "editor/plugins/multimesh_editor_plugin.h"
-#include "editor/plugins/navigation_mesh_editor_plugin.h"
#include "editor/plugins/navigation_polygon_editor_plugin.h"
#include "editor/plugins/particles_2d_editor_plugin.h"
#include "editor/plugins/particles_editor_plugin.h"
@@ -96,6 +100,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"
@@ -111,6 +116,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"
@@ -294,7 +300,6 @@ void EditorNode::_notification(int p_what) {
get_tree()->get_root()->set_as_audio_listener_2d(false);
get_tree()->set_auto_accept_quit(false);
get_tree()->connect("files_dropped", this, "_dropped_files");
- property_editable_warning->set_icon(gui_base->get_icon("NodeWarning", "EditorIcons"));
}
if (p_what == NOTIFICATION_EXIT_TREE) {
@@ -323,7 +328,7 @@ void EditorNode::_notification(int p_what) {
}
if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
- scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY));
+ scene_tabs->set_tab_close_display_policy((bool(EDITOR_GET("interface/scene_tabs/always_show_close_button")) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY));
Ref<Theme> theme = create_editor_theme(theme_base->get_theme());
theme_base->set_theme(theme);
@@ -342,8 +347,8 @@ void EditorNode::_notification(int p_what) {
settings_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
help_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
- if (bool(EDITOR_DEF("interface/scene_tabs/resize_if_many_tabs", true))) {
- scene_tabs->set_min_width(int(EDITOR_DEF("interface/scene_tabs/minimum_width", 50)) * EDSCALE);
+ if (EDITOR_GET("interface/scene_tabs/resize_if_many_tabs")) {
+ scene_tabs->set_min_width(int(EDITOR_GET("interface/scene_tabs/minimum_width")) * EDSCALE);
} else {
scene_tabs->set_min_width(0);
}
@@ -381,16 +386,6 @@ void EditorNode::_notification(int p_what) {
distraction_free->set_icon(gui_base->get_icon("DistractionFree", "EditorIcons"));
scene_tab_add->set_icon(gui_base->get_icon("Add", "EditorIcons"));
- resource_new_button->set_icon(gui_base->get_icon("New", "EditorIcons"));
- resource_load_button->set_icon(gui_base->get_icon("Load", "EditorIcons"));
- resource_save_button->set_icon(gui_base->get_icon("Save", "EditorIcons"));
-
- property_back->set_icon(gui_base->get_icon("Back", "EditorIcons"));
- property_forward->set_icon(gui_base->get_icon("Forward", "EditorIcons"));
- editor_history_menu->set_icon(gui_base->get_icon("History", "EditorIcons"));
-
- search_button->set_icon(gui_base->get_icon("Search", "EditorIcons"));
- object_menu->set_icon(gui_base->get_icon("Tools", "EditorIcons"));
// clear_button->set_icon(gui_base->get_icon("Close", "EditorIcons")); don't have access to that node. needs to become a class property
update_menu->set_icon(gui_base->get_icon("Collapse", "EditorIcons"));
dock_tab_move_left->set_icon(theme->get_icon("Back", "EditorIcons"));
@@ -483,7 +478,7 @@ void EditorNode::_fs_changed() {
// come during the export
export_defer.preset = "";
Error err = OK;
- if (!preset->is_runnable() && (export_defer.path.ends_with(".pck") || export_defer.path.ends_with(".zip"))) {
+ if (export_defer.path.ends_with(".pck") || export_defer.path.ends_with(".zip")) {
if (export_defer.path.ends_with(".zip")) {
err = platform->export_zip(preset, export_defer.debug, export_defer.path);
} else if (export_defer.path.ends_with(".pck")) {
@@ -548,8 +543,8 @@ void EditorNode::_vp_resized() {
void EditorNode::_node_renamed() {
- if (property_editor)
- property_editor->update_tree();
+ if (get_inspector())
+ get_inspector()->update_tree();
}
void EditorNode::_editor_select_next() {
@@ -581,42 +576,19 @@ Error EditorNode::load_resource(const String &p_scene) {
RES res = ResourceLoader::load(p_scene);
ERR_FAIL_COND_V(!res.is_valid(), ERR_CANT_OPEN);
- edit_resource(res);
+ inspector_dock->edit_resource(res);
return OK;
}
-void EditorNode::edit_resource(const Ref<Resource> &p_resource) {
-
- _resource_selected(p_resource, "");
-}
-
void EditorNode::edit_node(Node *p_node) {
push_item(p_node);
}
-void EditorNode::open_resource(const String &p_type) {
-
- file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
-
- List<String> extensions;
- ResourceLoader::get_recognized_extensions_for_type(p_type, &extensions);
-
- file->clear_filters();
- for (int i = 0; i < extensions.size(); i++) {
-
- file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
- }
-
- file->popup_centered_ratio();
- current_option = RESOURCE_LOAD;
-}
-
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;
@@ -625,9 +597,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;
}
@@ -713,26 +683,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();
}
}
@@ -740,34 +705,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();
}
}
@@ -1028,10 +988,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;
}
@@ -1059,10 +1016,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;
}
@@ -1070,10 +1024,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);
@@ -1084,8 +1035,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));
@@ -1103,8 +1069,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
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())
@@ -1112,7 +1077,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();
@@ -1144,20 +1108,6 @@ void EditorNode::_mark_unsaved_scenes() {
void EditorNode::_dialog_action(String p_file) {
switch (current_option) {
-
- case RESOURCE_LOAD: {
-
- RES res = ResourceLoader::load(p_file);
- if (res.is_null()) {
-
- current_option = -1;
- accept->get_ok()->set_text("Ugh");
- accept->set_text(TTR("Failed to load resource."));
- return;
- };
-
- push_item(res.operator->());
- } break;
case FILE_NEW_INHERITED_SCENE: {
load_scene(p_file, false, true);
@@ -1210,10 +1160,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;
}
}
@@ -1226,11 +1173,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;
}
@@ -1242,10 +1185,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;
}
@@ -1258,10 +1198,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;
@@ -1348,10 +1285,34 @@ void EditorNode::_dialog_action(String p_file) {
}
}
-void EditorNode::push_item(Object *p_object, const String &p_property) {
+bool EditorNode::item_has_editor(Object *p_object) {
+
+ return editor_data.get_subeditors(p_object).size() > 0;
+}
+
+void EditorNode::edit_item(Object *p_object) {
+
+ Vector<EditorPlugin *> sub_plugins;
+
+ if (p_object) {
+ sub_plugins = editor_data.get_subeditors(p_object);
+ }
+
+ if (!sub_plugins.empty()) {
+ _display_top_editors(false);
+
+ _set_top_editors(sub_plugins);
+ _set_editing_top_editors(p_object);
+ _display_top_editors(true);
+ } else {
+ _hide_top_editors();
+ }
+}
+
+void EditorNode::push_item(Object *p_object, const String &p_property, bool p_inspector_only) {
if (!p_object) {
- property_editor->edit(NULL);
+ get_inspector()->edit(NULL);
node_dock->set_node(NULL);
scene_tree_dock->set_selected(NULL);
return;
@@ -1360,7 +1321,9 @@ void EditorNode::push_item(Object *p_object, const String &p_property) {
uint32_t id = p_object->get_instance_id();
if (id != editor_history.get_current()) {
- if (p_property == "")
+ if (p_inspector_only) {
+ editor_history.add_object_inspector_only(id);
+ } else if (p_property == "")
editor_history.add_object(id);
else
editor_history.add_object(id, p_property);
@@ -1369,89 +1332,6 @@ void EditorNode::push_item(Object *p_object, const String &p_property) {
_edit_current();
}
-void EditorNode::_select_history(int p_idx) {
-
- //push it to the top, it is not correct, but it's more useful
- ObjectID id = editor_history.get_history_obj(p_idx);
- Object *obj = ObjectDB::get_instance(id);
- if (!obj)
- return;
- push_item(obj);
-}
-
-void EditorNode::_prepare_history() {
-
- int history_to = MAX(0, editor_history.get_history_len() - 25);
-
- editor_history_menu->get_popup()->clear();
-
- Ref<Texture> base_icon = gui_base->get_icon("Object", "EditorIcons");
- Set<ObjectID> already;
- for (int i = editor_history.get_history_len() - 1; i >= history_to; i--) {
-
- ObjectID id = editor_history.get_history_obj(i);
- Object *obj = ObjectDB::get_instance(id);
- if (!obj || already.has(id)) {
- if (history_to > 0) {
- history_to--;
- }
- continue;
- }
-
- already.insert(id);
-
- Ref<Texture> icon = gui_base->get_icon("Object", "EditorIcons");
- if (gui_base->has_icon(obj->get_class(), "EditorIcons"))
- icon = gui_base->get_icon(obj->get_class(), "EditorIcons");
- else
- icon = base_icon;
-
- String text;
- if (Object::cast_to<Resource>(obj)) {
- Resource *r = Object::cast_to<Resource>(obj);
- if (r->get_path().is_resource_file())
- text = r->get_path().get_file();
- else if (r->get_name() != String()) {
- text = r->get_name();
- } else {
- text = r->get_class();
- }
- } else if (Object::cast_to<Node>(obj)) {
- text = Object::cast_to<Node>(obj)->get_name();
- } else if (obj->is_class("ScriptEditorDebuggerInspectedObject")) {
- text = obj->call("get_title");
- } else {
- text = obj->get_class();
- }
-
- if (i == editor_history.get_history_pos()) {
- text = "[" + text + "]";
- }
- editor_history_menu->get_popup()->add_icon_item(icon, text, i);
- }
-}
-
-void EditorNode::_property_editor_forward() {
-
- if (editor_history.next())
- _edit_current();
-}
-void EditorNode::_property_editor_back() {
-
- if (editor_history.previous() || editor_history.get_path_size() == 1)
- _edit_current();
-}
-
-void EditorNode::_menu_collapseall() {
-
- property_editor->collapse_all_folding();
-}
-
-void EditorNode::_menu_expandall() {
-
- property_editor->expand_all_folding();
-}
-
void EditorNode::_save_default_environment() {
Ref<Environment> fallback = get_tree()->get_root()->get_world()->get_fallback_environment();
@@ -1459,8 +1339,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());
}
}
@@ -1493,52 +1372,40 @@ static bool overrides_external_editor(Object *p_object) {
return script->get_language()->overrides_external_editor();
}
-void EditorNode::_property_editable_warning_pressed() {
-
- property_editable_warning_dialog->popup_centered_minsize();
-}
-
void EditorNode::_edit_current() {
uint32_t current = editor_history.get_current();
Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
-
- property_back->set_disabled(editor_history.is_at_beginning());
- property_forward->set_disabled(editor_history.is_at_end());
+ bool inspector_only = editor_history.is_current_inspector_only();
this->current = current_obj;
- editor_path->update_path();
-
- String editable_warning; //none by default
- property_editable_warning->hide(); //hide by default
if (!current_obj) {
scene_tree_dock->set_selected(NULL);
- property_editor->edit(NULL);
+ get_inspector()->edit(NULL);
node_dock->set_node(NULL);
- object_menu->set_disabled(true);
+ inspector_dock->update(NULL);
_display_top_editors(false);
return;
}
- object_menu->set_disabled(true);
-
- bool capitalize = bool(EDITOR_DEF("interface/editor/capitalize_properties", true));
+ bool capitalize = bool(EDITOR_GET("interface/inspector/capitalize_properties"));
+ bool disable_folding = bool(EDITOR_GET("interface/inspector/disable_folding"));
bool is_resource = current_obj->is_class("Resource");
bool is_node = current_obj->is_class("Node");
- resource_save_button->set_disabled(!is_resource);
+
+ String editable_warning; //none by default
if (is_resource) {
Resource *current_res = Object::cast_to<Resource>(current_obj);
ERR_FAIL_COND(!current_res);
scene_tree_dock->set_selected(NULL);
- property_editor->edit(current_res);
+ get_inspector()->edit(current_res);
node_dock->set_node(NULL);
- object_menu->set_disabled(false);
EditorNode::get_singleton()->get_import_dock()->set_edit_path(current_res->get_path());
int subr_idx = current_res->get_path().find("::");
@@ -1561,7 +1428,7 @@ void EditorNode::_edit_current() {
Node *current_node = Object::cast_to<Node>(current_obj);
ERR_FAIL_COND(!current_node);
- property_editor->edit(current_node);
+ get_inspector()->edit(current_node);
if (current_node->is_inside_tree()) {
node_dock->set_node(current_node);
scene_tree_dock->set_selected(current_node);
@@ -1569,7 +1436,6 @@ void EditorNode::_edit_current() {
node_dock->set_node(NULL);
scene_tree_dock->set_selected(NULL);
}
- object_menu->get_popup()->clear();
if (get_edited_scene() && get_edited_scene()->get_filename() != String()) {
String source_scene = get_edited_scene()->get_filename();
@@ -1583,145 +1449,83 @@ void EditorNode::_edit_current() {
if (current_obj->is_class("ScriptEditorDebuggerInspectedObject")) {
editable_warning = TTR("This is a remote object so changes to it will not be kept.\nPlease read the documentation relevant to debugging to better understand this workflow.");
capitalize = false;
+ disable_folding = true;
}
- property_editor->edit(current_obj);
+ get_inspector()->edit(current_obj);
node_dock->set_node(NULL);
}
- if (editable_warning != String()) {
- property_editable_warning->show(); //hide by default
- property_editable_warning_dialog->set_text(editable_warning);
+ inspector_dock->set_warning(editable_warning);
+
+ if (get_inspector()->is_capitalize_paths_enabled() != capitalize) {
+ get_inspector()->set_enable_capitalize_paths(capitalize);
}
- if (property_editor->is_capitalize_paths_enabled() != capitalize) {
- property_editor->set_enable_capitalize_paths(capitalize);
+ if (get_inspector()->is_using_folding() == disable_folding) {
+ get_inspector()->set_use_folding(!disable_folding);
}
/* Take care of PLUGIN EDITOR */
- EditorPlugin *main_plugin = editor_data.get_editor(current_obj);
-
- if (main_plugin) {
-
- // special case if use of external editor is true
- if (main_plugin->get_name() == "Script" && (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) {
- if (!changing_scene)
- main_plugin->edit(current_obj);
- }
-
- else if (main_plugin != editor_plugin_screen && (!ScriptEditor::get_singleton() || !ScriptEditor::get_singleton()->is_visible_in_tree() || ScriptEditor::get_singleton()->can_take_away_focus())) {
- // update screen main_plugin
+ if (!inspector_only) {
- if (!changing_scene) {
-
- if (editor_plugin_screen)
- editor_plugin_screen->make_visible(false);
- editor_plugin_screen = main_plugin;
- editor_plugin_screen->edit(current_obj);
-
- editor_plugin_screen->make_visible(true);
-
- int plugin_count = editor_data.get_editor_plugin_count();
- for (int i = 0; i < plugin_count; i++) {
- editor_data.get_editor_plugin(i)->notify_main_screen_changed(editor_plugin_screen->get_name());
- }
+ EditorPlugin *main_plugin = editor_data.get_editor(current_obj);
- for (int i = 0; i < editor_table.size(); i++) {
+ if (main_plugin) {
- main_editor_buttons[i]->set_pressed(editor_table[i] == main_plugin);
- }
+ // special case if use of external editor is true
+ if (main_plugin->get_name() == "Script" && (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) {
+ if (!changing_scene)
+ main_plugin->edit(current_obj);
}
- } else {
+ else if (main_plugin != editor_plugin_screen && (!ScriptEditor::get_singleton() || !ScriptEditor::get_singleton()->is_visible_in_tree() || ScriptEditor::get_singleton()->can_take_away_focus())) {
+ // update screen main_plugin
- editor_plugin_screen->edit(current_obj);
- }
- }
+ if (!changing_scene) {
- Vector<EditorPlugin *> sub_plugins = editor_data.get_subeditors(current_obj);
+ if (editor_plugin_screen)
+ editor_plugin_screen->make_visible(false);
+ editor_plugin_screen = main_plugin;
+ editor_plugin_screen->edit(current_obj);
- if (!sub_plugins.empty()) {
- _display_top_editors(false);
+ editor_plugin_screen->make_visible(true);
- _set_top_editors(sub_plugins);
- _set_editing_top_editors(current_obj);
- _display_top_editors(true);
-
- } else if (!editor_plugins_over->get_plugins_list().empty()) {
-
- _hide_top_editors();
- }
-
- object_menu->set_disabled(false);
-
- PopupMenu *p = object_menu->get_popup();
-
- p->clear();
- p->add_shortcut(ED_SHORTCUT("property_editor/expand_all", TTR("Expand all properties")), EXPAND_ALL);
- p->add_shortcut(ED_SHORTCUT("property_editor/collapse_all", TTR("Collapse all properties")), COLLAPSE_ALL);
- p->add_separator();
- p->add_shortcut(ED_SHORTCUT("property_editor/copy_params", TTR("Copy Params")), OBJECT_COPY_PARAMS);
- p->add_shortcut(ED_SHORTCUT("property_editor/paste_params", TTR("Paste Params")), OBJECT_PASTE_PARAMS);
- p->add_separator();
- p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Paste Resource")), RESOURCE_PASTE);
- if (is_resource) {
- p->add_shortcut(ED_SHORTCUT("property_editor/copy_resource", TTR("Copy Resource")), RESOURCE_COPY);
- p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_UNREF);
- }
-
- if (is_resource || is_node) {
- p->add_separator();
- p->add_shortcut(ED_SHORTCUT("property_editor/make_subresources_unique", TTR("Make Sub-Resources Unique")), OBJECT_UNIQUE_RESOURCES);
- p->add_separator();
- p->add_icon_shortcut(gui_base->get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("property_editor/open_help", TTR("Open in Help")), OBJECT_REQUEST_HELP);
- }
+ int plugin_count = editor_data.get_editor_plugin_count();
+ for (int i = 0; i < plugin_count; i++) {
+ editor_data.get_editor_plugin(i)->notify_main_screen_changed(editor_plugin_screen->get_name());
+ }
- List<MethodInfo> methods;
- current_obj->get_method_list(&methods);
+ for (int i = 0; i < editor_table.size(); i++) {
- if (!methods.empty()) {
+ main_editor_buttons[i]->set_pressed(editor_table[i] == main_plugin);
+ }
+ }
- bool found = false;
- List<MethodInfo>::Element *I = methods.front();
- int i = 0;
- while (I) {
+ } else {
- if (I->get().flags & METHOD_FLAG_EDITOR) {
- if (!found) {
- p->add_separator();
- found = true;
- }
- p->add_item(I->get().name.capitalize(), OBJECT_METHOD_BASE + i);
+ editor_plugin_screen->edit(current_obj);
}
- i++;
- I = I->next();
}
- }
- update_keying();
-}
-
-void EditorNode::_resource_created() {
-
- Object *c = create_dialog->instance_selected();
+ Vector<EditorPlugin *> sub_plugins = editor_data.get_subeditors(current_obj);
- ERR_FAIL_COND(!c);
- Resource *r = Object::cast_to<Resource>(c);
- ERR_FAIL_COND(!r);
+ if (!sub_plugins.empty()) {
+ _display_top_editors(false);
- REF res(r);
-
- push_item(c);
-}
+ _set_top_editors(sub_plugins);
+ _set_editing_top_editors(current_obj);
+ _display_top_editors(true);
-void EditorNode::_resource_selected(const RES &p_res, const String &p_property) {
+ } else if (!editor_plugins_over->get_plugins_list().empty()) {
- if (p_res.is_null())
- return;
+ _hide_top_editors();
+ }
+ }
- RES r = p_res;
- push_item(r.operator->(), p_property);
+ inspector_dock->update(current_obj);
+ inspector_dock->update_keying();
}
void EditorNode::_run(bool p_current, const String &p_custom) {
@@ -1748,10 +1552,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;
}
@@ -1795,7 +1596,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
}
}
- if (bool(EDITOR_DEF("run/auto_save/save_before_running", true))) {
+ if (bool(EDITOR_GET("run/auto_save/save_before_running"))) {
if (unsaved_cache) {
@@ -1805,10 +1606,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;
}
@@ -1822,11 +1620,11 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
if (!call_build())
return;
- if (bool(EDITOR_DEF("run/output/always_clear_output_on_play", true))) {
+ if (bool(EDITOR_GET("run/output/always_clear_output_on_play"))) {
log->clear();
}
- if (bool(EDITOR_DEF("run/output/always_open_output_on_play", true))) {
+ if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) {
make_bottom_panel_item_visible(log);
}
@@ -1839,10 +1637,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;
}
@@ -1960,10 +1755,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;
}
@@ -2026,10 +1818,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;
}
@@ -2049,10 +1838,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;
}
@@ -2077,10 +1863,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 selected node."));
- accept->popup_centered_minsize();
+ show_accept(TTR("This operation can't be done without a selected node."), TTR("I see..."));
break;
}
@@ -2110,25 +1893,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: {
@@ -2162,136 +1949,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
scene_tabs->set_current_tab(cur_idx);
} break;
- case RESOURCE_NEW: {
-
- create_dialog->popup_create(true);
- } break;
- case RESOURCE_LOAD: {
-
- open_resource();
- } break;
- case RESOURCE_SAVE: {
-
- uint32_t current = editor_history.get_current();
- Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
-
- ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj))
-
- RES current_res = RES(Object::cast_to<Resource>(current_obj));
-
- save_resource(current_res);
-
- } break;
- case RESOURCE_SAVE_AS: {
-
- uint32_t current = editor_history.get_current();
- Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
-
- ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj))
-
- RES current_res = RES(Object::cast_to<Resource>(current_obj));
-
- save_resource_as(current_res);
-
- } break;
- case RESOURCE_UNREF: {
-
- uint32_t current = editor_history.get_current();
- Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
-
- ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj))
-
- RES current_res = RES(Object::cast_to<Resource>(current_obj));
- current_res->set_path("");
- _edit_current();
- } break;
- case RESOURCE_COPY: {
-
- uint32_t current = editor_history.get_current();
- Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
-
- ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj))
-
- RES current_res = RES(Object::cast_to<Resource>(current_obj));
-
- EditorSettings::get_singleton()->set_resource_clipboard(current_res);
-
- } break;
- case RESOURCE_PASTE: {
-
- RES r = EditorSettings::get_singleton()->get_resource_clipboard();
- if (r.is_valid()) {
- push_item(EditorSettings::get_singleton()->get_resource_clipboard().ptr(), String());
- }
-
- } break;
- case OBJECT_REQUEST_HELP: {
-
- if (current) {
- _editor_select(EDITOR_SCRIPT);
- emit_signal("request_help", current->get_class());
- }
-
- } break;
- case OBJECT_COPY_PARAMS: {
-
- editor_data.apply_changes_in_editors();
- if (current)
- editor_data.copy_object_params(current);
- } break;
- case OBJECT_PASTE_PARAMS: {
-
- editor_data.apply_changes_in_editors();
- if (current)
- editor_data.paste_object_params(current);
- editor_data.get_undo_redo().clear_history();
- } break;
- case OBJECT_UNIQUE_RESOURCES: {
-
- editor_data.apply_changes_in_editors();
- if (current) {
- List<PropertyInfo> props;
- current->get_property_list(&props);
- Map<RES, RES> duplicates;
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
-
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
- continue;
-
- Variant v = current->get(E->get().name);
- if (v.is_ref()) {
- REF ref = v;
- if (ref.is_valid()) {
-
- RES res = ref;
- if (res.is_valid()) {
-
- if (!duplicates.has(res)) {
- duplicates[res] = res->duplicate();
- }
- res = duplicates[res];
-
- current->set(E->get().name, res);
- }
- }
- }
- }
- }
-
- editor_data.get_undo_redo().clear_history();
-
- _set_editing_top_editors(NULL);
- _set_editing_top_editors(current);
-
- } break;
- case COLLAPSE_ALL: {
- _menu_collapseall();
-
- } break;
- case EXPAND_ALL: {
- _menu_expandall();
-
- } break;
case RUN_PLAY: {
_menu_option_confirm(RUN_STOP, true);
_run(false);
@@ -2325,7 +1982,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
play_custom_scene_button->set_icon(gui_base->get_icon("PlayCustom", "EditorIcons"));
stop_button->set_disabled(true);
- if (bool(EDITOR_DEF("run/output/always_close_output_on_stop", true))) {
+ if (bool(EDITOR_GET("run/output/always_close_output_on_stop"))) {
for (int i = 0; i < bottom_panel_items.size(); i++) {
if (bottom_panel_items[i].control == log) {
_bottom_panel_switch(false, i);
@@ -2345,7 +2002,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
} break;
case RUN_PLAY_NATIVE: {
- bool autosave = EDITOR_DEF("run/auto_save/save_before_running", true);
+ bool autosave = EDITOR_GET("run/auto_save/save_before_running");
if (autosave) {
_menu_option_confirm(FILE_SAVE_ALL_SCENES, false);
}
@@ -2371,10 +2028,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case RUN_PROJECT_MANAGER: {
if (!p_confirmed) {
- bool save_each = EDITOR_DEF("interface/editor/save_each_scene_on_quit", true);
+ bool save_each = EDITOR_GET("interface/editor/save_each_scene_on_quit");
if (_next_unsaved_scene(!save_each) == -1) {
- bool confirm = EDITOR_DEF("interface/editor/quit_confirmation", true);
+ bool confirm = EDITOR_GET("interface/editor/quit_confirmation");
if (confirm) {
confirmation->get_ok()->set_text(p_option == FILE_QUIT ? TTR("Quit") : TTR("Yes"));
@@ -2479,10 +2136,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: {
@@ -2507,7 +2161,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
export_template_manager->popup_manager();
} break;
- case SETTINGS_TOGGLE_FULLSCREN: {
+ case SETTINGS_TOGGLE_FULLSCREEN: {
OS::get_singleton()->set_window_fullscreen(!OS::get_singleton()->is_window_fullscreen());
@@ -2554,22 +2208,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
} break;
default: {
-
- if (p_option >= OBJECT_METHOD_BASE) {
-
- ERR_FAIL_COND(!current);
-
- int idx = p_option - OBJECT_METHOD_BASE;
-
- List<MethodInfo> methods;
- current->get_method_list(&methods);
-
- ERR_FAIL_INDEX(idx, methods.size());
- String name = methods[idx].name;
-
- if (current)
- current->call(name);
- } else if (p_option >= IMPORT_PLUGIN_BASE) {
+ if (p_option >= IMPORT_PLUGIN_BASE) {
}
}
}
@@ -2939,7 +2578,7 @@ Dictionary EditorNode::_get_main_scene_state() {
Dictionary state;
state["main_tab"] = _get_current_main_editor();
state["scene_tree_offset"] = scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value();
- state["property_edit_offset"] = get_property_editor()->get_scene_tree()->get_vscroll_bar()->get_value();
+ state["property_edit_offset"] = get_inspector()->get_scroll_offset();
state["saved_version"] = saved_version;
state["node_filter"] = scene_tree_dock->get_filter();
return state;
@@ -2985,7 +2624,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
if (p_state.has("scene_tree_offset"))
scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->set_value(p_state["scene_tree_offset"]);
if (p_state.has("property_edit_offset"))
- get_property_editor()->get_scene_tree()->get_vscroll_bar()->set_value(p_state["property_edit_offset"]);
+ get_inspector()->set_scroll_offset(p_state["property_edit_offset"]);
if (p_state.has("node_filter"))
scene_tree_dock->set_filter(p_state["node_filter"]);
@@ -3096,10 +2735,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;
}
@@ -3243,44 +2879,14 @@ SceneTreeDock *EditorNode::get_scene_tree_dock() {
return scene_tree_dock;
}
+InspectorDock *EditorNode::get_inspector_dock() {
-void EditorNode::_instance_request(const Vector<String> &p_files) {
-
- request_instance_scenes(p_files);
-}
-
-void EditorNode::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) {
-
- AnimationPlayerEditor::singleton->get_key_editor()->insert_value_key(p_keyed, p_value, p_advance);
+ return inspector_dock;
}
-void EditorNode::_transform_keyed(Object *sp, const String &p_sub, const Transform &p_key) {
-
- Spatial *s = Object::cast_to<Spatial>(sp);
- if (!s)
- return;
- AnimationPlayerEditor::singleton->get_key_editor()->insert_transform_key(s, p_sub, p_key);
-}
-
-void EditorNode::update_keying() {
-
- bool valid = false;
-
- if (AnimationPlayerEditor::singleton->get_key_editor()->has_keying()) {
-
- if (editor_history.get_path_size() >= 1) {
-
- Object *obj = ObjectDB::get_instance(editor_history.get_path_object(0));
- if (Object::cast_to<Node>(obj)) {
-
- valid = true;
- }
- }
- }
-
- property_editor->set_keying(valid);
+void EditorNode::_instance_request(const Vector<String> &p_files) {
- AnimationPlayerEditor::singleton->get_key_editor()->update_keying();
+ request_instance_scenes(p_files);
}
void EditorNode::_close_messages() {
@@ -3425,6 +3031,11 @@ void EditorNode::register_editor_types() {
ClassDB::register_class<EditorExportPlugin>();
ClassDB::register_class<EditorResourceConversionPlugin>();
ClassDB::register_class<EditorSceneImporter>();
+ ClassDB::register_class<EditorInspector>();
+ 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>();
@@ -3539,6 +3150,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);
@@ -4042,7 +3660,7 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String
}
void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
- if (!bool(EDITOR_DEF("interface/scene_tabs/restore_scenes_on_load", false))) {
+ if (!bool(EDITOR_GET("interface/scene_tabs/restore_scenes_on_load"))) {
return;
}
@@ -4136,7 +3754,7 @@ void EditorNode::_scene_tab_script_edited(int p_tab) {
Ref<Script> script = editor_data.get_scene_root_script(p_tab);
if (script.is_valid())
- edit_resource(script);
+ inspector_dock->edit_resource(script);
}
void EditorNode::_scene_tab_closed(int p_tab) {
@@ -4164,7 +3782,7 @@ void EditorNode::_scene_tab_closed(int p_tab) {
}
void EditorNode::_scene_tab_hover(int p_tab) {
- if (bool(EDITOR_DEF("interface/scene_tabs/show_thumbnail_on_hover", true)) == false) {
+ if (bool(EDITOR_GET("interface/scene_tabs/show_thumbnail_on_hover")) == false) {
return;
}
int current_tab = scene_tabs->get_current_tab();
@@ -4234,30 +3852,6 @@ void EditorNode::_scene_tab_changed(int p_tab) {
editor_data.get_undo_redo().commit_action();
}
-void EditorNode::_toggle_search_bar(bool p_pressed) {
-
- property_editor->set_use_filter(p_pressed);
-
- if (p_pressed) {
-
- search_bar->show();
- search_box->grab_focus();
- search_box->select_all();
- } else {
-
- search_bar->hide();
- }
-}
-
-void EditorNode::_clear_search_box() {
-
- if (search_box->get_text() == "")
- return;
-
- search_box->clear();
- property_editor->update_tree();
-}
-
ToolButton *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) {
ToolButton *tb = memnew(ToolButton);
@@ -4267,7 +3861,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;
@@ -4331,7 +3925,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;
@@ -4361,6 +3955,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++) {
@@ -4370,6 +3969,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();
+ }
}
}
@@ -4681,12 +4284,21 @@ EditorBuildCallback EditorNode::build_callbacks[EditorNode::MAX_BUILD_CALLBACKS]
bool EditorNode::call_build() {
- for (int i = 0; i < build_callback_count; i++) {
- if (!build_callbacks[i]())
- return false;
+ bool builds_successful = true;
+
+ for (int i = 0; i < build_callback_count && builds_successful; i++) {
+ if (!build_callbacks[i]()) {
+ ERR_PRINT("A Godot Engine build callback failed.");
+ builds_successful = false;
+ }
}
- return true;
+ if (builds_successful && !editor_data.call_build()) {
+ ERR_PRINT("An EditorPlugin build callback failed.");
+ builds_successful = false;
+ }
+
+ return builds_successful;
}
void EditorNode::_inherit_imported(const String &p_action) {
@@ -4770,15 +4382,23 @@ 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::_bind_methods() {
ClassDB::bind_method("_menu_option", &EditorNode::_menu_option);
ClassDB::bind_method("_tool_menu_option", &EditorNode::_tool_menu_option);
ClassDB::bind_method("_menu_confirm_current", &EditorNode::_menu_confirm_current);
ClassDB::bind_method("_dialog_action", &EditorNode::_dialog_action);
- ClassDB::bind_method("_resource_selected", &EditorNode::_resource_selected, DEFVAL(""));
- ClassDB::bind_method("_property_editor_forward", &EditorNode::_property_editor_forward);
- ClassDB::bind_method("_property_editor_back", &EditorNode::_property_editor_back);
ClassDB::bind_method("_editor_select", &EditorNode::_editor_select);
ClassDB::bind_method("_node_renamed", &EditorNode::_node_renamed);
ClassDB::bind_method("edit_node", &EditorNode::edit_node);
@@ -4788,20 +4408,18 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method("set_edited_scene", &EditorNode::set_edited_scene);
ClassDB::bind_method("open_request", &EditorNode::open_request);
ClassDB::bind_method("_instance_request", &EditorNode::_instance_request);
- ClassDB::bind_method("update_keying", &EditorNode::update_keying);
- ClassDB::bind_method("_property_keyed", &EditorNode::_property_keyed);
- ClassDB::bind_method("_transform_keyed", &EditorNode::_transform_keyed);
ClassDB::bind_method("_close_messages", &EditorNode::_close_messages);
ClassDB::bind_method("_show_messages", &EditorNode::_show_messages);
ClassDB::bind_method("_vp_resized", &EditorNode::_vp_resized);
ClassDB::bind_method("_quick_opened", &EditorNode::_quick_opened);
ClassDB::bind_method("_quick_run", &EditorNode::_quick_run);
- ClassDB::bind_method("_resource_created", &EditorNode::_resource_created);
ClassDB::bind_method("_open_recent_scene", &EditorNode::_open_recent_scene);
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);
@@ -4831,15 +4449,9 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method("_discard_changes", &EditorNode::_discard_changes);
ClassDB::bind_method("_update_recent_scenes", &EditorNode::_update_recent_scenes);
- ClassDB::bind_method("_prepare_history", &EditorNode::_prepare_history);
- ClassDB::bind_method("_select_history", &EditorNode::_select_history);
-
- ClassDB::bind_method("_toggle_search_bar", &EditorNode::_toggle_search_bar);
- ClassDB::bind_method("_clear_search_box", &EditorNode::_clear_search_box);
ClassDB::bind_method("_clear_undo_history", &EditorNode::_clear_undo_history);
ClassDB::bind_method("_dropped_files", &EditorNode::_dropped_files);
ClassDB::bind_method("_toggle_distraction_free_mode", &EditorNode::_toggle_distraction_free_mode);
- ClassDB::bind_method("_property_editable_warning_pressed", &EditorNode::_property_editable_warning_pressed);
ClassDB::bind_method(D_METHOD("get_gui_base"), &EditorNode::get_gui_base);
ClassDB::bind_method(D_METHOD("_bottom_panel_switch"), &EditorNode::_bottom_panel_switch);
@@ -4849,11 +4461,11 @@ 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);
ADD_SIGNAL(MethodInfo("play_pressed"));
ADD_SIGNAL(MethodInfo("pause_pressed"));
ADD_SIGNAL(MethodInfo("stop_pressed"));
- ADD_SIGNAL(MethodInfo("request_help"));
ADD_SIGNAL(MethodInfo("request_help_search"));
ADD_SIGNAL(MethodInfo("request_help_index"));
ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::POOL_STRING_ARRAY, "args")));
@@ -5007,6 +4619,20 @@ EditorNode::EditorNode() {
ResourceFormatImporter::get_singleton()->add_importer(import_bitmap);
}
+ {
+ 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();
editor_selection = memnew(EditorSelection);
@@ -5030,9 +4656,25 @@ 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("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("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_current_inspector", true);
+ EDITOR_DEF("interface/inspector/resources_types_to_open_in_new_inspector", "Material,Mesh");
+ EDITOR_DEF("run/auto_save/save_before_running", true);
theme_base = memnew(Control);
add_child(theme_base);
@@ -5228,7 +4870,11 @@ EditorNode::EditorNode() {
srt->add_child(tabbar_container);
tabbar_container->add_child(scene_tabs);
distraction_free = memnew(ToolButton);
+#ifdef OSX_ENABLED
+ distraction_free->set_shortcut(ED_SHORTCUT("editor/distraction_free_mode", TTR("Distraction Free Mode"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_D));
+#else
distraction_free->set_shortcut(ED_SHORTCUT("editor/distraction_free_mode", TTR("Distraction Free Mode"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F11));
+#endif
distraction_free->set_tooltip(TTR("Toggle distraction-free mode."));
distraction_free->connect("pressed", this, "_toggle_distraction_free_mode");
distraction_free->set_icon(gui_base->get_icon("DistractionFree", "EditorIcons"));
@@ -5335,7 +4981,7 @@ EditorNode::EditorNode() {
p->add_shortcut(ED_SHORTCUT("editor/save_scene_as", TTR("Save Scene As..."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_AS_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/save_all_scenes", TTR("Save all Scenes"), KEY_MASK_ALT + KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_ALL_SCENES);
p->add_separator();
- p->add_shortcut(ED_SHORTCUT("editor/close_scene", TTR("Close Scene"), KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_W), FILE_CLOSE);
+ p->add_shortcut(ED_SHORTCUT("editor/close_scene", TTR("Close Scene"), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_W), FILE_CLOSE);
p->add_separator();
p->add_submenu_item(TTR("Open Recent"), "RecentScenes", FILE_OPEN_RECENT);
p->add_separator();
@@ -5388,7 +5034,7 @@ EditorNode::EditorNode() {
#ifdef OSX_ENABLED
p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_Q);
#else
- p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_Q);
+ p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_Q);
#endif
PanelContainer *editor_region = memnew(PanelContainer);
@@ -5437,7 +5083,11 @@ EditorNode::EditorNode() {
p->add_child(editor_layouts);
editor_layouts->connect("id_pressed", this, "_layout_menu_option");
p->add_submenu_item(TTR("Editor Layout"), "Layouts");
- p->add_shortcut(ED_SHORTCUT("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_SHIFT | KEY_F11), SETTINGS_TOGGLE_FULLSCREN);
+#ifdef OSX_ENABLED
+ p->add_shortcut(ED_SHORTCUT("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_F), SETTINGS_TOGGLE_FULLSCREEN);
+#else
+ p->add_shortcut(ED_SHORTCUT("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_SHIFT | KEY_F11), SETTINGS_TOGGLE_FULLSCREEN);
+#endif
p->add_separator();
p->add_item(TTR("Manage Export Templates"), SETTINGS_MANAGE_EXPORT_TEMPLATES);
@@ -5477,7 +5127,11 @@ EditorNode::EditorNode() {
play_button->set_focus_mode(Control::FOCUS_NONE);
play_button->connect("pressed", this, "_menu_option", make_binds(RUN_PLAY));
play_button->set_tooltip(TTR("Play the project."));
+#ifdef OSX_ENABLED
+ play_button->set_shortcut(ED_SHORTCUT("editor/play", TTR("Play"), KEY_MASK_CMD | KEY_B));
+#else
play_button->set_shortcut(ED_SHORTCUT("editor/play", TTR("Play"), KEY_F5));
+#endif
pause_button = memnew(ToolButton);
pause_button->set_toggle_mode(true);
@@ -5486,7 +5140,11 @@ EditorNode::EditorNode() {
pause_button->set_tooltip(TTR("Pause the scene"));
pause_button->set_disabled(true);
play_hb->add_child(pause_button);
+#ifdef OSX_ENABLED
+ pause_button->set_shortcut(ED_SHORTCUT("editor/pause_scene", TTR("Pause Scene"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_Y));
+#else
pause_button->set_shortcut(ED_SHORTCUT("editor/pause_scene", TTR("Pause Scene"), KEY_F7));
+#endif
stop_button = memnew(ToolButton);
play_hb->add_child(stop_button);
@@ -5495,7 +5153,11 @@ EditorNode::EditorNode() {
stop_button->connect("pressed", this, "_menu_option", make_binds(RUN_STOP));
stop_button->set_tooltip(TTR("Stop the scene."));
stop_button->set_disabled(true);
+#ifdef OSX_ENABLED
+ stop_button->set_shortcut(ED_SHORTCUT("editor/stop", TTR("Stop"), KEY_MASK_CMD | KEY_PERIOD));
+#else
stop_button->set_shortcut(ED_SHORTCUT("editor/stop", TTR("Stop"), KEY_F8));
+#endif
run_native = memnew(EditorRunNative);
play_hb->add_child(run_native);
@@ -5513,7 +5175,11 @@ EditorNode::EditorNode() {
play_scene_button->set_icon(gui_base->get_icon("PlayScene", "EditorIcons"));
play_scene_button->connect("pressed", this, "_menu_option", make_binds(RUN_PLAY_SCENE));
play_scene_button->set_tooltip(TTR("Play the edited scene."));
+#ifdef OSX_ENABLED
+ play_scene_button->set_shortcut(ED_SHORTCUT("editor/play_scene", TTR("Play Scene"), KEY_MASK_CMD | KEY_R));
+#else
play_scene_button->set_shortcut(ED_SHORTCUT("editor/play_scene", TTR("Play Scene"), KEY_F6));
+#endif
play_custom_scene_button = memnew(ToolButton);
play_hb->add_child(play_custom_scene_button);
@@ -5522,7 +5188,11 @@ EditorNode::EditorNode() {
play_custom_scene_button->set_icon(gui_base->get_icon("PlayCustom", "EditorIcons"));
play_custom_scene_button->connect("pressed", this, "_menu_option", make_binds(RUN_PLAY_CUSTOM_SCENE));
play_custom_scene_button->set_tooltip(TTR("Play custom scene"));
+#ifdef OSX_ENABLED
+ play_custom_scene_button->set_shortcut(ED_SHORTCUT("editor/play_custom_scene", TTR("Play Custom Scene"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R));
+#else
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
progress_hb = memnew(BackgroundProgress);
@@ -5557,129 +5227,11 @@ EditorNode::EditorNode() {
dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(scene_tree_dock->get_index(), TTR("Scene"));
dock_slot[DOCK_SLOT_LEFT_BR]->hide();
- VBoxContainer *prop_editor_base = memnew(VBoxContainer);
- prop_editor_base->set_name("Inspector");
- dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(prop_editor_base);
- dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(prop_editor_base->get_index(), TTR("Inspector"));
-
- HBoxContainer *prop_editor_hb = memnew(HBoxContainer);
-
- prop_editor_base->add_child(prop_editor_hb);
- prop_editor_vb = prop_editor_base;
-
- resource_new_button = memnew(ToolButton);
- resource_new_button->set_tooltip(TTR("Create a new resource in memory and edit it."));
- resource_new_button->set_icon(gui_base->get_icon("New", "EditorIcons"));
- prop_editor_hb->add_child(resource_new_button);
- resource_new_button->connect("pressed", this, "_menu_option", varray(RESOURCE_NEW));
- resource_new_button->set_focus_mode(Control::FOCUS_NONE);
-
- resource_load_button = memnew(ToolButton);
- resource_load_button->set_tooltip(TTR("Load an existing resource from disk and edit it."));
- resource_load_button->set_icon(gui_base->get_icon("Load", "EditorIcons"));
- prop_editor_hb->add_child(resource_load_button);
- resource_load_button->connect("pressed", this, "_menu_option", varray(RESOURCE_LOAD));
- resource_load_button->set_focus_mode(Control::FOCUS_NONE);
-
- resource_save_button = memnew(MenuButton);
- resource_save_button->set_tooltip(TTR("Save the currently edited resource."));
- resource_save_button->set_icon(gui_base->get_icon("Save", "EditorIcons"));
- prop_editor_hb->add_child(resource_save_button);
- resource_save_button->get_popup()->add_item(TTR("Save"), RESOURCE_SAVE);
- resource_save_button->get_popup()->add_item(TTR("Save As..."), RESOURCE_SAVE_AS);
- resource_save_button->get_popup()->connect("id_pressed", this, "_menu_option");
- resource_save_button->set_focus_mode(Control::FOCUS_NONE);
- resource_save_button->set_disabled(true);
-
- prop_editor_hb->add_spacer();
-
- property_back = memnew(ToolButton);
- property_back->set_icon(gui_base->get_icon("Back", "EditorIcons"));
- property_back->set_flat(true);
- property_back->set_tooltip(TTR("Go to the previous edited object in history."));
- property_back->set_disabled(true);
-
- prop_editor_hb->add_child(property_back);
-
- property_forward = memnew(ToolButton);
- property_forward->set_icon(gui_base->get_icon("Forward", "EditorIcons"));
- property_forward->set_flat(true);
- property_forward->set_tooltip(TTR("Go to the next edited object in history."));
- property_forward->set_disabled(true);
-
- prop_editor_hb->add_child(property_forward);
-
- editor_history_menu = memnew(MenuButton);
- editor_history_menu->set_tooltip(TTR("History of recently edited objects."));
- editor_history_menu->set_icon(gui_base->get_icon("History", "EditorIcons"));
- prop_editor_hb->add_child(editor_history_menu);
- editor_history_menu->connect("about_to_show", this, "_prepare_history");
- editor_history_menu->get_popup()->connect("id_pressed", this, "_select_history");
-
- prop_editor_hb = memnew(HBoxContainer); //again...
- prop_editor_base->add_child(prop_editor_hb);
-
- editor_path = memnew(EditorPath(&editor_history));
- editor_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- prop_editor_hb->add_child(editor_path);
-
- search_button = memnew(ToolButton);
- search_button->set_toggle_mode(true);
- search_button->set_pressed(false);
- search_button->set_icon(gui_base->get_icon("Search", "EditorIcons"));
- prop_editor_hb->add_child(search_button);
- search_button->connect("toggled", this, "_toggle_search_bar");
-
- object_menu = memnew(MenuButton);
- object_menu->set_icon(gui_base->get_icon("Tools", "EditorIcons"));
- prop_editor_hb->add_child(object_menu);
- object_menu->set_tooltip(TTR("Object properties."));
-
- create_dialog = memnew(CreateDialog);
- gui_base->add_child(create_dialog);
- create_dialog->set_base_type("Resource");
- create_dialog->connect("create", this, "_resource_created");
-
- search_bar = memnew(HBoxContainer);
- search_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- prop_editor_base->add_child(search_bar);
- search_bar->hide();
-
- Label *l = memnew(Label(TTR("Search:") + " "));
- search_bar->add_child(l);
-
- search_box = memnew(LineEdit);
- search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- search_bar->add_child(search_box);
-
- ToolButton *clear_button = memnew(ToolButton);
- clear_button->set_icon(gui_base->get_icon("Close", "EditorIcons"));
- search_bar->add_child(clear_button);
- clear_button->connect("pressed", this, "_clear_search_box");
-
- property_editable_warning = memnew(Button);
- property_editable_warning->set_text(TTR("Changes may be lost!"));
- prop_editor_base->add_child(property_editable_warning);
- property_editable_warning_dialog = memnew(AcceptDialog);
- gui_base->add_child(property_editable_warning_dialog);
- property_editable_warning->hide();
- property_editable_warning->connect("pressed", this, "_property_editable_warning_pressed");
-
- property_editor = memnew(PropertyEditor);
- property_editor->set_autoclear(true);
- property_editor->set_show_categories(true);
- property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- property_editor->set_use_doc_hints(true);
- property_editor->set_hide_script(false);
- property_editor->set_enable_capitalize_paths(bool(EDITOR_DEF("interface/editor/capitalize_properties", true)));
- property_editor->set_use_folding(!bool(EDITOR_DEF("interface/editor/disable_inspector_folding", false)));
-
- property_editor->hide_top_label();
- property_editor->register_text_enter(search_box);
+ inspector_dock = memnew(InspectorDock(this, editor_data));
+ dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(inspector_dock);
+ dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(inspector_dock->get_index(), TTR("Inspector"));
Button *property_editable_warning;
- prop_editor_base->add_child(property_editor);
- property_editor->set_undo_redo(&editor_data.get_undo_redo());
import_dock = memnew(ImportDock);
dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(import_dock);
@@ -5738,6 +5290,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);
@@ -5805,19 +5370,15 @@ EditorNode::EditorNode() {
gui_base->add_child(file_script);
file_script->connect("file_selected", this, "_dialog_action");
- property_forward->connect("pressed", this, "_property_editor_forward");
- property_back->connect("pressed", this, "_property_editor_back");
-
file_menu->get_popup()->connect("id_pressed", this, "_menu_option");
- object_menu->get_popup()->connect("id_pressed", this, "_menu_option");
settings_menu->get_popup()->connect("id_pressed", this, "_menu_option");
file->connect("file_selected", this, "_dialog_action");
file_templates->connect("file_selected", this, "_dialog_action");
- property_editor->connect("resource_selected", this, "_resource_selected");
- property_editor->connect("property_keyed", this, "_property_keyed");
+ preview_gen = memnew(AudioStreamPreviewGenerator);
+ add_child(preview_gen);
//plugin stuff
file_server = memnew(EditorFileServer);
@@ -5846,8 +5407,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(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)));
@@ -5859,6 +5423,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)));
@@ -5882,7 +5447,6 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(TextureEditorPlugin(this)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
- add_editor_plugin(memnew(NavigationMeshEditorPlugin(this)));
add_editor_plugin(memnew(SkeletonEditorPlugin(this)));
add_editor_plugin(memnew(PhysicalBonePlugin(this)));
@@ -5901,10 +5465,10 @@ EditorNode::EditorNode() {
resource_preview->add_preview_generator(Ref<EditorPackedScenePreviewPlugin>(memnew(EditorPackedScenePreviewPlugin)));
resource_preview->add_preview_generator(Ref<EditorMaterialPreviewPlugin>(memnew(EditorMaterialPreviewPlugin)));
resource_preview->add_preview_generator(Ref<EditorScriptPreviewPlugin>(memnew(EditorScriptPreviewPlugin)));
- // FIXME: Needs to be rewritten for AudioStream in Godot 3.0+
- //resource_preview->add_preview_generator( Ref<EditorSamplePreviewPlugin>( memnew(EditorSamplePreviewPlugin )));
+ resource_preview->add_preview_generator(Ref<EditorAudioStreamPreviewPlugin>(memnew(EditorAudioStreamPreviewPlugin)));
resource_preview->add_preview_generator(Ref<EditorMeshPreviewPlugin>(memnew(EditorMeshPreviewPlugin)));
resource_preview->add_preview_generator(Ref<EditorBitmapPreviewPlugin>(memnew(EditorBitmapPreviewPlugin)));
+ resource_preview->add_preview_generator(Ref<EditorFontPreviewPlugin>(memnew(EditorFontPreviewPlugin)));
{
Ref<SpatialMaterialConversionPlugin> spatial_mat_convert;
@@ -6028,10 +5592,17 @@ EditorNode::EditorNode() {
print_handler.userdata = this;
add_print_handler(&print_handler);
+#ifdef OSX_ENABLED
+ ED_SHORTCUT("editor/editor_2d", TTR("Open 2D Editor"), KEY_MASK_ALT | KEY_1);
+ ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_ALT | KEY_2);
+ ED_SHORTCUT("editor/editor_script", TTR("Open Script Editor"), KEY_MASK_ALT | KEY_3);
+ ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_MASK_ALT | KEY_SPACE);
+#else
ED_SHORTCUT("editor/editor_2d", TTR("Open 2D Editor"), KEY_F1);
ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_F2);
ED_SHORTCUT("editor/editor_script", TTR("Open Script Editor"), KEY_F3); //hack neded for script editor F3 search to work :) Assign like this or don't use F3
ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_F4);
+#endif
ED_SHORTCUT("editor/editor_assetlib", TTR("Open Asset Library"));
ED_SHORTCUT("editor/editor_next", TTR("Open the next Editor"));
ED_SHORTCUT("editor/editor_prev", TTR("Open the previous Editor"));
@@ -6039,6 +5610,8 @@ EditorNode::EditorNode() {
EditorNode::~EditorNode() {
+ EditorInspector::cleanup_plugins();
+
remove_print_handler(&print_handler);
memdelete(EditorHelp::get_doc_data());
memdelete(editor_selection);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index f774fa0a2e..7aa060fe14 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -32,14 +32,15 @@
#define EDITOR_NODE_H
#include "core/print_string.h"
+#include "editor/audio_stream_preview.h"
#include "editor/connections_dialog.h"
#include "editor/create_dialog.h"
#include "editor/editor_about.h"
#include "editor/editor_data.h"
#include "editor/editor_export.h"
+#include "editor/editor_inspector.h"
#include "editor/editor_log.h"
#include "editor/editor_name_dialog.h"
-#include "editor/editor_path.h"
#include "editor/editor_plugin.h"
#include "editor/editor_resource_preview.h"
#include "editor/editor_run.h"
@@ -52,6 +53,7 @@
#include "editor/filesystem_dock.h"
#include "editor/groups_editor.h"
#include "editor/import_dock.h"
+#include "editor/inspector_dock.h"
#include "editor/node_dock.h"
#include "editor/pane_drag.h"
#include "editor/progress_dialog.h"
@@ -142,22 +144,10 @@ private:
EDIT_REVERT,
TOOLS_ORPHAN_RESOURCES,
TOOLS_CUSTOM,
- RESOURCE_NEW,
- RESOURCE_LOAD,
RESOURCE_SAVE,
RESOURCE_SAVE_AS,
- RESOURCE_UNREF,
- RESOURCE_COPY,
- RESOURCE_PASTE,
- OBJECT_COPY_PARAMS,
- OBJECT_PASTE_PARAMS,
- OBJECT_UNIQUE_RESOURCES,
- OBJECT_REQUEST_HELP,
RUN_PLAY,
- COLLAPSE_ALL,
- EXPAND_ALL,
-
RUN_STOP,
RUN_PLAY_SCENE,
RUN_PLAY_NATIVE,
@@ -181,7 +171,7 @@ private:
SETTINGS_LAYOUT_DEFAULT,
SETTINGS_MANAGE_EXPORT_TEMPLATES,
SETTINGS_PICK_MAIN_SCENE,
- SETTINGS_TOGGLE_FULLSCREN,
+ SETTINGS_TOGGLE_FULLSCREEN,
SETTINGS_HELP,
SCENE_TAB_CLOSE,
@@ -195,8 +185,6 @@ private:
IMPORT_PLUGIN_BASE = 100,
- OBJECT_METHOD_BASE = 500,
-
TOOL_MENU_BASE = 1000
};
@@ -247,7 +235,6 @@ private:
PopupMenu *tool_menu;
ToolButton *export_button;
ToolButton *prev_scene;
- MenuButton *object_menu;
ToolButton *play_button;
MenuButton *native_play_button;
ToolButton *pause_button;
@@ -264,24 +251,13 @@ private:
Ref<Theme> theme;
PopupMenu *recent_scenes;
- Button *property_back;
- Button *property_forward;
SceneTreeDock *scene_tree_dock;
- PropertyEditor *property_editor;
- Button *property_editable_warning;
- AcceptDialog *property_editable_warning_dialog;
- void _property_editable_warning_pressed();
+ InspectorDock *inspector_dock;
NodeDock *node_dock;
ImportDock *import_dock;
- VBoxContainer *prop_editor_vb;
FileSystemDock *filesystem_dock;
EditorRunNative *run_native;
- HBoxContainer *search_bar;
- LineEdit *search_box;
-
- CreateDialog *create_dialog;
-
ConfirmationDialog *confirmation;
ConfirmationDialog *save_confirmation;
ConfirmationDialog *import_confirmation;
@@ -314,11 +290,6 @@ private:
String defer_export_platform;
bool defer_export_debug;
Node *_last_instanced_scene;
- EditorPath *editor_path;
- ToolButton *resource_new_button;
- ToolButton *resource_load_button;
- MenuButton *resource_save_button;
- MenuButton *editor_history_menu;
EditorLog *log;
CenterContainer *tabs_center;
@@ -329,6 +300,7 @@ private:
Vector<ToolButton *> main_editor_buttons;
Vector<EditorPlugin *> editor_table;
+ AudioStreamPreviewGenerator *preview_gen;
ProgressDialog *progress_dialog;
BackgroundProgress *progress_hb;
@@ -405,7 +377,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;
@@ -422,23 +398,12 @@ private:
void _dialog_display_load_error(String p_file, Error p_error);
int current_option;
- void _resource_created();
- void _resource_selected(const RES &p_res, const String &p_property = "");
void _menu_option(int p_option);
void _menu_confirm_current();
void _menu_option_confirm(int p_option, bool p_confirmed);
void _tool_menu_option(int p_idx);
void _update_debug_options();
- void _property_editor_forward();
- void _property_editor_back();
-
- void _menu_collapseall();
- void _menu_expandall();
-
- void _select_history(int p_idx);
- void _prepare_history();
-
void _fs_changed();
void _resources_reimported(const Vector<String> &p_resources);
void _sources_changed(bool p_exist);
@@ -462,9 +427,6 @@ private:
void _instance_request(const Vector<String> &p_files);
- void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance);
- void _transform_keyed(Object *sp, const String &p_sub, const Transform &p_key);
-
void _hide_top_editors();
void _display_top_editors(bool p_display);
void _set_top_editors(Vector<EditorPlugin *> p_editor_plugins_over);
@@ -578,8 +540,6 @@ private:
void _update_layouts_menu();
void _layout_menu_option(int p_id);
- void _toggle_search_bar(bool p_pressed);
- void _clear_search_box();
void _clear_undo_history();
void _update_addon_config();
@@ -640,8 +600,9 @@ public:
EditorPluginList *get_editor_plugins_over() { return editor_plugins_over; }
EditorPluginList *get_editor_plugins_force_over() { return editor_plugins_force_over; }
EditorPluginList *get_editor_plugins_force_input_forwarding() { return editor_plugins_force_input_forwarding; }
- PropertyEditor *get_property_editor() { return property_editor; }
- VBoxContainer *get_property_editor_vb() { return prop_editor_vb; }
+ 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; }
@@ -663,8 +624,8 @@ public:
bool is_addon_plugin_enabled(const String &p_addon) const;
void edit_node(Node *p_node);
- void edit_resource(const Ref<Resource> &p_resource);
- void open_resource(const String &p_type = "");
+ void edit_resource(const Ref<Resource> &p_resource) { inspector_dock->edit_resource(p_resource); };
+ void open_resource(const String &p_type) { inspector_dock->open_resource(p_type); };
void save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path);
void save_resource(const Ref<Resource> &p_resource);
@@ -678,7 +639,9 @@ public:
static HBoxContainer *get_menu_hb() { return singleton->menu_hb; }
- void push_item(Object *p_object, const String &p_property = "");
+ void push_item(Object *p_object, const String &p_property = "", bool p_inspector_only = false);
+ void edit_item(Object *p_object);
+ bool item_has_editor(Object *p_object);
void open_request(const String &p_path);
@@ -713,6 +676,7 @@ public:
FileSystemDock *get_filesystem_dock();
ImportDock *get_import_dock();
SceneTreeDock *get_scene_tree_dock();
+ InspectorDock *get_inspector_dock();
static UndoRedo *get_undo_redo() { return &singleton->editor_data.get_undo_redo(); }
EditorSelection *get_editor_selection() { return editor_selection; }
@@ -726,6 +690,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);
@@ -759,8 +724,6 @@ public:
void save_layout();
- void update_keying();
-
void open_export_template_manager();
void reload_scene(const String &p_path);
@@ -785,6 +748,10 @@ public:
void dim_editor(bool p_dimming);
+ void edit_current() { _edit_current(); };
+
+ void update_keying() const { inspector_dock->update_keying(); };
+
EditorNode();
~EditorNode();
void get_singleton(const char *arg1, bool arg2);
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 2e4e887165..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) {
@@ -375,13 +376,14 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location, C
} break;
case CONTAINER_PROPERTY_EDITOR_BOTTOM: {
- EditorNode::get_singleton()->get_property_editor_vb()->add_child(p_control);
+ EditorNode::get_singleton()->get_inspector_dock_addon_area()->add_child(p_control);
} break;
}
}
void EditorPlugin::remove_control_from_container(CustomControlContainer p_location, Control *p_control) {
+ ERR_FAIL_NULL(p_control);
switch (p_location) {
@@ -422,7 +424,7 @@ void EditorPlugin::remove_control_from_container(CustomControlContainer p_locati
} break;
case CONTAINER_PROPERTY_EDITOR_BOTTOM: {
- EditorNode::get_singleton()->get_property_editor_vb()->remove_child(p_control);
+ EditorNode::get_singleton()->get_inspector_dock_addon_area()->remove_child(p_control);
} break;
}
@@ -659,6 +661,14 @@ void EditorPlugin::remove_export_plugin(const Ref<EditorExportPlugin> &p_exporte
EditorExport::get_singleton()->remove_export_plugin(p_exporter);
}
+void EditorPlugin::add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) {
+ EditorInspector::add_inspector_plugin(p_plugin);
+}
+
+void EditorPlugin::remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) {
+ EditorInspector::remove_inspector_plugin(p_plugin);
+}
+
void EditorPlugin::add_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer) {
ResourceImporterScene::get_singleton()->add_importer(p_importer);
}
@@ -681,6 +691,15 @@ void EditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) {
}
}
+bool EditorPlugin::build() {
+
+ if (get_script_instance() && get_script_instance()->has_method("build")) {
+ return get_script_instance()->call("build");
+ }
+
+ return true;
+}
+
void EditorPlugin::queue_save_layout() const {
EditorNode::get_singleton()->save_layout();
@@ -700,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);
@@ -728,12 +751,15 @@ void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_import_plugin", "importer"), &EditorPlugin::remove_import_plugin);
ClassDB::bind_method(D_METHOD("add_scene_import_plugin", "scene_importer"), &EditorPlugin::add_scene_import_plugin);
ClassDB::bind_method(D_METHOD("remove_scene_import_plugin", "scene_importer"), &EditorPlugin::remove_scene_import_plugin);
- ClassDB::bind_method(D_METHOD("add_export_plugin", "exporter"), &EditorPlugin::add_export_plugin);
- ClassDB::bind_method(D_METHOD("remove_export_plugin", "exporter"), &EditorPlugin::remove_export_plugin);
+ ClassDB::bind_method(D_METHOD("add_export_plugin", "plugin"), &EditorPlugin::add_export_plugin);
+ ClassDB::bind_method(D_METHOD("remove_export_plugin", "plugin"), &EditorPlugin::remove_export_plugin);
+ ClassDB::bind_method(D_METHOD("add_inspector_plugin", "plugin"), &EditorPlugin::add_inspector_plugin);
+ ClassDB::bind_method(D_METHOD("remove_inspector_plugin", "plugin"), &EditorPlugin::remove_inspector_plugin);
ClassDB::bind_method(D_METHOD("set_input_event_forwarding_always_enabled"), &EditorPlugin::set_input_event_forwarding_always_enabled);
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")));
@@ -757,6 +783,7 @@ void EditorPlugin::_bind_methods() {
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::POOL_STRING_ARRAY, "get_breakpoints"));
ClassDB::add_virtual_method(get_class_static(), MethodInfo("set_window_layout", PropertyInfo(Variant::OBJECT, "layout", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo("get_window_layout", PropertyInfo(Variant::OBJECT, "layout", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile")));
+ ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "build"));
ADD_SIGNAL(MethodInfo("scene_changed", PropertyInfo(Variant::OBJECT, "scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("scene_closed", PropertyInfo(Variant::STRING, "filepath")));
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index ebc4afdfeb..72e21b2f7f 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -31,8 +31,10 @@
#ifndef EDITOR_PLUGIN_H
#define EDITOR_PLUGIN_H
+#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"
@@ -191,8 +193,10 @@ public:
virtual void set_window_layout(Ref<ConfigFile> p_layout);
virtual void get_window_layout(Ref<ConfigFile> p_layout);
virtual void edited_scene_changed() {} // if changes are pending in editor, apply them
+ 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;
@@ -210,6 +214,9 @@ public:
void add_export_plugin(const Ref<EditorExportPlugin> &p_exporter);
void remove_export_plugin(const Ref<EditorExportPlugin> &p_exporter);
+ void add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
+ void remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
+
void add_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer);
void remove_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer);
diff --git a/editor/editor_profiler.cpp b/editor/editor_profiler.cpp
index 34c9ca6630..d4a97b7095 100644
--- a/editor/editor_profiler.cpp
+++ b/editor/editor_profiler.cpp
@@ -68,13 +68,13 @@ void EditorProfiler::add_frame_metric(const Metric &p_metric, bool p_final) {
}
updating_frame = false;
- if (!frame_delay->is_processing()) {
+ if (frame_delay->is_stopped()) {
frame_delay->set_wait_time(p_final ? 0.1 : 1);
frame_delay->start();
}
- if (!plot_delay->is_processing()) {
+ if (plot_delay->is_stopped()) {
plot_delay->set_wait_time(0.1);
plot_delay->start();
}
@@ -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
new file mode 100644
index 0000000000..077b58a40f
--- /dev/null
+++ b/editor/editor_properties.cpp
@@ -0,0 +1,2776 @@
+/*************************************************************************/
+/* editor_properties.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 "editor_properties.h"
+#include "editor/editor_resource_preview.h"
+#include "editor_node.h"
+#include "editor_properties_array_dict.h"
+#include "scene/main/viewport.h"
+
+///////////////////// NULL /////////////////////////
+
+void EditorPropertyNil::update_property() {
+}
+
+EditorPropertyNil::EditorPropertyNil() {
+ Label *label = memnew(Label);
+ label->set_text("[null]");
+ add_child(label);
+}
+
+///////////////////// TEXT /////////////////////////
+void EditorPropertyText::_text_changed(const String &p_string) {
+ if (updating)
+ return;
+
+ emit_signal("property_changed", get_edited_property(), p_string, true);
+}
+
+void EditorPropertyText::update_property() {
+ String s = get_edited_object()->get(get_edited_property());
+ updating = true;
+ text->set_text(s);
+ text->set_editable(!is_read_only());
+ updating = false;
+}
+
+void EditorPropertyText::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_text_changed", "txt"), &EditorPropertyText::_text_changed);
+}
+
+EditorPropertyText::EditorPropertyText() {
+ text = memnew(LineEdit);
+ add_child(text);
+ add_focusable(text);
+ text->connect("text_changed", this, "_text_changed");
+ updating = false;
+}
+
+///////////////////// MULTILINE TEXT /////////////////////////
+
+void EditorPropertyMultilineText::_big_text_changed() {
+ text->set_text(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(), true);
+}
+
+void EditorPropertyMultilineText::_open_big_text() {
+
+ if (!big_text_dialog) {
+ big_text = memnew(TextEdit);
+ big_text->connect("text_changed", this, "_big_text_changed");
+ big_text_dialog = memnew(AcceptDialog);
+ big_text_dialog->add_child(big_text);
+ big_text_dialog->set_title("Edit Text:");
+ add_child(big_text_dialog);
+ }
+
+ big_text->set_text(text->get_text());
+ big_text_dialog->popup_centered_ratio();
+}
+
+void EditorPropertyMultilineText::update_property() {
+ String t = get_edited_object()->get(get_edited_property());
+ text->set_text(t);
+ if (big_text && big_text->is_visible_in_tree()) {
+ big_text->set_text(t);
+ }
+}
+
+void EditorPropertyMultilineText::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_ENTER_TREE: {
+ Ref<Texture> df = get_icon("DistractionFree", "EditorIcons");
+ open_big_text->set_icon(df);
+ Ref<Font> font = get_font("font", "Label");
+ text->set_custom_minimum_size(Vector2(0, font->get_height() * 6));
+
+ } break;
+ }
+}
+
+void EditorPropertyMultilineText::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_text_changed"), &EditorPropertyMultilineText::_text_changed);
+ ClassDB::bind_method(D_METHOD("_big_text_changed"), &EditorPropertyMultilineText::_big_text_changed);
+ ClassDB::bind_method(D_METHOD("_open_big_text"), &EditorPropertyMultilineText::_open_big_text);
+}
+
+EditorPropertyMultilineText::EditorPropertyMultilineText() {
+ HBoxContainer *hb = memnew(HBoxContainer);
+ add_child(hb);
+ set_bottom_editor(hb);
+ text = memnew(TextEdit);
+ text->connect("text_changed", this, "_text_changed");
+ add_focusable(text);
+ hb->add_child(text);
+ text->set_h_size_flags(SIZE_EXPAND_FILL);
+ open_big_text = memnew(ToolButton);
+ open_big_text->connect("pressed", this, "_open_big_text");
+ hb->add_child(open_big_text);
+ big_text_dialog = NULL;
+ big_text = NULL;
+}
+
+///////////////////// TEXT ENUM /////////////////////////
+
+void EditorPropertyTextEnum::_option_selected(int p_which) {
+
+ emit_signal("property_changed", get_edited_property(), options->get_item_text(p_which));
+}
+
+void EditorPropertyTextEnum::update_property() {
+
+ String which = get_edited_object()->get(get_edited_property());
+ for (int i = 0; i < options->get_item_count(); i++) {
+ String t = options->get_item_text(i);
+ if (t == which) {
+ options->select(i);
+ return;
+ }
+ }
+}
+
+void EditorPropertyTextEnum::setup(const Vector<String> &p_options) {
+ for (int i = 0; i < p_options.size(); i++) {
+ options->add_item(p_options[i], i);
+ }
+}
+
+void EditorPropertyTextEnum::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyTextEnum::_option_selected);
+}
+
+EditorPropertyTextEnum::EditorPropertyTextEnum() {
+ options = memnew(OptionButton);
+ options->set_clip_text(true);
+ add_child(options);
+ add_focusable(options);
+ options->connect("item_selected", this, "_option_selected");
+}
+///////////////////// PATH /////////////////////////
+
+void EditorPropertyPath::_path_selected(const String &p_path) {
+
+ emit_signal("property_changed", get_edited_property(), p_path);
+ update_property();
+}
+void EditorPropertyPath::_path_pressed() {
+
+ if (!dialog) {
+ dialog = memnew(EditorFileDialog);
+ dialog->connect("file_selected", this, "_path_selected");
+ dialog->connect("dir_selected", this, "_path_selected");
+ add_child(dialog);
+ }
+
+ String full_path = get_edited_object()->get(get_edited_property());
+
+ dialog->clear_filters();
+
+ if (global) {
+ dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ } else {
+ dialog->set_access(EditorFileDialog::ACCESS_RESOURCES);
+ }
+
+ if (folder) {
+ dialog->set_mode(EditorFileDialog::MODE_OPEN_DIR);
+ dialog->set_current_dir(full_path);
+ } else {
+ dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+ for (int i = 0; i < extensions.size(); i++) {
+ String e = extensions[i].strip_edges();
+ if (e != String()) {
+ dialog->add_filter(extensions[i].strip_edges());
+ }
+ }
+ dialog->set_current_path(full_path);
+ }
+
+ dialog->popup_centered_ratio();
+}
+
+void EditorPropertyPath::update_property() {
+
+ String full_path = get_edited_object()->get(get_edited_property());
+ path->set_text(full_path);
+ path->set_tooltip(full_path);
+}
+
+void EditorPropertyPath::setup(const Vector<String> &p_extensions, bool p_folder, bool p_global) {
+
+ extensions = p_extensions;
+ folder = p_folder;
+ global = p_global;
+}
+
+void EditorPropertyPath::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_path_pressed"), &EditorPropertyPath::_path_pressed);
+ ClassDB::bind_method(D_METHOD("_path_selected"), &EditorPropertyPath::_path_selected);
+}
+
+EditorPropertyPath::EditorPropertyPath() {
+ path = memnew(Button);
+ path->set_clip_text(true);
+ add_child(path);
+ add_focusable(path);
+ dialog = NULL;
+ path->connect("pressed", this, "_path_pressed");
+ folder = false;
+ global = false;
+}
+
+///////////////////// MEMBER /////////////////////////
+
+void EditorPropertyMember::_property_selected(const String &p_selected) {
+
+ emit_signal("property_changed", get_edited_property(), p_selected);
+ update_property();
+}
+
+void EditorPropertyMember::_property_select() {
+
+ if (!selector) {
+ selector = memnew(PropertySelector);
+ selector->connect("selected", this, "_property_selected");
+ add_child(selector);
+ }
+
+ String current = get_edited_object()->get(get_edited_property());
+
+ if (hint == MEMBER_METHOD_OF_VARIANT_TYPE) {
+
+ Variant::Type type = Variant::NIL;
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ if (hint_text == Variant::get_type_name(Variant::Type(i))) {
+ type = Variant::Type(i);
+ }
+ }
+ if (type)
+ selector->select_method_from_basic_type(type, current);
+
+ } else if (hint == MEMBER_METHOD_OF_BASE_TYPE) {
+
+ selector->select_method_from_base_type(hint_text, current);
+
+ } else if (hint == MEMBER_METHOD_OF_INSTANCE) {
+
+ Object *instance = ObjectDB::get_instance(hint_text.to_int64());
+ if (instance)
+ selector->select_method_from_instance(instance, current);
+
+ } else if (hint == MEMBER_METHOD_OF_SCRIPT) {
+
+ Object *obj = ObjectDB::get_instance(hint_text.to_int64());
+ if (Object::cast_to<Script>(obj)) {
+ selector->select_method_from_script(Object::cast_to<Script>(obj), current);
+ }
+
+ } else if (hint == MEMBER_PROPERTY_OF_VARIANT_TYPE) {
+
+ Variant::Type type = Variant::NIL;
+ String tname = hint_text;
+ if (tname.find(".") != -1)
+ tname = tname.get_slice(".", 0);
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ if (tname == Variant::get_type_name(Variant::Type(i))) {
+ type = Variant::Type(Variant::Type(i));
+ }
+ }
+
+ if (type != Variant::NIL)
+ selector->select_property_from_basic_type(type, current);
+
+ } else if (hint == MEMBER_PROPERTY_OF_BASE_TYPE) {
+
+ selector->select_property_from_base_type(hint_text, current);
+
+ } else if (hint == MEMBER_PROPERTY_OF_INSTANCE) {
+
+ Object *instance = ObjectDB::get_instance(hint_text.to_int64());
+ if (instance)
+ selector->select_property_from_instance(instance, current);
+
+ } else if (hint == MEMBER_PROPERTY_OF_SCRIPT) {
+
+ Object *obj = ObjectDB::get_instance(hint_text.to_int64());
+ if (Object::cast_to<Script>(obj)) {
+ selector->select_property_from_script(Object::cast_to<Script>(obj), current);
+ }
+ }
+}
+
+void EditorPropertyMember::setup(Type p_hint, const String &p_hint_text) {
+ hint = p_hint;
+ hint_text = p_hint_text;
+}
+
+void EditorPropertyMember::update_property() {
+
+ String full_path = get_edited_object()->get(get_edited_property());
+ property->set_text(full_path);
+}
+
+void EditorPropertyMember::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_property_selected"), &EditorPropertyMember::_property_selected);
+ ClassDB::bind_method(D_METHOD("_property_select"), &EditorPropertyMember::_property_select);
+}
+
+EditorPropertyMember::EditorPropertyMember() {
+ selector = NULL;
+ property = memnew(Button);
+ property->set_clip_text(true);
+ add_child(property);
+ add_focusable(property);
+ property->connect("pressed", this, "_property_select");
+}
+
+///////////////////// CHECK /////////////////////////
+void EditorPropertyCheck::_checkbox_pressed() {
+
+ emit_signal("property_changed", get_edited_property(), checkbox->is_pressed());
+}
+
+void EditorPropertyCheck::update_property() {
+ bool c = get_edited_object()->get(get_edited_property());
+ checkbox->set_pressed(c);
+ checkbox->set_disabled(is_read_only());
+}
+
+void EditorPropertyCheck::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_checkbox_pressed"), &EditorPropertyCheck::_checkbox_pressed);
+}
+
+EditorPropertyCheck::EditorPropertyCheck() {
+ checkbox = memnew(CheckBox);
+ checkbox->set_text(TTR("On"));
+ add_child(checkbox);
+ add_focusable(checkbox);
+ checkbox->connect("pressed", this, "_checkbox_pressed");
+}
+
+///////////////////// ENUM /////////////////////////
+
+void EditorPropertyEnum::_option_selected(int p_which) {
+
+ emit_signal("property_changed", get_edited_property(), p_which);
+}
+
+void EditorPropertyEnum::update_property() {
+
+ int which = get_edited_object()->get(get_edited_property());
+ options->select(which);
+}
+
+void EditorPropertyEnum::setup(const Vector<String> &p_options) {
+ for (int i = 0; i < p_options.size(); i++) {
+ options->add_item(p_options[i], i);
+ }
+}
+
+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);
+}
+
+EditorPropertyEnum::EditorPropertyEnum() {
+ options = memnew(OptionButton);
+ options->set_clip_text(true);
+ add_child(options);
+ add_focusable(options);
+ options->connect("item_selected", this, "_option_selected");
+}
+
+///////////////////// FLAGS /////////////////////////
+
+void EditorPropertyFlags::_flag_toggled() {
+
+ uint32_t value = 0;
+ for (int i = 0; i < flags.size(); i++) {
+ if (flags[i]->is_pressed()) {
+ uint32_t val = 1;
+ val <<= flag_indices[i];
+ value |= val;
+ }
+ }
+
+ emit_signal("property_changed", get_edited_property(), value);
+}
+
+void EditorPropertyFlags::update_property() {
+
+ uint32_t value = get_edited_object()->get(get_edited_property());
+
+ for (int i = 0; i < flags.size(); i++) {
+ uint32_t val = 1;
+ val <<= flag_indices[i];
+ if (value & val) {
+
+ flags[i]->set_pressed(true);
+ } else {
+ flags[i]->set_pressed(false);
+ }
+ }
+}
+
+void EditorPropertyFlags::setup(const Vector<String> &p_options) {
+ ERR_FAIL_COND(flags.size());
+
+ bool first = true;
+ for (int i = 0; i < p_options.size(); i++) {
+ String option = p_options[i].strip_edges();
+ if (option != "") {
+ CheckBox *cb = memnew(CheckBox);
+ cb->set_text(option);
+ cb->set_clip_text(true);
+ cb->connect("pressed", this, "_flag_toggled");
+ add_focusable(cb);
+ vbox->add_child(cb);
+ flags.push_back(cb);
+ flag_indices.push_back(i);
+ if (first) {
+ set_label_reference(cb);
+ first = false;
+ }
+ }
+ }
+}
+
+void EditorPropertyFlags::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_flag_toggled"), &EditorPropertyFlags::_flag_toggled);
+}
+
+EditorPropertyFlags::EditorPropertyFlags() {
+
+ vbox = memnew(VBoxContainer);
+ add_child(vbox);
+}
+
+///////////////////// LAYERS /////////////////////////
+
+class EditorPropertyLayersGrid : public Control {
+ GDCLASS(EditorPropertyLayersGrid, Control)
+public:
+ uint32_t value;
+ Vector<Rect2> flag_rects;
+ Vector<String> names;
+
+ virtual Size2 get_minimum_size() const {
+ Ref<Font> font = get_font("font", "Label");
+ return Vector2(0, font->get_height() * 2);
+ }
+
+ virtual String get_tooltip(const Point2 &p_pos) const {
+ for (int i = 0; i < flag_rects.size(); i++) {
+ if (i < names.size() && flag_rects[i].has_point(p_pos)) {
+ return names[i];
+ }
+ }
+ return String();
+ }
+ void _gui_input(const Ref<InputEvent> &p_ev) {
+ Ref<InputEventMouseButton> mb = p_ev;
+ if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
+ for (int i = 0; i < flag_rects.size(); i++) {
+ if (flag_rects[i].has_point(mb->get_position())) {
+ //toggle
+ if (value & (1 << i)) {
+ value &= ~(1 << i);
+ } else {
+ value |= (1 << i);
+ }
+ emit_signal("flag_changed", value);
+ update();
+ }
+ }
+ }
+ }
+
+ void _notification(int p_what) {
+ if (p_what == NOTIFICATION_DRAW) {
+
+ Rect2 rect;
+ rect.size = get_size();
+ flag_rects.clear();
+
+ int bsize = (rect.size.height * 80 / 100) / 2;
+
+ int h = bsize * 2 + 1;
+ int vofs = (rect.size.height - h) / 2;
+
+ for (int i = 0; i < 2; i++) {
+
+ Point2 ofs(4, vofs);
+ if (i == 1)
+ ofs.y += bsize + 1;
+
+ ofs += rect.position;
+ for (int j = 0; j < 10; j++) {
+
+ Point2 o = ofs + Point2(j * (bsize + 1), 0);
+ if (j >= 5)
+ o.x += 1;
+
+ uint32_t idx = i * 10 + j;
+ bool on = value & (1 << idx);
+ Rect2 rect = Rect2(o, Size2(bsize, bsize));
+ draw_rect(rect, Color(0, 0, 0, on ? 0.8 : 0.3));
+ flag_rects.push_back(rect);
+ }
+ }
+ }
+ }
+
+ void set_flag(uint32_t p_flag) {
+ value = p_flag;
+ update();
+ }
+
+ static void _bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_gui_input"), &EditorPropertyLayersGrid::_gui_input);
+ ADD_SIGNAL(MethodInfo("flag_changed", PropertyInfo(Variant::INT, "flag")));
+ }
+
+ EditorPropertyLayersGrid() {
+ value = 0;
+ }
+};
+void EditorPropertyLayers::_grid_changed(uint32_t p_grid) {
+
+ emit_signal("property_changed", get_edited_property(), p_grid);
+}
+
+void EditorPropertyLayers::update_property() {
+
+ uint32_t value = get_edited_object()->get(get_edited_property());
+
+ grid->set_flag(value);
+}
+
+void EditorPropertyLayers::setup(LayerType p_layer_type) {
+
+ String basename;
+ switch (p_layer_type) {
+ case LAYER_RENDER_2D:
+ basename = "layer_names/2d_render";
+ break;
+ case LAYER_PHYSICS_2D:
+ basename = "layer_names/2d_physics";
+ break;
+ case LAYER_RENDER_3D:
+ basename = "layer_names/3d_render";
+ break;
+ case LAYER_PHYSICS_3D:
+ basename = "layer_names/3d_physics";
+ break;
+ }
+
+ Vector<String> names;
+ for (int i = 0; i < 20; i++) {
+ String name;
+
+ if (ProjectSettings::get_singleton()->has_setting(basename + "/layer_" + itos(i + 1))) {
+ name = ProjectSettings::get_singleton()->get(basename + "/layer_" + itos(i + 1));
+ }
+
+ if (name == "") {
+ name = TTR("Layer") + " " + itos(i + 1);
+ }
+
+ name += "\n" + vformat(TTR("Bit %d, value %d"), i, 1 << i);
+
+ names.push_back(name);
+ }
+
+ grid->names = names;
+}
+
+void EditorPropertyLayers::_button_pressed() {
+
+ layers->clear();
+ for (int i = 0; i < 20; i++) {
+ if (i == 5 || i == 10 || i == 15) {
+ layers->add_separator();
+ }
+ layers->add_check_item(grid->names[i], i);
+ int idx = layers->get_item_index(i);
+ layers->set_item_checked(idx, grid->value & (1 << i));
+ }
+
+ Rect2 gp = button->get_global_rect();
+ layers->set_as_minsize();
+ Vector2 popup_pos = gp.position - Vector2(layers->get_combined_minimum_size().x, 0);
+ layers->set_global_position(popup_pos);
+ layers->popup();
+}
+
+void EditorPropertyLayers::_menu_pressed(int p_menu) {
+ if (grid->value & (1 << p_menu)) {
+ grid->value &= ~(1 << p_menu);
+ } else {
+ grid->value |= (1 << p_menu);
+ }
+ grid->update();
+ _grid_changed(grid->value);
+}
+
+void EditorPropertyLayers::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_grid_changed"), &EditorPropertyLayers::_grid_changed);
+ ClassDB::bind_method(D_METHOD("_button_pressed"), &EditorPropertyLayers::_button_pressed);
+ ClassDB::bind_method(D_METHOD("_menu_pressed"), &EditorPropertyLayers::_menu_pressed);
+}
+
+EditorPropertyLayers::EditorPropertyLayers() {
+
+ HBoxContainer *hb = memnew(HBoxContainer);
+ add_child(hb);
+ grid = memnew(EditorPropertyLayersGrid);
+ grid->connect("flag_changed", this, "_grid_changed");
+ grid->set_h_size_flags(SIZE_EXPAND_FILL);
+ hb->add_child(grid);
+ button = memnew(Button);
+ button->set_text("..");
+ button->connect("pressed", this, "_button_pressed");
+ hb->add_child(button);
+ set_bottom_editor(hb);
+ layers = memnew(PopupMenu);
+ add_child(layers);
+ layers->connect("id_pressed", this, "_menu_pressed");
+}
+///////////////////// INT /////////////////////////
+
+void EditorPropertyInteger::_value_changed(double val) {
+ if (setting)
+ return;
+ emit_signal("property_changed", get_edited_property(), int(val));
+}
+
+void EditorPropertyInteger::update_property() {
+ int val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin->set_value(val);
+ setting = false;
+}
+
+void EditorPropertyInteger::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyInteger::_value_changed);
+}
+
+void EditorPropertyInteger::setup(int p_min, int p_max, bool p_allow_greater, bool p_allow_lesser) {
+ spin->set_min(p_min);
+ spin->set_max(p_max);
+ spin->set_step(1);
+ spin->set_allow_greater(p_allow_greater);
+ spin->set_allow_lesser(p_allow_lesser);
+}
+
+EditorPropertyInteger::EditorPropertyInteger() {
+ spin = memnew(EditorSpinSlider);
+ add_child(spin);
+ add_focusable(spin);
+ spin->connect("value_changed", this, "_value_changed");
+ setting = false;
+}
+
+///////////////////// OBJECT ID /////////////////////////
+
+void EditorPropertyObjectID::_edit_pressed() {
+
+ emit_signal("object_id_selected", get_edited_property(), get_edited_object()->get(get_edited_property()));
+}
+
+void EditorPropertyObjectID::update_property() {
+ String type = base_type;
+ if (type == "")
+ type = "Object";
+
+ String icon_type = type;
+ if (has_icon(icon_type, "EditorIcons")) {
+ type = icon_type;
+ } else {
+ type = "Object";
+ }
+
+ ObjectID id = get_edited_object()->get(get_edited_property());
+ if (id != 0) {
+ edit->set_text(type + " ID: " + itos(id));
+ edit->set_disabled(false);
+ edit->set_icon(get_icon(icon_type, "EditorIcons"));
+ } else {
+ edit->set_text(TTR("[Empty]"));
+ edit->set_disabled(true);
+ edit->set_icon(Ref<Texture>());
+ }
+}
+
+void EditorPropertyObjectID::setup(const String &p_base_type) {
+ base_type = p_base_type;
+}
+
+void EditorPropertyObjectID::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_edit_pressed"), &EditorPropertyObjectID::_edit_pressed);
+}
+
+EditorPropertyObjectID::EditorPropertyObjectID() {
+ edit = memnew(Button);
+ add_child(edit);
+ add_focusable(edit);
+ edit->connect("pressed", this, "_edit_pressed");
+}
+
+///////////////////// FLOAT /////////////////////////
+
+void EditorPropertyFloat::_value_changed(double val) {
+ if (setting)
+ return;
+
+ emit_signal("property_changed", get_edited_property(), val);
+}
+
+void EditorPropertyFloat::update_property() {
+ double val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin->set_value(val);
+ setting = false;
+}
+
+void EditorPropertyFloat::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyFloat::_value_changed);
+}
+
+void EditorPropertyFloat::setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_exp_range, bool p_greater, bool p_lesser) {
+
+ spin->set_min(p_min);
+ spin->set_max(p_max);
+ spin->set_step(p_step);
+ spin->set_hide_slider(p_no_slider);
+ spin->set_exp_ratio(p_exp_range);
+ spin->set_allow_greater(p_greater);
+ spin->set_allow_lesser(p_lesser);
+}
+
+EditorPropertyFloat::EditorPropertyFloat() {
+ spin = memnew(EditorSpinSlider);
+ add_child(spin);
+ add_focusable(spin);
+ spin->connect("value_changed", this, "_value_changed");
+ setting = false;
+}
+
+///////////////////// EASING /////////////////////////
+
+void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) {
+
+ Ref<InputEventMouseMotion> mm = p_ev;
+
+ if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
+
+ float rel = mm->get_relative().x;
+ if (rel == 0)
+ return;
+
+ if (flip)
+ rel = -rel;
+
+ float val = get_edited_object()->get(get_edited_property());
+ if (val == 0)
+ return;
+ bool sg = val < 0;
+ val = Math::absf(val);
+
+ val = Math::log(val) / Math::log((float)2.0);
+ //logspace
+ val += rel * 0.05;
+
+ val = Math::pow(2.0f, val);
+ if (sg)
+ val = -val;
+
+ emit_signal("property_changed", get_edited_property(), val);
+ easing_draw->update();
+ }
+}
+
+void EditorPropertyEasing::_draw_easing() {
+
+ RID ci = easing_draw->get_canvas_item();
+
+ Size2 s = easing_draw->get_size();
+ Rect2 r(Point2(), s);
+ r = r.grow(3);
+ get_stylebox("normal", "LineEdit")->draw(ci, r);
+
+ int points = 48;
+
+ float prev = 1.0;
+ float exp = get_edited_object()->get(get_edited_property());
+
+ Ref<Font> f = get_font("font", "Label");
+ Color color = get_color("font_color", "Label");
+
+ for (int i = 1; i <= points; i++) {
+
+ float ifl = i / float(points);
+ float iflp = (i - 1) / float(points);
+
+ float h = 1.0 - Math::ease(ifl, exp);
+
+ if (flip) {
+ ifl = 1.0 - ifl;
+ 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);
+ prev = h;
+ }
+
+ f->draw(ci, Point2(10, 10 + f->get_ascent()), String::num(exp, 2), color);
+}
+
+void EditorPropertyEasing::update_property() {
+ easing_draw->update();
+}
+
+void EditorPropertyEasing::_set_preset(float p_val) {
+ emit_signal("property_changed", get_edited_property(), p_val);
+ 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);
+ }
+}
+
+void EditorPropertyEasing::_notification(int p_what) {
+
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_ENTER_TREE: {
+ 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;
+ }
+}
+
+void EditorPropertyEasing::_bind_methods() {
+
+ ClassDB::bind_method("_draw_easing", &EditorPropertyEasing::_draw_easing);
+ ClassDB::bind_method("_drag_easing", &EditorPropertyEasing::_drag_easing);
+ ClassDB::bind_method("_set_preset", &EditorPropertyEasing::_set_preset);
+}
+
+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);
+
+ flip = false;
+}
+
+///////////////////// VECTOR2 /////////////////////////
+
+void EditorPropertyVector2::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Vector2 v2;
+ v2.x = spin[0]->get_value();
+ v2.y = spin[1]->get_value();
+ emit_signal("property_changed", get_edited_property(), v2);
+}
+
+void EditorPropertyVector2::update_property() {
+ Vector2 val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.x);
+ spin[1]->set_value(val.y);
+ setting = false;
+}
+
+void EditorPropertyVector2::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector2::_value_changed);
+}
+
+void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 2; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyVector2::EditorPropertyVector2() {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ add_child(vb);
+ static const char *desc[2] = { "x", "y" };
+ for (int i = 0; i < 2; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ vb->add_child(spin[i]);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ setting = false;
+}
+
+///////////////////// RECT2 /////////////////////////
+
+void EditorPropertyRect2::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Rect2 r2;
+ r2.position.x = spin[0]->get_value();
+ r2.position.y = spin[1]->get_value();
+ r2.size.x = spin[2]->get_value();
+ r2.size.y = spin[3]->get_value();
+ emit_signal("property_changed", get_edited_property(), r2);
+}
+
+void EditorPropertyRect2::update_property() {
+ Rect2 val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.position.x);
+ spin[1]->set_value(val.position.y);
+ spin[2]->set_value(val.size.x);
+ spin[3]->set_value(val.size.y);
+ setting = false;
+}
+
+void EditorPropertyRect2::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyRect2::_value_changed);
+}
+
+void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 4; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyRect2::EditorPropertyRect2() {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ add_child(vb);
+ static const char *desc[4] = { "x", "y", "w", "h" };
+ for (int i = 0; i < 4; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ vb->add_child(spin[i]);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ setting = false;
+}
+///////////////////// VECTOR3 /////////////////////////
+
+void EditorPropertyVector3::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Vector3 v3;
+ v3.x = spin[0]->get_value();
+ v3.y = spin[1]->get_value();
+ v3.z = spin[2]->get_value();
+ emit_signal("property_changed", get_edited_property(), v3);
+}
+
+void EditorPropertyVector3::update_property() {
+ Vector3 val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.x);
+ spin[1]->set_value(val.y);
+ spin[2]->set_value(val.z);
+ setting = false;
+}
+
+void EditorPropertyVector3::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector3::_value_changed);
+}
+
+void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 3; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyVector3::EditorPropertyVector3() {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ add_child(vb);
+ static const char *desc[3] = { "x", "y", "z" };
+ for (int i = 0; i < 3; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ vb->add_child(spin[i]);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ setting = false;
+}
+///////////////////// PLANE /////////////////////////
+
+void EditorPropertyPlane::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Plane p;
+ p.normal.x = spin[0]->get_value();
+ p.normal.y = spin[1]->get_value();
+ p.normal.z = spin[2]->get_value();
+ p.d = spin[3]->get_value();
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyPlane::update_property() {
+ Plane val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.normal.x);
+ spin[1]->set_value(val.normal.y);
+ spin[2]->set_value(val.normal.z);
+ spin[3]->set_value(val.d);
+ setting = false;
+}
+
+void EditorPropertyPlane::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyPlane::_value_changed);
+}
+
+void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 4; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyPlane::EditorPropertyPlane() {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ add_child(vb);
+ static const char *desc[4] = { "x", "y", "z", "d" };
+ for (int i = 0; i < 4; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ vb->add_child(spin[i]);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ setting = false;
+}
+
+///////////////////// QUAT /////////////////////////
+
+void EditorPropertyQuat::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Quat p;
+ p.x = spin[0]->get_value();
+ p.y = spin[1]->get_value();
+ p.z = spin[2]->get_value();
+ p.w = spin[3]->get_value();
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyQuat::update_property() {
+ Quat val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.x);
+ spin[1]->set_value(val.y);
+ spin[2]->set_value(val.z);
+ spin[3]->set_value(val.w);
+ setting = false;
+}
+
+void EditorPropertyQuat::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyQuat::_value_changed);
+}
+
+void EditorPropertyQuat::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 4; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyQuat::EditorPropertyQuat() {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ add_child(vb);
+ static const char *desc[4] = { "x", "y", "z", "w" };
+ for (int i = 0; i < 4; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ vb->add_child(spin[i]);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ setting = false;
+}
+
+///////////////////// AABB /////////////////////////
+
+void EditorPropertyAABB::_value_changed(double val) {
+ if (setting)
+ return;
+
+ AABB p;
+ p.position.x = spin[0]->get_value();
+ p.position.y = spin[1]->get_value();
+ p.position.z = spin[2]->get_value();
+ p.size.x = spin[3]->get_value();
+ p.size.y = spin[4]->get_value();
+ p.size.z = spin[5]->get_value();
+
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyAABB::update_property() {
+ AABB val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.position.x);
+ spin[1]->set_value(val.position.y);
+ spin[2]->set_value(val.position.z);
+ spin[3]->set_value(val.size.x);
+ spin[4]->set_value(val.size.y);
+ spin[5]->set_value(val.size.z);
+
+ setting = false;
+}
+
+void EditorPropertyAABB::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyAABB::_value_changed);
+}
+
+void EditorPropertyAABB::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 6; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyAABB::EditorPropertyAABB() {
+ GridContainer *g = memnew(GridContainer);
+ g->set_columns(3);
+ add_child(g);
+
+ static const char *desc[6] = { "x", "y", "z", "w", "h", "d" };
+ for (int i = 0; i < 6; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ g->add_child(spin[i]);
+ spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_bottom_editor(g);
+ setting = false;
+}
+
+///////////////////// TRANSFORM2D /////////////////////////
+
+void EditorPropertyTransform2D::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Transform2D p;
+ p[0][0] = spin[0]->get_value();
+ p[0][1] = spin[1]->get_value();
+ p[1][0] = spin[2]->get_value();
+ p[1][1] = spin[3]->get_value();
+ p[2][0] = spin[4]->get_value();
+ p[2][1] = spin[5]->get_value();
+
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyTransform2D::update_property() {
+ Transform2D val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val[0][0]);
+ spin[1]->set_value(val[0][1]);
+ spin[2]->set_value(val[1][0]);
+ spin[3]->set_value(val[1][1]);
+ spin[4]->set_value(val[2][0]);
+ spin[5]->set_value(val[2][1]);
+
+ setting = false;
+}
+
+void EditorPropertyTransform2D::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyTransform2D::_value_changed);
+}
+
+void EditorPropertyTransform2D::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 6; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyTransform2D::EditorPropertyTransform2D() {
+ GridContainer *g = memnew(GridContainer);
+ g->set_columns(2);
+ add_child(g);
+
+ static const char *desc[6] = { "xx", "xy", "yx", "yy", "ox", "oy" };
+ for (int i = 0; i < 6; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ g->add_child(spin[i]);
+ spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_bottom_editor(g);
+ setting = false;
+}
+
+///////////////////// BASIS /////////////////////////
+
+void EditorPropertyBasis::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Basis p;
+ p[0][0] = spin[0]->get_value();
+ p[1][0] = spin[1]->get_value();
+ p[2][0] = spin[2]->get_value();
+ p[0][1] = spin[3]->get_value();
+ p[1][1] = spin[4]->get_value();
+ p[2][1] = spin[5]->get_value();
+ p[0][2] = spin[6]->get_value();
+ p[1][2] = spin[7]->get_value();
+ p[2][2] = spin[8]->get_value();
+
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyBasis::update_property() {
+ Basis val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val[0][0]);
+ spin[1]->set_value(val[1][0]);
+ spin[2]->set_value(val[2][0]);
+ spin[3]->set_value(val[0][1]);
+ spin[4]->set_value(val[1][1]);
+ spin[5]->set_value(val[2][1]);
+ spin[6]->set_value(val[0][2]);
+ spin[7]->set_value(val[1][2]);
+ spin[8]->set_value(val[2][2]);
+
+ setting = false;
+}
+
+void EditorPropertyBasis::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyBasis::_value_changed);
+}
+
+void EditorPropertyBasis::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 9; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyBasis::EditorPropertyBasis() {
+ GridContainer *g = memnew(GridContainer);
+ g->set_columns(3);
+ add_child(g);
+
+ static const char *desc[9] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz" };
+ for (int i = 0; i < 9; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ g->add_child(spin[i]);
+ spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_bottom_editor(g);
+ setting = false;
+}
+
+///////////////////// TRANSFORM /////////////////////////
+
+void EditorPropertyTransform::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Transform p;
+ p.basis[0][0] = spin[0]->get_value();
+ p.basis[1][0] = spin[1]->get_value();
+ p.basis[2][0] = spin[2]->get_value();
+ p.basis[0][1] = spin[3]->get_value();
+ p.basis[1][1] = spin[4]->get_value();
+ p.basis[2][1] = spin[5]->get_value();
+ p.basis[0][2] = spin[6]->get_value();
+ p.basis[1][2] = spin[7]->get_value();
+ p.basis[2][2] = spin[8]->get_value();
+ p.origin[0] = spin[9]->get_value();
+ p.origin[1] = spin[10]->get_value();
+ p.origin[2] = spin[11]->get_value();
+
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyTransform::update_property() {
+ Transform val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.basis[0][0]);
+ spin[1]->set_value(val.basis[1][0]);
+ spin[2]->set_value(val.basis[2][0]);
+ spin[3]->set_value(val.basis[0][1]);
+ spin[4]->set_value(val.basis[1][1]);
+ spin[5]->set_value(val.basis[2][1]);
+ spin[6]->set_value(val.basis[0][2]);
+ spin[7]->set_value(val.basis[1][2]);
+ spin[8]->set_value(val.basis[2][2]);
+ spin[9]->set_value(val.origin[0]);
+ spin[10]->set_value(val.origin[1]);
+ spin[11]->set_value(val.origin[2]);
+
+ setting = false;
+}
+
+void EditorPropertyTransform::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyTransform::_value_changed);
+}
+
+void EditorPropertyTransform::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 12; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyTransform::EditorPropertyTransform() {
+ GridContainer *g = memnew(GridContainer);
+ g->set_columns(3);
+ add_child(g);
+
+ static const char *desc[12] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz", "ox", "oy", "oz" };
+ for (int i = 0; i < 12; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ g->add_child(spin[i]);
+ spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_bottom_editor(g);
+ setting = false;
+}
+
+////////////// COLOR PICKER //////////////////////
+
+void EditorPropertyColor::_color_changed(const Color &p_color) {
+
+ emit_signal("property_changed", get_edited_property(), p_color);
+}
+
+void EditorPropertyColor::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_color_changed"), &EditorPropertyColor::_color_changed);
+}
+
+void EditorPropertyColor::update_property() {
+
+ picker->set_pick_color(get_edited_object()->get(get_edited_property()));
+}
+
+void EditorPropertyColor::setup(bool p_show_alpha) {
+ picker->set_edit_alpha(p_show_alpha);
+}
+
+EditorPropertyColor::EditorPropertyColor() {
+
+ picker = memnew(ColorPickerButton);
+ add_child(picker);
+ picker->set_flat(true);
+ picker->connect("color_changed", this, "_color_changed");
+}
+
+////////////// NODE PATH //////////////////////
+
+void EditorPropertyNodePath::_node_selected(const NodePath &p_path) {
+
+ NodePath path = p_path;
+ Node *base_node = Object::cast_to<Node>(get_edited_object());
+ 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");
+ }
+ scene_tree->popup_centered_ratio();
+}
+
+void EditorPropertyNodePath::_node_clear() {
+
+ emit_signal("property_changed", get_edited_property(), NodePath());
+ update_property();
+}
+
+void EditorPropertyNodePath::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 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) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Ref<Texture> t = get_icon("Clear", "EditorIcons");
+ clear->set_icon(t);
+ }
+}
+
+void EditorPropertyNodePath::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_node_selected"), &EditorPropertyNodePath::_node_selected);
+ ClassDB::bind_method(D_METHOD("_node_assign"), &EditorPropertyNodePath::_node_assign);
+ ClassDB::bind_method(D_METHOD("_node_clear"), &EditorPropertyNodePath::_node_clear);
+}
+
+EditorPropertyNodePath::EditorPropertyNodePath() {
+
+ 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);
+
+ scene_tree = NULL; //do not allocate unnecesarily
+}
+
+////////////// RESOURCE //////////////////////
+
+void EditorPropertyResource::_file_selected(const String &p_path) {
+
+ RES res = ResourceLoader::load(p_path);
+ emit_signal("property_changed", get_edited_property(), res);
+ update_property();
+}
+
+void EditorPropertyResource::_menu_option(int p_which) {
+
+ // scene_tree->popup_centered_ratio();
+ switch (p_which) {
+ case OBJ_MENU_LOAD: {
+
+ if (!file) {
+ file = memnew(EditorFileDialog);
+ file->connect("file_selected", this, "_file_selected");
+ add_child(file);
+ }
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+ String type = base_type;
+
+ List<String> extensions;
+ for (int i = 0; i < type.get_slice_count(","); i++) {
+
+ ResourceLoader::get_recognized_extensions_for_type(type.get_slice(",", i), &extensions);
+ }
+
+ Set<String> valid_extensions;
+ for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
+ valid_extensions.insert(E->get());
+ }
+
+ file->clear_filters();
+ for (Set<String>::Element *E = valid_extensions.front(); E; E = E->next()) {
+
+ file->add_filter("*." + E->get() + " ; " + E->get().to_upper());
+ }
+
+ file->popup_centered_ratio();
+ } break;
+
+ case OBJ_MENU_EDIT: {
+
+ RES res = get_edited_object()->get(get_edited_property());
+
+ if (!res.is_null()) {
+
+ emit_signal("resource_selected", get_edited_property(), res);
+ }
+ } break;
+ case OBJ_MENU_CLEAR: {
+
+ emit_signal("property_changed", get_edited_property(), RES());
+ update_property();
+
+ } break;
+
+ case OBJ_MENU_MAKE_UNIQUE: {
+
+ RES res_orig = get_edited_object()->get(get_edited_property());
+ if (res_orig.is_null())
+ return;
+
+ List<PropertyInfo> property_list;
+ res_orig->get_property_list(&property_list);
+ List<Pair<String, Variant> > propvalues;
+
+ for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
+
+ Pair<String, Variant> p;
+ PropertyInfo &pi = E->get();
+ if (pi.usage & PROPERTY_USAGE_STORAGE) {
+
+ p.first = pi.name;
+ p.second = res_orig->get(pi.name);
+ }
+
+ propvalues.push_back(p);
+ }
+
+ String orig_type = res_orig->get_class();
+
+ Object *inst = ClassDB::instance(orig_type);
+
+ Ref<Resource> res = Ref<Resource>(Object::cast_to<Resource>(inst));
+
+ ERR_FAIL_COND(res.is_null());
+
+ for (List<Pair<String, Variant> >::Element *E = propvalues.front(); E; E = E->next()) {
+
+ Pair<String, Variant> &p = E->get();
+ res->set(p.first, p.second);
+ }
+
+ emit_signal("property_changed", get_edited_property(), res);
+ update_property();
+
+ } break;
+
+ case OBJ_MENU_COPY: {
+ RES res = get_edited_object()->get(get_edited_property());
+
+ EditorSettings::get_singleton()->set_resource_clipboard(res);
+
+ } break;
+ case OBJ_MENU_PASTE: {
+
+ RES res = EditorSettings::get_singleton()->get_resource_clipboard();
+ emit_signal("property_changed", get_edited_property(), res);
+ update_property();
+
+ } break;
+ case OBJ_MENU_NEW_SCRIPT: {
+
+ if (Object::cast_to<Node>(get_edited_object())) {
+ EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(get_edited_object()));
+ }
+
+ } break;
+ case OBJ_MENU_SHOW_IN_FILE_SYSTEM: {
+ RES res = get_edited_object()->get(get_edited_property());
+
+ FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
+ file_system_dock->navigate_to_path(res->get_path());
+ // Ensure that the FileSystem dock is visible.
+ TabContainer *tab_container = (TabContainer *)file_system_dock->get_parent_control();
+ tab_container->set_current_tab(file_system_dock->get_position_in_parent());
+ } break;
+ default: {
+
+ RES res = get_edited_object()->get(get_edited_property());
+
+ if (p_which >= CONVERT_BASE_ID) {
+
+ int to_type = p_which - CONVERT_BASE_ID;
+
+ Vector<Ref<EditorResourceConversionPlugin> > conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(res);
+
+ ERR_FAIL_INDEX(to_type, conversions.size());
+
+ Ref<Resource> new_res = conversions[to_type]->convert(res);
+
+ emit_signal("property_changed", get_edited_property(), new_res);
+ update_property();
+ break;
+ }
+ ERR_FAIL_COND(inheritors_array.empty());
+
+ String intype = inheritors_array[p_which - TYPE_BASE_ID];
+
+ if (intype == "ViewportTexture") {
+
+ 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"));
+ }
+ scene_tree->popup_centered_ratio();
+
+ return;
+ }
+
+ Object *obj = ClassDB::instance(intype);
+
+ if (!obj) {
+ obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+ }
+
+ ERR_BREAK(!obj);
+ Resource *resp = Object::cast_to<Resource>(obj);
+ ERR_BREAK(!resp);
+ if (get_edited_object() && base_type != String() && base_type == "Script") {
+ //make visual script the right type
+ res->call("set_instance_base_type", get_edited_object()->get_class());
+ }
+
+ res = Ref<Resource>(resp);
+ emit_signal("property_changed", get_edited_property(), res);
+ update_property();
+
+ } break;
+ }
+}
+
+void EditorPropertyResource::_resource_preview(const String &p_path, const Ref<Texture> &p_preview, ObjectID p_obj) {
+
+ 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);
+ }
+ }
+}
+
+void EditorPropertyResource::_update_menu() {
+ //////////////////// UPDATE MENU //////////////////////////
+ RES res = get_edited_object()->get(get_edited_property());
+
+ menu->clear();
+
+ if (get_edited_property() == "script" && base_type == "Script" && Object::cast_to<Node>(get_edited_object())) {
+ menu->add_icon_item(get_icon("Script", "EditorIcons"), TTR("New Script"), OBJ_MENU_NEW_SCRIPT);
+ menu->add_separator();
+ } else if (base_type != "") {
+ int idx = 0;
+
+ Vector<EditorData::CustomType> custom_resources;
+
+ if (EditorNode::get_editor_data().get_custom_types().has("Resource")) {
+ custom_resources = EditorNode::get_editor_data().get_custom_types()["Resource"];
+ }
+
+ for (int i = 0; i < base_type.get_slice_count(","); i++) {
+
+ String base = base_type.get_slice(",", i);
+
+ Set<String> valid_inheritors;
+ valid_inheritors.insert(base);
+ List<StringName> inheritors;
+ ClassDB::get_inheriters_from_class(base.strip_edges(), &inheritors);
+
+ for (int i = 0; i < custom_resources.size(); i++) {
+ inheritors.push_back(custom_resources[i].name);
+ }
+
+ List<StringName>::Element *E = inheritors.front();
+ while (E) {
+ valid_inheritors.insert(E->get());
+ E = E->next();
+ }
+
+ for (Set<String>::Element *E = valid_inheritors.front(); E; E = E->next()) {
+ String t = E->get();
+
+ bool is_custom_resource = false;
+ Ref<Texture> icon;
+ if (!custom_resources.empty()) {
+ for (int i = 0; i < custom_resources.size(); i++) {
+ if (custom_resources[i].name == t) {
+ is_custom_resource = true;
+ if (custom_resources[i].icon.is_valid())
+ icon = custom_resources[i].icon;
+ break;
+ }
+ }
+ }
+
+ if (!is_custom_resource && !ClassDB::can_instance(t))
+ continue;
+
+ inheritors_array.push_back(t);
+
+ int id = TYPE_BASE_ID + idx;
+
+ if (!icon.is_valid() && has_icon(t, "EditorIcons")) {
+ icon = get_icon(t, "EditorIcons");
+ }
+
+ if (icon.is_valid()) {
+
+ menu->add_icon_item(icon, vformat(TTR("New %s"), t), id);
+ } else {
+
+ menu->add_item(vformat(TTR("New %s"), t), id);
+ }
+
+ idx++;
+ }
+ }
+
+ if (menu->get_item_count())
+ menu->add_separator();
+ }
+
+ menu->add_icon_item(get_icon("Load", "EditorIcons"), TTR("Load"), OBJ_MENU_LOAD);
+
+ if (!res.is_null()) {
+
+ menu->add_icon_item(get_icon("Edit", "EditorIcons"), TTR("Edit"), OBJ_MENU_EDIT);
+ menu->add_icon_item(get_icon("Clear", "EditorIcons"), TTR("Clear"), OBJ_MENU_CLEAR);
+ menu->add_icon_item(get_icon("Duplicate", "EditorIcons"), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE);
+ RES r = res;
+ if (r.is_valid() && r->get_path().is_resource_file()) {
+ menu->add_separator();
+ menu->add_item(TTR("Show in File System"), OBJ_MENU_SHOW_IN_FILE_SYSTEM);
+ }
+ } else {
+ }
+
+ RES cb = EditorSettings::get_singleton()->get_resource_clipboard();
+ bool paste_valid = false;
+ if (cb.is_valid()) {
+ if (base_type == "")
+ paste_valid = true;
+ else
+ for (int i = 0; i < base_type.get_slice_count(","); i++)
+ if (ClassDB::is_parent_class(cb->get_class(), base_type.get_slice(",", i))) {
+ paste_valid = true;
+ break;
+ }
+ }
+
+ if (!res.is_null() || paste_valid) {
+ menu->add_separator();
+
+ if (!res.is_null()) {
+
+ menu->add_item(TTR("Copy"), OBJ_MENU_COPY);
+ }
+
+ if (paste_valid) {
+
+ menu->add_item(TTR("Paste"), OBJ_MENU_PASTE);
+ }
+ }
+
+ if (!res.is_null()) {
+
+ Vector<Ref<EditorResourceConversionPlugin> > conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(res);
+ if (conversions.size()) {
+ menu->add_separator();
+ }
+ for (int i = 0; i < conversions.size(); i++) {
+ String what = conversions[i]->converts_to();
+ Ref<Texture> icon;
+ if (has_icon(what, "EditorIcons")) {
+
+ icon = get_icon(what, "EditorIcons");
+ } else {
+
+ icon = get_icon(what, "Resource");
+ }
+
+ menu->add_icon_item(icon, vformat(TTR("Convert To %s"), what), CONVERT_BASE_ID + i);
+ }
+ }
+
+ Rect2 gt = edit->get_global_rect();
+ menu->set_as_minsize();
+ int ms = menu->get_combined_minimum_size().width;
+ Vector2 popup_pos = gt.position + gt.size - Vector2(ms, 0);
+ menu->set_global_position(popup_pos);
+ menu->popup();
+}
+
+void EditorPropertyResource::_sub_inspector_property_keyed(const String &p_property, const Variant &p_value, bool) {
+
+ emit_signal("property_keyed_with_value", String(get_edited_property()) + ":" + p_property, p_value);
+}
+
+void EditorPropertyResource::_sub_inspector_resource_selected(const RES &p_resource, const String &p_property) {
+
+ emit_signal("resource_selected", String(get_edited_property()) + ":" + p_property, p_resource);
+}
+
+void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) {
+
+ emit_signal("object_id_selected", get_edited_property(), p_id);
+}
+
+void EditorPropertyResource::_open_editor_pressed() {
+ RES res = get_edited_object()->get(get_edited_property());
+ if (res.is_valid()) {
+ EditorNode::get_singleton()->edit_resource(res.ptr());
+ }
+}
+
+void EditorPropertyResource::update_property() {
+
+ RES res = get_edited_object()->get(get_edited_property());
+
+ if (use_sub_inspector) {
+
+ if (res.is_valid() != assign->is_toggle_mode()) {
+ assign->set_toggle_mode(res.is_valid());
+ }
+#ifdef TOOLS_ENABLED
+ if (res.is_valid() && get_edited_object()->editor_is_section_unfolded(get_edited_property())) {
+
+ if (!sub_inspector) {
+ sub_inspector = memnew(EditorInspector);
+ sub_inspector->set_enable_v_scroll(false);
+
+ 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");
+ sub_inspector->set_keying(is_keying());
+ sub_inspector->set_read_only(is_read_only());
+ sub_inspector->set_use_folding(is_using_folding());
+
+ sub_inspector_vbox = memnew(VBoxContainer);
+ add_child(sub_inspector_vbox);
+ set_bottom_editor(sub_inspector_vbox);
+
+ sub_inspector_vbox->add_child(sub_inspector);
+ assign->set_pressed(true);
+
+ bool use_editor = false;
+ for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_editor_plugin_count(); i++) {
+ EditorPlugin *ep = EditorNode::get_singleton()->get_editor_data().get_editor_plugin(i);
+ if (ep->handles(res.ptr())) {
+ use_editor = true;
+ }
+ }
+
+ if (use_editor) {
+ Button *open_in_editor = memnew(Button);
+ open_in_editor->set_text(TTR("Open Editor"));
+ open_in_editor->set_icon(get_icon("Edit", "EditorIcons"));
+ sub_inspector_vbox->add_child(open_in_editor);
+ open_in_editor->connect("pressed", this, "_open_editor_pressed");
+ open_in_editor->set_h_size_flags(SIZE_SHRINK_CENTER);
+ }
+ }
+
+ if (res.ptr() != sub_inspector->get_edited_object()) {
+ sub_inspector->edit(res.ptr());
+ }
+
+ } else {
+ if (sub_inspector) {
+ set_bottom_editor(NULL);
+ memdelete(sub_inspector_vbox);
+ sub_inspector = NULL;
+ sub_inspector_vbox = NULL;
+ }
+ }
+#endif
+ }
+
+ if (res == RES()) {
+ assign->set_icon(Ref<Texture>());
+ assign->set_text(TTR("[empty]"));
+ } else {
+
+ Ref<Texture> icon;
+ if (has_icon(res->get_class(), "EditorIcons"))
+ icon = get_icon(res->get_class(), "EditorIcons");
+ else
+ icon = get_icon("Node", "EditorIcons");
+
+ assign->set_icon(icon);
+
+ if (res->get_name() != String()) {
+ assign->set_text(res->get_name());
+ } else if (res->get_path().is_resource_file()) {
+ assign->set_text(res->get_name());
+ assign->set_tooltip(res->get_path());
+ } else {
+ assign->set_text(res->get_class());
+ }
+
+ if (res->get_path().is_resource_file()) {
+ assign->set_tooltip(res->get_path());
+ }
+
+ //preview will override the above, so called at the end
+ EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_resource_preview", res->get_instance_id());
+ }
+}
+
+void EditorPropertyResource::_resource_selected() {
+ RES res = get_edited_object()->get(get_edited_property());
+
+ if (res.is_null()) {
+ _update_menu();
+ return;
+ }
+
+ if (use_sub_inspector) {
+
+ get_edited_object()->editor_set_section_unfold(get_edited_property(), assign->is_pressed());
+ update_property();
+ } else {
+
+ emit_signal("resource_selected", get_edited_property(), res);
+ }
+}
+
+void EditorPropertyResource::setup(const String &p_base_type) {
+ base_type = p_base_type;
+}
+
+void EditorPropertyResource::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Ref<Texture> t = get_icon("select_arrow", "Tree");
+ edit->set_icon(t);
+ }
+
+ if (p_what == NOTIFICATION_DRAG_BEGIN) {
+
+ if (is_visible_in_tree()) {
+ if (_is_drop_valid(get_viewport()->gui_get_drag_data())) {
+ dropping = true;
+ assign->update();
+ }
+ }
+ }
+
+ if (p_what == NOTIFICATION_DRAG_END) {
+ if (dropping) {
+ dropping = false;
+ assign->update();
+ }
+ }
+}
+
+void EditorPropertyResource::_viewport_selected(const NodePath &p_path) {
+
+ Node *to_node = get_node(p_path);
+ if (!Object::cast_to<Viewport>(to_node)) {
+ EditorNode::get_singleton()->show_warning(TTR("Selected node is not a Viewport!"));
+ return;
+ }
+
+ Ref<ViewportTexture> vt;
+ vt.instance();
+ vt->set_viewport_path_in_scene(get_tree()->get_edited_scene_root()->get_path_to(to_node));
+ vt->setup_local_to_scene();
+
+ emit_signal("property_changed", get_edited_property(), vt);
+ update_property();
+}
+
+void EditorPropertyResource::collapse_all_folding() {
+ if (sub_inspector) {
+ sub_inspector->collapse_all_folding();
+ }
+}
+
+void EditorPropertyResource::expand_all_folding() {
+
+ if (sub_inspector) {
+ sub_inspector->expand_all_folding();
+ }
+}
+
+void EditorPropertyResource::_button_draw() {
+
+ if (dropping) {
+ Color color = get_color("accent_color", "Editor");
+ assign->draw_rect(Rect2(Point2(), assign->get_size()), color, false);
+ }
+}
+
+Variant EditorPropertyResource::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
+
+ RES res = get_edited_object()->get(get_edited_property());
+ if (res.is_valid()) {
+
+ return EditorNode::get_singleton()->drag_resource(res, p_from);
+ }
+
+ return Variant();
+}
+
+bool EditorPropertyResource::_is_drop_valid(const Dictionary &p_drag_data) const {
+
+ String allowed_type = base_type;
+
+ Dictionary drag_data = p_drag_data;
+ if (drag_data.has("type") && String(drag_data["type"]) == "resource") {
+ Ref<Resource> res = drag_data["resource"];
+ for (int i = 0; i < allowed_type.get_slice_count(","); i++) {
+ String at = allowed_type.get_slice(",", i).strip_edges();
+ if (res.is_valid() && ClassDB::is_parent_class(res->get_class(), at)) {
+ return true;
+ }
+ }
+ }
+
+ if (drag_data.has("type") && String(drag_data["type"]) == "files") {
+
+ Vector<String> files = drag_data["files"];
+
+ if (files.size() == 1) {
+ String file = files[0];
+ String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
+
+ if (ftype != "") {
+
+ for (int i = 0; i < allowed_type.get_slice_count(","); i++) {
+ String at = allowed_type.get_slice(",", i).strip_edges();
+ if (ClassDB::is_parent_class(ftype, at)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool EditorPropertyResource::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+
+ return _is_drop_valid(p_data);
+}
+void EditorPropertyResource::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+
+ ERR_FAIL_COND(!_is_drop_valid(p_data));
+
+ Dictionary drag_data = p_data;
+ if (drag_data.has("type") && String(drag_data["type"]) == "resource") {
+ Ref<Resource> res = drag_data["resource"];
+ if (res.is_valid()) {
+ emit_signal("property_changed", get_edited_property(), res);
+ update_property();
+ return;
+ }
+ }
+
+ if (drag_data.has("type") && String(drag_data["type"]) == "files") {
+
+ Vector<String> files = drag_data["files"];
+
+ if (files.size() == 1) {
+ String file = files[0];
+ RES res = ResourceLoader::load(file);
+ if (res.is_valid()) {
+ emit_signal("property_changed", get_edited_property(), res);
+ update_property();
+ return;
+ }
+ }
+ }
+}
+
+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);
+ ClassDB::bind_method(D_METHOD("_menu_option"), &EditorPropertyResource::_menu_option);
+ ClassDB::bind_method(D_METHOD("_update_menu"), &EditorPropertyResource::_update_menu);
+ ClassDB::bind_method(D_METHOD("_resource_preview"), &EditorPropertyResource::_resource_preview);
+ ClassDB::bind_method(D_METHOD("_resource_selected"), &EditorPropertyResource::_resource_selected);
+ ClassDB::bind_method(D_METHOD("_viewport_selected"), &EditorPropertyResource::_viewport_selected);
+ ClassDB::bind_method(D_METHOD("_sub_inspector_property_keyed"), &EditorPropertyResource::_sub_inspector_property_keyed);
+ ClassDB::bind_method(D_METHOD("_sub_inspector_resource_selected"), &EditorPropertyResource::_sub_inspector_resource_selected);
+ ClassDB::bind_method(D_METHOD("_sub_inspector_object_id_selected"), &EditorPropertyResource::_sub_inspector_object_id_selected);
+ ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &EditorPropertyResource::get_drag_data_fw);
+ ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &EditorPropertyResource::can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("drop_data_fw"), &EditorPropertyResource::drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_button_draw"), &EditorPropertyResource::_button_draw);
+ ClassDB::bind_method(D_METHOD("_open_editor_pressed"), &EditorPropertyResource::_open_editor_pressed);
+}
+
+EditorPropertyResource::EditorPropertyResource() {
+
+ sub_inspector = NULL;
+ sub_inspector_vbox = NULL;
+ use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector"));
+
+ 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, "_resource_selected");
+ assign->set_drag_forwarding(this);
+ assign->connect("draw", this, "_button_draw");
+ hbc->add_child(assign);
+
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ edit = memnew(Button);
+ edit->set_flat(true);
+ menu->connect("id_pressed", this, "_menu_option");
+ edit->connect("pressed", this, "_update_menu");
+ hbc->add_child(edit);
+
+ file = NULL;
+ scene_tree = NULL;
+ dropping = false;
+}
+
+////////////// DEFAULT PLUGIN //////////////////////
+
+bool EditorInspectorDefaultPlugin::can_handle(Object *p_object) {
+ return true; //can handle everything
+}
+
+void EditorInspectorDefaultPlugin::parse_begin(Object *p_object) {
+ //do none
+}
+
+bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) {
+
+ switch (p_type) {
+
+ // atomic types
+ case Variant::NIL: {
+ EditorPropertyNil *editor = memnew(EditorPropertyNil);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::BOOL: {
+ EditorPropertyCheck *editor = memnew(EditorPropertyCheck);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::INT: {
+
+ if (p_hint == PROPERTY_HINT_ENUM) {
+ EditorPropertyEnum *editor = memnew(EditorPropertyEnum);
+ Vector<String> options = p_hint_text.split(",");
+ editor->setup(options);
+ add_property_editor(p_path, editor);
+
+ } else if (p_hint == PROPERTY_HINT_FLAGS) {
+ EditorPropertyFlags *editor = memnew(EditorPropertyFlags);
+ Vector<String> options = p_hint_text.split(",");
+ editor->setup(options);
+ add_property_editor(p_path, editor);
+
+ } else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_2D_RENDER || p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_3D_RENDER) {
+
+ EditorPropertyLayers::LayerType lt;
+ switch (p_hint) {
+ case PROPERTY_HINT_LAYERS_2D_RENDER:
+ lt = EditorPropertyLayers::LAYER_RENDER_2D;
+ break;
+ case PROPERTY_HINT_LAYERS_2D_PHYSICS:
+ lt = EditorPropertyLayers::LAYER_PHYSICS_2D;
+ break;
+ case PROPERTY_HINT_LAYERS_3D_RENDER:
+ lt = EditorPropertyLayers::LAYER_RENDER_3D;
+ break;
+ case PROPERTY_HINT_LAYERS_3D_PHYSICS:
+ lt = EditorPropertyLayers::LAYER_PHYSICS_3D;
+ break;
+ default: {} //compiler could be smarter here and realize this cant happen
+ }
+ EditorPropertyLayers *editor = memnew(EditorPropertyLayers);
+ editor->setup(lt);
+ add_property_editor(p_path, editor);
+ } else if (p_hint == PROPERTY_HINT_OBJECT_ID) {
+
+ EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);
+ editor->setup(p_hint_text);
+ add_property_editor(p_path, editor);
+
+ } else {
+ EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
+ int min = 0, max = 65535;
+ bool greater = true, lesser = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ greater = false; //if using ranged, asume false by default
+ lesser = false;
+ min = p_hint_text.get_slice(",", 0).to_int();
+ max = p_hint_text.get_slice(",", 1).to_int();
+ for (int i = 2; i < p_hint_text.get_slice_count(","); i++) {
+ String slice = p_hint_text.get_slice(",", i).strip_edges();
+ if (slice == "or_greater") {
+ greater = true;
+ }
+ if (slice == "or_lesser") {
+ lesser = true;
+ }
+ }
+ }
+
+ editor->setup(min, max, greater, lesser);
+
+ add_property_editor(p_path, editor);
+ }
+ } break;
+ case Variant::REAL: {
+
+ if (p_hint == PROPERTY_HINT_EXP_EASING) {
+ EditorPropertyEasing *editor = memnew(EditorPropertyEasing);
+ bool full = true;
+ bool flip = false;
+ Vector<String> hints = p_hint_text.split(",");
+ for (int i = 0; i < hints.size(); i++) {
+ String h = hints[i].strip_edges();
+ if (h == "attenuation") {
+ flip = true;
+ }
+ if (h == "inout") {
+ full = true;
+ }
+ }
+
+ editor->setup(full, flip);
+ add_property_editor(p_path, editor);
+
+ } else {
+ EditorPropertyFloat *editor = memnew(EditorPropertyFloat);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+ bool exp_range = false;
+ bool greater = true, lesser = true;
+
+ if ((p_hint == PROPERTY_HINT_RANGE || p_hint == PROPERTY_HINT_EXP_RANGE) && p_hint_text.get_slice_count(",") >= 2) {
+ greater = false; //if using ranged, asume false by default
+ lesser = false;
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ exp_range = p_hint == PROPERTY_HINT_EXP_RANGE;
+ for (int i = 2; i < p_hint_text.get_slice_count(","); i++) {
+ String slice = p_hint_text.get_slice(",", i).strip_edges();
+ if (slice == "or_greater") {
+ greater = true;
+ }
+ if (slice == "or_lesser") {
+ lesser = true;
+ }
+ }
+ }
+
+ editor->setup(min, max, step, hide_slider, exp_range, greater, lesser);
+
+ add_property_editor(p_path, editor);
+ }
+ } break;
+ case Variant::STRING: {
+
+ if (p_hint == PROPERTY_HINT_ENUM) {
+ EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);
+ Vector<String> options = p_hint_text.split(",");
+ editor->setup(options);
+ add_property_editor(p_path, editor);
+ } else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) {
+ EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText);
+ add_property_editor(p_path, editor);
+ } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) {
+
+ Vector<String> extensions = p_hint_text.split(",");
+ bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE;
+ bool folder = p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_GLOBAL_DIR;
+ EditorPropertyPath *editor = memnew(EditorPropertyPath);
+ editor->setup(extensions, folder, global);
+ add_property_editor(p_path, editor);
+ } else if (p_hint == PROPERTY_HINT_METHOD_OF_VARIANT_TYPE ||
+ p_hint == PROPERTY_HINT_METHOD_OF_BASE_TYPE ||
+ p_hint == PROPERTY_HINT_METHOD_OF_INSTANCE ||
+ p_hint == PROPERTY_HINT_METHOD_OF_SCRIPT ||
+ p_hint == PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE ||
+ p_hint == PROPERTY_HINT_PROPERTY_OF_BASE_TYPE ||
+ p_hint == PROPERTY_HINT_PROPERTY_OF_INSTANCE ||
+ p_hint == PROPERTY_HINT_PROPERTY_OF_SCRIPT) {
+
+ EditorPropertyMember *editor = memnew(EditorPropertyMember);
+
+ EditorPropertyMember::Type type = EditorPropertyMember::MEMBER_METHOD_OF_BASE_TYPE;
+ switch (p_hint) {
+ case PROPERTY_HINT_METHOD_OF_BASE_TYPE: type = EditorPropertyMember::MEMBER_METHOD_OF_BASE_TYPE; break;
+ case PROPERTY_HINT_METHOD_OF_INSTANCE: type = EditorPropertyMember::MEMBER_METHOD_OF_INSTANCE; break;
+ case PROPERTY_HINT_METHOD_OF_SCRIPT: type = EditorPropertyMember::MEMBER_METHOD_OF_SCRIPT; break;
+ case PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE: type = EditorPropertyMember::MEMBER_PROPERTY_OF_VARIANT_TYPE; break;
+ case PROPERTY_HINT_PROPERTY_OF_BASE_TYPE: type = EditorPropertyMember::MEMBER_PROPERTY_OF_BASE_TYPE; break;
+ case PROPERTY_HINT_PROPERTY_OF_INSTANCE: type = EditorPropertyMember::MEMBER_PROPERTY_OF_INSTANCE; break;
+ case PROPERTY_HINT_PROPERTY_OF_SCRIPT: type = EditorPropertyMember::MEMBER_PROPERTY_OF_SCRIPT; break;
+ default: {}
+ }
+ editor->setup(type, p_hint_text);
+ add_property_editor(p_path, editor);
+
+ } else {
+
+ EditorPropertyText *editor = memnew(EditorPropertyText);
+ add_property_editor(p_path, editor);
+ }
+ } break;
+
+ // math types
+
+ case Variant::VECTOR2: {
+ EditorPropertyVector2 *editor = memnew(EditorPropertyVector2);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+
+ } break; // 5
+ case Variant::RECT2: {
+ EditorPropertyRect2 *editor = memnew(EditorPropertyRect2);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::VECTOR3: {
+ EditorPropertyVector3 *editor = memnew(EditorPropertyVector3);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+
+ } break;
+ case Variant::TRANSFORM2D: {
+ EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+
+ } break;
+ case Variant::PLANE: {
+ EditorPropertyPlane *editor = memnew(EditorPropertyPlane);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::QUAT: {
+ EditorPropertyQuat *editor = memnew(EditorPropertyQuat);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+ } break; // 10
+ case Variant::AABB: {
+ EditorPropertyAABB *editor = memnew(EditorPropertyAABB);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::BASIS: {
+ EditorPropertyBasis *editor = memnew(EditorPropertyBasis);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::TRANSFORM: {
+ EditorPropertyTransform *editor = memnew(EditorPropertyTransform);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+
+ } break;
+
+ // misc types
+ case Variant::COLOR: {
+ EditorPropertyColor *editor = memnew(EditorPropertyColor);
+ editor->setup(p_hint != PROPERTY_HINT_COLOR_NO_ALPHA);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::NODE_PATH: {
+
+ EditorPropertyNodePath *editor = memnew(EditorPropertyNodePath);
+ if (p_hint == PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE && p_hint_text != String()) {
+ 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);
+
+ } break; // 15
+ case Variant::_RID: {
+ } break;
+ 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;
+ case Variant::DICTIONARY: {
+ EditorPropertyDictionary *editor = memnew(EditorPropertyDictionary);
+ add_property_editor(p_path, editor);
+ } 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: {}
+ }
+
+ return false; //can be overriden, although it will most likely be last anyway
+}
+
+void EditorInspectorDefaultPlugin::parse_end() {
+ //do none
+}
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
new file mode 100644
index 0000000000..ecccd7274d
--- /dev/null
+++ b/editor/editor_properties.h
@@ -0,0 +1,553 @@
+/*************************************************************************/
+/* editor_properties.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 EDITOR_PROPERTIES_H
+#define EDITOR_PROPERTIES_H
+
+#include "editor/create_dialog.h"
+#include "editor/editor_file_system.h"
+#include "editor/editor_inspector.h"
+#include "editor/editor_spin_slider.h"
+#include "editor/property_selector.h"
+#include "editor/scene_tree_editor.h"
+#include "scene/gui/color_picker.h"
+
+class EditorPropertyNil : public EditorProperty {
+ GDCLASS(EditorPropertyNil, EditorProperty)
+ LineEdit *text;
+
+public:
+ virtual void update_property();
+ EditorPropertyNil();
+};
+
+class EditorPropertyText : public EditorProperty {
+ GDCLASS(EditorPropertyText, EditorProperty)
+ LineEdit *text;
+
+ bool updating;
+ void _text_changed(const String &p_string);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ EditorPropertyText();
+};
+
+class EditorPropertyMultilineText : public EditorProperty {
+ GDCLASS(EditorPropertyMultilineText, EditorProperty)
+ TextEdit *text;
+
+ AcceptDialog *big_text_dialog;
+ TextEdit *big_text;
+ Button *open_big_text;
+
+ void _big_text_changed();
+ void _text_changed();
+ void _open_big_text();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ EditorPropertyMultilineText();
+};
+
+class EditorPropertyTextEnum : public EditorProperty {
+ GDCLASS(EditorPropertyTextEnum, 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();
+ EditorPropertyTextEnum();
+};
+
+class EditorPropertyPath : public EditorProperty {
+ GDCLASS(EditorPropertyPath, EditorProperty)
+ Vector<String> extensions;
+ bool folder;
+ bool global;
+ EditorFileDialog *dialog;
+ Button *path;
+
+ void _path_selected(const String &p_path);
+ void _path_pressed();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(const Vector<String> &p_extensions, bool p_folder, bool p_global);
+ virtual void update_property();
+ EditorPropertyPath();
+};
+
+class EditorPropertyMember : public EditorProperty {
+ GDCLASS(EditorPropertyMember, EditorProperty)
+public:
+ enum Type {
+ MEMBER_METHOD_OF_VARIANT_TYPE, ///< a method of a type
+ MEMBER_METHOD_OF_BASE_TYPE, ///< a method of a base type
+ MEMBER_METHOD_OF_INSTANCE, ///< a method of an instance
+ MEMBER_METHOD_OF_SCRIPT, ///< a method of a script & base
+ MEMBER_PROPERTY_OF_VARIANT_TYPE, ///< a property of a type
+ MEMBER_PROPERTY_OF_BASE_TYPE, ///< a property of a base type
+ MEMBER_PROPERTY_OF_INSTANCE, ///< a property of an instance
+ MEMBER_PROPERTY_OF_SCRIPT, ///< a property of a script & base
+
+ };
+
+private:
+ Type hint;
+ PropertySelector *selector;
+ Button *property;
+ String hint_text;
+
+ void _property_selected(const String &p_selected);
+ void _property_select();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(Type p_hint, const String &p_hint_text);
+ virtual void update_property();
+ EditorPropertyMember();
+};
+
+class EditorPropertyCheck : public EditorProperty {
+ GDCLASS(EditorPropertyCheck, EditorProperty)
+ CheckBox *checkbox;
+
+ void _checkbox_pressed();
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ EditorPropertyCheck();
+};
+
+class EditorPropertyEnum : public EditorProperty {
+ GDCLASS(EditorPropertyEnum, 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);
+ EditorPropertyEnum();
+};
+
+class EditorPropertyFlags : public EditorProperty {
+ GDCLASS(EditorPropertyFlags, EditorProperty)
+ VBoxContainer *vbox;
+ Vector<CheckBox *> flags;
+ Vector<int> flag_indices;
+
+ void _flag_toggled();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(const Vector<String> &p_options);
+ virtual void update_property();
+ EditorPropertyFlags();
+};
+
+class EditorPropertyLayersGrid;
+
+class EditorPropertyLayers : public EditorProperty {
+ GDCLASS(EditorPropertyLayers, EditorProperty)
+public:
+ enum LayerType {
+ LAYER_PHYSICS_2D,
+ LAYER_RENDER_2D,
+ LAYER_PHYSICS_3D,
+ LAYER_RENDER_3D,
+ };
+
+private:
+ EditorPropertyLayersGrid *grid;
+ void _grid_changed(uint32_t p_grid);
+ LayerType layer_type;
+ PopupMenu *layers;
+ Button *button;
+
+ void _button_pressed();
+ void _menu_pressed(int p_menu);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(LayerType p_layer_type);
+ virtual void update_property();
+ EditorPropertyLayers();
+};
+
+class EditorPropertyInteger : public EditorProperty {
+ GDCLASS(EditorPropertyInteger, EditorProperty)
+ EditorSpinSlider *spin;
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(int p_min, int p_max, bool p_allow_greater, bool p_allow_lesser);
+ EditorPropertyInteger();
+};
+
+class EditorPropertyObjectID : public EditorProperty {
+ GDCLASS(EditorPropertyObjectID, EditorProperty)
+ Button *edit;
+ String base_type;
+ void _edit_pressed();
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(const String &p_base_type);
+ EditorPropertyObjectID();
+};
+
+class EditorPropertyFloat : public EditorProperty {
+ GDCLASS(EditorPropertyFloat, EditorProperty)
+ EditorSpinSlider *spin;
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_exp_range, bool p_greater, bool p_lesser);
+ EditorPropertyFloat();
+};
+
+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;
+
+ bool flip;
+
+ void _drag_easing(const Ref<InputEvent> &p_ev);
+ void _draw_easing();
+ void _notification(int p_what);
+ void _set_preset(float p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(bool p_full, bool p_flip);
+ EditorPropertyEasing();
+};
+
+class EditorPropertyVector2 : public EditorProperty {
+ GDCLASS(EditorPropertyVector2, EditorProperty)
+ EditorSpinSlider *spin[2];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyVector2();
+};
+
+class EditorPropertyRect2 : public EditorProperty {
+ GDCLASS(EditorPropertyRect2, EditorProperty)
+ EditorSpinSlider *spin[4];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyRect2();
+};
+
+class EditorPropertyVector3 : public EditorProperty {
+ GDCLASS(EditorPropertyVector3, EditorProperty)
+ EditorSpinSlider *spin[3];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyVector3();
+};
+
+class EditorPropertyPlane : public EditorProperty {
+ GDCLASS(EditorPropertyPlane, EditorProperty)
+ EditorSpinSlider *spin[4];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyPlane();
+};
+
+class EditorPropertyQuat : public EditorProperty {
+ GDCLASS(EditorPropertyQuat, EditorProperty)
+ EditorSpinSlider *spin[4];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyQuat();
+};
+
+class EditorPropertyAABB : public EditorProperty {
+ GDCLASS(EditorPropertyAABB, EditorProperty)
+ EditorSpinSlider *spin[6];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyAABB();
+};
+
+class EditorPropertyTransform2D : public EditorProperty {
+ GDCLASS(EditorPropertyTransform2D, EditorProperty)
+ EditorSpinSlider *spin[6];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyTransform2D();
+};
+
+class EditorPropertyBasis : public EditorProperty {
+ GDCLASS(EditorPropertyBasis, EditorProperty)
+ EditorSpinSlider *spin[9];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyBasis();
+};
+
+class EditorPropertyTransform : public EditorProperty {
+ GDCLASS(EditorPropertyTransform, EditorProperty)
+ EditorSpinSlider *spin[12];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyTransform();
+};
+
+class EditorPropertyColor : public EditorProperty {
+ GDCLASS(EditorPropertyColor, EditorProperty)
+ ColorPickerButton *picker;
+ void _color_changed(const Color &p_color);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(bool p_show_alpha);
+ EditorPropertyColor();
+};
+
+class EditorPropertyNodePath : public EditorProperty {
+ GDCLASS(EditorPropertyNodePath, EditorProperty)
+ Button *assign;
+ Button *clear;
+ SceneTreeDialog *scene_tree;
+ NodePath base_hint;
+
+ Vector<StringName> valid_types;
+ void _node_selected(const NodePath &p_path);
+ 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, Vector<StringName> p_valid_types);
+ EditorPropertyNodePath();
+};
+
+class EditorPropertyResource : public EditorProperty {
+ GDCLASS(EditorPropertyResource, EditorProperty)
+
+ enum MenuOption {
+
+ OBJ_MENU_LOAD = 0,
+ OBJ_MENU_EDIT = 1,
+ OBJ_MENU_CLEAR = 2,
+ OBJ_MENU_MAKE_UNIQUE = 3,
+ OBJ_MENU_COPY = 4,
+ OBJ_MENU_PASTE = 5,
+ OBJ_MENU_NEW_SCRIPT = 6,
+ OBJ_MENU_SHOW_IN_FILE_SYSTEM = 7,
+ TYPE_BASE_ID = 100,
+ CONVERT_BASE_ID = 1000
+
+ };
+
+ Button *assign;
+ Button *edit;
+ PopupMenu *menu;
+ EditorFileDialog *file;
+ Vector<String> inheritors_array;
+ EditorInspector *sub_inspector;
+ VBoxContainer *sub_inspector_vbox;
+
+ bool use_sub_inspector;
+ bool dropping;
+ String base_type;
+
+ SceneTreeDialog *scene_tree;
+
+ void _file_selected(const String &p_path);
+ void _menu_option(int p_which);
+ void _resource_preview(const String &p_path, const Ref<Texture> &p_preview, ObjectID p_obj);
+ void _resource_selected();
+ void _viewport_selected(const NodePath &p_path);
+
+ void _update_menu();
+
+ void _sub_inspector_property_keyed(const String &p_property, const Variant &p_value, bool);
+ void _sub_inspector_resource_selected(const RES &p_resource, const String &p_property);
+ void _sub_inspector_object_id_selected(int p_id);
+
+ void _button_draw();
+ Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
+ bool _is_drop_valid(const Dictionary &p_drag_data) const;
+ bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+ void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+
+ void _open_editor_pressed();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ virtual void update_property();
+ void setup(const String &p_base_type);
+
+ void collapse_all_folding();
+ void expand_all_folding();
+
+ void set_use_sub_inspector(bool p_enable);
+
+ EditorPropertyResource();
+};
+
+///////////////////////////////////////////////////
+/// \brief The EditorInspectorDefaultPlugin class
+///
+class EditorInspectorDefaultPlugin : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorDefaultPlugin, 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 // EDITOR_PROPERTIES_H
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
new file mode 100644
index 0000000000..2bd28170e7
--- /dev/null
+++ b/editor/editor_properties_array_dict.cpp
@@ -0,0 +1,1017 @@
+#include "editor_properties_array_dict.h"
+#include "editor/editor_scale.h"
+#include "editor_properties.h"
+
+bool EditorPropertyArrayObject::_set(const StringName &p_name, const Variant &p_value) {
+
+ String pn = p_name;
+
+ if (pn.begins_with("indices")) {
+ int idx = pn.get_slicec('/', 1).to_int();
+ array.set(idx, p_value);
+ return true;
+ }
+
+ return false;
+}
+
+bool EditorPropertyArrayObject::_get(const StringName &p_name, Variant &r_ret) const {
+
+ String pn = p_name;
+
+ if (pn.begins_with("indices")) {
+
+ int idx = pn.get_slicec('/', 1).to_int();
+ bool valid;
+ r_ret = array.get(idx, &valid);
+ return valid;
+ }
+
+ return false;
+}
+
+void EditorPropertyArrayObject::set_array(const Variant &p_array) {
+ array = p_array;
+}
+
+Variant EditorPropertyArrayObject::get_array() {
+ return array;
+}
+
+EditorPropertyArrayObject::EditorPropertyArrayObject() {
+}
+
+///////////////////
+
+bool EditorPropertyDictionaryObject::_set(const StringName &p_name, const Variant &p_value) {
+
+ String pn = p_name;
+
+ if (pn == "new_item_key") {
+
+ new_item_key = p_value;
+ return true;
+ }
+
+ if (pn == "new_item_value") {
+
+ new_item_value = p_value;
+ return true;
+ }
+
+ if (pn.begins_with("indices")) {
+ int idx = pn.get_slicec('/', 1).to_int();
+ Variant key = dict.get_key_at_index(idx);
+ dict[key] = p_value;
+ return true;
+ }
+
+ return false;
+}
+
+bool EditorPropertyDictionaryObject::_get(const StringName &p_name, Variant &r_ret) const {
+
+ String pn = p_name;
+
+ if (pn == "new_item_key") {
+
+ r_ret = new_item_key;
+ return true;
+ }
+
+ if (pn == "new_item_value") {
+
+ r_ret = new_item_value;
+ return true;
+ }
+
+ if (pn.begins_with("indices")) {
+
+ int idx = pn.get_slicec('/', 1).to_int();
+ Variant key = dict.get_key_at_index(idx);
+ r_ret = dict[key];
+ return true;
+ }
+
+ return false;
+}
+
+void EditorPropertyDictionaryObject::set_dict(const Dictionary &p_dict) {
+ dict = p_dict;
+}
+
+Dictionary EditorPropertyDictionaryObject::get_dict() {
+ return dict;
+}
+
+void EditorPropertyDictionaryObject::set_new_item_key(const Variant &p_new_item) {
+ new_item_key = p_new_item;
+}
+
+Variant EditorPropertyDictionaryObject::get_new_item_key() {
+ return new_item_key;
+}
+
+void EditorPropertyDictionaryObject::set_new_item_value(const Variant &p_new_item) {
+ new_item_value = p_new_item;
+}
+
+Variant EditorPropertyDictionaryObject::get_new_item_value() {
+ return new_item_value;
+}
+
+EditorPropertyDictionaryObject::EditorPropertyDictionaryObject() {
+}
+
+///////////////////// ARRAY ///////////////////////////
+
+void EditorPropertyArray::_property_changed(const String &p_prop, Variant p_value) {
+
+ if (p_prop.begins_with("indices")) {
+ int idx = p_prop.get_slice("/", 1).to_int();
+ Variant array = object->get_array();
+ array.set(idx, p_value);
+ emit_signal("property_changed", get_edited_property(), array);
+
+ if (array.get_type() == Variant::ARRAY) {
+ array = array.call("duplicate"); //dupe, so undo/redo works better
+ }
+ object->set_array(array);
+ }
+}
+
+void EditorPropertyArray::_change_type(Object *p_button, int p_index) {
+
+ Button *button = Object::cast_to<Button>(p_button);
+
+ Rect2 rect = button->get_global_rect();
+ change_type->set_as_minsize();
+ change_type->set_global_position(rect.position + rect.size - Vector2(change_type->get_combined_minimum_size().x, 0));
+ change_type->popup();
+ changing_type_idx = p_index;
+}
+
+void EditorPropertyArray::_change_type_menu(int p_index) {
+
+ Variant value;
+ Variant::CallError ce;
+ value = Variant::construct(Variant::Type(p_index), NULL, 0, ce);
+ Variant array = object->get_array();
+ array.set(changing_type_idx, value);
+
+ emit_signal("property_changed", get_edited_property(), array);
+
+ if (array.get_type() == Variant::ARRAY) {
+ array = array.call("duplicate"); //dupe, so undo/redo works better
+ }
+ object->set_array(array);
+ update_property();
+}
+
+void EditorPropertyArray::update_property() {
+
+ Variant array = get_edited_object()->get(get_edited_property());
+
+ String arrtype = "";
+ switch (array_type) {
+ case Variant::ARRAY: {
+ arrtype = "Array";
+
+ } break;
+
+ // arrays
+ case Variant::POOL_BYTE_ARRAY: {
+ arrtype = "ByteArray";
+
+ } break;
+ case Variant::POOL_INT_ARRAY: {
+ arrtype = "IntArray";
+
+ } break;
+ case Variant::POOL_REAL_ARRAY: {
+
+ arrtype = "FltArray";
+ } break;
+ case Variant::POOL_STRING_ARRAY: {
+
+ arrtype = "StrArray";
+ } break;
+ case Variant::POOL_VECTOR2_ARRAY: {
+
+ arrtype = "Vec2Array";
+ } break;
+ case Variant::POOL_VECTOR3_ARRAY: {
+ arrtype = "Vec3Array";
+
+ } break;
+ case Variant::POOL_COLOR_ARRAY: {
+ arrtype = "ColArray";
+ } break;
+ 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
+
+ bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
+ if (edit->is_pressed() != unfolded) {
+ edit->set_pressed(unfolded);
+ }
+
+ if (unfolded) {
+
+ updating = true;
+
+ if (!vbox) {
+
+ vbox = memnew(VBoxContainer);
+ add_child(vbox);
+ set_bottom_editor(vbox);
+ HBoxContainer *hbc = memnew(HBoxContainer);
+ vbox->add_child(hbc);
+ Label *label = memnew(Label(TTR("Size: ")));
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+ hbc->add_child(label);
+ length = memnew(EditorSpinSlider);
+ length->set_step(1);
+ length->set_max(1000000);
+ length->set_h_size_flags(SIZE_EXPAND_FILL);
+ hbc->add_child(length);
+ length->connect("value_changed", this, "_length_changed");
+
+ page_hb = memnew(HBoxContainer);
+ vbox->add_child(page_hb);
+ label = memnew(Label(TTR("Page: ")));
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+ page_hb->add_child(label);
+ page = memnew(EditorSpinSlider);
+ page->set_step(1);
+ page_hb->add_child(page);
+ page->set_h_size_flags(SIZE_EXPAND_FILL);
+ page->connect("value_changed", this, "_page_changed");
+ } else {
+ //bye bye children of the box
+ while (vbox->get_child_count() > 2) {
+ memdelete(vbox->get_child(2));
+ }
+ }
+
+ int len = array.call("size");
+
+ length->set_value(len);
+
+ int pages = MAX(0, len - 1) / page_len + 1;
+
+ page->set_max(pages);
+ page_idx = MIN(page_idx, pages - 1);
+ page->set_value(page_idx);
+ page_hb->set_visible(pages > 1);
+
+ int offset = page_idx * page_len;
+
+ int amount = MIN(len - offset, page_len);
+
+ if (array.get_type() == Variant::ARRAY) {
+ array = array.call("duplicate");
+ }
+
+ object->set_array(array);
+
+ for (int i = 0; i < amount; i++) {
+ String prop_name = "indices/" + itos(i + offset);
+
+ EditorProperty *prop = NULL;
+ Variant value = array.get(i + offset);
+
+ switch (value.get_type()) {
+ case Variant::NIL: {
+ prop = memnew(EditorPropertyNil);
+
+ } break;
+
+ // atomic types
+ case Variant::BOOL: {
+
+ prop = memnew(EditorPropertyCheck);
+
+ } break;
+ case Variant::INT: {
+ EditorPropertyInteger *ed = memnew(EditorPropertyInteger);
+ ed->setup(-100000, 100000, true, true);
+ prop = ed;
+
+ } break;
+ case Variant::REAL: {
+
+ EditorPropertyFloat *ed = memnew(EditorPropertyFloat);
+ ed->setup(-100000, 100000, 0.001, true, false, true, true);
+ prop = ed;
+ } break;
+ case Variant::STRING: {
+
+ prop = memnew(EditorPropertyText);
+
+ } break;
+
+ // math types
+
+ case Variant::VECTOR2: {
+
+ EditorPropertyVector2 *ed = memnew(EditorPropertyVector2);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::RECT2: {
+
+ EditorPropertyRect2 *ed = memnew(EditorPropertyRect2);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::VECTOR3: {
+
+ EditorPropertyVector3 *ed = memnew(EditorPropertyVector3);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::TRANSFORM2D: {
+
+ EditorPropertyTransform2D *ed = memnew(EditorPropertyTransform2D);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::PLANE: {
+
+ EditorPropertyPlane *ed = memnew(EditorPropertyPlane);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::QUAT: {
+
+ EditorPropertyQuat *ed = memnew(EditorPropertyQuat);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::AABB: {
+
+ EditorPropertyAABB *ed = memnew(EditorPropertyAABB);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::BASIS: {
+ EditorPropertyBasis *ed = memnew(EditorPropertyBasis);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::TRANSFORM: {
+ EditorPropertyTransform *ed = memnew(EditorPropertyTransform);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+
+ // misc types
+ case Variant::COLOR: {
+ prop = memnew(EditorPropertyColor);
+
+ } break;
+ case Variant::NODE_PATH: {
+ prop = memnew(EditorPropertyNodePath);
+
+ } break;
+ case Variant::_RID: {
+ prop = memnew(EditorPropertyNil);
+
+ } break;
+ case Variant::OBJECT: {
+
+ prop = memnew(EditorPropertyResource);
+
+ } break;
+ case Variant::DICTIONARY: {
+ prop = memnew(EditorPropertyDictionary);
+
+ } break;
+
+ // arrays
+ case Variant::ARRAY: {
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::ARRAY);
+ prop = editor;
+
+ } break;
+ case Variant::POOL_BYTE_ARRAY: {
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_BYTE_ARRAY);
+ prop = editor;
+
+ } break;
+ case Variant::POOL_INT_ARRAY: {
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_INT_ARRAY);
+ prop = editor;
+
+ } break;
+ case Variant::POOL_REAL_ARRAY: {
+
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_REAL_ARRAY);
+ prop = editor;
+ } break;
+ case Variant::POOL_STRING_ARRAY: {
+
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_STRING_ARRAY);
+ prop = editor;
+ } break;
+ case Variant::POOL_VECTOR2_ARRAY: {
+
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_VECTOR2_ARRAY);
+ prop = editor;
+ } break;
+ case Variant::POOL_VECTOR3_ARRAY: {
+
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_VECTOR3_ARRAY);
+ prop = editor;
+ } break;
+ case Variant::POOL_COLOR_ARRAY: {
+
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_COLOR_ARRAY);
+ prop = editor;
+ } break;
+ default: {}
+ }
+
+ prop->set_object_and_property(object.ptr(), prop_name);
+ prop->set_label(itos(i + offset));
+ prop->set_selectable(false);
+ prop->connect("property_changed", this, "_property_changed");
+ if (array.get_type() == Variant::ARRAY) {
+ HBoxContainer *hb = memnew(HBoxContainer);
+ vbox->add_child(hb);
+ hb->add_child(prop);
+ prop->set_h_size_flags(SIZE_EXPAND_FILL);
+ Button *edit = memnew(Button);
+ edit->set_icon(get_icon("Edit", "EditorIcons"));
+ hb->add_child(edit);
+ edit->connect("pressed", this, "_change_type", varray(edit, i + offset));
+ } else {
+ vbox->add_child(prop);
+ }
+
+ prop->update_property();
+ }
+
+ updating = false;
+
+ } else {
+ if (vbox) {
+ set_bottom_editor(NULL);
+ memdelete(vbox);
+ vbox = NULL;
+ }
+ }
+#endif
+}
+
+void EditorPropertyArray::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ }
+}
+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();
+}
+
+void EditorPropertyArray::_page_changed(double p_page) {
+ if (updating)
+ return;
+ page_idx = p_page;
+ update_property();
+}
+
+void EditorPropertyArray::_length_changed(double p_page) {
+ if (updating)
+ return;
+
+ Variant array = object->get_array();
+ array.call("resize", int(p_page));
+ emit_signal("property_changed", get_edited_property(), array);
+
+ if (array.get_type() == Variant::ARRAY) {
+ array = array.call("duplicate"); //dupe, so undo/redo works better
+ }
+ object->set_array(array);
+ 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);
+ ClassDB::bind_method("_length_changed", &EditorPropertyArray::_length_changed);
+ ClassDB::bind_method("_property_changed", &EditorPropertyArray::_property_changed);
+ ClassDB::bind_method("_change_type", &EditorPropertyArray::_change_type);
+ ClassDB::bind_method("_change_type_menu", &EditorPropertyArray::_change_type_menu);
+}
+
+EditorPropertyArray::EditorPropertyArray() {
+
+ object.instance();
+ page_idx = 0;
+ page_len = 10;
+ edit = memnew(Button);
+ edit->set_flat(true);
+ edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ edit->set_clip_text(true);
+ edit->connect("pressed", this, "_edit_pressed");
+ edit->set_toggle_mode(true);
+ add_child(edit);
+ add_focusable(edit);
+ vbox = NULL;
+ page = NULL;
+ length = NULL;
+ updating = false;
+ change_type = memnew(PopupMenu);
+ add_child(change_type);
+ change_type->connect("id_pressed", this, "_change_type_menu");
+ changing_type_idx = -1;
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ String type = Variant::get_type_name(Variant::Type(i));
+ change_type->add_item(type, i);
+ }
+ changing_type_idx = -1;
+}
+
+///////////////////// DICTIONARY ///////////////////////////
+
+void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p_value) {
+
+ if (p_prop == "new_item_key") {
+
+ object->set_new_item_key(p_value);
+ } else if (p_prop == "new_item_value") {
+
+ object->set_new_item_value(p_value);
+ } else if (p_prop.begins_with("indices")) {
+ int idx = p_prop.get_slice("/", 1).to_int();
+ Dictionary dict = object->get_dict();
+ Variant key = dict.get_key_at_index(idx);
+ dict[key] = p_value;
+
+ emit_signal("property_changed", get_edited_property(), dict);
+
+ dict = dict.duplicate(); //dupe, so undo/redo works better
+ object->set_dict(dict);
+ }
+}
+
+void EditorPropertyDictionary::_change_type(Object *p_button, int p_index) {
+
+ Button *button = Object::cast_to<Button>(p_button);
+
+ Rect2 rect = button->get_global_rect();
+ change_type->set_as_minsize();
+ change_type->set_global_position(rect.position + rect.size - Vector2(change_type->get_combined_minimum_size().x, 0));
+ change_type->popup();
+ changing_type_idx = p_index;
+}
+
+void EditorPropertyDictionary::_add_key_value() {
+
+ Dictionary dict = object->get_dict();
+ dict[object->get_new_item_key()] = object->get_new_item_value();
+ object->set_new_item_key(Variant());
+ object->set_new_item_value(Variant());
+
+ emit_signal("property_changed", get_edited_property(), dict);
+
+ dict = dict.duplicate(); //dupe, so undo/redo works better
+ object->set_dict(dict);
+ update_property();
+}
+
+void EditorPropertyDictionary::_change_type_menu(int p_index) {
+
+ if (changing_type_idx < 0) {
+ Variant value;
+ Variant::CallError ce;
+ value = Variant::construct(Variant::Type(p_index), NULL, 0, ce);
+ if (changing_type_idx == -1) {
+ object->set_new_item_key(value);
+ } else {
+ object->set_new_item_value(value);
+ }
+ update_property();
+ return;
+ }
+
+ Dictionary dict = object->get_dict();
+
+ if (p_index < Variant::VARIANT_MAX) {
+
+ Variant value;
+ Variant::CallError ce;
+ value = Variant::construct(Variant::Type(p_index), NULL, 0, ce);
+ Variant key = dict.get_key_at_index(changing_type_idx);
+ dict[key] = value;
+ } else {
+ Variant key = dict.get_key_at_index(changing_type_idx);
+ dict.erase(key);
+ }
+
+ emit_signal("property_changed", get_edited_property(), dict);
+
+ dict = dict.duplicate(); //dupe, so undo/redo works better
+ object->set_dict(dict);
+ update_property();
+}
+
+void EditorPropertyDictionary::update_property() {
+
+ Dictionary dict = get_edited_object()->get(get_edited_property());
+
+ edit->set_text("Dict[" + itos(dict.size()) + "]");
+
+#ifdef TOOLS_ENABLED
+
+ bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
+ if (edit->is_pressed() != unfolded) {
+ edit->set_pressed(unfolded);
+ }
+
+ if (unfolded) {
+
+ updating = true;
+
+ if (!vbox) {
+
+ vbox = memnew(VBoxContainer);
+ add_child(vbox);
+ set_bottom_editor(vbox);
+
+ page_hb = memnew(HBoxContainer);
+ vbox->add_child(page_hb);
+ Label *label = memnew(Label(TTR("Page: ")));
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+ page_hb->add_child(label);
+ page = memnew(EditorSpinSlider);
+ page->set_step(1);
+ page_hb->add_child(page);
+ page->set_h_size_flags(SIZE_EXPAND_FILL);
+ page->connect("value_changed", this, "_page_changed");
+ } else {
+ //bye bye children of the box
+ while (vbox->get_child_count() > 1) {
+ memdelete(vbox->get_child(1));
+ }
+ }
+
+ int len = dict.size();
+
+ int pages = MAX(0, len - 1) / page_len + 1;
+
+ page->set_max(pages);
+ page_idx = MIN(page_idx, pages - 1);
+ page->set_value(page_idx);
+ page_hb->set_visible(pages > 1);
+
+ int offset = page_idx * page_len;
+
+ int amount = MIN(len - offset, page_len);
+
+ dict = dict.duplicate();
+
+ object->set_dict(dict);
+ VBoxContainer *add_vbox = NULL;
+
+ for (int i = 0; i < amount + 2; i++) {
+ String prop_name;
+ Variant key;
+ Variant value;
+
+ if (i < amount) {
+ prop_name = "indices/" + itos(i + offset);
+ key = dict.get_key_at_index(i + offset);
+ value = dict.get_value_at_index(i + offset);
+ } else if (i == amount) {
+ prop_name = "new_item_key";
+ value = object->get_new_item_key();
+ } else if (i == amount + 1) {
+ prop_name = "new_item_value";
+ value = object->get_new_item_value();
+ }
+
+ EditorProperty *prop = NULL;
+
+ switch (value.get_type()) {
+ case Variant::NIL: {
+ prop = memnew(EditorPropertyNil);
+
+ } break;
+
+ // atomic types
+ case Variant::BOOL: {
+
+ prop = memnew(EditorPropertyCheck);
+
+ } break;
+ case Variant::INT: {
+ EditorPropertyInteger *ed = memnew(EditorPropertyInteger);
+ ed->setup(-100000, 100000, true, true);
+ prop = ed;
+
+ } break;
+ case Variant::REAL: {
+
+ EditorPropertyFloat *ed = memnew(EditorPropertyFloat);
+ ed->setup(-100000, 100000, 0.001, true, false, true, true);
+ prop = ed;
+ } break;
+ case Variant::STRING: {
+
+ prop = memnew(EditorPropertyText);
+
+ } break;
+
+ // math types
+
+ case Variant::VECTOR2: {
+
+ EditorPropertyVector2 *ed = memnew(EditorPropertyVector2);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::RECT2: {
+
+ EditorPropertyRect2 *ed = memnew(EditorPropertyRect2);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::VECTOR3: {
+
+ EditorPropertyVector3 *ed = memnew(EditorPropertyVector3);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::TRANSFORM2D: {
+
+ EditorPropertyTransform2D *ed = memnew(EditorPropertyTransform2D);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::PLANE: {
+
+ EditorPropertyPlane *ed = memnew(EditorPropertyPlane);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::QUAT: {
+
+ EditorPropertyQuat *ed = memnew(EditorPropertyQuat);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::AABB: {
+
+ EditorPropertyAABB *ed = memnew(EditorPropertyAABB);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::BASIS: {
+ EditorPropertyBasis *ed = memnew(EditorPropertyBasis);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+ case Variant::TRANSFORM: {
+ EditorPropertyTransform *ed = memnew(EditorPropertyTransform);
+ ed->setup(-100000, 100000, 0.001, true);
+ prop = ed;
+
+ } break;
+
+ // misc types
+ case Variant::COLOR: {
+ prop = memnew(EditorPropertyColor);
+
+ } break;
+ case Variant::NODE_PATH: {
+ prop = memnew(EditorPropertyNodePath);
+
+ } break;
+ case Variant::_RID: {
+ prop = memnew(EditorPropertyNil);
+
+ } break;
+ case Variant::OBJECT: {
+
+ prop = memnew(EditorPropertyResource);
+
+ } break;
+ case Variant::DICTIONARY: {
+ prop = memnew(EditorPropertyDictionary);
+
+ } break;
+ case Variant::ARRAY: {
+
+ prop = memnew(EditorPropertyArray);
+
+ } break;
+
+ // arrays
+ case Variant::POOL_BYTE_ARRAY: {
+ prop = memnew(EditorPropertyArray);
+
+ } break;
+ case Variant::POOL_INT_ARRAY: {
+ prop = memnew(EditorPropertyArray);
+
+ } break;
+ case Variant::POOL_REAL_ARRAY: {
+
+ prop = memnew(EditorPropertyArray);
+ } break;
+ case Variant::POOL_STRING_ARRAY: {
+
+ prop = memnew(EditorPropertyArray);
+ } break;
+ case Variant::POOL_VECTOR2_ARRAY: {
+
+ prop = memnew(EditorPropertyArray);
+ } break;
+ case Variant::POOL_VECTOR3_ARRAY: {
+ prop = memnew(EditorPropertyArray);
+
+ } break;
+ case Variant::POOL_COLOR_ARRAY: {
+ prop = memnew(EditorPropertyArray);
+
+ } break;
+ default: {}
+ }
+
+ if (i == amount) {
+ PanelContainer *pc = memnew(PanelContainer);
+ vbox->add_child(pc);
+ Ref<StyleBoxFlat> flat;
+ flat.instance();
+ for (int j = 0; j < 4; j++) {
+ flat->set_default_margin(Margin(j), 2 * EDSCALE);
+ }
+ flat->set_bg_color(get_color("prop_subsection", "Editor"));
+
+ pc->add_style_override("panel", flat);
+ add_vbox = memnew(VBoxContainer);
+ pc->add_child(add_vbox);
+ }
+ prop->set_object_and_property(object.ptr(), prop_name);
+ int change_index;
+
+ if (i < amount) {
+ String cs = key.get_construct_string();
+ prop->set_label(key.get_construct_string());
+ prop->set_tooltip(cs);
+ change_index = i + offset;
+ } else if (i == amount) {
+ prop->set_label(TTR("New Key:"));
+ change_index = -1;
+ } else if (i == amount + 1) {
+ prop->set_label(TTR("New Value:"));
+ change_index = -2;
+ }
+
+ prop->set_selectable(false);
+ prop->connect("property_changed", this, "_property_changed");
+
+ HBoxContainer *hb = memnew(HBoxContainer);
+ if (add_vbox) {
+ add_vbox->add_child(hb);
+ } else {
+ vbox->add_child(hb);
+ }
+ hb->add_child(prop);
+ prop->set_h_size_flags(SIZE_EXPAND_FILL);
+ Button *edit = memnew(Button);
+ edit->set_icon(get_icon("Edit", "EditorIcons"));
+ hb->add_child(edit);
+ edit->connect("pressed", this, "_change_type", varray(edit, change_index));
+
+ prop->update_property();
+
+ if (i == amount + 1) {
+ Button *add_item = memnew(Button);
+ add_item->set_text(TTR("Add Key/Value Pair"));
+ add_vbox->add_child(add_item);
+ add_item->connect("pressed", this, "_add_key_value");
+ }
+ }
+
+ updating = false;
+
+ } else {
+ if (vbox) {
+ set_bottom_editor(NULL);
+ memdelete(vbox);
+ vbox = NULL;
+ }
+ }
+#endif
+}
+
+void EditorPropertyDictionary::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ }
+}
+void EditorPropertyDictionary::_edit_pressed() {
+
+ get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed());
+ update_property();
+}
+
+void EditorPropertyDictionary::_page_changed(double p_page) {
+ if (updating)
+ return;
+ page_idx = p_page;
+ update_property();
+}
+
+void EditorPropertyDictionary::_bind_methods() {
+ ClassDB::bind_method("_edit_pressed", &EditorPropertyDictionary::_edit_pressed);
+ ClassDB::bind_method("_page_changed", &EditorPropertyDictionary::_page_changed);
+ ClassDB::bind_method("_property_changed", &EditorPropertyDictionary::_property_changed);
+ ClassDB::bind_method("_change_type", &EditorPropertyDictionary::_change_type);
+ ClassDB::bind_method("_change_type_menu", &EditorPropertyDictionary::_change_type_menu);
+ ClassDB::bind_method("_add_key_value", &EditorPropertyDictionary::_add_key_value);
+}
+
+EditorPropertyDictionary::EditorPropertyDictionary() {
+
+ object.instance();
+ page_idx = 0;
+ page_len = 10;
+ edit = memnew(Button);
+ edit->set_flat(true);
+ edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ edit->set_clip_text(true);
+ edit->connect("pressed", this, "_edit_pressed");
+ edit->set_toggle_mode(true);
+ add_child(edit);
+ add_focusable(edit);
+ vbox = NULL;
+ page = NULL;
+ updating = false;
+ change_type = memnew(PopupMenu);
+ add_child(change_type);
+ change_type->connect("id_pressed", this, "_change_type_menu");
+ changing_type_idx = -1;
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ String type = Variant::get_type_name(Variant::Type(i));
+ change_type->add_item(type, i);
+ }
+ change_type->add_separator();
+ change_type->add_item(TTR("Remove Item"), Variant::VARIANT_MAX);
+ changing_type_idx = -1;
+}
diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h
new file mode 100644
index 0000000000..75c67d280d
--- /dev/null
+++ b/editor/editor_properties_array_dict.h
@@ -0,0 +1,117 @@
+#ifndef EDITOR_PROPERTIES_ARRAY_DICT_H
+#define EDITOR_PROPERTIES_ARRAY_DICT_H
+
+#include "editor/editor_inspector.h"
+#include "editor/editor_spin_slider.h"
+#include "scene/gui/button.h"
+
+class EditorPropertyArrayObject : public Reference {
+
+ GDCLASS(EditorPropertyArrayObject, Reference);
+
+ Variant array;
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+
+public:
+ void set_array(const Variant &p_array);
+ Variant get_array();
+
+ EditorPropertyArrayObject();
+};
+
+class EditorPropertyDictionaryObject : public Reference {
+
+ GDCLASS(EditorPropertyDictionaryObject, Reference);
+
+ Variant new_item_key;
+ Variant new_item_value;
+ Dictionary dict;
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+
+public:
+ void set_dict(const Dictionary &p_dict);
+ Dictionary get_dict();
+
+ void set_new_item_key(const Variant &p_new_item);
+ Variant get_new_item_key();
+
+ void set_new_item_value(const Variant &p_new_item);
+ Variant get_new_item_value();
+
+ EditorPropertyDictionaryObject();
+};
+
+class EditorPropertyArray : public EditorProperty {
+ GDCLASS(EditorPropertyArray, EditorProperty)
+
+ PopupMenu *change_type;
+ bool updating;
+
+ Ref<EditorPropertyArrayObject> object;
+ int page_len;
+ int page_idx;
+ int changing_type_idx;
+ Button *edit;
+ VBoxContainer *vbox;
+ EditorSpinSlider *length;
+ EditorSpinSlider *page;
+ HBoxContainer *page_hb;
+ Variant::Type array_type;
+
+ void _page_changed(double p_page);
+ void _length_changed(double p_page);
+ void _edit_pressed();
+ void _property_changed(const String &p_prop, Variant p_value);
+ void _change_type(Object *p_button, int p_index);
+ void _change_type_menu(int p_index);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ void setup(Variant::Type p_array_type);
+ virtual void update_property();
+ EditorPropertyArray();
+};
+
+class EditorPropertyDictionary : public EditorProperty {
+ GDCLASS(EditorPropertyDictionary, EditorProperty)
+
+ PopupMenu *change_type;
+ bool updating;
+
+ Ref<EditorPropertyDictionaryObject> object;
+ int page_len;
+ int page_idx;
+ int changing_type_idx;
+ Button *edit;
+ VBoxContainer *vbox;
+ EditorSpinSlider *length;
+ EditorSpinSlider *page;
+ HBoxContainer *page_hb;
+
+ void _page_changed(double p_page);
+ void _edit_pressed();
+ void _property_changed(const String &p_prop, Variant p_value);
+ void _change_type(Object *p_button, int p_index);
+ void _change_type_menu(int p_index);
+
+ void _add_key_value();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ virtual void update_property();
+ EditorPropertyDictionary();
+};
+
+#endif // EDITOR_PROPERTIES_ARRAY_DICT_H
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index c4e6d18163..4045d6c3d3 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -298,8 +298,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
hints["interface/editor/code_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/code_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/editor/main_font", "");
hints["interface/editor/main_font"] = PropertyInfo(Variant::STRING, "interface/editor/main_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
- _initial_set("interface/editor/main__bold_font", "");
- hints["interface/editor/main_font_bold"] = PropertyInfo(Variant::STRING, "interface/editor/main_bold_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
+ _initial_set("interface/editor/main_font_bold", "");
+ hints["interface/editor/main_font_bold"] = PropertyInfo(Variant::STRING, "interface/editor/main_font_bold", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/editor/code_font", "");
hints["interface/editor/code_font"] = PropertyInfo(Variant::STRING, "interface/editor/code_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/editor/dim_editor_on_dialog_popup", true);
@@ -313,8 +313,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,Grey,Godot 2,Arc,Light,Alien,Custom", 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"));
@@ -371,6 +371,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/line_numbers/line_numbers_zero_padded", false);
_initial_set("text_editor/line_numbers/show_breakpoint_gutter", true);
_initial_set("text_editor/line_numbers/code_folding", true);
+ _initial_set("text_editor/line_numbers/word_wrap", false);
_initial_set("text_editor/line_numbers/show_line_length_guideline", false);
_initial_set("text_editor/line_numbers/line_length_guideline_column", 80);
hints["text_editor/line_numbers/line_length_guideline_column"] = PropertyInfo(Variant::INT, "text_editor/line_numbers/line_length_guideline_column", PROPERTY_HINT_RANGE, "20, 160, 1");
@@ -382,6 +383,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/files/trim_trailing_whitespace_on_save", false);
_initial_set("text_editor/completion/idle_parse_delay", 2);
_initial_set("text_editor/tools/create_signal_callbacks", true);
+ _initial_set("text_editor/tools/sort_members_outline_alphabetically", false);
_initial_set("text_editor/files/autosave_interval_secs", 0);
_initial_set("text_editor/cursor/block_caret", false);
@@ -566,34 +568,37 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
}
void EditorSettings::_load_default_text_editor_theme() {
- _initial_set("text_editor/highlighting/background_color", Color::html("3b000000"));
+
+ bool dark_theme = is_dark_theme();
+
+ _initial_set("text_editor/highlighting/symbol_color", Color::html("badfff"));
+ _initial_set("text_editor/highlighting/keyword_color", Color::html("ffffb3"));
+ _initial_set("text_editor/highlighting/base_type_color", Color::html("a4ffd4"));
+ _initial_set("text_editor/highlighting/engine_type_color", Color::html("83d3ff"));
+ _initial_set("text_editor/highlighting/comment_color", Color::html("676767"));
+ _initial_set("text_editor/highlighting/string_color", Color::html("ef6ebe"));
+ _initial_set("text_editor/highlighting/background_color", dark_theme ? Color::html("3b000000") : Color::html("#323b4f"));
_initial_set("text_editor/highlighting/completion_background_color", Color::html("2C2A32"));
_initial_set("text_editor/highlighting/completion_selected_color", Color::html("434244"));
_initial_set("text_editor/highlighting/completion_existing_color", Color::html("21dfdfdf"));
_initial_set("text_editor/highlighting/completion_scroll_color", Color::html("ffffff"));
_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/caret_color", Color::html("aaaaaa"));
_initial_set("text_editor/highlighting/caret_background_color", Color::html("000000"));
- _initial_set("text_editor/highlighting/line_number_color", Color::html("66aaaaaa"));
- _initial_set("text_editor/highlighting/text_color", Color::html("aaaaaa"));
_initial_set("text_editor/highlighting/text_selected_color", Color::html("000000"));
- _initial_set("text_editor/highlighting/keyword_color", Color::html("ffffb3"));
- _initial_set("text_editor/highlighting/base_type_color", Color::html("a4ffd4"));
- _initial_set("text_editor/highlighting/engine_type_color", Color::html("83d3ff"));
- _initial_set("text_editor/highlighting/function_color", Color::html("66a2ce"));
- _initial_set("text_editor/highlighting/member_variable_color", Color::html("e64e59"));
- _initial_set("text_editor/highlighting/comment_color", Color::html("676767"));
- _initial_set("text_editor/highlighting/string_color", Color::html("ef6ebe"));
- _initial_set("text_editor/highlighting/number_color", Color::html("EB9532"));
- _initial_set("text_editor/highlighting/symbol_color", Color::html("badfff"));
_initial_set("text_editor/highlighting/selection_color", Color::html("6ca9c2"));
_initial_set("text_editor/highlighting/brace_mismatch_color", Color(1, 0.2, 0.2));
_initial_set("text_editor/highlighting/current_line_color", Color(0.3, 0.5, 0.8, 0.15));
_initial_set("text_editor/highlighting/line_length_guideline_color", Color(0.3, 0.5, 0.8, 0.1));
+ _initial_set("text_editor/highlighting/word_highlighted_color", Color(0.8, 0.9, 0.9, 0.15));
+ _initial_set("text_editor/highlighting/number_color", Color::html("EB9532"));
+ _initial_set("text_editor/highlighting/function_color", Color::html("66a2ce"));
+ _initial_set("text_editor/highlighting/member_variable_color", Color::html("e64e59"));
_initial_set("text_editor/highlighting/mark_color", Color(1.0, 0.4, 0.4, 0.4));
_initial_set("text_editor/highlighting/breakpoint_color", Color(0.8, 0.8, 0.4, 0.2));
_initial_set("text_editor/highlighting/code_folding_color", Color(0.8, 0.8, 0.8, 0.8));
- _initial_set("text_editor/highlighting/word_highlighted_color", Color(0.8, 0.9, 0.9, 0.15));
_initial_set("text_editor/highlighting/search_result_color", Color(0.05, 0.25, 0.05, 1));
_initial_set("text_editor/highlighting/search_result_border_color", Color(0.1, 0.45, 0.1, 1));
}
@@ -601,36 +606,17 @@ void EditorSettings::_load_default_text_editor_theme() {
bool EditorSettings::_save_text_editor_theme(String p_file) {
String theme_section = "color_theme";
Ref<ConfigFile> cf = memnew(ConfigFile); // hex is better?
- cf->set_value(theme_section, "background_color", ((Color)get("text_editor/highlighting/background_color")).to_html());
- cf->set_value(theme_section, "completion_background_color", ((Color)get("text_editor/highlighting/completion_background_color")).to_html());
- cf->set_value(theme_section, "completion_selected_color", ((Color)get("text_editor/highlighting/completion_selected_color")).to_html());
- cf->set_value(theme_section, "completion_existing_color", ((Color)get("text_editor/highlighting/completion_existing_color")).to_html());
- cf->set_value(theme_section, "completion_scroll_color", ((Color)get("text_editor/highlighting/completion_scroll_color")).to_html());
- cf->set_value(theme_section, "completion_font_color", ((Color)get("text_editor/highlighting/completion_font_color")).to_html());
- cf->set_value(theme_section, "caret_color", ((Color)get("text_editor/highlighting/caret_color")).to_html());
- cf->set_value(theme_section, "caret_background_color", ((Color)get("text_editor/highlighting/caret_background_color")).to_html());
- cf->set_value(theme_section, "line_number_color", ((Color)get("text_editor/highlighting/line_number_color")).to_html());
- cf->set_value(theme_section, "text_color", ((Color)get("text_editor/highlighting/text_color")).to_html());
- cf->set_value(theme_section, "text_selected_color", ((Color)get("text_editor/highlighting/text_selected_color")).to_html());
- cf->set_value(theme_section, "keyword_color", ((Color)get("text_editor/highlighting/keyword_color")).to_html());
- cf->set_value(theme_section, "base_type_color", ((Color)get("text_editor/highlighting/base_type_color")).to_html());
- cf->set_value(theme_section, "engine_type_color", ((Color)get("text_editor/highlighting/engine_type_color")).to_html());
- cf->set_value(theme_section, "function_color", ((Color)get("text_editor/highlighting/function_color")).to_html());
- cf->set_value(theme_section, "member_variable_color", ((Color)get("text_editor/highlighting/member_variable_color")).to_html());
- cf->set_value(theme_section, "comment_color", ((Color)get("text_editor/highlighting/comment_color")).to_html());
- cf->set_value(theme_section, "string_color", ((Color)get("text_editor/highlighting/string_color")).to_html());
- cf->set_value(theme_section, "number_color", ((Color)get("text_editor/highlighting/number_color")).to_html());
- cf->set_value(theme_section, "symbol_color", ((Color)get("text_editor/highlighting/symbol_color")).to_html());
- cf->set_value(theme_section, "selection_color", ((Color)get("text_editor/highlighting/selection_color")).to_html());
- cf->set_value(theme_section, "brace_mismatch_color", ((Color)get("text_editor/highlighting/brace_mismatch_color")).to_html());
- cf->set_value(theme_section, "current_line_color", ((Color)get("text_editor/highlighting/current_line_color")).to_html());
- cf->set_value(theme_section, "line_length_guideline_color", ((Color)get("text_editor/highlighting/line_length_guideline_color")).to_html());
- cf->set_value(theme_section, "mark_color", ((Color)get("text_editor/highlighting/mark_color")).to_html());
- cf->set_value(theme_section, "breakpoint_color", ((Color)get("text_editor/highlighting/breakpoint_color")).to_html());
- cf->set_value(theme_section, "code_folding_color", ((Color)get("text_editor/highlighting/code_folding_color")).to_html());
- cf->set_value(theme_section, "word_highlighted_color", ((Color)get("text_editor/highlighting/word_highlighted_color")).to_html());
- cf->set_value(theme_section, "search_result_color", ((Color)get("text_editor/highlighting/search_result_color")).to_html());
- cf->set_value(theme_section, "search_result_border_color", ((Color)get("text_editor/highlighting/search_result_border_color")).to_html());
+
+ List<String> keys;
+ props.get_key_list(&keys);
+ keys.sort();
+
+ for (const List<String>::Element *E = keys.front(); E; E = E->next()) {
+ String key = E->get();
+ if (key.begins_with("text_editor/highlighting/") && key.find("color") >= 0) {
+ cf->set_value(theme_section, key.replace("text_editor/highlighting/", ""), ((Color)props[key].variant).to_html());
+ }
+ }
Error err = cf->save(p_file);
@@ -1141,7 +1127,7 @@ void EditorSettings::set_project_metadata(const String &p_section, const String
cf->save(path);
}
-Variant EditorSettings::get_project_metadata(const String &p_section, const String &p_key, Variant p_default) {
+Variant EditorSettings::get_project_metadata(const String &p_section, const String &p_key, Variant p_default) const {
Ref<ConfigFile> cf = memnew(ConfigFile);
String path = get_project_settings_dir().plus_file("project_metadata.cfg");
Error err = cf->load(path);
@@ -1206,6 +1192,14 @@ void EditorSettings::load_favorites() {
}
}
+bool EditorSettings::is_dark_theme() {
+ int AUTO_COLOR = 0;
+ int LIGHT_COLOR = 2;
+ Color base_color = get("interface/theme/base_color");
+ int icon_font_color_setting = get("interface/theme/icon_and_font_color");
+ return (icon_font_color_setting == AUTO_COLOR && ((base_color.r + base_color.g + base_color.b) / 3.0) < 0.5) || icon_font_color_setting == LIGHT_COLOR;
+}
+
void EditorSettings::list_text_editor_themes() {
String themes = "Adaptive,Default,Custom";
DirAccess *d = DirAccess::open(get_text_editor_themes_dir());
@@ -1392,33 +1386,9 @@ struct ShortCutMapping {
Ref<ShortCut> ED_SHORTCUT(const String &p_path, const String &p_name, uint32_t p_keycode) {
#ifdef OSX_ENABLED
- static const ShortCutMapping macos_mappings[] = {
- { "editor/play", KEY_MASK_CMD | KEY_B },
- { "editor/play_scene", KEY_MASK_CMD | KEY_R },
- { "editor/pause_scene", KEY_MASK_CMD | KEY_MASK_CTRL | KEY_Y },
- { "editor/stop", KEY_MASK_CMD | KEY_PERIOD },
- { "editor/play_custom_scene", KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_R },
- { "editor/editor_2d", KEY_MASK_ALT | KEY_1 },
- { "editor/editor_3d", KEY_MASK_ALT | KEY_2 },
- { "editor/editor_script", KEY_MASK_ALT | KEY_3 },
- { "editor/editor_help", KEY_MASK_ALT | KEY_SPACE },
- { "editor/fullscreen_mode", KEY_MASK_CMD | KEY_MASK_CTRL | KEY_F },
- { "editor/distraction_free_mode", KEY_MASK_CMD | KEY_MASK_CTRL | KEY_D },
- { "script_text_editor/contextual_help", KEY_MASK_ALT | KEY_MASK_SHIFT | KEY_SPACE },
- { "script_text_editor/find_next", KEY_MASK_CMD | KEY_G },
- { "script_text_editor/find_previous", KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G },
- { "script_text_editor/toggle_breakpoint", KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B }
- };
-
+ // Use Cmd+Backspace as a general replacement for Delete shortcuts on macOS
if (p_keycode == KEY_DELETE) {
p_keycode = KEY_MASK_CMD | KEY_BACKSPACE;
- } else {
- for (int i = 0; i < sizeof(macos_mappings) / sizeof(ShortCutMapping); i++) {
- if (p_path == macos_mappings[i].path) {
- p_keycode = macos_mappings[i].keycode;
- break;
- }
- }
}
#endif
@@ -1483,6 +1453,9 @@ void EditorSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_settings_dir"), &EditorSettings::get_settings_dir);
ClassDB::bind_method(D_METHOD("get_project_settings_dir"), &EditorSettings::get_project_settings_dir);
+ ClassDB::bind_method(D_METHOD("set_project_metadata", "section", "key", "data"), &EditorSettings::set_project_metadata);
+ ClassDB::bind_method(D_METHOD("get_project_metadata", "section", "key", "default"), &EditorSettings::get_project_metadata, DEFVAL(Variant()));
+
ClassDB::bind_method(D_METHOD("set_favorite_dirs", "dirs"), &EditorSettings::set_favorite_dirs);
ClassDB::bind_method(D_METHOD("get_favorite_dirs"), &EditorSettings::get_favorite_dirs);
ClassDB::bind_method(D_METHOD("set_recent_dirs", "dirs"), &EditorSettings::set_recent_dirs);
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index e196ca506e..420e067cad 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -167,7 +167,7 @@ public:
String get_cache_dir() const;
void set_project_metadata(const String &p_section, const String &p_key, Variant p_data);
- Variant get_project_metadata(const String &p_section, const String &p_key, Variant p_default);
+ Variant get_project_metadata(const String &p_section, const String &p_key, Variant p_default) const;
void set_favorite_dirs(const Vector<String> &p_favorites_dirs);
Vector<String> get_favorite_dirs() const;
@@ -175,6 +175,8 @@ public:
Vector<String> get_recent_dirs() const;
void load_favorites();
+ bool is_dark_theme();
+
void list_text_editor_themes();
void load_text_editor_theme();
bool import_text_editor_theme(String p_file);
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
new file mode 100644
index 0000000000..c7a33de3f1
--- /dev/null
+++ b/editor/editor_spin_slider.cpp
@@ -0,0 +1,398 @@
+/*************************************************************************/
+/* editor_spin_slider.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 "editor_spin_slider.h"
+#include "editor_scale.h"
+#include "os/input.h"
+String EditorSpinSlider::get_text_value() const {
+ int zeros = Math::step_decimals(get_step());
+ return String::num(get_value(), zeros);
+}
+void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) {
+
+ if (read_only)
+ return;
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) {
+
+ if (mb->is_pressed()) {
+
+ if (updown_offset != -1 && mb->get_position().x > updown_offset) {
+ //there is an updown, so use it.
+ if (mb->get_position().y < get_size().height / 2) {
+ set_value(get_value() + get_step());
+ } else {
+ set_value(get_value() - get_step());
+ }
+ return;
+ } else {
+
+ grabbing_spinner_attempt = true;
+ grabbing_spinner_dist_cache = 0;
+ grabbing_spinner = false;
+ grabbing_spinner_mouse_pos = Input::get_singleton()->get_mouse_position();
+ }
+ } else {
+
+ if (grabbing_spinner_attempt) {
+
+ if (grabbing_spinner) {
+
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ Input::get_singleton()->warp_mouse_position(grabbing_spinner_mouse_pos);
+ update();
+ } else {
+ _focus_entered();
+ }
+
+ grabbing_spinner = false;
+ grabbing_spinner_attempt = false;
+ }
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+
+ if (grabbing_spinner_attempt) {
+
+ 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) {
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
+ grabbing_spinner = true;
+ } else {
+ 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) {
+ hover_updown = new_hover;
+ update();
+ }
+ }
+ }
+
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid() && k->is_pressed() && k->is_action("ui_accept")) {
+ _focus_entered();
+ }
+}
+
+void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) {
+
+ if (mb->is_pressed()) {
+
+ grabbing_grabber = true;
+ grabbing_ratio = get_as_ratio();
+ grabbing_from = grabber->get_transform().xform(mb->get_position()).x;
+ } else {
+ grabbing_grabber = false;
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid() && grabbing_grabber) {
+
+ float grabbing_ofs = (grabber->get_transform().xform(mm->get_position()).x - grabbing_from) / float(grabber_range);
+ set_as_ratio(grabbing_ratio + grabbing_ofs);
+ update();
+ }
+}
+
+void EditorSpinSlider::_notification(int p_what) {
+
+ if (p_what == MainLoop::NOTIFICATION_WM_FOCUS_OUT || p_what == MainLoop::NOTIFICATION_WM_FOCUS_OUT) {
+ if (grabbing_spinner) {
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ grabbing_spinner = false;
+ grabbing_spinner_attempt = false;
+ }
+ }
+
+ if (p_what == NOTIFICATION_DRAW) {
+
+ updown_offset = -1;
+
+ Ref<StyleBox> sb = get_stylebox("normal", "LineEdit");
+ draw_style_box(sb, Rect2(Vector2(), get_size()));
+ Ref<Font> font = get_font("font", "LineEdit");
+
+ int avail_width = get_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()));
+ }
+
+ String numstr = get_text_value();
+
+ int vofs = (get_size().height - font->get_height()) / 2 + font->get_ascent();
+
+ Color fc = get_color("font_color", "LineEdit");
+
+ 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 (get_step() == 1) {
+ Ref<Texture> updown = get_icon("updown", "SpinBox");
+ int updown_vofs = (get_size().height - updown->get_height()) / 2;
+ updown_offset = get_size().width - sb->get_margin(MARGIN_RIGHT) - updown->get_width();
+ Color c(1, 1, 1);
+ if (hover_updown) {
+ c *= Color(1.2, 1.2, 1.2);
+ }
+ draw_texture(updown, Vector2(updown_offset, updown_vofs), c);
+ if (grabber->is_visible()) {
+ grabber->hide();
+ }
+ } else if (!hide_slider) {
+ int grabber_w = 4 * EDSCALE;
+ int width = get_size().width - sb->get_minimum_size().width - grabber_w;
+ int ofs = sb->get_offset().x;
+ int svofs = (get_size().height + vofs) / 2 - 1;
+ Color c = fc;
+ c.a = 0.2;
+
+ draw_rect(Rect2(ofs, svofs + 1, width, 2 * EDSCALE), c);
+ int gofs = get_as_ratio() * width;
+ c.a = 0.9;
+ 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 && !value_input->is_visible();
+ if (grabber->is_visible() != display_grabber) {
+ if (display_grabber) {
+ grabber->show();
+ } else {
+ grabber->hide();
+ }
+ }
+
+ if (display_grabber) {
+ Ref<Texture> grabber_tex;
+ if (mouse_over_grabber) {
+ grabber_tex = get_icon("grabber_highlight", "HSlider");
+ } else {
+ grabber_tex = get_icon("grabber", "HSlider");
+ }
+
+ if (grabber->get_texture() != grabber_tex) {
+ grabber->set_texture(grabber_tex);
+ }
+
+ grabber->set_size(Size2(0, 0));
+ grabber->set_position(get_global_position() + grabber_rect.position + grabber_rect.size * 0.5 - grabber->get_size() * 0.5);
+ grabber_range = width;
+ }
+ }
+ }
+
+ if (p_what == NOTIFICATION_MOUSE_ENTER) {
+
+ mouse_over_spin = true;
+ update();
+ }
+ if (p_what == NOTIFICATION_MOUSE_EXIT) {
+
+ mouse_over_spin = false;
+ update();
+ }
+ if (p_what == NOTIFICATION_FOCUS_ENTER) {
+ 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 {
+
+ Ref<StyleBox> sb = get_stylebox("normal", "LineEdit");
+ Ref<Font> font = get_font("font", "LineEdit");
+
+ Size2 ms = sb->get_minimum_size();
+ ms.height += font->get_height();
+
+ return ms;
+}
+
+void EditorSpinSlider::set_hide_slider(bool p_hide) {
+ hide_slider = p_hide;
+ update();
+}
+
+bool EditorSpinSlider::is_hiding_slider() const {
+ return hide_slider;
+}
+
+void EditorSpinSlider::set_label(const String &p_label) {
+ label = p_label;
+ update();
+}
+
+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();
+}
+
+void EditorSpinSlider::_grabber_mouse_exited() {
+ mouse_over_grabber = false;
+ update();
+}
+
+void EditorSpinSlider::set_read_only(bool p_enable) {
+
+ read_only = p_enable;
+ update();
+}
+
+bool EditorSpinSlider::is_read_only() const {
+ return read_only;
+}
+
+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);
+
+ 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("_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");
+}
+
+EditorSpinSlider::EditorSpinSlider() {
+
+ grabbing_spinner_attempt = false;
+ grabbing_spinner = false;
+ grabbing_spinner_dist_cache = 0;
+ set_focus_mode(FOCUS_ALL);
+ updown_offset = -1;
+ hover_updown = false;
+ grabber = memnew(TextureRect);
+ add_child(grabber);
+ grabber->hide();
+ grabber->set_as_toplevel(true);
+ grabber->set_mouse_filter(MOUSE_FILTER_STOP);
+ grabber->connect("mouse_entered", this, "_grabber_mouse_entered");
+ grabber->connect("mouse_exited", this, "_grabber_mouse_exited");
+ grabber->connect("gui_input", this, "_grabber_gui_input");
+ mouse_over_spin = false;
+ mouse_over_grabber = false;
+ grabbing_grabber = false;
+ grabber_range = 1;
+ value_input = memnew(LineEdit);
+ add_child(value_input);
+ value_input->set_as_toplevel(true);
+ 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;
+}
diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h
new file mode 100644
index 0000000000..5316c0264a
--- /dev/null
+++ b/editor/editor_spin_slider.h
@@ -0,0 +1,95 @@
+/*************************************************************************/
+/* editor_spin_slider.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 EDITOR_SPIN_SLIDER_H
+#define EDITOR_SPIN_SLIDER_H
+
+#include "scene/gui/line_edit.h"
+#include "scene/gui/range.h"
+#include "scene/gui/texture_rect.h"
+
+class EditorSpinSlider : public Range {
+ GDCLASS(EditorSpinSlider, Range)
+
+ String label;
+ int updown_offset;
+ bool hover_updown;
+ bool mouse_hover;
+
+ TextureRect *grabber;
+ int grabber_range;
+
+ bool mouse_over_spin;
+ bool mouse_over_grabber;
+
+ bool grabbing_grabber;
+ int grabbing_from;
+ float grabbing_ratio;
+
+ bool grabbing_spinner_attempt;
+ 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;
+
+protected:
+ void _notification(int p_what);
+ void _gui_input(const Ref<InputEvent> &p_event);
+ static void _bind_methods();
+ void _grabber_mouse_entered();
+ void _grabber_mouse_exited();
+ void _focus_entered();
+
+public:
+ String get_text_value() const;
+ void set_label(const String &p_label);
+ String get_label() const;
+
+ void set_hide_slider(bool p_hide);
+ bool is_hiding_slider() const;
+
+ void set_read_only(bool p_enable);
+ bool is_read_only() const;
+
+ virtual Size2 get_minimum_size() const;
+ EditorSpinSlider();
+};
+
+#endif // EDITOR_SPIN_SLIDER_H
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index bf7236cc2b..0534a398f4 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,45 +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: { // Grey
- preset_accent_color = Color::html("#b8e4ff");
- preset_base_color = Color::html("#3d3d3d");
- preset_contrast = 0.2;
- } break;
- case 2: { // Godot 2
- preset_accent_color = Color::html("#86ace2");
- preset_base_color = Color::html("#3C3A44");
- preset_contrast = 0.25;
- } break;
- case 3: { // Arc
- preset_accent_color = Color::html("#5294e2");
- preset_base_color = Color::html("#383c4a");
- preset_contrast = 0.25;
- } break;
- case 4: { // Light
- preset_accent_color = Color::html("#2070ff");
- preset_base_color = Color::html("#ffffff");
- preset_contrast = 0.08;
- } break;
- case 5: { // Alien
- preset_accent_color = Color::html("#1bfe99");
- preset_base_color = Color::html("#2f373f");
- preset_contrast = 0.25;
- }
- default: { // 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);
- }
+
+ // 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 != 6) {
+ if (preset != "Custom") {
accent_color = preset_accent_color;
base_color = preset_base_color;
contrast = preset_contrast;
@@ -318,9 +327,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
EditorSettings::get_singleton()->set_manually("interface/theme/contrast", contrast);
//Colors
- int AUTO_COLOR = 0;
- int LIGHT_COLOR = 2;
- bool dark_theme = (icon_font_color_setting == AUTO_COLOR && ((base_color.r + base_color.g + base_color.b) / 3.0) < 0.5) || icon_font_color_setting == LIGHT_COLOR;
+ bool dark_theme = EditorSettings::get_singleton()->is_dark_theme();
const Color dark_color_1 = base_color.linear_interpolate(Color(0, 0, 0, 1), contrast);
const Color dark_color_2 = base_color.linear_interpolate(Color(0, 0, 0, 1), contrast * 1.5);
@@ -456,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
@@ -572,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);
@@ -620,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);
@@ -774,7 +796,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
@@ -940,6 +962,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("bg", "GraphEdit", style_tree_bg);
theme->set_color("grid_major", "GraphEdit", grid_major_color);
theme->set_color("grid_minor", "GraphEdit", grid_minor_color);
+ theme->set_color("activity", "GraphEdit", accent_color);
theme->set_icon("minus", "GraphEdit", theme->get_icon("ZoomLess", "EditorIcons"));
theme->set_icon("more", "GraphEdit", theme->get_icon("ZoomMore", "EditorIcons"));
theme->set_icon("reset", "GraphEdit", theme->get_icon("ZoomReset", "EditorIcons"));
@@ -998,6 +1021,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"));
@@ -1047,8 +1072,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color comment_color = dim_color;
const Color string_color = Color::html(dark_theme ? "#ffd942" : "#ffd118").linear_interpolate(mono_color, dark_theme ? 0.5 : 0.3);
- const Color te_background_color = Color(0, 0, 0, 0);
- const Color completion_background_color = base_color;
+ const Color te_background_color = dark_theme ? background_color : base_color;
+ const Color completion_background_color = dark_theme ? base_color : background_color;
const Color completion_selected_color = alpha1;
const Color completion_existing_color = alpha2;
const Color completion_scroll_color = alpha1;
@@ -1061,7 +1086,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color selection_color = alpha2;
const Color brace_mismatch_color = error_color;
const Color current_line_color = alpha1;
- const Color line_length_guideline_color = warning_color;
+ const Color line_length_guideline_color = dark_theme ? base_color : background_color;
const Color word_highlighted_color = alpha1;
const Color number_color = basetype_color.linear_interpolate(mono_color, dark_theme ? 0.5 : 0.3);
const Color function_color = main_color;
@@ -1081,7 +1106,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
setting->set_initial_value("text_editor/highlighting/engine_type_color", type_color, true);
setting->set_initial_value("text_editor/highlighting/comment_color", comment_color, true);
setting->set_initial_value("text_editor/highlighting/string_color", string_color, true);
- setting->set_initial_value("text_editor/highlighting/background_color", background_color, true);
+ setting->set_initial_value("text_editor/highlighting/background_color", te_background_color, true);
setting->set_initial_value("text_editor/highlighting/completion_background_color", completion_background_color, true);
setting->set_initial_value("text_editor/highlighting/completion_selected_color", completion_selected_color, true);
setting->set_initial_value("text_editor/highlighting/completion_existing_color", completion_existing_color, true);
@@ -1106,36 +1131,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
setting->set_initial_value("text_editor/highlighting/search_result_color", search_result_color, true);
setting->set_initial_value("text_editor/highlighting/search_result_border_color", search_result_border_color, true);
} else if (text_editor_color_theme == "Default") {
- setting->set_initial_value("text_editor/highlighting/symbol_color", Color::html("badfff"), true);
- setting->set_initial_value("text_editor/highlighting/keyword_color", Color::html("ffffb3"), true);
- setting->set_initial_value("text_editor/highlighting/base_type_color", Color::html("a4ffd4"), true);
- setting->set_initial_value("text_editor/highlighting/engine_type_color", Color::html("83d3ff"), true);
- setting->set_initial_value("text_editor/highlighting/comment_color", Color::html("676767"), true);
- setting->set_initial_value("text_editor/highlighting/string_color", Color::html("ef6ebe"), true);
- setting->set_initial_value("text_editor/highlighting/background_color", Color::html("3b000000"), true);
- setting->set_initial_value("text_editor/highlighting/completion_background_color", Color::html("2C2A32"), true);
- setting->set_initial_value("text_editor/highlighting/completion_selected_color", Color::html("434244"), true);
- setting->set_initial_value("text_editor/highlighting/completion_existing_color", Color::html("21dfdfdf"), true);
- setting->set_initial_value("text_editor/highlighting/completion_scroll_color", Color::html("ffffff"), true);
- setting->set_initial_value("text_editor/highlighting/completion_font_color", Color::html("aaaaaa"), true);
- setting->set_initial_value("text_editor/highlighting/text_color", Color::html("aaaaaa"), true);
- setting->set_initial_value("text_editor/highlighting/line_number_color", Color::html("66aaaaaa"), true);
- setting->set_initial_value("text_editor/highlighting/caret_color", Color::html("aaaaaa"), true);
- setting->set_initial_value("text_editor/highlighting/caret_background_color", Color::html("000000"), true);
- setting->set_initial_value("text_editor/highlighting/text_selected_color", Color::html("000000"), true);
- setting->set_initial_value("text_editor/highlighting/selection_color", Color::html("6ca9c2"), true);
- setting->set_initial_value("text_editor/highlighting/brace_mismatch_color", Color(1, 0.2, 0.2), true);
- setting->set_initial_value("text_editor/highlighting/current_line_color", Color(0.3, 0.5, 0.8, 0.15), true);
- setting->set_initial_value("text_editor/highlighting/line_length_guideline_color", Color(0.3, 0.5, 0.8, 0.1), true);
- setting->set_initial_value("text_editor/highlighting/word_highlighted_color", Color(0.8, 0.9, 0.9, 0.15), true);
- setting->set_initial_value("text_editor/highlighting/number_color", Color::html("EB9532"), true);
- setting->set_initial_value("text_editor/highlighting/function_color", Color::html("66a2ce"), true);
- setting->set_initial_value("text_editor/highlighting/member_variable_color", Color::html("e64e59"), true);
- setting->set_initial_value("text_editor/highlighting/mark_color", Color(1.0, 0.4, 0.4, 0.4), true);
- setting->set_initial_value("text_editor/highlighting/breakpoint_color", Color(0.8, 0.8, 0.4, 0.2), true);
- setting->set_initial_value("text_editor/highlighting/code_folding_color", Color(0.8, 0.8, 0.8, 0.8), true);
- setting->set_initial_value("text_editor/highlighting/search_result_color", Color(0.05, 0.25, 0.05, 1), true);
- setting->set_initial_value("text_editor/highlighting/search_result_border_color", Color(0.1, 0.45, 0.1, 1), true);
+ setting->load_text_editor_theme();
}
return theme;
diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp
index a39c8b2209..541c848ca3 100644
--- a/editor/export_template_manager.cpp
+++ b/editor/export_template_manager.cpp
@@ -436,6 +436,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) {
@@ -496,7 +500,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 +514,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 +564,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 e7741c7926..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();
@@ -383,11 +393,11 @@ void FileSystemDock::_update_file_display_toggle_button() {
if (button_display_mode->is_pressed()) {
display_mode = DISPLAY_LIST;
button_display_mode->set_icon(get_icon("FileThumbnail", "EditorIcons"));
- button_display_mode->set_tooltip(TTR("View items as a grid of thumbnails"));
+ button_display_mode->set_tooltip(TTR("View items as a grid of thumbnails."));
} else {
display_mode = DISPLAY_THUMBNAILS;
button_display_mode->set_icon(get_icon("FileList", "EditorIcons"));
- button_display_mode->set_tooltip(TTR("View items as a list"));
+ button_display_mode->set_tooltip(TTR("View items as a list."));
}
}
@@ -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);
@@ -1860,10 +1929,17 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
button_favorite->set_flat(true);
button_favorite->set_toggle_mode(true);
button_favorite->connect("pressed", this, "_favorites_pressed");
- button_favorite->set_tooltip(TTR("Toggle folder status as Favorite"));
+ button_favorite->set_tooltip(TTR("Toggle folder status as Favorite."));
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);
/*
@@ -1916,11 +1992,13 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
file_list_vb->add_child(path_hb);
button_tree = memnew(ToolButton);
+ button_tree->set_tooltip(TTR("Enter tree-view."));
button_tree->hide();
path_hb->add_child(button_tree);
search_box = memnew(LineEdit);
search_box->set_h_size_flags(SIZE_EXPAND_FILL);
+ search_box->set_placeholder(TTR("Search files"));
search_box->connect("text_changed", this, "_search_changed");
path_hb->add_child(search_box);
@@ -1977,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/find_in_files.cpp b/editor/find_in_files.cpp
index 74ea46838b..004a49e2b4 100644
--- a/editor/find_in_files.cpp
+++ b/editor/find_in_files.cpp
@@ -109,6 +109,7 @@ void FindInFiles::start() {
_current_dir = "";
PoolStringArray init_folder;
init_folder.append(_root_dir);
+ _folders_stack.clear();
_folders_stack.push_back(init_folder);
_initial_files_count = 0;
@@ -127,11 +128,12 @@ void FindInFiles::_process() {
// This part can be moved to a thread if needed
OS &os = *OS::get_singleton();
- float duration = 0.0;
- while (duration < 1.0 / 120.0) {
- float time_before = os.get_ticks_msec();
+ float time_before = os.get_ticks_msec();
+ while (is_processing()) {
_iterate();
- duration += (os.get_ticks_msec() - time_before);
+ float elapsed = (os.get_ticks_msec() - time_before);
+ if (elapsed > 1000.0 / 120.0)
+ break;
}
}
@@ -428,6 +430,7 @@ FindInFilesDialog::FindInFilesDialog() {
void FindInFilesDialog::set_search_text(String text) {
_search_text_line_edit->set_text(text);
+ _on_search_text_modified(text);
}
String FindInFilesDialog::get_search_text() const {
diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp
index 8443311a54..2bfd2eb5c3 100644
--- a/editor/groups_editor.cpp
+++ b/editor/groups_editor.cpp
@@ -286,6 +286,9 @@ void GroupDialog::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
add_button->set_icon(get_icon("Forward", "EditorIcons"));
remove_button->set_icon(get_icon("Back", "EditorIcons"));
+
+ add_filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+ remove_filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
} break;
}
}
@@ -441,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_slider_grabber.svg b/editor/icons/icon_GUI_slider_grabber.svg
index b1dcf980a5..b8e6f0a654 100644
--- a/editor/icons/icon_GUI_slider_grabber.svg
+++ b/editor/icons/icon_GUI_slider_grabber.svg
@@ -1,5 +1,82 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<circle cx="8" cy="1044.4" r="3" fill="#fff" fill-opacity=".78431" stroke-linejoin="round" stroke-opacity=".39216" stroke-width="3"/>
-</g>
+<?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 15.999999"
+ id="svg8"
+ sodipodi:docname="icon_GUI_slider_grabber.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="1211"
+ inkscape:window-height="644"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="-5.7627119"
+ inkscape:cy="8"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g6" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g6">
+ <path
+ transform="translate(0 1036.4)"
+ d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7 -7 7 7 0 0 0 -7 -7zm0 2a5 5 0 0 1 0.5 0.025391 5 5 0 0 1 0.49414 0.074219 5 5 0 0 1 0.48438 0.12305 5 5 0 0 1 0.46875 0.17188 5 5 0 0 1 0.44922 0.2168 5 5 0 0 1 0.42578 0.26172 5 5 0 0 1 0.39844 0.30273 5 5 0 0 1 0.36524 0.33984 5 5 0 0 1 0.33008 0.37695 5 5 0 0 1 0.29102 0.40625 5 5 0 0 1 0.24805 0.43359 5 5 0 0 1 0.20508 0.45508 5 5 0 0 1 0.1582 0.47461 5 5 0 0 1 0.10938 0.48828 5 5 0 0 1 0.060547 0.49609 5 5 0 0 1 0.011719 0.35352 5 5 0 0 1 -0.025391 0.5 5 5 0 0 1 -0.074218 0.49414 5 5 0 0 1 -0.12305 0.48438 5 5 0 0 1 -0.17188 0.46875 5 5 0 0 1 -0.2168 0.44922 5 5 0 0 1 -0.26172 0.42578 5 5 0 0 1 -0.30273 0.39844 5 5 0 0 1 -0.33984 0.36524 5 5 0 0 1 -0.37695 0.33008 5 5 0 0 1 -0.40625 0.29102 5 5 0 0 1 -0.43359 0.24805 5 5 0 0 1 -0.45508 0.20508 5 5 0 0 1 -0.47461 0.1582 5 5 0 0 1 -0.48828 0.10938 5 5 0 0 1 -0.49609 0.060547 5 5 0 0 1 -0.35352 0.011719 5 5 0 0 1 -0.5 -0.025391 5 5 0 0 1 -0.49414 -0.074218 5 5 0 0 1 -0.48438 -0.12305 5 5 0 0 1 -0.46875 -0.17188 5 5 0 0 1 -0.44922 -0.2168 5 5 0 0 1 -0.42578 -0.26172 5 5 0 0 1 -0.39844 -0.30273 5 5 0 0 1 -0.36523 -0.33984 5 5 0 0 1 -0.33008 -0.37695 5 5 0 0 1 -0.29102 -0.40625 5 5 0 0 1 -0.24805 -0.43359 5 5 0 0 1 -0.20508 -0.45508 5 5 0 0 1 -0.1582 -0.47461 5 5 0 0 1 -0.10938 -0.48828 5 5 0 0 1 -0.060547 -0.49609 5 5 0 0 1 -0.011719 -0.35352 5 5 0 0 1 0.025391 -0.5 5 5 0 0 1 0.074219 -0.49414 5 5 0 0 1 0.12305 -0.48438 5 5 0 0 1 0.17188 -0.46875 5 5 0 0 1 0.2168 -0.44922 5 5 0 0 1 0.26172 -0.42578 5 5 0 0 1 0.30273 -0.39844 5 5 0 0 1 0.33984 -0.36523 5 5 0 0 1 0.37695 -0.33008 5 5 0 0 1 0.40625 -0.29102 5 5 0 0 1 0.43359 -0.24805 5 5 0 0 1 0.45508 -0.20508 5 5 0 0 1 0.47461 -0.1582 5 5 0 0 1 0.48828 -0.10938 5 5 0 0 1 0.49609 -0.060547 5 5 0 0 1 0.35352 -0.011719z"
+ fill="#e0e0e0"
+ id="path2"
+ style="fill:#e0e0e0;fill-opacity:0.28925619" />
+ <circle
+ cx="8"
+ cy="1044.4"
+ r="3"
+ fill="#fff"
+ fill-opacity=".58824"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ stroke-opacity=".32549"
+ stroke-width="3"
+ id="circle4" />
+ </g>
+ <g
+ transform="translate(-0.06779632,-1036.4)"
+ id="g18">
+ <circle
+ style="fill:#ffffff;fill-opacity:0.78430996;stroke-width:3;stroke-linejoin:round;stroke-opacity:0.39216003"
+ cx="8"
+ cy="1044.4"
+ r="3"
+ id="circle16" />
+ </g>
</svg>
diff --git a/editor/icons/icon_GUI_slider_grabber_hl.svg b/editor/icons/icon_GUI_slider_grabber_hl.svg
index 73252751ce..a04ac44cf6 100644
--- a/editor/icons/icon_GUI_slider_grabber_hl.svg
+++ b/editor/icons/icon_GUI_slider_grabber_hl.svg
@@ -1,6 +1,80 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7 -7 7 7 0 0 0 -7 -7zm0 2a5 5 0 0 1 0.5 0.025391 5 5 0 0 1 0.49414 0.074219 5 5 0 0 1 0.48438 0.12305 5 5 0 0 1 0.46875 0.17188 5 5 0 0 1 0.44922 0.2168 5 5 0 0 1 0.42578 0.26172 5 5 0 0 1 0.39844 0.30273 5 5 0 0 1 0.36524 0.33984 5 5 0 0 1 0.33008 0.37695 5 5 0 0 1 0.29102 0.40625 5 5 0 0 1 0.24805 0.43359 5 5 0 0 1 0.20508 0.45508 5 5 0 0 1 0.1582 0.47461 5 5 0 0 1 0.10938 0.48828 5 5 0 0 1 0.060547 0.49609 5 5 0 0 1 0.011719 0.35352 5 5 0 0 1 -0.025391 0.5 5 5 0 0 1 -0.074218 0.49414 5 5 0 0 1 -0.12305 0.48438 5 5 0 0 1 -0.17188 0.46875 5 5 0 0 1 -0.2168 0.44922 5 5 0 0 1 -0.26172 0.42578 5 5 0 0 1 -0.30273 0.39844 5 5 0 0 1 -0.33984 0.36524 5 5 0 0 1 -0.37695 0.33008 5 5 0 0 1 -0.40625 0.29102 5 5 0 0 1 -0.43359 0.24805 5 5 0 0 1 -0.45508 0.20508 5 5 0 0 1 -0.47461 0.1582 5 5 0 0 1 -0.48828 0.10938 5 5 0 0 1 -0.49609 0.060547 5 5 0 0 1 -0.35352 0.011719 5 5 0 0 1 -0.5 -0.025391 5 5 0 0 1 -0.49414 -0.074218 5 5 0 0 1 -0.48438 -0.12305 5 5 0 0 1 -0.46875 -0.17188 5 5 0 0 1 -0.44922 -0.2168 5 5 0 0 1 -0.42578 -0.26172 5 5 0 0 1 -0.39844 -0.30273 5 5 0 0 1 -0.36523 -0.33984 5 5 0 0 1 -0.33008 -0.37695 5 5 0 0 1 -0.29102 -0.40625 5 5 0 0 1 -0.24805 -0.43359 5 5 0 0 1 -0.20508 -0.45508 5 5 0 0 1 -0.1582 -0.47461 5 5 0 0 1 -0.10938 -0.48828 5 5 0 0 1 -0.060547 -0.49609 5 5 0 0 1 -0.011719 -0.35352 5 5 0 0 1 0.025391 -0.5 5 5 0 0 1 0.074219 -0.49414 5 5 0 0 1 0.12305 -0.48438 5 5 0 0 1 0.17188 -0.46875 5 5 0 0 1 0.2168 -0.44922 5 5 0 0 1 0.26172 -0.42578 5 5 0 0 1 0.30273 -0.39844 5 5 0 0 1 0.33984 -0.36523 5 5 0 0 1 0.37695 -0.33008 5 5 0 0 1 0.40625 -0.29102 5 5 0 0 1 0.43359 -0.24805 5 5 0 0 1 0.45508 -0.20508 5 5 0 0 1 0.47461 -0.1582 5 5 0 0 1 0.48828 -0.10938 5 5 0 0 1 0.49609 -0.060547 5 5 0 0 1 0.35352 -0.011719z" fill="#e0e0e0"/>
-<circle cx="8" cy="1044.4" r="3" fill="#fff" fill-opacity=".58824" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".32549" stroke-width="3"/>
-</g>
+<?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 15.999999"
+ id="svg8"
+ sodipodi:docname="icon_GUI_slider_grabber_hl.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" />
+ </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="944"
+ inkscape:window-height="480"
+ 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="svg8" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g6">
+ <path
+ transform="translate(0 1036.4)"
+ d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7 -7 7 7 0 0 0 -7 -7zm0 2a5 5 0 0 1 0.5 0.025391 5 5 0 0 1 0.49414 0.074219 5 5 0 0 1 0.48438 0.12305 5 5 0 0 1 0.46875 0.17188 5 5 0 0 1 0.44922 0.2168 5 5 0 0 1 0.42578 0.26172 5 5 0 0 1 0.39844 0.30273 5 5 0 0 1 0.36524 0.33984 5 5 0 0 1 0.33008 0.37695 5 5 0 0 1 0.29102 0.40625 5 5 0 0 1 0.24805 0.43359 5 5 0 0 1 0.20508 0.45508 5 5 0 0 1 0.1582 0.47461 5 5 0 0 1 0.10938 0.48828 5 5 0 0 1 0.060547 0.49609 5 5 0 0 1 0.011719 0.35352 5 5 0 0 1 -0.025391 0.5 5 5 0 0 1 -0.074218 0.49414 5 5 0 0 1 -0.12305 0.48438 5 5 0 0 1 -0.17188 0.46875 5 5 0 0 1 -0.2168 0.44922 5 5 0 0 1 -0.26172 0.42578 5 5 0 0 1 -0.30273 0.39844 5 5 0 0 1 -0.33984 0.36524 5 5 0 0 1 -0.37695 0.33008 5 5 0 0 1 -0.40625 0.29102 5 5 0 0 1 -0.43359 0.24805 5 5 0 0 1 -0.45508 0.20508 5 5 0 0 1 -0.47461 0.1582 5 5 0 0 1 -0.48828 0.10938 5 5 0 0 1 -0.49609 0.060547 5 5 0 0 1 -0.35352 0.011719 5 5 0 0 1 -0.5 -0.025391 5 5 0 0 1 -0.49414 -0.074218 5 5 0 0 1 -0.48438 -0.12305 5 5 0 0 1 -0.46875 -0.17188 5 5 0 0 1 -0.44922 -0.2168 5 5 0 0 1 -0.42578 -0.26172 5 5 0 0 1 -0.39844 -0.30273 5 5 0 0 1 -0.36523 -0.33984 5 5 0 0 1 -0.33008 -0.37695 5 5 0 0 1 -0.29102 -0.40625 5 5 0 0 1 -0.24805 -0.43359 5 5 0 0 1 -0.20508 -0.45508 5 5 0 0 1 -0.1582 -0.47461 5 5 0 0 1 -0.10938 -0.48828 5 5 0 0 1 -0.060547 -0.49609 5 5 0 0 1 -0.011719 -0.35352 5 5 0 0 1 0.025391 -0.5 5 5 0 0 1 0.074219 -0.49414 5 5 0 0 1 0.12305 -0.48438 5 5 0 0 1 0.17188 -0.46875 5 5 0 0 1 0.2168 -0.44922 5 5 0 0 1 0.26172 -0.42578 5 5 0 0 1 0.30273 -0.39844 5 5 0 0 1 0.33984 -0.36523 5 5 0 0 1 0.37695 -0.33008 5 5 0 0 1 0.40625 -0.29102 5 5 0 0 1 0.43359 -0.24805 5 5 0 0 1 0.45508 -0.20508 5 5 0 0 1 0.47461 -0.1582 5 5 0 0 1 0.48828 -0.10938 5 5 0 0 1 0.49609 -0.060547 5 5 0 0 1 0.35352 -0.011719z"
+ fill="#e0e0e0"
+ id="path2" />
+ <circle
+ cx="8"
+ cy="1044.4"
+ r="3"
+ fill="#fff"
+ fill-opacity=".58824"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ stroke-opacity=".32549"
+ stroke-width="3"
+ id="circle4" />
+ </g>
+ <g
+ transform="translate(-0.06779632,-1036.4)"
+ id="g18">
+ <circle
+ style="fill:#ffffff;fill-opacity:0.78430996;stroke-width:3;stroke-linejoin:round;stroke-opacity:0.39216003"
+ cx="8"
+ cy="1044.4"
+ r="3"
+ id="circle16" />
+ </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_filter.svg b/editor/icons/icon_animation_filter.svg
new file mode 100644
index 0000000000..4f8e881ea8
--- /dev/null
+++ b/editor/icons/icon_animation_filter.svg
@@ -0,0 +1,63 @@
+<?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_animation_filter.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="1089"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="10.429825"
+ inkscape:cx="-5.6414698"
+ inkscape:cy="10.961343"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g10" />
+ <g
+ transform="matrix(0.02719109,0,0,0.02719109,1.3153462,1.0022864)"
+ id="g12">
+ <g
+ id="g10">
+ <path
+ inkscape:connector-curvature="0"
+ d="M 495.289,20.143 H 16.709 c -14.938,0 -22.344,18.205 -11.666,28.636 l 169.7,165.778 v 260.587 c 0,14.041 16.259,21.739 27.131,13.031 L 331.017,384.743 c 3.956,-3.169 6.258,-7.962 6.258,-13.031 V 214.556 L 506.955,48.779 c 10.688,-10.44 3.259,-28.636 -11.666,-28.636 z"
+ id="path8"
+ style="fill:#e0e0e0;fill-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/editor/icons/icon_animation_track_group.svg b/editor/icons/icon_animation_track_group.svg
new file mode 100644
index 0000000000..9c4748a528
--- /dev/null
+++ b/editor/icons/icon_animation_track_group.svg
@@ -0,0 +1,63 @@
+<?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_animation_track_group.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="1089"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="10.429825"
+ inkscape:cx="6.2135985"
+ inkscape:cy="6.5622523"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <path
+ style="fill:#e0e0e0"
+ inkscape:connector-curvature="0"
+ id="path2"
+ d="M 5.0508475,2 V 4 H 14 V 2 Z m -3.322034,-0.016949 v 2 h 2 v -2 z M 8.9830508,7 V 9 H 14 V 7 Z m -3.5254237,5 v 2 h 2 v -2 z m 3.5254237,0 v 2 H 14 v -2 z"
+ sodipodi:nodetypes="ccccccccccccccccccccccccc" />
+ <path
+ style="fill:#e0e0e0"
+ inkscape:connector-curvature="0"
+ id="path2-3"
+ d="m 5.4915255,6.9322039 v 1.999999 h 2 v -1.999999 z"
+ sodipodi:nodetypes="ccccc" />
+</svg>
diff --git a/editor/icons/icon_animation_track_list.svg b/editor/icons/icon_animation_track_list.svg
new file mode 100644
index 0000000000..40e8414598
--- /dev/null
+++ b/editor/icons/icon_animation_track_list.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_animation_track_list.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="1089"
+ 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="svg6" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g4">
+ <path
+ transform="translate(0 1036.4)"
+ d="m2 2v2h2v-2h-2zm4 0v2h8v-2h-8zm-4 5v2h2v-2h-2zm4 0v2h8v-2h-8zm-4 5v2h2v-2h-2zm4 0v2h8v-2h-8z"
+ fill="#e0e0e0"
+ id="path2" />
+ </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_auto_triangle.svg b/editor/icons/icon_auto_triangle.svg
new file mode 100644
index 0000000000..631f259452
--- /dev/null
+++ b/editor/icons/icon_auto_triangle.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_auto_triangle.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="1853"
+ inkscape:window-height="1016"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="29.5"
+ inkscape:cx="17.168167"
+ inkscape:cy="5.5708575"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ 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;stroke:none;stroke-width:1.87616086;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 8.2324219 0.67773438 L 0.64453125 15.289062 L 15.355469 15.289062 L 8.2324219 0.67773438 z M 6.9414062 5.4433594 L 9.2109375 5.4433594 C 9.5561128 6.0670927 9.8954447 6.7088542 10.230469 7.3671875 C 10.565492 8.0167875 10.901304 8.703974 11.236328 9.4316406 C 11.581503 10.159241 11.931781 10.934946 12.287109 11.757812 C 12.642437 12.580746 13.018126 13.477066 13.414062 14.447266 L 10.871094 14.447266 C 10.75942 14.135399 10.632366 13.815528 10.490234 13.486328 C 10.358255 13.157195 10.225729 12.827247 10.09375 12.498047 L 5.9824219 12.498047 C 5.8504432 12.827247 5.7143976 13.157195 5.5722656 13.486328 C 5.440287 13.815528 5.3167521 14.135399 5.2050781 14.447266 L 2.7382812 14.447266 C 3.1342186 13.477066 3.5099064 12.580746 3.8652344 11.757812 C 4.2205624 10.934946 4.5673204 10.159241 4.9023438 9.4316406 C 5.2475197 8.703974 5.5813793 8.0167875 5.90625 7.3671875 C 6.2412733 6.7088542 6.5860782 6.0670927 6.9414062 5.4433594 z M 8.0234375 7.4824219 C 7.9726708 7.6123552 7.8964385 7.790425 7.7949219 8.015625 C 7.6933999 8.240825 7.5772912 8.5003885 7.4453125 8.7949219 C 7.3133332 9.0894552 7.1643891 9.4143979 7.0019531 9.7695312 C 6.8496698 10.124665 6.6936847 10.496919 6.53125 10.886719 L 9.53125 10.886719 C 9.368814 10.496919 9.2108764 10.124665 9.0585938 9.7695312 C 8.9063104 9.4143979 8.7593188 9.0894552 8.6171875 8.7949219 C 8.4852082 8.5003885 8.3691001 8.240825 8.2675781 8.015625 C 8.1660555 7.790425 8.0843508 7.6123552 8.0234375 7.4824219 z "
+ transform="translate(26.001,1046.2683)"
+ id="path821" />
+ </g>
+ </g>
+</svg>
diff --git a/editor/icons/icon_bezier_handles_balanced.svg b/editor/icons/icon_bezier_handles_balanced.svg
new file mode 100644
index 0000000000..8ab99d79bb
--- /dev/null
+++ b/editor/icons/icon_bezier_handles_balanced.svg
@@ -0,0 +1,98 @@
+<?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_bezier_handles_balanced.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="1417"
+ inkscape:window-height="685"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="4.2910315"
+ inkscape:cy="11.857644"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <path
+ style="fill:none;stroke:#84c2ff;stroke-width:1.70000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1.7627119,13.627119 c 0,0 1.2881355,-6.847458 6.5762712,-8.1355935 5.0847459,0.9491522 5.9661009,8.1355925 5.9661009,8.1355925"
+ id="path4526"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc" />
+ <ellipse
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path5846"
+ cx="1.8983043"
+ cy="13.491526"
+ rx="1.2675855"
+ ry="1.1997888" />
+ <ellipse
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path5846-3"
+ cx="14.237288"
+ cy="13.491526"
+ rx="1.2675855"
+ ry="1.1997888" />
+ <path
+ style="fill:none;stroke:#84c2ff;stroke-width:0.61799997;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 7.4559186,5.1473018 2.7203863,6.7014816"
+ id="path5878"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#84c2ff;stroke-width:0.61489719;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 10.790357,4.2063094 8.2893822,5.149623"
+ id="path5878-7"
+ inkscape:connector-curvature="0" />
+ <ellipse
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path5846-3-6"
+ cx="8.2711868"
+ cy="4.7796612"
+ rx="1.2675855"
+ ry="1.1997888" />
+ <path
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ d="M 1.7157324,5.8754878 A 1.2675855,1.1997888 0 0 0 0.44815434,7.0747066 1.2675855,1.1997888 0 0 0 1.7157324,8.2739253 1.2675855,1.1997888 0 0 0 2.9833105,7.0747066 1.2675855,1.1997888 0 0 0 1.7157324,5.8754878 Z m 0.00195,0.4238282 A 0.84677333,0.80148375 0 0 1 2.5653417,7.1000972 0.84677333,0.80148375 0 0 1 1.7176855,7.9008784 0.84677333,0.80148375 0 0 1 0.87002934,7.1000972 0.84677333,0.80148375 0 0 1 1.7176855,6.299316 Z"
+ id="path5846-5"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.7567277;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ d="M 11.909414,2.4642073 A 1.2836218,1.231838 0 0 0 10.6258,3.6954601 1.2836218,1.231838 0 0 0 11.909414,4.9267128 1.2836218,1.231838 0 0 0 13.193028,3.6954601 1.2836218,1.231838 0 0 0 11.909414,2.4642073 Z m 0.002,0.4351497 a 0.85748593,0.82289328 0 0 1 0.858383,0.8221719 0.85748593,0.82289328 0 0 1 -0.85838,0.822172 0.85748593,0.82289328 0 0 1 -0.858379,-0.822172 0.85748593,0.82289328 0 0 1 0.858379,-0.8221719 z"
+ id="path5846-5-6" />
+</svg>
diff --git a/editor/icons/icon_bezier_handles_free.svg b/editor/icons/icon_bezier_handles_free.svg
new file mode 100644
index 0000000000..e5dfb8d0fc
--- /dev/null
+++ b/editor/icons/icon_bezier_handles_free.svg
@@ -0,0 +1,98 @@
+<?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_bezier_handles_separate.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="1417"
+ inkscape:window-height="685"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="4.2910315"
+ inkscape:cy="11.857644"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <path
+ style="fill:none;stroke:#84c2ff;stroke-width:1.70000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1.7627119,13.627119 c 0,0 1.2881355,-6.847458 6.5762712,-8.1355935 5.0847459,0.9491522 5.9661009,8.1355925 5.9661009,8.1355925"
+ id="path4526"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc" />
+ <ellipse
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path5846"
+ cx="1.8983043"
+ cy="13.491526"
+ rx="1.2675855"
+ ry="1.1997888" />
+ <ellipse
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path5846-3"
+ cx="14.237288"
+ cy="13.491526"
+ rx="1.2675855"
+ ry="1.1997888" />
+ <path
+ style="fill:none;stroke:#84c2ff;stroke-width:0.80513805;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 7.6850253,4.7560401 3.9088983,5.4168"
+ id="path5878"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#84c2ff;stroke-width:0.73079807;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 11.695505,2.3941651 8.696384,4.6876729"
+ id="path5878-7"
+ inkscape:connector-curvature="0" />
+ <ellipse
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path5846-3-6"
+ cx="8.2711868"
+ cy="4.7796612"
+ rx="1.2675855"
+ ry="1.1997888" />
+ <path
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ d="M 2.4961199,4.3976698 A 1.1997888,1.2675855 80.074672 0 0 1.4542161,5.7974257 1.1997888,1.2675855 80.074672 0 0 2.9095255,6.7602105 1.1997888,1.2675855 80.074672 0 0 3.9514292,5.3604547 1.1997888,1.2675855 80.074672 0 0 2.4961199,4.3976698 Z m 0.074974,0.4171488 A 0.80148375,0.84677333 80.074672 0 1 3.5440925,5.4575082 0.80148375,0.84677333 80.074672 0 1 2.8471493,6.3924102 0.80148375,0.84677333 80.074672 0 1 1.8741535,5.74972 0.80148375,0.84677333 80.074672 0 1 2.5710967,4.814818 Z"
+ id="path5846-5"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.7567277;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ d="m 11.838896,0.64428913 a 1.231838,1.2836218 52.593897 0 0 -0.271701,1.75779027 1.231838,1.2836218 52.593897 0 0 1.767576,0.1983008 1.231838,1.2836218 52.593897 0 0 0.271701,-1.75779027 1.231838,1.2836218 52.593897 0 0 -1.767576,-0.1983008 z m 0.265925,0.3444462 A 0.82289328,0.85748593 52.593897 0 1 13.286115,1.1203938 0.82289328,0.85748593 52.593897 0 1 13.103698,2.2949179 0.82289328,0.85748593 52.593897 0 1 11.922407,2.163257 0.82289328,0.85748593 52.593897 0 1 12.104824,0.98873353 Z"
+ id="path5846-5-6" />
+</svg>
diff --git a/editor/icons/icon_bezier_handles_mirror.svg b/editor/icons/icon_bezier_handles_mirror.svg
new file mode 100644
index 0000000000..682c898368
--- /dev/null
+++ b/editor/icons/icon_bezier_handles_mirror.svg
@@ -0,0 +1,98 @@
+<?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_bezier_handles_mirror.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="1417"
+ inkscape:window-height="685"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="4.2910315"
+ inkscape:cy="11.857644"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <path
+ style="fill:none;stroke:#84c2ff;stroke-width:1.70000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1.7627119,13.627119 c 0,0 1.2881355,-6.847458 6.5762712,-8.1355935 5.0847459,0.9491522 5.9661009,8.1355925 5.9661009,8.1355925"
+ id="path4526"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc" />
+ <ellipse
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path5846"
+ cx="1.8983043"
+ cy="13.491526"
+ rx="1.2675855"
+ ry="1.1997888" />
+ <ellipse
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path5846-3"
+ cx="14.237288"
+ cy="13.491526"
+ rx="1.2675855"
+ ry="1.1997888" />
+ <path
+ style="fill:none;stroke:#84c2ff;stroke-width:0.80513805;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 8.2033896,4.6779662 H 4.3698875"
+ id="path5878"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#84c2ff;stroke-width:0.71670938;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 11.931789,4.6440679 H 8.2033896"
+ id="path5878-7"
+ inkscape:connector-curvature="0" />
+ <ellipse
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path5846-3-6"
+ cx="8.2711868"
+ cy="4.7796612"
+ rx="1.2675855"
+ ry="1.1997888" />
+ <path
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ d="M 3.1539157,3.4305762 A 1.2675855,1.1997888 0 0 0 1.8863376,4.629795 1.2675855,1.1997888 0 0 0 3.1539157,5.8290137 1.2675855,1.1997888 0 0 0 4.4214938,4.629795 1.2675855,1.1997888 0 0 0 3.1539157,3.4305762 Z m 0.00195,0.4238282 A 0.84677333,0.80148375 0 0 1 4.003525,4.6551856 0.84677333,0.80148375 0 0 1 3.1558688,5.4559668 0.84677333,0.80148375 0 0 1 2.3082126,4.6551856 0.84677333,0.80148375 0 0 1 3.1558688,3.8544044 Z"
+ id="path5846-5"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ d="m 13.093969,3.3750567 a 1.2675855,1.1997888 0 0 0 -1.267578,1.1992188 1.2675855,1.1997888 0 0 0 1.267578,1.1992187 1.2675855,1.1997888 0 0 0 1.267578,-1.1992187 1.2675855,1.1997888 0 0 0 -1.267578,-1.1992188 z m 0.002,0.4238282 a 0.84677333,0.80148375 0 0 1 0.847659,0.8007812 0.84677333,0.80148375 0 0 1 -0.847656,0.8007812 0.84677333,0.80148375 0 0 1 -0.847656,-0.8007812 0.84677333,0.80148375 0 0 1 0.847656,-0.8007812 z"
+ id="path5846-5-6" />
+</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_color_track_vu.svg b/editor/icons/icon_color_track_vu.svg
new file mode 100644
index 0000000000..cad76d0234
--- /dev/null
+++ b/editor/icons/icon_color_track_vu.svg
@@ -0,0 +1,115 @@
+<?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:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="24"
+ version="1.1"
+ viewBox="0 0 16 24"
+ id="svg6"
+ sodipodi:docname="icon_color_track_vu.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">
+ <linearGradient
+ id="linearGradient4583"
+ inkscape:collect="always">
+ <stop
+ id="stop4579"
+ offset="0"
+ style="stop-color:#f70000;stop-opacity:1" />
+ <stop
+ id="stop4581"
+ offset="1"
+ style="stop-color:#eec315;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4549">
+ <stop
+ style="stop-color:#288027;stop-opacity:1"
+ offset="0"
+ id="stop4545" />
+ <stop
+ style="stop-color:#dbee15;stop-opacity:1"
+ offset="1"
+ id="stop4547" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4549"
+ id="linearGradient4551"
+ x1="7.7288136"
+ y1="16.474577"
+ x2="7.7288136"
+ y2="3.8644071"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0931873,0,0,1.4762899,-0.98021429,0.08553021)" />
+ <linearGradient
+ gradientTransform="matrix(1.1036585,0,0,0.47778193,-16.507235,-7.9018165)"
+ inkscape:collect="always"
+ xlink:href="#linearGradient4583"
+ id="linearGradient4551-7"
+ x1="7.7288136"
+ y1="16.474577"
+ x2="7.7288136"
+ y2="3.8644071"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1170"
+ inkscape:window-height="712"
+ id="namedview8"
+ showgrid="false"
+ showguides="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="5.3261277"
+ inkscape:cy="13.681053"
+ inkscape:window-x="397"
+ inkscape:window-y="233"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <rect
+ style="fill:url(#linearGradient4551);fill-opacity:1;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;paint-order:fill markers stroke"
+ id="rect822"
+ width="18.232145"
+ height="18.416088"
+ x="-1.3507863"
+ y="5.9906898"
+ ry="0.84580106" />
+ <rect
+ style="fill:url(#linearGradient4551-7);fill-opacity:1;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;paint-order:fill markers stroke"
+ id="rect822-5"
+ width="18.406782"
+ height="5.9601259"
+ x="-16.881357"
+ y="-5.9906898"
+ ry="0.27373245"
+ transform="scale(-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_edit_bezier.svg b/editor/icons/icon_edit_bezier.svg
new file mode 100644
index 0000000000..542ff52aac
--- /dev/null
+++ b/editor/icons/icon_edit_bezier.svg
@@ -0,0 +1,73 @@
+<?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_edit_bezier.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="1039"
+ inkscape:window-height="585"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="11.65471"
+ inkscape:cy="9.0988062"
+ inkscape:window-x="277"
+ inkscape:window-y="113"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g4">
+ <path
+ style="fill:none;stroke:#84c2ff;stroke-width:2.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1.4758015,1050.3064 c 11.6492855,0.7191 3.1098343,-11.4976 12.2331255,-11.3475"
+ id="path4526"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <circle
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1"
+ id="path5846-3"
+ cy="1038.7133"
+ cx="13.470984"
+ r="1.8230016" />
+ <circle
+ r="1.8230016"
+ cx="2.4449117"
+ cy="1050.1708"
+ id="circle1374"
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:5.64574671;stroke-miterlimit:4.9000001;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+</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_key_animation.svg b/editor/icons/icon_key_animation.svg
new file mode 100644
index 0000000000..a09567498f
--- /dev/null
+++ b/editor/icons/icon_key_animation.svg
@@ -0,0 +1,65 @@
+<?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="8"
+ height="8"
+ version="1.1"
+ viewBox="0 0 8 8"
+ id="svg6"
+ sodipodi:docname="icon_key_animation.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="1852"
+ inkscape:window-height="781"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="29.5"
+ inkscape:cx="-10.271186"
+ inkscape:cy="3.4149032"
+ inkscape:window-x="68"
+ inkscape:window-y="117"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1044.4)"
+ id="g4">
+ <rect
+ transform="rotate(-45)"
+ x="-741.53"
+ y="741.08"
+ width="6.1027"
+ height="6.1027"
+ ry=".76286"
+ fill="#ea686c"
+ id="rect2"
+ style="fill:#b76ef0;fill-opacity:1" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_key_audio.svg b/editor/icons/icon_key_audio.svg
new file mode 100644
index 0000000000..7c728bfd01
--- /dev/null
+++ b/editor/icons/icon_key_audio.svg
@@ -0,0 +1,65 @@
+<?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="8"
+ height="8"
+ version="1.1"
+ viewBox="0 0 8 8"
+ id="svg6"
+ sodipodi:docname="icon_key_audio.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="1053"
+ inkscape:window-height="591"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="29.5"
+ inkscape:cx="4"
+ inkscape:cy="4"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1044.4)"
+ id="g4">
+ <rect
+ transform="rotate(-45)"
+ x="-741.53"
+ y="741.08"
+ width="6.1027"
+ height="6.1027"
+ ry=".76286"
+ fill="#ea686c"
+ id="rect2"
+ style="fill:#eae668;fill-opacity:1" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_key_bezier.svg b/editor/icons/icon_key_bezier.svg
new file mode 100644
index 0000000000..62af6fdb34
--- /dev/null
+++ b/editor/icons/icon_key_bezier.svg
@@ -0,0 +1,65 @@
+<?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="8"
+ height="8"
+ version="1.1"
+ viewBox="0 0 8 8"
+ id="svg6"
+ sodipodi:docname="icon_key_bezier.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="1852"
+ inkscape:window-height="781"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="29.5"
+ inkscape:cx="-17.152542"
+ inkscape:cy="3.4149032"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1044.4)"
+ id="g4">
+ <rect
+ transform="rotate(-45)"
+ x="-741.53"
+ y="741.08"
+ width="6.1027"
+ height="6.1027"
+ ry=".76286"
+ fill="#ea686c"
+ id="rect2"
+ style="fill:#5792f6;fill-opacity:1" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_key_bezier_handle.svg b/editor/icons/icon_key_bezier_handle.svg
new file mode 100644
index 0000000000..d7b22d0905
--- /dev/null
+++ b/editor/icons/icon_key_bezier_handle.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="8"
+ height="8"
+ version="1.1"
+ viewBox="0 0 8 8"
+ id="svg6"
+ sodipodi:docname="icon_key_bezier_handle.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="1853"
+ inkscape:window-height="1016"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="59"
+ inkscape:cx="2.0952442"
+ inkscape:cy="4.6061633"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1044.4)"
+ id="g4">
+ <path
+ style="fill:#e0e0e0;fill-opacity:1"
+ d="M 3.9960938 -0.037109375 C 3.8010931 -0.037109375 3.6064535 0.038077731 3.4570312 0.1875 L 0.22070312 3.4238281 C -0.078134343 3.7226656 -0.078141414 4.2050617 0.22070312 4.5039062 L 3.4570312 7.7402344 C 3.7558687 8.0390718 4.2382719 8.0390718 4.5371094 7.7402344 L 7.7734375 4.5039062 C 8.072282 4.2050617 8.072275 3.7226656 7.7734375 3.4238281 L 4.5371094 0.1875 C 4.3876871 0.038077731 4.1910944 -0.037109375 3.9960938 -0.037109375 z M 4.0253906 0.81445312 C 4.1770098 0.81445312 4.3291322 0.87241756 4.4453125 0.98828125 L 6.9609375 3.4960938 C 7.193298 3.7278211 7.193298 4.102257 6.9609375 4.3339844 L 4.4453125 6.84375 C 4.212952 7.0754774 3.8378293 7.0754774 3.6054688 6.84375 L 1.0898438 4.3339844 C 0.85748323 4.102257 0.85748323 3.7278211 1.0898438 3.4960938 L 3.6054688 0.98828125 C 3.721649 0.87241756 3.8737714 0.81445312 4.0253906 0.81445312 z "
+ transform="translate(0,1044.4)"
+ id="rect2" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_key_bezier_point.svg b/editor/icons/icon_key_bezier_point.svg
new file mode 100644
index 0000000000..aa33063c95
--- /dev/null
+++ b/editor/icons/icon_key_bezier_point.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="8"
+ height="8"
+ version="1.1"
+ viewBox="0 0 8 8"
+ id="svg6"
+ sodipodi:docname="icon_key_bezier_point.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="836"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="29.5"
+ inkscape:cx="4"
+ inkscape:cy="4"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <g
+ transform="translate(0 -1044.4)"
+ id="g4">
+ <rect
+ transform="rotate(-45)"
+ x="-741.53"
+ y="741.08"
+ width="6.1027"
+ height="6.1027"
+ ry=".76286"
+ fill="#e0e0e0"
+ id="rect2" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_key_bezier_selected.svg b/editor/icons/icon_key_bezier_selected.svg
new file mode 100644
index 0000000000..e3f967707a
--- /dev/null
+++ b/editor/icons/icon_key_bezier_selected.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="8"
+ height="8"
+ version="1.1"
+ viewBox="0 0 8 8"
+ id="svg6"
+ sodipodi:docname="icon_key_bezier_selected.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="836"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="29.5"
+ inkscape:cx="4"
+ inkscape:cy="4"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <g
+ transform="translate(0 -1044.4)"
+ id="g4">
+ <rect
+ transform="rotate(-45)"
+ x="-741.53"
+ y="741.08"
+ width="6.1027"
+ height="6.1027"
+ ry=".76286"
+ fill="#84c2ff"
+ id="rect2" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_key_call.svg b/editor/icons/icon_key_call.svg
index 7fcc65801a..e702898288 100644
--- a/editor/icons/icon_key_call.svg
+++ b/editor/icons/icon_key_call.svg
@@ -1,5 +1,64 @@
-<svg width="8" height="8" version="1.1" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1044.4)">
-<rect transform="rotate(-45)" x="-741.53" y="741.08" width="6.1027" height="6.1027" ry=".76286" fill="#adf18f"/>
-</g>
+<?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="8"
+ height="8"
+ version="1.1"
+ viewBox="0 0 8 8"
+ id="svg6"
+ sodipodi:docname="icon_key_call.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" />
+ </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="836"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="29.5"
+ inkscape:cx="4"
+ inkscape:cy="4"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1044.4)"
+ id="g4">
+ <rect
+ transform="rotate(-45)"
+ x="-741.53"
+ y="741.08"
+ width="6.1027"
+ height="6.1027"
+ ry=".76286"
+ fill="#adf18f"
+ id="rect2"
+ style="fill:#66f376;fill-opacity:1" />
+ </g>
</svg>
diff --git a/editor/icons/icon_key_selected.svg b/editor/icons/icon_key_selected.svg
index c73d31981d..2842fd93eb 100644
--- a/editor/icons/icon_key_selected.svg
+++ b/editor/icons/icon_key_selected.svg
@@ -1,5 +1,76 @@
-<svg width="8" height="8" version="1.1" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1044.4)">
-<rect transform="rotate(-45)" x="-741.53" y="741.08" width="6.1027" height="6.1027" ry=".76286" fill="#84c2ff"/>
-</g>
+<?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="8"
+ height="8"
+ version="1.1"
+ viewBox="0 0 8 8"
+ id="svg6"
+ sodipodi:docname="icon_key_selected.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" />
+ </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="1568"
+ inkscape:window-height="767"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="41.7193"
+ inkscape:cx="-0.48848775"
+ inkscape:cy="3.5639274"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4-3" />
+ <g
+ transform="translate(0 -1044.4)"
+ id="g4">
+ <rect
+ transform="rotate(-45)"
+ x="-741.53"
+ y="741.08"
+ width="6.1027"
+ height="6.1027"
+ ry=".76286"
+ fill="#84c2ff"
+ id="rect2" />
+ </g>
+ <g
+ transform="translate(0,-1044.4)"
+ id="g4-3">
+ <rect
+ style="fill:#003e7a;fill-opacity:1;stroke-width:0.56281364"
+ transform="matrix(0.71728847,-0.69677633,0.71728847,0.69677633,0,0)"
+ x="-751.20953"
+ y="753.42743"
+ width="3.4346831"
+ height="3.4346831"
+ ry="0.42934799"
+ id="rect2-6" />
+ </g>
</svg>
diff --git a/editor/icons/icon_key_xform.svg b/editor/icons/icon_key_xform.svg
index 7b73715771..fd22b67f52 100644
--- a/editor/icons/icon_key_xform.svg
+++ b/editor/icons/icon_key_xform.svg
@@ -1,5 +1,64 @@
-<svg width="8" height="8" version="1.1" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1044.4)">
-<rect transform="rotate(-45)" x="-741.53" y="741.08" width="6.1027" height="6.1027" ry=".76286" fill="#ea686c"/>
-</g>
+<?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="8"
+ height="8"
+ version="1.1"
+ viewBox="0 0 8 8"
+ id="svg6"
+ sodipodi:docname="icon_key_xform.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" />
+ </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="836"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="29.5"
+ inkscape:cx="4"
+ inkscape:cy="4"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1044.4)"
+ id="g4">
+ <rect
+ transform="rotate(-45)"
+ x="-741.53"
+ y="741.08"
+ width="6.1027"
+ height="6.1027"
+ ry=".76286"
+ fill="#ea686c"
+ id="rect2"
+ style="fill:#ea9568;fill-opacity:1" />
+ </g>
</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_oriented_path_follow.svg b/editor/icons/icon_oriented_path_follow.svg
new file mode 100644
index 0000000000..bd3f585e54
--- /dev/null
+++ b/editor/icons/icon_oriented_path_follow.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="m13 0l-3 4h1.9473c-0.1385 1.3203-0.5583 1.9074-1.084 2.2754-0.64426 0.451-1.7129 0.60547-2.9629 0.73047s-2.6814 0.22053-3.9121 1.082c-0.89278 0.62493-1.5321 1.6522-1.8184 3.0957a2 2 0 0 0 -1.1699 1.8164 2 2 0 0 0 2 2 2 2 0 0 0 2 -2 2 2 0 0 0 -0.84961 -1.6328c0.19235-0.88496 0.55306-1.3373 0.98633-1.6406 0.64426-0.451 1.7129-0.60547 2.9629-0.73047s2.6814-0.22053 3.9121-1.082c1.0528-0.73697 1.7552-2.032 1.9375-3.9141h2.0508l-3-4z" fill="#fc9c9c" fill-opacity=".99608"/>
+</g>
+</svg>
diff --git a/editor/icons/icon_play_overlay.svg b/editor/icons/icon_play_overlay.svg
new file mode 100644
index 0000000000..eff33f1b6b
--- /dev/null
+++ b/editor/icons/icon_play_overlay.svg
@@ -0,0 +1,4 @@
+<svg width="64" height="64" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
+ <rect x="0" y="0" width="64" height="64" rx="5" ry="5" fill="#044B94" fill-opacity="0.6"/>
+ <path d="M16 16 L48 32 L16 48" fill="#f2f2f2"/>
+</svg> \ No newline at end of file
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_time.svg b/editor/icons/icon_time.svg
new file mode 100644
index 0000000000..d50c9570b3
--- /dev/null
+++ b/editor/icons/icon_time.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_time.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="836"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="7.375"
+ inkscape:cx="4.4999435"
+ inkscape:cy="13.04848"
+ inkscape:window-x="744"
+ inkscape:window-y="280"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g4">
+ <g
+ id="g8"
+ style="fill:#e0e0e0;fill-opacity:1"
+ transform="matrix(0.0279396,0,0,0.02755726,0.91401567,1037.1343)">
+ <g
+ id="g6"
+ style="fill:#e0e0e0;fill-opacity:1">
+ <path
+ d="M 276.193,58.507 V 40.389 h 14.578 c 11.153,0 20.194,-9.042 20.194,-20.194 C 310.965,9.043 301.923,0 290.771,0 h -69.544 c -11.153,0 -20.194,9.042 -20.194,20.194 0,11.152 9.042,20.194 20.194,20.194 h 14.578 V 58.506 C 119.952,68.76 28.799,166.327 28.799,284.799 28.799,410.078 130.721,512 256,512 381.279,512 483.201,410.078 483.201,284.799 483.2,166.327 392.046,68.76 276.193,58.507 Z m 0,412.009 v -20.124 c 0,-11.153 -9.042,-20.194 -20.194,-20.194 -11.153,0 -20.194,9.042 -20.194,20.194 v 20.124 C 148.895,461.131 79.668,391.902 70.283,304.994 h 20.124 c 11.153,0 20.194,-9.042 20.194,-20.194 0,-11.152 -9.042,-20.194 -20.194,-20.194 H 70.282 c 9.385,-86.91 78.614,-156.137 165.522,-165.523 v 20.124 c 0,11.153 9.042,20.194 20.194,20.194 11.153,0 20.194,-9.042 20.194,-20.194 V 99.081 c 86.91,9.385 156.137,78.614 165.522,165.523 H 421.59 c -11.153,0 -20.194,9.042 -20.194,20.194 0,11.152 9.042,20.194 20.194,20.194 h 20.126 c -9.385,86.911 -78.613,156.14 -165.523,165.524 z"
+ id="path2"
+ style="fill:#e0e0e0;fill-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 317.248,194.99 -58.179,58.18 c -1.011,-0.097 -2.034,-0.151 -3.071,-0.151 -17.552,0 -31.779,14.229 -31.779,31.779 0,17.552 14.228,31.779 31.779,31.779 17.551,0 31.779,-14.229 31.779,-31.779 0,-1.037 -0.054,-2.06 -0.151,-3.07 l 58.178,-58.18 c 7.887,-7.885 7.887,-20.672 0,-28.559 -7.882,-7.886 -20.669,-7.886 -28.556,0.001 z"
+ id="path4"
+ style="fill:#e0e0e0;fill-opacity:1"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ </g>
+</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_tool_triangle.svg b/editor/icons/icon_tool_triangle.svg
new file mode 100644
index 0000000000..5696008767
--- /dev/null
+++ b/editor/icons/icon_tool_triangle.svg
@@ -0,0 +1,82 @@
+<?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_triangle.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="1204"
+ inkscape:window-height="703"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="29.5"
+ inkscape:cx="8.0650451"
+ inkscape:cy="7.0341257"
+ inkscape:window-x="542"
+ inkscape:window-y="205"
+ 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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 27.695915,1056.3022 c 0,0 7.457627,-8.0678 7.118644,-7.8644 -0.338983,0.2034 5.830509,11.7288 5.830509,11.7288 z"
+ id="path821"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:#4b4b4b;fill-opacity:1;stroke:#e0e0e0;stroke-width:0.51200002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path825"
+ cx="34.662014"
+ cy="1048.5903"
+ r="1.607564" />
+ <circle
+ style="fill:#4b4b4b;fill-opacity:1;stroke:#e0e0e0;stroke-width:0.51200002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path825-3"
+ cx="39.933205"
+ cy="1059.6581"
+ r="1.607564" />
+ <circle
+ style="fill:#4b4b4b;fill-opacity:1;stroke:#e0e0e0;stroke-width:0.51200002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path825-3-6"
+ cx="28.17049"
+ cy="1056.2683"
+ r="1.607564" />
+ </g>
+ </g>
+</svg>
diff --git a/editor/icons/icon_track_capture.svg b/editor/icons/icon_track_capture.svg
new file mode 100644
index 0000000000..da6a662746
--- /dev/null
+++ b/editor/icons/icon_track_capture.svg
@@ -0,0 +1,61 @@
+<?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="8"
+ version="1.1"
+ viewBox="0 0 16 8"
+ id="svg6"
+ sodipodi:docname="icon_track_capture.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="1350"
+ inkscape:window-height="593"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="27.577164"
+ inkscape:cx="8.347146"
+ inkscape:cy="4.4012076"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <path
+ style="fill:#e0e0e0;fill-opacity:1"
+ d="m 2.1665128,0.99764963 c -0.422625,0 -0.763672,0.34104737 -0.763672,0.76367187 v 4.5742187 c 0,0.4226242 0.341047,0.7617192 0.763672,0.7617192 h 4.472656 c 0.422625,0 0.763672,-0.339095 0.763672,-0.7617192 V 5.347259 h -3.300781 c -0.1662,0 -0.298828,-0.3390943 -0.298828,-0.7617188 V 3.3609308 c 0,-0.4226244 0.132628,-0.7636718 0.298828,-0.7636718 h 3.300781 V 1.7613215 c 0,-0.4226245 -0.341047,-0.76367187 -0.763672,-0.76367187 z"
+ id="rect1389"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#e0e0e0;fill-opacity:1;stroke:#e0e0e0;stroke-width:0.80299997;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 9.1827441,4.7953408 C 9.6993662,3.7537783 10.278269,2.5835979 10.469195,2.1949398 l 0.347137,-0.7066511 0.679654,0.00665 0.679654,0.00665 0.956945,2.3125 c 0.526319,1.271875 1.007254,2.4334375 1.068744,2.5812497 l 0.1118,0.26875 H 13.715914 13.1187 L 12.785851,6.0203387 12.453002,5.3765887 H 11.319176 10.185351 L 9.8066761,6.032702 9.4280014,6.6888154 l -0.5922856,1.37e-4 -0.592285,1.36e-4 z m 3.1779349,-0.369483 c 0.0042,-0.00346 -0.233487,-0.4884588 -0.528245,-1.0777779 l -0.535922,-1.0714891 -0.03691,0.0875 c -0.0203,0.048125 -0.183516,0.425 -0.362699,0.8375 -0.179182,0.4125 -0.355738,0.85125 -0.392346,0.975 -0.03661,0.12375 -0.07127,0.2390723 -0.07703,0.2562715 -0.0083,0.024853 0.188215,0.027989 0.957503,0.015278 0.532385,-0.0088 0.971429,-0.018823 0.975651,-0.022283 z"
+ id="path1424"
+ inkscape:connector-curvature="0" />
+</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/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 1d7545f182..a13f741ee7 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -339,7 +339,7 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) {
NodeMap nm;
nm.node = node;
node_map[p_node->id] = nm;
- node_name_map[p_node->name] = p_node->id;
+ node_name_map[node->get_name()] = p_node->id;
Transform xf = p_node->default_transform;
xf = collada.fix_transform(xf) * p_node->post_transform;
@@ -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 af79f9946a..eb0bc0f782 100644
--- a/editor/import/editor_scene_importer_gltf.cpp
+++ b/editor/import/editor_scene_importer_gltf.cpp
@@ -863,6 +863,7 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) {
ERR_FAIL_COND_V(!d.has("primitives"), ERR_PARSE_ERROR);
Array primitives = d["primitives"];
+ Dictionary extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary();
for (int j = 0; j < primitives.size(); j++) {
@@ -1000,8 +1001,10 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) {
Array targets = p["targets"];
if (j == 0) {
+ Array target_names = extras.has("targetNames") ? (Array)extras["targetNames"] : Array();
for (int k = 0; k < targets.size(); k++) {
- mesh.mesh->add_blend_shape(String("morph_") + itos(k));
+ String name = k < target_names.size() ? (String)target_names[k] : String("morph_") + itos(k);
+ mesh.mesh->add_blend_shape(name);
}
}
@@ -1163,7 +1166,7 @@ Error EditorSceneImporterGLTF::_parse_images(GLTFState &state, const String &p_b
continue;
}
- if (mimetype.findn("jpg") != -1) {
+ if (mimetype.findn("jpeg") != -1) {
//is a jpg
Ref<Image> img = Image::_jpg_mem_loader_func(data_ptr, data_size);
@@ -1253,12 +1256,15 @@ Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) {
}
if (mr.has("metallicFactor")) {
-
material->set_metallic(mr["metallicFactor"]);
+ } else {
+ material->set_metallic(1.0);
}
- if (mr.has("roughnessFactor")) {
+ if (mr.has("roughnessFactor")) {
material->set_roughness(mr["roughnessFactor"]);
+ } else {
+ material->set_roughness(1.0);
}
if (mr.has("metallicRoughnessTexture")) {
@@ -1983,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_bitmask.cpp b/editor/import/resource_importer_bitmask.cpp
index a5d3959952..7b330936f6 100644
--- a/editor/import/resource_importer_bitmask.cpp
+++ b/editor/import/resource_importer_bitmask.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* resource_importer_bitmask.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 "resource_importer_bitmask.h"
#include "core/image.h"
#include "editor/editor_file_system.h"
diff --git a/editor/import/resource_importer_bitmask.h b/editor/import/resource_importer_bitmask.h
index 8a3cafa7ce..f3537df819 100644
--- a/editor/import/resource_importer_bitmask.h
+++ b/editor/import/resource_importer_bitmask.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* resource_importer_bitmask.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. */
+/*************************************************************************/
+
#ifndef RESOURCE_IMPORTER_BITMASK_H
#define RESOURCE_IMPORTER_BITMASK_H
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/import_dock.cpp b/editor/import_dock.cpp
index 128196be5a..f91802b352 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -407,6 +407,7 @@ ImportDock::ImportDock() {
set_name("Import");
imported = memnew(Label);
imported->add_style_override("normal", EditorNode::get_singleton()->get_gui_base()->get_stylebox("normal", "LineEdit"));
+ imported->set_clip_text(true);
add_child(imported);
HBoxContainer *hb = memnew(HBoxContainer);
add_margin_child(TTR("Import As:"), hb);
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
new file mode 100644
index 0000000000..0d0b12c911
--- /dev/null
+++ b/editor/inspector_dock.cpp
@@ -0,0 +1,568 @@
+/*************************************************************************/
+/* inspector_dock.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 "inspector_dock.h"
+
+#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
+#include "editor/plugins/animation_player_editor_plugin.h"
+
+void InspectorDock::_menu_option(int p_option) {
+ switch (p_option) {
+ case RESOURCE_SAVE: {
+ _save_resource(false);
+ } break;
+ case RESOURCE_SAVE_AS: {
+ _save_resource(true);
+ } break;
+
+ case OBJECT_REQUEST_HELP: {
+ if (current) {
+ editor->set_visible_editor(EditorNode::EDITOR_SCRIPT);
+ emit_signal("request_help", current->get_class());
+ }
+ } break;
+
+ case OBJECT_COPY_PARAMS: {
+ editor_data->apply_changes_in_editors();
+ if (current)
+ editor_data->copy_object_params(current);
+ } break;
+
+ case OBJECT_PASTE_PARAMS: {
+ editor_data->apply_changes_in_editors();
+ if (current)
+ editor_data->paste_object_params(current);
+ editor_data->get_undo_redo().clear_history();
+ } break;
+
+ case OBJECT_UNIQUE_RESOURCES: {
+ editor_data->apply_changes_in_editors();
+ if (current) {
+ List<PropertyInfo> props;
+ current->get_property_list(&props);
+ Map<RES, RES> duplicates;
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+
+ if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
+ continue;
+
+ Variant v = current->get(E->get().name);
+ if (v.is_ref()) {
+ REF ref = v;
+ if (ref.is_valid()) {
+
+ RES res = ref;
+ if (res.is_valid()) {
+
+ if (!duplicates.has(res)) {
+ duplicates[res] = res->duplicate();
+ }
+ res = duplicates[res];
+
+ current->set(E->get().name, res);
+ }
+ }
+ }
+ }
+ }
+
+ editor_data->get_undo_redo().clear_history();
+
+ editor->get_editor_plugins_over()->edit(NULL);
+ editor->get_editor_plugins_over()->edit(current);
+
+ } break;
+
+ default: {
+ if (p_option >= OBJECT_METHOD_BASE) {
+ ERR_FAIL_COND(!current);
+
+ int idx = p_option - OBJECT_METHOD_BASE;
+
+ List<MethodInfo> methods;
+ current->get_method_list(&methods);
+
+ ERR_FAIL_INDEX(idx, methods.size());
+ String name = methods[idx].name;
+
+ if (current)
+ current->call(name);
+ }
+ }
+ }
+}
+
+void InspectorDock::_new_resource() {
+ new_resource_dialog->popup_create(true);
+}
+
+void InspectorDock::_load_resource(const String &p_type) {
+ load_resource_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+
+ List<String> extensions;
+ ResourceLoader::get_recognized_extensions_for_type(p_type, &extensions);
+
+ load_resource_dialog->clear_filters();
+ for (int i = 0; i < extensions.size(); i++) {
+ load_resource_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+ }
+
+ load_resource_dialog->popup_centered_ratio();
+}
+
+void InspectorDock::_resource_file_selected(String p_file) {
+ RES res = ResourceLoader::load(p_file);
+ if (res.is_null()) {
+ warning_dialog->get_ok()->set_text("Ugh");
+ warning_dialog->set_text(TTR("Failed to load resource."));
+ return;
+ };
+
+ editor->push_item(res.operator->());
+}
+
+void InspectorDock::_save_resource(bool save_as) const {
+ uint32_t current = EditorNode::get_singleton()->get_editor_history()->get_current();
+ Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
+
+ ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj))
+
+ RES current_res = RES(Object::cast_to<Resource>(current_obj));
+
+ if (save_as)
+ editor->save_resource_as(current_res);
+ else
+ editor->save_resource(current_res);
+}
+
+void InspectorDock::_unref_resource() const {
+ uint32_t current = EditorNode::get_singleton()->get_editor_history()->get_current();
+ Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
+
+ ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj))
+
+ RES current_res = RES(Object::cast_to<Resource>(current_obj));
+ current_res->set_path("");
+ editor->edit_current();
+}
+
+void InspectorDock::_copy_resource() const {
+ uint32_t current = EditorNode::get_singleton()->get_editor_history()->get_current();
+ Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
+
+ ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj))
+
+ RES current_res = RES(Object::cast_to<Resource>(current_obj));
+
+ EditorSettings::get_singleton()->set_resource_clipboard(current_res);
+}
+
+void InspectorDock::_paste_resource() const {
+ RES r = EditorSettings::get_singleton()->get_resource_clipboard();
+ if (r.is_valid()) {
+ editor->push_item(EditorSettings::get_singleton()->get_resource_clipboard().ptr(), String());
+ }
+}
+
+void InspectorDock::_prepare_history() {
+ EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
+
+ int history_to = MAX(0, editor_history->get_history_len() - 25);
+
+ history_menu->get_popup()->clear();
+
+ Ref<Texture> base_icon = get_icon("Object", "EditorIcons");
+ Set<ObjectID> already;
+ for (int i = editor_history->get_history_len() - 1; i >= history_to; i--) {
+
+ ObjectID id = editor_history->get_history_obj(i);
+ Object *obj = ObjectDB::get_instance(id);
+ if (!obj || already.has(id)) {
+ if (history_to > 0) {
+ history_to--;
+ }
+ continue;
+ }
+
+ already.insert(id);
+
+ Ref<Texture> icon = get_icon("Object", "EditorIcons");
+ if (has_icon(obj->get_class(), "EditorIcons"))
+ icon = get_icon(obj->get_class(), "EditorIcons");
+ else
+ icon = base_icon;
+
+ String text;
+ if (Object::cast_to<Resource>(obj)) {
+ Resource *r = Object::cast_to<Resource>(obj);
+ if (r->get_path().is_resource_file())
+ text = r->get_path().get_file();
+ else if (r->get_name() != String()) {
+ text = r->get_name();
+ } else {
+ text = r->get_class();
+ }
+ } else if (Object::cast_to<Node>(obj)) {
+ text = Object::cast_to<Node>(obj)->get_name();
+ } else if (obj->is_class("ScriptEditorDebuggerInspectedObject")) {
+ text = obj->call("get_title");
+ } else {
+ text = obj->get_class();
+ }
+
+ if (i == editor_history->get_history_pos()) {
+ text = "[" + text + "]";
+ }
+ history_menu->get_popup()->add_icon_item(icon, text, i);
+ }
+}
+
+void InspectorDock::_select_history(int p_idx) const {
+ //push it to the top, it is not correct, but it's more useful
+ ObjectID id = EditorNode::get_singleton()->get_editor_history()->get_history_obj(p_idx);
+ Object *obj = ObjectDB::get_instance(id);
+ if (!obj)
+ return;
+ editor->push_item(obj);
+}
+
+void InspectorDock::_resource_created() const {
+ Object *c = new_resource_dialog->instance_selected();
+
+ ERR_FAIL_COND(!c);
+ Resource *r = Object::cast_to<Resource>(c);
+ ERR_FAIL_COND(!r);
+
+ REF res(r);
+ editor->push_item(c);
+}
+
+void InspectorDock::_resource_selected(const RES &p_res, const String &p_property) const {
+ if (p_res.is_null())
+ return;
+
+ RES r = p_res;
+ editor->push_item(r.operator->(), p_property);
+}
+
+void InspectorDock::_edit_forward() {
+ if (EditorNode::get_singleton()->get_editor_history()->next())
+ editor->edit_current();
+}
+void InspectorDock::_edit_back() {
+ EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
+ if (editor_history->previous() || editor_history->get_path_size() == 1)
+ editor->edit_current();
+}
+
+void InspectorDock::_menu_collapseall() {
+ inspector->collapse_all_folding();
+}
+
+void InspectorDock::_menu_expandall() {
+ inspector->expand_all_folding();
+}
+
+void InspectorDock::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) {
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_value_key(p_keyed, p_value, p_advance);
+}
+
+void InspectorDock::_transform_keyed(Object *sp, const String &p_sub, const Transform &p_key) {
+ Spatial *s = Object::cast_to<Spatial>(sp);
+ if (!s)
+ return;
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(s, p_sub, p_key);
+}
+
+void InspectorDock::_warning_pressed() {
+ warning_dialog->get_ok()->set_text(TTR("Ok"));
+ warning_dialog->popup_centered_minsize();
+}
+
+Container *InspectorDock::get_addon_area() {
+ return this;
+}
+
+void InspectorDock::_bind_methods() {
+ ClassDB::bind_method("_menu_option", &InspectorDock::_menu_option);
+
+ ClassDB::bind_method("update_keying", &InspectorDock::update_keying);
+ ClassDB::bind_method("_property_keyed", &InspectorDock::_property_keyed);
+ ClassDB::bind_method("_transform_keyed", &InspectorDock::_transform_keyed);
+
+ ClassDB::bind_method("_new_resource", &InspectorDock::_new_resource);
+ ClassDB::bind_method("_resource_file_selected", &InspectorDock::_resource_file_selected);
+ ClassDB::bind_method("_open_resource_selector", &InspectorDock::_open_resource_selector);
+ ClassDB::bind_method("_unref_resource", &InspectorDock::_unref_resource);
+ ClassDB::bind_method("_paste_resource", &InspectorDock::_paste_resource);
+ ClassDB::bind_method("_copy_resource", &InspectorDock::_copy_resource);
+
+ ClassDB::bind_method("_select_history", &InspectorDock::_select_history);
+ ClassDB::bind_method("_prepare_history", &InspectorDock::_prepare_history);
+ ClassDB::bind_method("_resource_created", &InspectorDock::_resource_created);
+ ClassDB::bind_method("_resource_selected", &InspectorDock::_resource_selected, DEFVAL(""));
+ ClassDB::bind_method("_menu_collapseall", &InspectorDock::_menu_collapseall);
+ ClassDB::bind_method("_menu_expandall", &InspectorDock::_menu_expandall);
+ ClassDB::bind_method("_warning_pressed", &InspectorDock::_warning_pressed);
+ ClassDB::bind_method("_edit_forward", &InspectorDock::_edit_forward);
+ ClassDB::bind_method("_edit_back", &InspectorDock::_edit_back);
+
+ ADD_SIGNAL(MethodInfo("request_help"));
+}
+
+void InspectorDock::edit_resource(const Ref<Resource> &p_resource) {
+ _resource_selected(p_resource, "");
+}
+
+void InspectorDock::open_resource(const String &p_type) {
+ _load_resource(p_type);
+}
+
+void InspectorDock::set_warning(const String &p_message) {
+ warning->hide();
+ if (p_message != String()) {
+ warning->show();
+ warning_dialog->set_text(p_message);
+ }
+}
+
+void InspectorDock::clear() {
+}
+
+void InspectorDock::update(Object *p_object) {
+
+ EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
+ backward_button->set_disabled(editor_history->is_at_beginning());
+ forward_button->set_disabled(editor_history->is_at_end());
+
+ history_menu->set_disabled(true);
+ if (editor_history->get_history_len() > 0) {
+ history_menu->set_disabled(false);
+ }
+ editor_path->update_path();
+
+ current = p_object;
+
+ if (!p_object) {
+ object_menu->set_disabled(true);
+ warning->hide();
+ search->set_editable(false);
+
+ return;
+ }
+
+ bool is_resource = p_object->is_class("Resource");
+ bool is_node = p_object->is_class("Node");
+
+ object_menu->set_disabled(false);
+ search->set_editable(true);
+
+ PopupMenu *p = object_menu->get_popup();
+
+ p->clear();
+ p->add_shortcut(ED_SHORTCUT("property_editor/expand_all", TTR("Expand all properties")), EXPAND_ALL);
+ p->add_shortcut(ED_SHORTCUT("property_editor/collapse_all", TTR("Collapse all properties")), COLLAPSE_ALL);
+ p->add_separator();
+ if (is_resource) {
+ p->add_item(TTR("Save"), RESOURCE_SAVE);
+ p->add_item(TTR("Save As..."), RESOURCE_SAVE_AS);
+ p->add_separator();
+ }
+ p->add_shortcut(ED_SHORTCUT("property_editor/copy_params", TTR("Copy Params")), OBJECT_COPY_PARAMS);
+ p->add_shortcut(ED_SHORTCUT("property_editor/paste_params", TTR("Paste Params")), OBJECT_PASTE_PARAMS);
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Paste Resource")), RESOURCE_PASTE);
+ if (is_resource) {
+ p->add_shortcut(ED_SHORTCUT("property_editor/copy_resource", TTR("Copy Resource")), RESOURCE_COPY);
+ p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_UNREF);
+ }
+
+ if (is_resource || is_node) {
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("property_editor/make_subresources_unique", TTR("Make Sub-Resources Unique")), OBJECT_UNIQUE_RESOURCES);
+ p->add_separator();
+ p->add_icon_shortcut(get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("property_editor/open_help", TTR("Open in Help")), OBJECT_REQUEST_HELP);
+ }
+
+ List<MethodInfo> methods;
+ p_object->get_method_list(&methods);
+
+ if (!methods.empty()) {
+
+ bool found = false;
+ List<MethodInfo>::Element *I = methods.front();
+ int i = 0;
+ while (I) {
+
+ if (I->get().flags & METHOD_FLAG_EDITOR) {
+ if (!found) {
+ p->add_separator();
+ found = true;
+ }
+ p->add_item(I->get().name.capitalize(), OBJECT_METHOD_BASE + i);
+ }
+ i++;
+ I = I->next();
+ }
+ }
+}
+
+void InspectorDock::go_back() {
+ _edit_back();
+}
+
+void InspectorDock::update_keying() {
+ bool valid = false;
+
+ if (AnimationPlayerEditor::singleton->get_track_editor()->has_keying()) {
+
+ EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
+ if (editor_history->get_path_size() >= 1) {
+
+ Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0));
+ if (Object::cast_to<Node>(obj)) {
+
+ valid = true;
+ }
+ }
+ }
+
+ inspector->set_keying(valid);
+}
+
+InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
+ set_name("Inspector");
+ set_theme(p_editor->get_gui_base()->get_theme());
+
+ editor = p_editor;
+ editor_data = &p_editor_data;
+
+ HBoxContainer *general_options_hb = memnew(HBoxContainer);
+ add_child(general_options_hb);
+
+ resource_new_button = memnew(ToolButton);
+ resource_new_button->set_tooltip(TTR("Create a new resource in memory and edit it."));
+ resource_new_button->set_icon(get_icon("New", "EditorIcons"));
+ general_options_hb->add_child(resource_new_button);
+ resource_new_button->connect("pressed", this, "_new_resource");
+ resource_new_button->set_focus_mode(Control::FOCUS_NONE);
+
+ resource_load_button = memnew(ToolButton);
+ resource_load_button->set_tooltip(TTR("Load an existing resource from disk and edit it."));
+ resource_load_button->set_icon(get_icon("Load", "EditorIcons"));
+ general_options_hb->add_child(resource_load_button);
+ resource_load_button->connect("pressed", this, "_open_resource_selector");
+ resource_load_button->set_focus_mode(Control::FOCUS_NONE);
+
+ general_options_hb->add_spacer();
+
+ backward_button = memnew(ToolButton);
+ general_options_hb->add_child(backward_button);
+ backward_button->set_icon(get_icon("Back", "EditorIcons"));
+ backward_button->set_flat(true);
+ backward_button->set_tooltip(TTR("Go to the previous edited object in history."));
+ backward_button->set_disabled(true);
+ backward_button->connect("pressed", this, "_edit_back");
+
+ forward_button = memnew(ToolButton);
+ general_options_hb->add_child(forward_button);
+ forward_button->set_icon(get_icon("Forward", "EditorIcons"));
+ forward_button->set_flat(true);
+ forward_button->set_tooltip(TTR("Go to the next edited object in history."));
+ forward_button->set_disabled(true);
+ forward_button->connect("pressed", this, "_edit_forward");
+
+ history_menu = memnew(MenuButton);
+ history_menu->set_tooltip(TTR("History of recently edited objects."));
+ history_menu->set_icon(get_icon("History", "EditorIcons"));
+ general_options_hb->add_child(history_menu);
+ history_menu->connect("about_to_show", this, "_prepare_history");
+ history_menu->get_popup()->connect("id_pressed", this, "_select_history");
+
+ HBoxContainer *node_info_hb = memnew(HBoxContainer);
+ add_child(node_info_hb);
+
+ editor_path = memnew(EditorPath(editor->get_editor_history()));
+ editor_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ node_info_hb->add_child(editor_path);
+
+ object_menu = memnew(MenuButton);
+ object_menu->set_icon(get_icon("Tools", "EditorIcons"));
+ node_info_hb->add_child(object_menu);
+ object_menu->set_tooltip(TTR("Object properties."));
+ object_menu->get_popup()->connect("id_pressed", this, "_menu_option");
+
+ new_resource_dialog = memnew(CreateDialog);
+ editor->get_gui_base()->add_child(new_resource_dialog);
+ new_resource_dialog->set_base_type("Resource");
+ new_resource_dialog->connect("create", this, "_resource_created");
+
+ search = memnew(LineEdit);
+ search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ search->set_placeholder(TTR("Filter properties"));
+ search->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+ add_child(search);
+
+ warning = memnew(Button);
+ add_child(warning);
+ warning->set_text(TTR("Changes may be lost!"));
+ warning->set_icon(get_icon("NodeWarning", "EditorIcons"));
+ warning->hide();
+ warning->connect("pressed", this, "_warning_pressed");
+
+ warning_dialog = memnew(AcceptDialog);
+ editor->get_gui_base()->add_child(warning_dialog);
+
+ load_resource_dialog = memnew(EditorFileDialog);
+ add_child(load_resource_dialog);
+ load_resource_dialog->set_current_dir("res://");
+ load_resource_dialog->connect("file_selected", this, "_resource_file_selected");
+
+ inspector = memnew(EditorInspector);
+ add_child(inspector);
+ inspector->set_autoclear(true);
+ inspector->set_show_categories(true);
+ inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ inspector->set_use_doc_hints(true);
+ inspector->set_hide_script(false);
+ inspector->set_enable_capitalize_paths(bool(EDITOR_GET("interface/inspector/capitalize_properties")));
+ inspector->set_use_folding(!bool(EDITOR_GET("interface/inspector/disable_folding")));
+ inspector->register_text_enter(search);
+ inspector->set_undo_redo(&editor_data->get_undo_redo());
+
+ inspector->set_use_filter(true); // TODO: check me
+
+ inspector->connect("resource_selected", this, "_resource_selected");
+ inspector->connect("property_keyed", this, "_property_keyed");
+}
+
+InspectorDock::~InspectorDock() {
+}
diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h
new file mode 100644
index 0000000000..f347056158
--- /dev/null
+++ b/editor/inspector_dock.h
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* inspector_dock.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 INSPECTOR_DOCK_H
+#define INSPECTOR_DOCK_H
+
+#include "editor/animation_track_editor.h"
+#include "editor/connections_dialog.h"
+#include "editor/create_dialog.h"
+#include "editor/editor_data.h"
+#include "editor/editor_inspector.h"
+#include "editor/editor_path.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/control.h"
+#include "scene/gui/label.h"
+#include "scene/gui/popup_menu.h"
+#include "scene/gui/tool_button.h"
+
+class EditorNode;
+
+class InspectorDock : public VBoxContainer {
+
+ GDCLASS(InspectorDock, VBoxContainer);
+
+ enum MenuOptions {
+ RESOURCE_NEW,
+ RESOURCE_LOAD,
+ RESOURCE_SAVE,
+ RESOURCE_SAVE_AS,
+ RESOURCE_UNREF,
+ RESOURCE_COPY,
+ RESOURCE_PASTE,
+ OBJECT_COPY_PARAMS,
+ OBJECT_PASTE_PARAMS,
+ OBJECT_UNIQUE_RESOURCES,
+ OBJECT_REQUEST_HELP,
+
+ COLLAPSE_ALL,
+ EXPAND_ALL,
+
+ OBJECT_METHOD_BASE = 500
+ };
+
+ EditorNode *editor;
+ EditorData *editor_data;
+
+ EditorInspector *inspector;
+
+ Object *current;
+
+ ToolButton *backward_button;
+ ToolButton *forward_button;
+
+ EditorFileDialog *load_resource_dialog;
+ CreateDialog *new_resource_dialog;
+ ToolButton *resource_new_button;
+ ToolButton *resource_load_button;
+ MenuButton *resource_save_button;
+ MenuButton *history_menu;
+ LineEdit *search;
+
+ MenuButton *object_menu;
+ EditorPath *editor_path;
+
+ Button *warning;
+ AcceptDialog *warning_dialog;
+
+ void _menu_option(int p_option);
+
+ void _new_resource();
+ void _load_resource(const String &p_type = "");
+ void _open_resource_selector() { _load_resource(); }; // just used to call from arg-less signal
+ void _resource_file_selected(String p_file);
+ void _save_resource(bool save_as) const;
+ void _unref_resource() const;
+ void _copy_resource() const;
+ void _paste_resource() const;
+
+ void _warning_pressed();
+ void _resource_created() const;
+ void _resource_selected(const RES &p_res, const String &p_property = "") const;
+ void _edit_forward();
+ void _edit_back();
+ void _menu_collapseall();
+ void _menu_expandall();
+ void _select_history(int p_idx) const;
+ void _prepare_history();
+
+ void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance);
+ void _transform_keyed(Object *sp, const String &p_sub, const Transform &p_key);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void go_back();
+ void update_keying();
+ void edit_resource(const Ref<Resource> &p_resource);
+ void open_resource(const String &p_type);
+ void clear();
+ void set_warning(const String &p_message);
+ void update(Object *p_object);
+ Container *get_addon_area();
+ EditorInspector *get_inspector() { return inspector; }
+
+ InspectorDock(EditorNode *p_editor, EditorData &p_editor_data);
+ ~InspectorDock();
+};
+
+#endif
diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp
index 73e78ddf2a..173be01586 100644
--- a/editor/multi_node_edit.cpp
+++ b/editor/multi_node_edit.cpp
@@ -80,8 +80,8 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value,
ur->add_undo_property(n, name, n->get(name));
}
- ur->add_do_method(EditorNode::get_singleton()->get_property_editor(), "refresh");
- ur->add_undo_method(EditorNode::get_singleton()->get_property_editor(), "refresh");
+ ur->add_do_method(EditorNode::get_singleton()->get_inspector(), "refresh");
+ ur->add_undo_method(EditorNode::get_singleton()->get_inspector(), "refresh");
ur->commit_action();
return true;
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_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
new file mode 100644
index 0000000000..8d17062248
--- /dev/null
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -0,0 +1,1023 @@
+#include "animation_blend_space_2d_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 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<AnimationNodeBlendSpace2D>(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();
+ }
+}
+
+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()) {
+ if (selected_point != -1 || selected_triangle != -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;
+ classes.sort_custom<StringName::AlphCompare>();
+
+ ClassDB::get_inheriters_from_class("AnimationRootNode", &classes);
+ menu->add_submenu_item(TTR("Add Animation"), "animations");
+
+ 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()));
+ 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(blend_space_draw->get_global_transform().xform(mb->get_position()));
+ menu->popup();
+ add_point_pos = (mb->get_position() / blend_space_draw->get_size());
+ add_point_pos.y = 1.0 - add_point_pos.y;
+ 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.x = Math::stepify(add_point_pos.x, blend_space->get_snap().x);
+ add_point_pos.y = Math::stepify(add_point_pos.y, blend_space->get_snap().y);
+ }
+ }
+
+ if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ blend_space_draw->update(); //update anyway
+ //try to see if a point can be selected
+ selected_point = -1;
+ selected_triangle = -1;
+ _update_tool_erase();
+
+ for (int i = 0; i < points.size(); i++) {
+
+ if (points[i].distance_to(mb->get_position()) < 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;
+ }
+ }
+
+ //then try to see if a triangle can be selected
+ if (!blend_space->get_auto_triangles()) { //if autotriangles use, disable this
+ for (int i = 0; i < blend_space->get_triangle_count(); i++) {
+ Vector<Vector2> triangle;
+
+ for (int j = 0; j < 3; j++) {
+ int idx = blend_space->get_triangle_point(i, j);
+ ERR_FAIL_INDEX(idx, points.size());
+ triangle.push_back(points[idx]);
+ }
+
+ if (Geometry::is_point_in_triangle(mb->get_position(), triangle[0], triangle[1], triangle[2])) {
+ selected_triangle = i;
+ _update_tool_erase();
+ return;
+ }
+ }
+ }
+ }
+
+ if (mb.is_valid() && mb->is_pressed() && tool_triangle->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ blend_space_draw->update(); //update anyway
+ //try to see if a point can be selected
+ selected_point = -1;
+
+ for (int i = 0; i < points.size(); i++) {
+
+ if (making_triangle.find(i) != -1)
+ continue;
+
+ if (points[i].distance_to(mb->get_position()) < 10 * EDSCALE) {
+ making_triangle.push_back(i);
+ if (making_triangle.size() == 3) {
+ //add triangle!
+ if (blend_space->has_triangle(making_triangle[0], making_triangle[1], making_triangle[2])) {
+ making_triangle.clear();
+ EditorNode::get_singleton()->show_warning(TTR("Triangle already exists"));
+ return;
+ }
+
+ updating = true;
+ undo_redo->create_action("Add Triangle");
+ undo_redo->add_do_method(blend_space.ptr(), "add_triangle", making_triangle[0], making_triangle[1], making_triangle[2]);
+ undo_redo->add_undo_method(blend_space.ptr(), "remove_triangle", blend_space->get_triangle_count());
+ undo_redo->add_do_method(this, "_update_space");
+ undo_redo->add_undo_method(this, "_update_space");
+ undo_redo->commit_action();
+ updating = false;
+ making_triangle.clear();
+ }
+ return;
+ }
+ }
+ }
+
+ if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == BUTTON_LEFT) {
+ if (dragging_selected) {
+ //move
+ Vector2 point = blend_space->get_blend_point_position(selected_point);
+ point += drag_ofs;
+ if (snap->is_pressed()) {
+ point.x = Math::stepify(point.x, blend_space->get_snap().x);
+ point.y = Math::stepify(point.y, blend_space->get_snap().y);
+ }
+
+ 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();
+ }
+
+ if (mb.is_valid() && mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ Vector2 blend_pos = (mb->get_position() / blend_space_draw->get_size());
+ blend_pos.y = 1.0 - blend_pos.y;
+ blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
+ blend_pos += blend_space->get_min_space();
+
+ blend_space->set_blend_position(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, -1);
+ blend_space_draw->update();
+ _update_edited_point_pos();
+ }
+
+ if (mm.is_valid() && tool_triangle->is_pressed() && making_triangle.size()) {
+ blend_space_draw->update();
+ }
+
+ if (mm.is_valid() && !tool_triangle->is_pressed() && making_triangle.size()) {
+ making_triangle.clear();
+ blend_space_draw->update();
+ }
+
+ if (mm.is_valid() && tool_blend->is_pressed() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
+
+ Vector2 blend_pos = (mm->get_position() / blend_space_draw->get_size());
+ blend_pos.y = 1.0 - blend_pos.y;
+ blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
+ blend_pos += blend_space->get_min_space();
+
+ blend_space->set_blend_position(blend_pos);
+ blend_space_draw->update();
+ }
+}
+
+void AnimationNodeBlendSpace2DEditor::_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 AnimationNodeBlendSpace2DEditor::_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 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);
+ if (EditorNode::get_singleton()->item_has_editor(an.ptr())) {
+ open_editor->show();
+ } else {
+ open_editor->hide();
+ }
+ edit_hb->show();
+ } else {
+ edit_hb->hide();
+ }
+}
+
+void AnimationNodeBlendSpace2DEditor::_tool_switch(int p_tool) {
+ making_triangle.clear();
+
+ if (p_tool == 2) {
+ Vector<Vector2> points;
+ for (int i = 0; i < blend_space->get_blend_point_count(); i++) {
+ points.push_back(blend_space->get_blend_point_position(i));
+ }
+ Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(points);
+ print_line("triangleS: " + itos(tr.size()));
+ for (int i = 0; i < tr.size(); i++) {
+ blend_space->add_triangle(tr[i].points[0], tr[i].points[1], tr[i].points[2]);
+ }
+ }
+
+ 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 AnimationNodeBlendSpace2DEditor::_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, 0), Point2(1, s.height - 1), linecolor);
+ blend_space_draw->draw_line(Point2(1, s.height - 1), Point2(s.width - 1, s.height - 1), linecolor);
+
+ blend_space_draw->draw_line(Point2(0, 0), Point2(5 * EDSCALE, 0), linecolor);
+ if (blend_space->get_min_space().y < 0) {
+ int y = (blend_space->get_max_space().y / (blend_space->get_max_space().y - blend_space->get_min_space().y)) * s.height;
+ blend_space_draw->draw_line(Point2(0, y), Point2(5 * EDSCALE, y), linecolor);
+ blend_space_draw->draw_string(font, Point2(2 * EDSCALE, y - font->get_height() + font->get_ascent()), "0", linecolor);
+ blend_space_draw->draw_line(Point2(5 * EDSCALE, y), Point2(s.width, y), linecolor_soft);
+ }
+
+ if (blend_space->get_min_space().x < 0) {
+ 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);
+ }
+
+ if (snap->is_pressed()) {
+
+ linecolor_soft.a = linecolor.a * 0.1;
+
+ if (blend_space->get_snap().x > 0) {
+
+ int prev_idx;
+ for (int i = 0; i < s.x; i++) {
+
+ float v = blend_space->get_min_space().x + i * (blend_space->get_max_space().x - blend_space->get_min_space().x) / s.x;
+ int idx = int(v / blend_space->get_snap().x);
+
+ if (i > 0 && prev_idx != idx) {
+ blend_space_draw->draw_line(Point2(i, 0), Point2(i, s.height), linecolor_soft);
+ }
+
+ prev_idx = idx;
+ }
+ }
+
+ if (blend_space->get_snap().y > 0) {
+
+ int prev_idx;
+ for (int i = 0; i < s.y; i++) {
+
+ float v = blend_space->get_max_space().y - i * (blend_space->get_max_space().y - blend_space->get_min_space().y) / s.y;
+ int idx = int(v / blend_space->get_snap().y);
+
+ if (i > 0 && prev_idx != idx) {
+ blend_space_draw->draw_line(Point2(0, i), Point2(s.width, i), linecolor_soft);
+ }
+
+ prev_idx = idx;
+ }
+ }
+ }
+
+ //triangles first
+ for (int i = 0; i < blend_space->get_triangle_count(); i++) {
+
+ Vector<Vector2> points;
+ points.resize(3);
+
+ for (int j = 0; j < 3; j++) {
+ int point_idx = blend_space->get_triangle_point(i, j);
+ Vector2 point = blend_space->get_blend_point_position(point_idx);
+ if (dragging_selected && selected_point == point_idx) {
+ point += drag_ofs;
+ if (snap->is_pressed()) {
+ point.x = Math::stepify(point.x, blend_space->get_snap().x);
+ point.y = Math::stepify(point.y, blend_space->get_snap().y);
+ }
+ }
+ 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;
+ points[j] = point;
+ }
+
+ for (int j = 0; j < 3; j++) {
+ blend_space_draw->draw_line(points[j], points[(j + 1) % 3], linecolor, 1, true);
+ }
+
+ Color color;
+ if (i == selected_triangle) {
+ color = get_color("accent_color", "Editor");
+ color.a *= 0.5;
+ } else {
+ color = linecolor;
+ color.a *= 0.2;
+ }
+
+ Vector<Color> colors;
+ colors.push_back(color);
+ colors.push_back(color);
+ colors.push_back(color);
+ blend_space_draw->draw_primitive(points, colors, Vector<Vector2>());
+ }
+
+ points.clear();
+ for (int i = 0; i < blend_space->get_blend_point_count(); i++) {
+
+ Vector2 point = blend_space->get_blend_point_position(i);
+ if (dragging_selected && selected_point == i) {
+ point += drag_ofs;
+ if (snap->is_pressed()) {
+ point.x = Math::stepify(point.x, blend_space->get_snap().x);
+ point.y = Math::stepify(point.y, blend_space->get_snap().y);
+ }
+ }
+ 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;
+
+ points.push_back(point);
+ point -= (icon->get_size() / 2);
+ point = point.floor();
+
+ if (i == selected_point) {
+ blend_space_draw->draw_texture(icon_selected, point);
+ } else {
+ blend_space_draw->draw_texture(icon, point);
+ }
+ }
+
+ if (making_triangle.size()) {
+ Vector<Vector2> points;
+ for (int i = 0; i < making_triangle.size(); i++) {
+ Vector2 point = blend_space->get_blend_point_position(making_triangle[i]);
+ 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;
+ points.push_back(point);
+ }
+
+ for (int i = 0; i < points.size() - 1; i++) {
+ blend_space_draw->draw_line(points[i], points[i + 1], linecolor, 2, true);
+ }
+ blend_space_draw->draw_line(points[points.size() - 1], blend_space_draw->get_local_mouse_position(), linecolor, 2, true);
+ }
+
+ ///draw cursor position
+
+ {
+ Color color;
+ if (tool_blend->is_pressed()) {
+ color = get_color("accent_color", "Editor");
+ } else {
+ color = linecolor;
+ color.a *= 0.5;
+ }
+
+ 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_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;
+
+ Color lcol = color;
+ lcol.a *= 0.4;
+ blend_space_draw->draw_line(point, closest, lcol, 2);
+ }
+
+ float mind = 5 * EDSCALE;
+ float maxd = 15 * EDSCALE;
+ blend_space_draw->draw_line(point + Vector2(mind, 0), point + Vector2(maxd, 0), color, 2);
+ blend_space_draw->draw_line(point + Vector2(-mind, 0), point + Vector2(-maxd, 0), color, 2);
+ blend_space_draw->draw_line(point + Vector2(0, mind), point + Vector2(0, maxd), color, 2);
+ blend_space_draw->draw_line(point + Vector2(0, -mind), point + Vector2(0, -maxd), color, 2);
+ }
+}
+
+void AnimationNodeBlendSpace2DEditor::_snap_toggled() {
+
+ blend_space_draw->update();
+}
+
+void AnimationNodeBlendSpace2DEditor::_update_space() {
+
+ if (updating)
+ return;
+
+ updating = true;
+
+ if (blend_space->get_parent().is_valid()) {
+ goto_parent_hb->show();
+ } else {
+ goto_parent_hb->hide();
+ }
+
+ if (blend_space->get_auto_triangles()) {
+ tool_triangle->hide();
+ } else {
+ tool_triangle->show();
+ }
+
+ auto_triangles->set_pressed(blend_space->get_auto_triangles());
+
+ max_x_value->set_value(blend_space->get_max_space().x);
+ max_y_value->set_value(blend_space->get_max_space().y);
+
+ min_x_value->set_value(blend_space->get_min_space().x);
+ min_y_value->set_value(blend_space->get_min_space().y);
+
+ label_x->set_text(blend_space->get_x_label());
+ label_y->set_text(blend_space->get_y_label());
+
+ snap_x->set_value(blend_space->get_snap().x);
+ snap_y->set_value(blend_space->get_snap().y);
+
+ blend_space_draw->update();
+
+ updating = false;
+}
+
+void AnimationNodeBlendSpace2DEditor::_config_changed(double) {
+ if (updating)
+ return;
+
+ updating = true;
+ 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()));
+ 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", Vector2(snap_x->get_value(), snap_y->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 AnimationNodeBlendSpace2DEditor::_labels_changed(String) {
+ if (updating)
+ return;
+
+ updating = true;
+ 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());
+ undo_redo->add_undo_method(blend_space.ptr(), "set_y_label", blend_space->get_y_label());
+ undo_redo->add_do_method(this, "_update_space");
+ undo_redo->add_undo_method(this, "_update_space");
+ undo_redo->commit_action();
+ updating = false;
+}
+
+void AnimationNodeBlendSpace2DEditor::_erase_selected() {
+
+ if (selected_point != -1) {
+
+ updating = true;
+ 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);
+
+ //restore triangles using this point
+ for (int i = 0; i < blend_space->get_triangle_count(); i++) {
+ for (int j = 0; j < 3; j++) {
+ if (blend_space->get_triangle_point(i, j) == selected_point) {
+ undo_redo->add_undo_method(blend_space.ptr(), "add_triangle", blend_space->get_triangle_point(i, 0), blend_space->get_triangle_point(i, 1), blend_space->get_triangle_point(i, 2), i);
+ break;
+ }
+ }
+ }
+
+ 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();
+ } else if (selected_triangle != -1) {
+
+ updating = true;
+ 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);
+
+ 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 AnimationNodeBlendSpace2DEditor::_update_edited_point_pos() {
+ if (updating)
+ return;
+
+ if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
+ Vector2 pos = blend_space->get_blend_point_position(selected_point);
+ if (dragging_selected) {
+ pos += drag_ofs;
+ if (snap->is_pressed()) {
+ pos.x = Math::stepify(pos.x, blend_space->get_snap().x);
+ pos.y = Math::stepify(pos.y, blend_space->get_snap().y);
+ }
+ }
+ updating = true;
+ edit_x->set_value(pos.x);
+ edit_y->set_value(pos.y);
+ updating = false;
+ }
+}
+
+void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) {
+ if (updating)
+ return;
+ updating = true;
+ undo_redo->create_action("Move Node Point");
+ undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, Vector2(edit_x->get_value(), edit_y->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 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"));
+ 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_triangle->set_icon(get_icon("ToolTriangle", "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"));
+ auto_triangles->set_icon(get_icon("AutoTriangle", "EditorIcons"));
+ }
+
+ if (p_what == NOTIFICATION_PROCESS) {
+
+ String error;
+
+ 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.");
+ }
+
+ if (error != error_label->get_text()) {
+ error_label->set_text(error);
+ if (error != String()) {
+ error_panel->show();
+ } else {
+ error_panel->hide();
+ }
+ }
+ }
+}
+
+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);
+ ERR_FAIL_COND(!an.is_valid());
+ EditorNode::get_singleton()->edit_item(an.ptr());
+ }
+}
+
+void AnimationNodeBlendSpace2DEditor::_goto_parent() {
+
+ EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr());
+}
+
+void AnimationNodeBlendSpace2DEditor::_removed_from_graph() {
+ EditorNode::get_singleton()->edit_item(NULL);
+}
+
+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());
+ undo_redo->add_undo_method(blend_space.ptr(), "set_auto_triangles", blend_space->get_auto_triangles());
+ undo_redo->add_do_method(this, "_update_space");
+ undo_redo->add_undo_method(this, "_update_space");
+ undo_redo->commit_action();
+}
+
+void AnimationNodeBlendSpace2DEditor::_bind_methods() {
+
+ 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", &AnimationNodeBlendSpace2DEditor::_add_menu_type);
+ ClassDB::bind_method("_add_animation_type", &AnimationNodeBlendSpace2DEditor::_add_animation_type);
+
+ ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace2DEditor::_update_edited_point_pos);
+
+ ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace2DEditor::_open_editor);
+ ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace2DEditor::_goto_parent);
+
+ ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace2DEditor::_removed_from_graph);
+
+ ClassDB::bind_method("_auto_triangles_toggled", &AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled);
+}
+
+AnimationNodeBlendSpace2DEditor *AnimationNodeBlendSpace2DEditor::singleton = NULL;
+
+AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
+
+ 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_triangle = memnew(ToolButton);
+ tool_triangle->set_toggle_mode(true);
+ tool_triangle->set_button_group(bg);
+ top_hb->add_child(tool_triangle);
+ tool_triangle->set_tooltip(TTR("Create triangles by connecting points."));
+ tool_triangle->connect("pressed", this, "_tool_switch", varray(2));
+
+ 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 and triangles."));
+ tool_erase->connect("pressed", this, "_erase_selected");
+ tool_erase->set_disabled(true);
+
+ top_hb->add_child(memnew(VSeparator));
+
+ auto_triangles = memnew(ToolButton);
+ top_hb->add_child(auto_triangles);
+ auto_triangles->connect("pressed", this, "_auto_triangles_toggled");
+ auto_triangles->set_toggle_mode(true);
+ auto_triangles->set_tooltip(TTR("Generate blend triangles automatically (instead of manually)"));
+
+ top_hb->add_child(memnew(VSeparator));
+
+ snap = memnew(ToolButton);
+ snap->set_toggle_mode(true);
+ top_hb->add_child(snap);
+ //snap->set_text(TTR("Snap"));
+ snap->set_pressed(true);
+ snap->connect("pressed", this, "_snap_toggled");
+
+ snap_x = memnew(SpinBox);
+ top_hb->add_child(snap_x);
+ snap_x->set_prefix("x:");
+ snap_x->set_min(0.01);
+ snap_x->set_step(0.01);
+ snap_x->set_max(1000);
+
+ snap_y = memnew(SpinBox);
+ top_hb->add_child(snap_y);
+ snap_y->set_prefix("y:");
+ snap_y->set_min(0.01);
+ snap_y->set_step(0.01);
+ snap_y->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_x = memnew(SpinBox);
+ edit_hb->add_child(edit_x);
+ edit_x->set_min(-1000);
+ edit_x->set_step(0.01);
+ edit_x->set_max(1000);
+ edit_x->connect("value_changed", this, "_edit_point_pos");
+ edit_y = memnew(SpinBox);
+ edit_hb->add_child(edit_y);
+ edit_y->set_min(-1000);
+ edit_y->set_step(0.01);
+ edit_y->set_max(1000);
+ edit_y->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();
+
+ HBoxContainer *main_hb = memnew(HBoxContainer);
+ add_child(main_hb);
+ main_hb->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ GridContainer *main_grid = memnew(GridContainer);
+ main_grid->set_columns(2);
+ main_hb->add_child(main_grid);
+ main_grid->set_h_size_flags(SIZE_EXPAND_FILL);
+ {
+ VBoxContainer *left_vbox = memnew(VBoxContainer);
+ main_grid->add_child(left_vbox);
+ left_vbox->set_v_size_flags(SIZE_EXPAND_FILL);
+ max_y_value = memnew(SpinBox);
+ left_vbox->add_child(max_y_value);
+ left_vbox->add_spacer();
+ label_y = memnew(LineEdit);
+ left_vbox->add_child(label_y);
+ label_y->set_expand_to_text_length(true);
+ left_vbox->add_spacer();
+ min_y_value = memnew(SpinBox);
+ left_vbox->add_child(min_y_value);
+
+ max_y_value->set_max(10000);
+ max_y_value->set_min(0.01);
+ max_y_value->set_step(0.01);
+
+ min_y_value->set_min(-10000);
+ min_y_value->set_max(0);
+ min_y_value->set_step(0.01);
+ }
+
+ panel = memnew(PanelContainer);
+ panel->set_clip_contents(true);
+ main_grid->add_child(panel);
+ panel->set_h_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);
+ main_grid->add_child(memnew(Control)); //empty bottom left
+
+ {
+ HBoxContainer *bottom_vbox = memnew(HBoxContainer);
+ main_grid->add_child(bottom_vbox);
+ bottom_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
+ min_x_value = memnew(SpinBox);
+ bottom_vbox->add_child(min_x_value);
+ bottom_vbox->add_spacer();
+ label_x = memnew(LineEdit);
+ bottom_vbox->add_child(label_x);
+ label_x->set_expand_to_text_length(true);
+ bottom_vbox->add_spacer();
+ max_x_value = memnew(SpinBox);
+ bottom_vbox->add_child(max_x_value);
+
+ max_x_value->set_max(10000);
+ max_x_value->set_min(0.01);
+ max_x_value->set_step(0.01);
+
+ min_x_value->set_min(-10000);
+ min_x_value->set_max(0);
+ min_x_value->set_step(0.01);
+ }
+
+ snap_x->connect("value_changed", this, "_config_changed");
+ snap_y->connect("value_changed", this, "_config_changed");
+ max_x_value->connect("value_changed", this, "_config_changed");
+ min_x_value->connect("value_changed", this, "_config_changed");
+ max_y_value->connect("value_changed", this, "_config_changed");
+ min_y_value->connect("value_changed", this, "_config_changed");
+ label_x->connect("text_changed", this, "_labels_changed");
+ label_y->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("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");
+
+ selected_point = -1;
+ selected_triangle = -1;
+
+ dragging_selected = false;
+ dragging_selected_attempt = false;
+}
+
+void AnimationNodeBlendSpace2DEditorPlugin::edit(Object *p_object) {
+
+ anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace2D>(p_object));
+}
+
+bool AnimationNodeBlendSpace2DEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("AnimationNodeBlendSpace2D");
+}
+
+void AnimationNodeBlendSpace2DEditorPlugin::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);
+ }
+}
+
+AnimationNodeBlendSpace2DEditorPlugin::AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ anim_tree_editor = memnew(AnimationNodeBlendSpace2DEditor);
+ anim_tree_editor->set_custom_minimum_size(Size2(0, 300));
+
+ button = editor->add_bottom_panel_item(TTR("BlendSpace2D"), anim_tree_editor);
+ button->hide();
+}
+
+AnimationNodeBlendSpace2DEditorPlugin::~AnimationNodeBlendSpace2DEditorPlugin() {
+}
diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h
new file mode 100644
index 0000000000..a0e497804e
--- /dev/null
+++ b/editor/plugins/animation_blend_space_2d_editor.h
@@ -0,0 +1,130 @@
+#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_2d.h"
+#include "scene/gui/button.h"
+#include "scene/gui/graph_edit.h"
+#include "scene/gui/popup.h"
+#include "scene/gui/tree.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+class AnimationNodeBlendSpace2DEditor : public VBoxContainer {
+
+ GDCLASS(AnimationNodeBlendSpace2DEditor, VBoxContainer);
+
+ Ref<AnimationNodeBlendSpace2D> blend_space;
+
+ HBoxContainer *goto_parent_hb;
+ ToolButton *goto_parent;
+
+ PanelContainer *panel;
+ ToolButton *tool_blend;
+ ToolButton *tool_select;
+ ToolButton *tool_create;
+ ToolButton *tool_triangle;
+ VSeparator *tool_erase_sep;
+ ToolButton *tool_erase;
+ ToolButton *snap;
+ SpinBox *snap_x;
+ SpinBox *snap_y;
+
+ ToolButton *auto_triangles;
+
+ LineEdit *label_x;
+ LineEdit *label_y;
+ SpinBox *max_x_value;
+ SpinBox *min_x_value;
+ SpinBox *max_y_value;
+ SpinBox *min_y_value;
+
+ HBoxContainer *edit_hb;
+ SpinBox *edit_x;
+ SpinBox *edit_y;
+ Button *open_editor;
+
+ int selected_point;
+ int selected_triangle;
+
+ Control *blend_space_draw;
+
+ PanelContainer *error_panel;
+ Label *error_label;
+
+ bool updating;
+
+ UndoRedo *undo_redo;
+
+ static AnimationNodeBlendSpace2DEditor *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;
+ Vector2 add_point_pos;
+ Vector<Vector2> points;
+
+ bool dragging_selected_attempt;
+ bool dragging_selected;
+ Vector2 drag_from;
+ Vector2 drag_ofs;
+
+ Vector<int> making_triangle;
+
+ 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();
+
+ void _auto_triangles_toggled();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ static AnimationNodeBlendSpace2DEditor *get_singleton() { return singleton; }
+ void edit(AnimationNodeBlendSpace2D *p_blend_space);
+ AnimationNodeBlendSpace2DEditor();
+};
+
+class AnimationNodeBlendSpace2DEditorPlugin : public EditorPlugin {
+
+ GDCLASS(AnimationNodeBlendSpace2DEditorPlugin, EditorPlugin);
+
+ AnimationNodeBlendSpace2DEditor *anim_tree_editor;
+ EditorNode *editor;
+ Button *button;
+
+public:
+ 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);
+
+ AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node);
+ ~AnimationNodeBlendSpace2DEditorPlugin();
+};
+#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
new file mode 100644
index 0000000000..c00ad451fa
--- /dev/null
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -0,0 +1,848 @@
+#include "animation_blend_tree_editor_plugin.h"
+
+#include "core/io/resource_loader.h"
+#include "core/project_settings.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"
+
+void AnimationNodeBlendTreeEditor::edit(AnimationNodeBlendTree *p_blend_tree) {
+
+ if (blend_tree.is_valid()) {
+ blend_tree->disconnect("removed_from_graph", this, "_removed_from_graph");
+ }
+
+ if (p_blend_tree) {
+ blend_tree = Ref<AnimationNodeBlendTree>(p_blend_tree);
+ } else {
+ blend_tree.unref();
+ }
+
+ if (blend_tree.is_null()) {
+ hide();
+ } else {
+ blend_tree->connect("removed_from_graph", this, "_removed_from_graph");
+
+ _update_graph();
+ }
+}
+
+void AnimationNodeBlendTreeEditor::add_custom_type(const String &p_name, 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;
+ add_options.push_back(ao);
+
+ _update_options_menu();
+}
+
+void AnimationNodeBlendTreeEditor::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 AnimationNodeBlendTreeEditor::_update_options_menu() {
+
+ add_node->get_popup()->clear();
+ for (int i = 0; i < add_options.size(); i++) {
+ add_node->get_popup()->add_item(add_options[i].name);
+ }
+}
+
+Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const {
+
+ return Size2(10, 200);
+}
+
+void AnimationNodeBlendTreeEditor::_update_graph() {
+
+ if (updating)
+ return;
+
+ graph->set_scroll_ofs(blend_tree->get_graph_offset() * EDSCALE);
+
+ if (blend_tree->get_parent().is_valid()) {
+ goto_parent->show();
+ } else {
+ goto_parent->hide();
+ }
+ 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--;
+ }
+ }
+
+ animations.clear();
+
+ List<StringName> nodes;
+ blend_tree->get_node_list(&nodes);
+
+ for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) {
+
+ GraphNode *node = memnew(GraphNode);
+ graph->add_child(node);
+
+ Ref<AnimationNode> agnode = blend_tree->get_node(E->get());
+
+ if (!agnode->is_connected("changed", this, "_node_changed")) {
+ agnode->connect("changed", this, "_node_changed", varray(agnode->get_instance_id()), CONNECT_DEFERRED);
+ }
+
+ node->set_offset(agnode->get_position() * EDSCALE);
+
+ node->set_title(agnode->get_caption());
+ node->set_name(E->get());
+
+ int base = 0;
+ if (String(E->get()) != "output") {
+ LineEdit *name = memnew(LineEdit);
+ name->set_text(E->get());
+ name->set_expand_to_text_length(true);
+ node->add_child(name);
+ node->set_slot(0, false, 0, Color(), true, 0, get_color("font_color", "Label"));
+ name->connect("text_entered", this, "_node_renamed", varray(agnode));
+ name->connect("focus_exited", this, "_node_renamed_focus_out", varray(name, agnode));
+ base = 1;
+ node->set_show_close_button(true);
+ node->connect("close_request", this, "_delete_request", varray(E->get()), CONNECT_DEFERRED);
+ }
+
+ for (int i = 0; i < agnode->get_input_count(); i++) {
+ Label *in_name = memnew(Label);
+ node->add_child(in_name);
+ in_name->set_text(agnode->get_input_name(i));
+ node->set_slot(base + i, true, 0, get_color("font_color", "Label"), false, 0, Color());
+ }
+
+ node->connect("dragged", this, "_node_dragged", varray(agnode));
+
+ if (EditorNode::get_singleton()->item_has_editor(agnode.ptr())) {
+ node->add_child(memnew(HSeparator));
+ Button *open_in_editor = memnew(Button);
+ open_in_editor->set_text(TTR("Open Editor"));
+ open_in_editor->set_icon(get_icon("Edit", "EditorIcons"));
+ node->add_child(open_in_editor);
+ open_in_editor->connect("pressed", this, "_open_in_editor", varray(E->get()), CONNECT_DEFERRED);
+ open_in_editor->set_h_size_flags(SIZE_SHRINK_CENTER);
+ }
+
+ if (agnode->has_filter()) {
+
+ node->add_child(memnew(HSeparator));
+ Button *edit_filters = memnew(Button);
+ edit_filters->set_text(TTR("Edit Filters"));
+ edit_filters->set_icon(get_icon("AnimationFilter", "EditorIcons"));
+ node->add_child(edit_filters);
+ edit_filters->connect("pressed", this, "_edit_filters", varray(E->get()), CONNECT_DEFERRED);
+ edit_filters->set_h_size_flags(SIZE_SHRINK_CENTER);
+ }
+
+ Ref<AnimationNodeAnimation> anim = agnode;
+ if (anim.is_valid()) {
+
+ MenuButton *mb = memnew(MenuButton);
+ mb->set_text(anim->get_animation());
+ mb->set_icon(get_icon("Animation", "EditorIcons"));
+ Array options;
+
+ node->add_child(memnew(HSeparator));
+ node->add_child(mb);
+
+ ProgressBar *pb = memnew(ProgressBar);
+
+ 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) {
+ List<StringName> anims;
+ ap->get_animation_list(&anims);
+
+ for (List<StringName>::Element *F = anims.front(); F; F = F->next()) {
+ mb->get_popup()->add_item(F->get());
+ options.push_back(F->get());
+ }
+
+ if (ap->has_animation(anim->get_animation())) {
+ pb->set_max(ap->get_animation(anim->get_animation())->get_length());
+ }
+ }
+ }
+
+ pb->set_percent_visible(false);
+ animations[E->get()] = pb;
+ node->add_child(pb);
+
+ mb->get_popup()->connect("index_pressed", this, "_anim_selected", varray(options, E->get()), CONNECT_DEFERRED);
+ }
+
+ Ref<AnimationNodeOneShot> oneshot = agnode;
+ if (oneshot.is_valid()) {
+
+ HBoxContainer *play_stop = memnew(HBoxContainer);
+ play_stop->add_spacer();
+ Button *play = memnew(Button);
+ play->set_icon(get_icon("Play", "EditorIcons"));
+ play->connect("pressed", this, "_oneshot_start", varray(E->get()), CONNECT_DEFERRED);
+ play_stop->add_child(play);
+ Button *stop = memnew(Button);
+ stop->set_icon(get_icon("Stop", "EditorIcons"));
+ stop->connect("pressed", this, "_oneshot_stop", varray(E->get()), CONNECT_DEFERRED);
+ play_stop->add_child(stop);
+ play_stop->add_spacer();
+ node->add_child(play_stop);
+ }
+ }
+
+ List<AnimationNodeBlendTree::NodeConnection> connections;
+ blend_tree->get_node_connections(&connections);
+
+ for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = connections.front(); E; E = E->next()) {
+
+ StringName from = E->get().output_node;
+ StringName to = E->get().input_node;
+ int to_idx = E->get().input_index;
+
+ graph->connect_node(from, 0, to, to_idx);
+ }
+}
+
+void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
+
+ ERR_FAIL_INDEX(p_idx, add_options.size());
+
+ Ref<AnimationNode> anode;
+
+ if (add_options[p_idx].type != String()) {
+ AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instance(add_options[p_idx].type));
+ ERR_FAIL_COND(!an);
+ anode = Ref<AnimationNode>(an);
+ } else {
+ ERR_FAIL_COND(add_options[p_idx].script.is_null());
+ String base_type = add_options[p_idx].script->get_instance_base_type();
+ AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instance(base_type));
+ ERR_FAIL_COND(!an);
+ anode = Ref<AnimationNode>(an);
+ anode->set_script(add_options[p_idx].script.get_ref_ptr());
+ }
+
+ Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5;
+
+ anode->set_position(instance_pos / EDSCALE);
+
+ String base_name = add_options[p_idx].name;
+ int base = 1;
+ String name = base_name;
+ while (blend_tree->has_node(name)) {
+ base++;
+ name = base_name + " " + itos(base);
+ }
+
+ undo_redo->create_action("Add Node to BlendTree");
+ undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode);
+ undo_redo->add_undo_method(blend_tree.ptr(), "remove_node", name);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, Ref<AnimationNode> p_node) {
+
+ updating = true;
+ undo_redo->create_action("Node Moved");
+ undo_redo->add_do_method(p_node.ptr(), "set_position", p_to / EDSCALE);
+ undo_redo->add_undo_method(p_node.ptr(), "set_position", p_from / EDSCALE);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+}
+
+void AnimationNodeBlendTreeEditor::_connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) {
+
+ AnimationNodeBlendTree::ConnectionError err = blend_tree->can_connect_node(p_to, p_to_index, p_from);
+
+ if (err != AnimationNodeBlendTree::CONNECTION_OK) {
+ 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(blend_tree.ptr(), "connect_node", p_to, p_to_index, p_from);
+ undo_redo->add_undo_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index, p_from);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void AnimationNodeBlendTreeEditor::_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);
+
+ updating = true;
+ undo_redo->create_action("Nodes Disconnected");
+ undo_redo->add_do_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index);
+ undo_redo->add_undo_method(blend_tree.ptr(), "connect_node", p_to, p_to_index, 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 AnimationNodeBlendTreeEditor::_anim_selected(int p_index, Array p_options, const String &p_node) {
+
+ String option = p_options[p_index];
+
+ Ref<AnimationNodeAnimation> anim = blend_tree->get_node(p_node);
+ ERR_FAIL_COND(!anim.is_valid());
+
+ undo_redo->create_action("Set Animation");
+ undo_redo->add_do_method(anim.ptr(), "set_animation", option);
+ undo_redo->add_undo_method(anim.ptr(), "set_animation", anim->get_animation());
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) {
+
+ undo_redo->create_action("Delete Node");
+ undo_redo->add_do_method(blend_tree.ptr(), "remove_node", p_which);
+ undo_redo->add_undo_method(blend_tree.ptr(), "add_node", p_which, blend_tree->get_node(p_which));
+
+ List<AnimationNodeBlendTree::NodeConnection> conns;
+ blend_tree->get_node_connections(&conns);
+
+ for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = conns.front(); E; E = E->next()) {
+ if (E->get().output_node == p_which || E->get().input_node == p_which) {
+ undo_redo->add_undo_method(blend_tree.ptr(), "connect_node", E->get().input_node, E->get().input_index, E->get().output_node);
+ }
+ }
+
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void AnimationNodeBlendTreeEditor::_oneshot_start(const StringName &p_name) {
+
+ Ref<AnimationNodeOneShot> os = blend_tree->get_node(p_name);
+ ERR_FAIL_COND(!os.is_valid());
+ os->start();
+}
+
+void AnimationNodeBlendTreeEditor::_oneshot_stop(const StringName &p_name) {
+
+ Ref<AnimationNodeOneShot> os = blend_tree->get_node(p_name);
+ ERR_FAIL_COND(!os.is_valid());
+ os->stop();
+}
+
+void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) {
+
+ GraphNode *gn = Object::cast_to<GraphNode>(p_node);
+ ERR_FAIL_COND(!gn);
+
+ String name = gn->get_name();
+
+ Ref<AnimationNode> anode = blend_tree->get_node(name);
+ ERR_FAIL_COND(!anode.is_valid());
+
+ EditorNode::get_singleton()->push_item(anode.ptr(), "", true);
+}
+
+void AnimationNodeBlendTreeEditor::_open_in_editor(const String &p_which) {
+
+ Ref<AnimationNode> an = blend_tree->get_node(p_which);
+ ERR_FAIL_COND(!an.is_valid())
+ EditorNode::get_singleton()->edit_item(an.ptr());
+}
+
+void AnimationNodeBlendTreeEditor::_open_parent() {
+ if (blend_tree->get_parent().is_valid()) {
+ EditorNode::get_singleton()->edit_item(blend_tree->get_parent().ptr());
+ }
+}
+
+void AnimationNodeBlendTreeEditor::_filter_toggled() {
+
+ updating = true;
+ undo_redo->create_action("Toggle filter on/off");
+ undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_enabled", filter_enabled->is_pressed());
+ undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_enabled", _filter_edit->is_filter_enabled());
+ undo_redo->add_do_method(this, "_update_filters", _filter_edit);
+ undo_redo->add_undo_method(this, "_update_filters", _filter_edit);
+ undo_redo->commit_action();
+ updating = false;
+}
+
+void AnimationNodeBlendTreeEditor::_filter_edited() {
+
+ TreeItem *edited = filters->get_edited();
+ ERR_FAIL_COND(!edited);
+
+ NodePath edited_path = edited->get_metadata(0);
+ bool filtered = edited->is_checked(0);
+
+ updating = true;
+ undo_redo->create_action("Change filter");
+ undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", edited_path, filtered);
+ undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", edited_path, _filter_edit->is_path_filtered(edited_path));
+ undo_redo->add_do_method(this, "_update_filters", _filter_edit);
+ undo_redo->add_undo_method(this, "_update_filters", _filter_edit);
+ undo_redo->commit_action();
+ updating = false;
+}
+
+bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &anode) {
+
+ if (updating || _filter_edit != anode)
+ return false;
+
+ NodePath player_path = anode->get_tree()->get_animation_player();
+
+ 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_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;
+ }
+
+ 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 false;
+ }
+
+ updating = true;
+
+ 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));
+ }
+ }
+ }
+
+ filter_enabled->set_pressed(anode->is_filter_enabled());
+ 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, false);
+ ti->set_editable(0, false);
+ ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons"));
+ } else {
+ ti = parenthood[accum];
+ }
+ }
+
+ ti->set_editable(0, true);
+ ti->set_selectable(0, true);
+ ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ ti->set_text(0, concat);
+ ti->set_checked(0, anode->is_path_filtered(path));
+ ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons"));
+ ti->set_metadata(0, path);
+
+ } else {
+ //just a property
+ ti = filters->create_item(ti);
+ ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ ti->set_text(0, concat);
+ ti->set_editable(0, true);
+ ti->set_selectable(0, true);
+ ti->set_checked(0, anode->is_path_filtered(path));
+ ti->set_metadata(0, path);
+ }
+ } else {
+ if (ti) {
+ //just a node, likely call or animation track
+ ti->set_editable(0, true);
+ ti->set_selectable(0, true);
+ ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ ti->set_checked(0, anode->is_path_filtered(path));
+ ti->set_metadata(0, path);
+ }
+ }
+ }
+
+ updating = false;
+
+ return true;
+}
+
+void AnimationNodeBlendTreeEditor::_edit_filters(const String &p_which) {
+
+ Ref<AnimationNode> anode = blend_tree->get_node(p_which);
+ ERR_FAIL_COND(!anode.is_valid());
+
+ _filter_edit = anode;
+ if (!_update_filters(anode))
+ return;
+
+ filter_dialog->popup_centered_minsize(Size2(500, 500) * EDSCALE);
+}
+
+void AnimationNodeBlendTreeEditor::_removed_from_graph() {
+ if (is_visible()) {
+ EditorNode::get_singleton()->edit_item(NULL);
+ }
+}
+
+void AnimationNodeBlendTreeEditor::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+
+ goto_parent->set_icon(get_icon("MoveUp", "EditorIcons"));
+
+ 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) {
+
+ String error;
+
+ 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()) {
+ error_label->set_text(error);
+ if (error != String()) {
+ error_panel->show();
+ } else {
+ error_panel->hide();
+ }
+ }
+
+ List<AnimationNodeBlendTree::NodeConnection> conns;
+ 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_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);
+ }
+
+ 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()));
+ }
+
+ if (player) {
+ for (Map<StringName, ProgressBar *>::Element *E = animations.front(); E; E = E->next()) {
+ Ref<AnimationNodeAnimation> an = blend_tree->get_node(E->key());
+ if (an.is_valid()) {
+ if (player->has_animation(an->get_animation())) {
+ Ref<Animation> anim = player->get_animation(an->get_animation());
+ if (anim.is_valid()) {
+ E->get()->set_max(anim->get_length());
+ E->get()->set_value(an->get_playback_time());
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void AnimationNodeBlendTreeEditor::_scroll_changed(const Vector2 &p_scroll) {
+ if (updating)
+ return;
+ updating = true;
+ blend_tree->set_graph_offset(p_scroll / EDSCALE);
+ updating = false;
+}
+
+void AnimationNodeBlendTreeEditor::_node_changed(ObjectID p_node) {
+
+ AnimationNode *an = Object::cast_to<AnimationNode>(ObjectDB::get_instance(p_node));
+ if (an && an->get_parent() == blend_tree) {
+ _update_graph();
+ }
+}
+
+void AnimationNodeBlendTreeEditor::_bind_methods() {
+
+ ClassDB::bind_method("_update_graph", &AnimationNodeBlendTreeEditor::_update_graph);
+ ClassDB::bind_method("_add_node", &AnimationNodeBlendTreeEditor::_add_node);
+ ClassDB::bind_method("_node_dragged", &AnimationNodeBlendTreeEditor::_node_dragged);
+ ClassDB::bind_method("_node_renamed", &AnimationNodeBlendTreeEditor::_node_renamed);
+ ClassDB::bind_method("_node_renamed_focus_out", &AnimationNodeBlendTreeEditor::_node_renamed_focus_out);
+ ClassDB::bind_method("_connection_request", &AnimationNodeBlendTreeEditor::_connection_request);
+ ClassDB::bind_method("_disconnection_request", &AnimationNodeBlendTreeEditor::_disconnection_request);
+ ClassDB::bind_method("_node_selected", &AnimationNodeBlendTreeEditor::_node_selected);
+ ClassDB::bind_method("_open_in_editor", &AnimationNodeBlendTreeEditor::_open_in_editor);
+ ClassDB::bind_method("_open_parent", &AnimationNodeBlendTreeEditor::_open_parent);
+ ClassDB::bind_method("_scroll_changed", &AnimationNodeBlendTreeEditor::_scroll_changed);
+ ClassDB::bind_method("_delete_request", &AnimationNodeBlendTreeEditor::_delete_request);
+ ClassDB::bind_method("_edit_filters", &AnimationNodeBlendTreeEditor::_edit_filters);
+ ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters);
+ ClassDB::bind_method("_filter_edited", &AnimationNodeBlendTreeEditor::_filter_edited);
+ ClassDB::bind_method("_filter_toggled", &AnimationNodeBlendTreeEditor::_filter_toggled);
+ ClassDB::bind_method("_oneshot_start", &AnimationNodeBlendTreeEditor::_oneshot_start);
+ ClassDB::bind_method("_oneshot_stop", &AnimationNodeBlendTreeEditor::_oneshot_stop);
+ ClassDB::bind_method("_node_changed", &AnimationNodeBlendTreeEditor::_node_changed);
+ ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendTreeEditor::_removed_from_graph);
+
+ ClassDB::bind_method("_anim_selected", &AnimationNodeBlendTreeEditor::_anim_selected);
+}
+
+AnimationNodeBlendTreeEditor *AnimationNodeBlendTreeEditor::singleton = NULL;
+
+void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<AnimationNode> p_node) {
+
+ String prev_name = blend_tree->get_node_name(p_node);
+ ERR_FAIL_COND(prev_name == String());
+ GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(prev_name));
+ ERR_FAIL_COND(!gn);
+
+ 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 (blend_tree->has_node(name)) {
+ base++;
+ name = base_name + " " + itos(base);
+ }
+
+ updating = true;
+ undo_redo->create_action("Node Renamed");
+ undo_redo->add_do_method(blend_tree.ptr(), "rename_node", prev_name, name);
+ undo_redo->add_undo_method(blend_tree.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;
+ gn->set_name(new_name);
+ gn->set_size(gn->get_minimum_size());
+}
+
+void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node) {
+ _node_renamed(le->call("get_text"), p_node);
+}
+
+AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
+
+ singleton = this;
+ updating = false;
+
+ graph = memnew(GraphEdit);
+ add_child(graph);
+ graph->add_valid_right_disconnect_type(0);
+ 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");
+
+ VSeparator *vs = memnew(VSeparator);
+ graph->get_zoom_hbox()->add_child(vs);
+ graph->get_zoom_hbox()->move_child(vs, 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("index_pressed", this, "_add_node");
+
+ goto_parent = memnew(Button);
+ graph->get_zoom_hbox()->add_child(goto_parent);
+ graph->get_zoom_hbox()->move_child(goto_parent, 0);
+ goto_parent->hide();
+ goto_parent->connect("pressed", this, "_open_parent");
+
+ add_options.push_back(AddOption("Animation", "AnimationNodeAnimation"));
+ add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot"));
+ 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("BlendSpace1D", "AnimationNodeBlendSpace1D"));
+ add_options.push_back(AddOption("BlendSpace2D", "AnimationNodeBlendSpace2D"));
+ add_options.push_back(AddOption("StateMachine", "AnimationNodeStateMachine"));
+ _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");
+
+ filter_dialog = memnew(AcceptDialog);
+ add_child(filter_dialog);
+ filter_dialog->set_title(TTR("Edit Filtered Tracks:"));
+
+ VBoxContainer *filter_vbox = memnew(VBoxContainer);
+ filter_dialog->add_child(filter_vbox);
+
+ filter_enabled = memnew(CheckBox);
+ filter_enabled->set_text(TTR("Enable filtering"));
+ filter_enabled->connect("pressed", this, "_filter_toggled");
+ filter_vbox->add_child(filter_enabled);
+
+ filters = memnew(Tree);
+ filter_vbox->add_child(filters);
+ filters->set_v_size_flags(SIZE_EXPAND_FILL);
+ filters->set_hide_root(true);
+ filters->connect("item_edited", this, "_filter_edited");
+
+ undo_redo = EditorNode::get_singleton()->get_undo_redo();
+}
+
+void AnimationNodeBlendTreeEditorPlugin::edit(Object *p_object) {
+
+ anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendTree>(p_object));
+}
+
+bool AnimationNodeBlendTreeEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("AnimationNodeBlendTree");
+}
+
+void AnimationNodeBlendTreeEditorPlugin::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);
+ }
+}
+
+AnimationNodeBlendTreeEditorPlugin::AnimationNodeBlendTreeEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ anim_tree_editor = memnew(AnimationNodeBlendTreeEditor);
+ anim_tree_editor->set_custom_minimum_size(Size2(0, 300));
+
+ button = editor->add_bottom_panel_item(TTR("BlendTree"), anim_tree_editor);
+ button->hide();
+}
+
+AnimationNodeBlendTreeEditorPlugin::~AnimationNodeBlendTreeEditorPlugin() {
+}
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h
new file mode 100644
index 0000000000..deba3b2b0e
--- /dev/null
+++ b/editor/plugins/animation_blend_tree_editor_plugin.h
@@ -0,0 +1,117 @@
+#ifndef ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H
+#define ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "editor/property_editor.h"
+#include "scene/animation/animation_blend_tree.h"
+#include "scene/gui/button.h"
+#include "scene/gui/graph_edit.h"
+#include "scene/gui/popup.h"
+#include "scene/gui/tree.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+class AnimationNodeBlendTreeEditor : public VBoxContainer {
+
+ GDCLASS(AnimationNodeBlendTreeEditor, VBoxContainer);
+
+ Ref<AnimationNodeBlendTree> blend_tree;
+ GraphEdit *graph;
+ MenuButton *add_node;
+ Button *goto_parent;
+
+ PanelContainer *error_panel;
+ Label *error_label;
+
+ UndoRedo *undo_redo;
+
+ AcceptDialog *filter_dialog;
+ Tree *filters;
+ CheckBox *filter_enabled;
+
+ Map<StringName, ProgressBar *> animations;
+
+ void _update_graph();
+
+ struct AddOption {
+ String name;
+ String type;
+ Ref<Script> script;
+ AddOption(const String &p_name = String(), const String &p_type = String()) {
+ name = p_name;
+ type = p_type;
+ }
+ };
+
+ Vector<AddOption> add_options;
+
+ void _add_node(int p_idx);
+ void _update_options_menu();
+
+ static AnimationNodeBlendTreeEditor *singleton;
+
+ void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, Ref<AnimationNode> p_node);
+ void _node_renamed(const String &p_text, Ref<AnimationNode> p_node);
+ void _node_renamed_focus_out(Node *le, Ref<AnimationNode> 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 _open_in_editor(const String &p_which);
+ void _open_parent();
+ void _anim_selected(int p_index, Array p_options, const String &p_node);
+ void _delete_request(const String &p_which);
+ void _oneshot_start(const StringName &p_name);
+ void _oneshot_stop(const StringName &p_name);
+
+ bool _update_filters(const Ref<AnimationNode> &anode);
+ void _edit_filters(const String &p_which);
+ void _filter_edited();
+ void _filter_toggled();
+ Ref<AnimationNode> _filter_edit;
+
+ void _node_changed(ObjectID p_node);
+
+ void _removed_from_graph();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ static AnimationNodeBlendTreeEditor *get_singleton() { return singleton; }
+
+ void add_custom_type(const String &p_name, const Ref<Script> &p_script);
+ void remove_custom_type(const Ref<Script> &p_script);
+
+ virtual Size2 get_minimum_size() const;
+ void edit(AnimationNodeBlendTree *p_blend_tree);
+ AnimationNodeBlendTreeEditor();
+};
+
+class AnimationNodeBlendTreeEditorPlugin : public EditorPlugin {
+
+ GDCLASS(AnimationNodeBlendTreeEditorPlugin, EditorPlugin);
+
+ AnimationNodeBlendTreeEditor *anim_tree_editor;
+ EditorNode *editor;
+ Button *button;
+
+public:
+ virtual String get_name() const { return "BlendTree"; }
+ 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);
+
+ AnimationNodeBlendTreeEditorPlugin(EditorNode *p_node);
+ ~AnimationNodeBlendTreeEditorPlugin();
+};
+
+#endif // ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index b387972558..23eeef9f20 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -30,7 +30,7 @@
#include "animation_player_editor_plugin.h"
-#include "editor/animation_editor.h"
+#include "editor/animation_track_editor.h"
#include "editor/editor_settings.h"
#include "io/resource_loader.h"
#include "io/resource_saver.h"
@@ -50,9 +50,9 @@ void AnimationPlayerEditor::_node_removed(Node *p_node) {
set_process(false);
- key_editor->set_animation(Ref<Animation>());
- key_editor->set_root(NULL);
- key_editor->show_select_node_warning(true);
+ track_editor->set_animation(Ref<Animation>());
+ track_editor->set_root(NULL);
+ track_editor->show_select_node_warning(true);
_update_player();
//editor->animation_editor_make_visible(false);
}
@@ -84,8 +84,8 @@ void AnimationPlayerEditor::_notification(int p_what) {
}
}
frame->set_value(player->get_current_animation_position());
- key_editor->set_anim_pos(player->get_current_animation_position());
- EditorNode::get_singleton()->get_property_editor()->refresh();
+ track_editor->set_anim_pos(player->get_current_animation_position());
+ EditorNode::get_singleton()->get_inspector()->refresh();
} else if (last_active) {
//need the last frame after it stopped
@@ -101,8 +101,6 @@ void AnimationPlayerEditor::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
- save_anim->get_popup()->connect("id_pressed", this, "_animation_save_menu");
-
tool_anim->get_popup()->connect("id_pressed", this, "_animation_tool_menu");
onion_skinning->get_popup()->connect("id_pressed", this, "_onion_skinning_menu");
@@ -121,16 +119,8 @@ void AnimationPlayerEditor::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
- add_anim->set_icon(get_icon("New", "EditorIcons"));
- rename_anim->set_icon(get_icon("Rename", "EditorIcons"));
- duplicate_anim->set_icon(get_icon("Duplicate", "EditorIcons"));
autoplay->set_icon(get_icon("AutoPlay", "EditorIcons"));
- load_anim->set_icon(get_icon("Folder", "EditorIcons"));
- save_anim->set_icon(get_icon("Save", "EditorIcons"));
-
- remove_anim->set_icon(get_icon("Remove", "EditorIcons"));
- blend_anim->set_icon(get_icon("Blend", "EditorIcons"));
play->set_icon(get_icon("PlayStart", "EditorIcons"));
play_from->set_icon(get_icon("Play", "EditorIcons"));
play_bw->set_icon(get_icon("PlayStartBackwards", "EditorIcons"));
@@ -138,11 +128,27 @@ void AnimationPlayerEditor::_notification(int p_what) {
autoplay_icon = get_icon("AutoPlay", "EditorIcons");
stop->set_icon(get_icon("Stop", "EditorIcons"));
- resource_edit_anim->set_icon(get_icon("EditResource", "EditorIcons"));
+
pin->set_icon(get_icon("Pin", "EditorIcons"));
- tool_anim->set_icon(get_icon("Tools", "EditorIcons"));
onion_skinning->set_icon(get_icon("Onion", "EditorIcons"));
+ tool_anim->add_style_override("normal", get_stylebox("normal", "Button"));
+ track_editor->get_edit_menu()->add_style_override("normal", get_stylebox("normal", "Button"));
+
+#define ITEM_ICON(m_item, m_icon) tool_anim->get_popup()->set_item_icon(tool_anim->get_popup()->get_item_index(m_item), get_icon(m_icon, "EditorIcons"))
+
+ ITEM_ICON(TOOL_NEW_ANIM, "New");
+ ITEM_ICON(TOOL_LOAD_ANIM, "Load");
+ ITEM_ICON(TOOL_SAVE_ANIM, "Save");
+ ITEM_ICON(TOOL_SAVE_AS_ANIM, "Save");
+ 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");
+
} break;
}
}
@@ -304,10 +310,10 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
Ref<Animation> anim = player->get_animation(current);
{
- key_editor->set_animation(anim);
+ track_editor->set_animation(anim);
Node *root = player->get_node(player->get_root());
if (root) {
- key_editor->set_root(root);
+ track_editor->set_root(root);
}
}
frame->set_max(anim->get_length());
@@ -317,11 +323,14 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
frame->set_step(0.00001);
} else {
- key_editor->set_animation(Ref<Animation>());
- key_editor->set_root(NULL);
+ track_editor->set_animation(Ref<Animation>());
+ track_editor->set_root(NULL);
}
autoplay->set_pressed(current == player->get_autoplay());
+
+ AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
+ EditorNode::get_singleton()->update_keying();
}
void AnimationPlayerEditor::_animation_new() {
@@ -704,16 +713,16 @@ void AnimationPlayerEditor::_animation_edit() {
if (animation->get_item_count()) {
String current = animation->get_item_text(animation->get_selected());
Ref<Animation> anim = player->get_animation(current);
- key_editor->set_animation(anim);
+ track_editor->set_animation(anim);
Node *root = player->get_node(player->get_root());
if (root) {
- key_editor->set_root(root);
+ track_editor->set_root(root);
}
} else {
- key_editor->set_animation(Ref<Animation>());
- key_editor->set_root(NULL);
+ track_editor->set_animation(Ref<Animation>());
+ track_editor->set_root(NULL);
}
}
void AnimationPlayerEditor::_dialog_action(String p_file) {
@@ -810,8 +819,16 @@ void AnimationPlayerEditor::_update_player() {
animation->clear();
- add_anim->set_disabled(player == NULL);
- load_anim->set_disabled(player == NULL);
+#define ITEM_DISABLED(m_item, m_disabled) tool_anim->get_popup()->set_item_disabled(tool_anim->get_popup()->get_item_index(m_item), m_disabled)
+
+ ITEM_DISABLED(TOOL_SAVE_ANIM, animlist.size() == 0);
+ ITEM_DISABLED(TOOL_SAVE_AS_ANIM, animlist.size() == 0);
+ ITEM_DISABLED(TOOL_DUPLICATE_ANIM, animlist.size() == 0);
+ ITEM_DISABLED(TOOL_RENAME_ANIM, animlist.size() == 0);
+ ITEM_DISABLED(TOOL_EDIT_TRANSITIONS, animlist.size() == 0);
+ ITEM_DISABLED(TOOL_COPY_ANIM, animlist.size() == 0);
+ ITEM_DISABLED(TOOL_REMOVE_ANIM, animlist.size() == 0);
+
stop->set_disabled(animlist.size() == 0);
play->set_disabled(animlist.size() == 0);
play_bw->set_disabled(animlist.size() == 0);
@@ -820,12 +837,6 @@ void AnimationPlayerEditor::_update_player() {
frame->set_editable(animlist.size() != 0);
animation->set_disabled(animlist.size() == 0);
autoplay->set_disabled(animlist.size() == 0);
- duplicate_anim->set_disabled(animlist.size() == 0);
- rename_anim->set_disabled(animlist.size() == 0);
- blend_anim->set_disabled(animlist.size() == 0);
- remove_anim->set_disabled(animlist.size() == 0);
- resource_edit_anim->set_disabled(animlist.size() == 0);
- save_anim->set_disabled(animlist.size() == 0);
tool_anim->set_disabled(player == NULL);
onion_skinning->set_disabled(player == NULL);
pin->set_disabled(player == NULL);
@@ -842,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) {
@@ -856,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());
@@ -863,10 +879,10 @@ void AnimationPlayerEditor::_update_player() {
if (animation->get_item_count()) {
String current = animation->get_item_text(animation->get_selected());
Ref<Animation> anim = player->get_animation(current);
- key_editor->set_animation(anim);
+ track_editor->set_animation(anim);
Node *root = player->get_node(player->get_root());
if (root) {
- key_editor->set_root(root);
+ track_editor->set_root(root);
}
}
@@ -884,9 +900,9 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) {
if (player) {
_update_player();
- key_editor->show_select_node_warning(false);
+ track_editor->show_select_node_warning(false);
} else {
- key_editor->show_select_node_warning(true);
+ track_editor->show_select_node_warning(true);
//hide();
}
@@ -1024,7 +1040,7 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set) {
player->seek(pos, true);
}
- key_editor->set_anim_pos(pos);
+ track_editor->set_anim_pos(pos);
updating = true;
};
@@ -1073,7 +1089,7 @@ void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag)
updating = false;
_seek_value_changed(p_pos, !p_drag);
- EditorNode::get_singleton()->get_property_editor()->refresh();
+ EditorNode::get_singleton()->get_inspector()->refresh();
//seekit
}
@@ -1084,16 +1100,58 @@ void AnimationPlayerEditor::_hide_anim_editors() {
hide();
set_process(false);
- key_editor->set_animation(Ref<Animation>());
- key_editor->set_root(NULL);
- key_editor->show_select_node_warning(true);
+ track_editor->set_animation(Ref<Animation>());
+ track_editor->set_root(NULL);
+ track_editor->show_select_node_warning(true);
//editor->animation_editor_make_visible(false);
}
+void AnimationPlayerEditor::_animation_about_to_show_menu() {
+}
+
void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
+ 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 != String()) {
+ anim = player->get_animation(current);
+ }
+
switch (p_option) {
+ case TOOL_NEW_ANIM: {
+ _animation_new();
+ } break;
+
+ case TOOL_LOAD_ANIM: {
+ _animation_load();
+ break;
+ } break;
+ case TOOL_SAVE_ANIM: {
+ if (anim.is_valid()) {
+ _animation_save(anim);
+ }
+ } break;
+ case TOOL_SAVE_AS_ANIM: {
+ if (anim.is_valid()) {
+ _animation_save_as(anim);
+ }
+ } break;
+ case TOOL_DUPLICATE_ANIM: {
+ _animation_duplicate();
+ } break;
+ case TOOL_RENAME_ANIM: {
+ _animation_rename();
+ } break;
+ case TOOL_EDIT_TRANSITIONS: {
+ _animation_blend();
+ } break;
+ case TOOL_REMOVE_ANIM: {
+ _animation_remove();
+ } break;
case TOOL_COPY_ANIM: {
if (!animation->get_item_count()) {
@@ -1156,23 +1214,6 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
}
}
-void AnimationPlayerEditor::_animation_save_menu(int p_option) {
-
- String current = animation->get_item_text(animation->get_selected());
- if (current != "") {
- Ref<Animation> anim = player->get_animation(current);
-
- switch (p_option) {
- case ANIM_SAVE:
- _animation_save(anim);
- break;
- case ANIM_SAVE_AS:
- _animation_save_as(anim);
- break;
- }
- }
-}
-
void AnimationPlayerEditor::_onion_skinning_menu(int p_option) {
PopupMenu *menu = onion_skinning->get_popup();
@@ -1431,7 +1472,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
float pos = cpos + step_off * anim->get_step();
- bool valid = anim->has_loop() || pos >= 0 && pos <= anim->get_length();
+ bool valid = anim->has_loop() || (pos >= 0 && pos <= anim->get_length());
onion.captures_valid[cidx] = valid;
if (valid) {
player->seek(pos, true);
@@ -1494,6 +1535,10 @@ void AnimationPlayerEditor::_stop_onion_skinning() {
}
}
+void AnimationPlayerEditor::_pin_pressed() {
+ EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->update_tree();
+}
+
void AnimationPlayerEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &AnimationPlayerEditor::_gui_input);
@@ -1532,11 +1577,13 @@ void AnimationPlayerEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_blend_editor_next_changed"), &AnimationPlayerEditor::_blend_editor_next_changed);
ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &AnimationPlayerEditor::_unhandled_key_input);
ClassDB::bind_method(D_METHOD("_animation_tool_menu"), &AnimationPlayerEditor::_animation_tool_menu);
- ClassDB::bind_method(D_METHOD("_animation_save_menu"), &AnimationPlayerEditor::_animation_save_menu);
+
ClassDB::bind_method(D_METHOD("_onion_skinning_menu"), &AnimationPlayerEditor::_onion_skinning_menu);
ClassDB::bind_method(D_METHOD("_editor_visibility_changed"), &AnimationPlayerEditor::_editor_visibility_changed);
ClassDB::bind_method(D_METHOD("_prepare_onion_layers_1"), &AnimationPlayerEditor::_prepare_onion_layers_1);
ClassDB::bind_method(D_METHOD("_prepare_onion_layers_2"), &AnimationPlayerEditor::_prepare_onion_layers_2);
+
+ ClassDB::bind_method(D_METHOD("_pin_pressed"), &AnimationPlayerEditor::_pin_pressed);
}
AnimationPlayerEditor *AnimationPlayerEditor::singleton = NULL;
@@ -1606,26 +1653,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
scale->set_tooltip(TTR("Scale animation playback globally for the node."));
scale->hide();
- add_anim = memnew(ToolButton);
- ED_SHORTCUT("animation_player_editor/add_animation", TTR("Create new animation in player."));
- add_anim->set_shortcut(ED_GET_SHORTCUT("animation_player_editor/add_animation"));
- add_anim->set_tooltip(TTR("Create new animation in player."));
-
- hb->add_child(add_anim);
-
- load_anim = memnew(ToolButton);
- ED_SHORTCUT("animation_player_editor/load_from_disk", TTR("Load animation from disk."));
- add_anim->set_shortcut(ED_GET_SHORTCUT("animation_player_editor/load_from_disk"));
- load_anim->set_tooltip(TTR("Load an animation from disk."));
- hb->add_child(load_anim);
-
- save_anim = memnew(MenuButton);
- save_anim->set_tooltip(TTR("Save the current animation."));
- save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save", TTR("Save")), ANIM_SAVE);
- save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_as", TTR("Save As")), ANIM_SAVE_AS);
- save_anim->set_focus_mode(Control::FOCUS_NONE);
- hb->add_child(save_anim);
-
accept = memnew(AcceptDialog);
add_child(accept);
accept->connect("confirmed", this, "_menu_confirm_current");
@@ -1634,23 +1661,28 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
add_child(delete_dialog);
delete_dialog->connect("confirmed", this, "_animation_remove_confirmed");
- duplicate_anim = memnew(ToolButton);
- hb->add_child(duplicate_anim);
- ED_SHORTCUT("animation_player_editor/duplicate_animation", TTR("Duplicate Animation"));
- duplicate_anim->set_shortcut(ED_GET_SHORTCUT("animation_player_editor/duplicate_animation"));
- duplicate_anim->set_tooltip(TTR("Duplicate Animation"));
-
- rename_anim = memnew(ToolButton);
- hb->add_child(rename_anim);
- ED_SHORTCUT("animation_player_editor/rename_animation", TTR("Rename Animation"));
- rename_anim->set_shortcut(ED_GET_SHORTCUT("animation_player_editor/rename_animation"));
- rename_anim->set_tooltip(TTR("Rename Animation"));
-
- remove_anim = memnew(ToolButton);
- hb->add_child(remove_anim);
- ED_SHORTCUT("animation_player_editor/remove_animation", TTR("Remove Animation"));
- remove_anim->set_shortcut(ED_GET_SHORTCUT("animation_player_editor/remove_animation"));
- remove_anim->set_tooltip(TTR("Remove Animation"));
+ tool_anim = memnew(MenuButton);
+ tool_anim->set_flat(false);
+ //tool_anim->set_flat(false);
+ tool_anim->set_tooltip(TTR("Animation Tools"));
+ tool_anim->set_text(TTR("Animation"));
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/new_animation", TTR("New")), TOOL_NEW_ANIM);
+ tool_anim->get_popup()->add_separator();
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/open_animation", TTR("Load")), TOOL_LOAD_ANIM);
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_animation", TTR("Save")), TOOL_SAVE_ANIM);
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_as_animation", TTR("Save As...")), TOOL_SAVE_AS_ANIM);
+ tool_anim->get_popup()->add_separator();
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/copy_animation", TTR("Copy")), TOOL_COPY_ANIM);
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/paste_animation", TTR("Paste")), TOOL_PASTE_ANIM);
+ tool_anim->get_popup()->add_separator();
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/duplicate_animation", TTR("Duplicate")), TOOL_DUPLICATE_ANIM);
+ 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);
animation = memnew(OptionButton);
hb->add_child(animation);
@@ -1662,18 +1694,14 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
hb->add_child(autoplay);
autoplay->set_tooltip(TTR("Autoplay on Load"));
- blend_anim = memnew(ToolButton);
- hb->add_child(blend_anim);
- blend_anim->set_tooltip(TTR("Edit Target Blend Times"));
-
- tool_anim = memnew(MenuButton);
- //tool_anim->set_flat(false);
- tool_anim->set_tooltip(TTR("Animation Tools"));
- tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/copy_animation", TTR("Copy Animation")), TOOL_COPY_ANIM);
- tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/paste_animation", TTR("Paste Animation")), TOOL_PASTE_ANIM);
//tool_anim->get_popup()->add_separator();
//tool_anim->get_popup()->add_item("Edit Anim Resource",TOOL_PASTE_ANIM);
- hb->add_child(tool_anim);
+
+ hb->add_child(memnew(VSeparator));
+
+ track_editor = memnew(AnimationTrackEditor);
+
+ hb->add_child(track_editor->get_edit_menu());
onion_skinning = memnew(MenuButton);
//onion_skinning->set_flat(false);
@@ -1702,10 +1730,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
pin->set_toggle_mode(true);
pin->set_tooltip(TTR("Pin AnimationPlayer"));
hb->add_child(pin);
-
- resource_edit_anim = memnew(Button);
- hb->add_child(resource_edit_anim);
- resource_edit_anim->hide();
+ pin->connect("pressed", this, "_pin_pressed");
file = memnew(EditorFileDialog);
add_child(file);
@@ -1758,16 +1783,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
play_bw_from->connect("pressed", this, "_play_bw_from_pressed");
stop->connect("pressed", this, "_stop_pressed");
//pause->connect("pressed", this,"_pause_pressed");
- add_anim->connect("pressed", this, "_animation_new");
- rename_anim->connect("pressed", this, "_animation_rename");
- load_anim->connect("pressed", this, "_animation_load");
- duplicate_anim->connect("pressed", this, "_animation_duplicate");
//frame->connect("text_entered", this,"_seek_frame_changed");
- blend_anim->connect("pressed", this, "_animation_blend");
- remove_anim->connect("pressed", this, "_animation_remove");
animation->connect("item_selected", this, "_animation_selected", Vector<Variant>(), true);
- resource_edit_anim->connect("pressed", this, "_animation_resource_edit");
+
file->connect("file_selected", this, "_dialog_action");
frame->connect("value_changed", this, "_seek_value_changed", Vector<Variant>(), true);
scale->connect("text_entered", this, "_scale_changed", Vector<Variant>(), true);
@@ -1777,18 +1796,17 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
set_process_unhandled_key_input(true);
- key_editor = memnew(AnimationKeyEditor);
- add_child(key_editor);
- key_editor->set_v_size_flags(SIZE_EXPAND_FILL);
- key_editor->connect("timeline_changed", this, "_animation_key_editor_seek");
- key_editor->connect("animation_len_changed", this, "_animation_key_editor_anim_len_changed");
- key_editor->connect("animation_step_changed", this, "_animation_key_editor_anim_step_changed");
+ add_child(track_editor);
+ track_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ track_editor->connect("timeline_changed", this, "_animation_key_editor_seek");
+ track_editor->connect("animation_len_changed", this, "_animation_key_editor_anim_len_changed");
+ track_editor->connect("animation_step_changed", this, "_animation_key_editor_anim_step_changed");
_update_player();
// Onion skinning
- key_editor->connect("visibility_changed", this, "_editor_visibility_changed");
+ track_editor->connect("visibility_changed", this, "_editor_visibility_changed");
onion.enabled = false;
onion.past = true;
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index a7b7c6c465..5ac7b99903 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -42,8 +42,9 @@
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
-class AnimationKeyEditor;
+class AnimationTrackEditor;
class AnimationPlayerEditorPlugin;
+
class AnimationPlayerEditor : public VBoxContainer {
GDCLASS(AnimationPlayerEditor, VBoxContainer);
@@ -53,6 +54,14 @@ class AnimationPlayerEditor : public VBoxContainer {
AnimationPlayer *player;
enum {
+ TOOL_NEW_ANIM,
+ TOOL_LOAD_ANIM,
+ TOOL_SAVE_ANIM,
+ TOOL_SAVE_AS_ANIM,
+ TOOL_DUPLICATE_ANIM,
+ TOOL_RENAME_ANIM,
+ TOOL_EDIT_TRANSITIONS,
+ TOOL_REMOVE_ANIM,
TOOL_COPY_ANIM,
TOOL_PASTE_ANIM,
TOOL_EDIT_RESOURCE
@@ -72,6 +81,7 @@ class AnimationPlayerEditor : public VBoxContainer {
};
enum {
+ ANIM_OPEN,
ANIM_SAVE,
ANIM_SAVE_AS
};
@@ -89,16 +99,8 @@ class AnimationPlayerEditor : public VBoxContainer {
Button *play_bw_from;
//Button *pause;
- Button *add_anim;
Button *autoplay;
- Button *rename_anim;
- Button *duplicate_anim;
-
- Button *resource_edit_anim;
- Button *load_anim;
- MenuButton *save_anim;
- Button *blend_anim;
- Button *remove_anim;
+
MenuButton *tool_anim;
MenuButton *onion_skinning;
ToolButton *pin;
@@ -130,7 +132,7 @@ class AnimationPlayerEditor : public VBoxContainer {
bool updating;
bool updating_blends;
- AnimationKeyEditor *key_editor;
+ AnimationTrackEditor *track_editor;
// Onion skinning
struct {
@@ -207,8 +209,8 @@ class AnimationPlayerEditor : public VBoxContainer {
void _unhandled_key_input(const Ref<InputEvent> &p_ev);
void _animation_tool_menu(int p_option);
- void _animation_save_menu(int p_option);
void _onion_skinning_menu(int p_option);
+ void _animation_about_to_show_menu();
void _editor_visibility_changed();
bool _are_onion_layers_valid();
@@ -219,6 +221,8 @@ class AnimationPlayerEditor : public VBoxContainer {
void _start_onion_skinning();
void _stop_onion_skinning();
+ void _pin_pressed();
+
AnimationPlayerEditor();
~AnimationPlayerEditor();
@@ -232,7 +236,9 @@ public:
AnimationPlayer *get_player() const;
static AnimationPlayerEditor *singleton;
- AnimationKeyEditor *get_key_editor() { return key_editor; }
+ bool is_pinned() const { return pin->is_pressed(); }
+ void unpin() { pin->set_pressed(false); }
+ AnimationTrackEditor *get_track_editor() { return track_editor; }
Dictionary get_state() const;
void set_state(const Dictionary &p_state);
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/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 05833704d1..505dd4ab76 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -65,7 +65,7 @@ void EditorAssetLibraryItem::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
- icon->set_normal_texture(get_icon("GodotAssetDefault", "EditorIcons"));
+ icon->set_normal_texture(get_icon("DefaultProjectIcon", "EditorIcons"));
category->add_color_override("font_color", Color(0.5, 0.5, 0.5));
author->add_color_override("font_color", Color(0.5, 0.5, 0.5));
}
@@ -110,6 +110,7 @@ EditorAssetLibraryItem::EditorAssetLibraryItem() {
add_child(hb);
icon = memnew(TextureButton);
+ icon->set_custom_minimum_size(Size2(64, 64));
icon->set_default_cursor_shape(CURSOR_POINTING_HAND);
icon->connect("pressed", this, "_asset_clicked");
@@ -170,7 +171,23 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const
for (int i = 0; i < preview_images.size(); i++) {
if (preview_images[i].id == p_index) {
- preview_images[i].button->set_icon(p_image);
+ if (preview_images[i].is_video) {
+ Ref<Image> overlay = get_icon("PlayOverlay", "EditorIcons")->get_data();
+ Ref<Image> thumbnail = p_image->get_data();
+ Point2 overlay_pos = Point2((thumbnail->get_width() - overlay->get_width()) / 2, (thumbnail->get_height() - overlay->get_height()) / 2);
+
+ thumbnail->lock();
+ thumbnail->blend_rect(overlay, overlay->get_used_rect(), overlay_pos);
+ thumbnail->unlock();
+
+ Ref<ImageTexture> tex;
+ tex.instance();
+ tex->create_from_image(thumbnail);
+
+ preview_images[i].button->set_icon(tex);
+ } else {
+ preview_images[i].button->set_icon(p_image);
+ }
break;
}
}
@@ -217,6 +234,7 @@ void EditorAssetLibraryItemDescription::_preview_click(int p_id) {
if (!preview_images[i].is_video) {
if (preview_images[i].image.is_valid()) {
preview->set_texture(preview_images[i].image);
+ minimum_size_changed();
}
} else {
_link_click(preview_images[i].video_link);
@@ -383,7 +401,7 @@ void EditorAssetLibraryItemDownload::configure(const String &p_title, int p_asse
icon->set_texture(p_preview);
asset_id = p_asset_id;
if (!p_preview.is_valid())
- icon->set_texture(get_icon("GodotAssetDefault", "EditorIcons"));
+ icon->set_texture(get_icon("DefaultProjectIcon", "EditorIcons"));
host = p_download_url;
sha256 = p_sha256_hash;
asset_installer->connect("confirmed", this, "_close");
@@ -688,13 +706,24 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByt
int len = image_data.size();
PoolByteArray::Read r = image_data.read();
- Ref<Image> image = Ref<Image>(memnew(Image(r.ptr(), len)));
+ Ref<Image> image = Ref<Image>(memnew(Image));
+
+ uint8_t png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+ uint8_t jpg_signature[3] = { 255, 216, 255 };
+
+ if (r.ptr()) {
+ if (memcmp(&r[0], &png_signature[0], 8) == 0) {
+ image->copy_internals_from(Image::_png_mem_loader_func(r.ptr(), len));
+ } else if (memcmp(&r[0], &jpg_signature[0], 3) == 0) {
+ image->copy_internals_from(Image::_jpg_mem_loader_func(r.ptr(), len));
+ }
+ }
if (!image->empty()) {
switch (image_queue[p_queue_id].image_type) {
case IMAGE_QUEUE_ICON:
- image->resize(80 * EDSCALE, 80 * EDSCALE, Image::INTERPOLATE_CUBIC);
+ image->resize(64 * EDSCALE, 64 * EDSCALE, Image::INTERPOLATE_CUBIC);
break;
case IMAGE_QUEUE_THUMBNAIL: {
@@ -724,7 +753,7 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByt
}
if (!image_set && final) {
- obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("ErrorSign", "EditorIcons"));
+ obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("DefaultProjectIcon", "EditorIcons"));
}
}
}
@@ -733,7 +762,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
ERR_FAIL_COND(!image_queue.has(p_queue_id));
- if (p_status == HTTPRequest::RESULT_SUCCESS) {
+ if (p_status == HTTPRequest::RESULT_SUCCESS && p_code < HTTPClient::RESPONSE_BAD_REQUEST) {
if (p_code != HTTPClient::RESPONSE_NOT_MODIFIED) {
for (int i = 0; i < headers.size(); i++) {
@@ -764,10 +793,10 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
_image_update(p_code == HTTPClient::RESPONSE_NOT_MODIFIED, true, p_data, p_queue_id);
} else {
- WARN_PRINTS("Error getting PNG file from URL: " + image_queue[p_queue_id].image_url);
+ // WARN_PRINTS("Error getting image file from URL: " + image_queue[p_queue_id].image_url);
Object *obj = ObjectDB::get_instance(image_queue[p_queue_id].target);
if (obj) {
- obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("ErrorSign", "EditorIcons"));
+ obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("DefaultProjectIcon", "EditorIcons"));
}
}
@@ -919,41 +948,43 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int
if (to > p_page_count)
to = p_page_count;
- Color gray = Color(0.65, 0.65, 0.65);
-
hbc->add_spacer();
- hbc->add_constant_override("separation", 10);
+ hbc->add_constant_override("separation", 5);
+ Button *first = memnew(Button);
+ first->set_text(TTR("First"));
if (p_page != 0) {
- LinkButton *first = memnew(LinkButton);
- first->set_text(TTR("first"));
- first->add_color_override("font_color", gray);
- first->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
first->connect("pressed", this, "_search", varray(0));
- hbc->add_child(first);
+ } else {
+ first->set_disabled(true);
+ first->set_focus_mode(Control::FOCUS_NONE);
}
+ hbc->add_child(first);
+ Button *prev = memnew(Button);
+ prev->set_text(TTR("Previous"));
if (p_page > 0) {
- LinkButton *prev = memnew(LinkButton);
- prev->set_text(TTR("prev"));
- prev->add_color_override("font_color", gray);
- prev->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
prev->connect("pressed", this, "_search", varray(p_page - 1));
- hbc->add_child(prev);
+ } else {
+ prev->set_disabled(true);
+ prev->set_focus_mode(Control::FOCUS_NONE);
}
+ hbc->add_child(prev);
+ hbc->add_child(memnew(VSeparator));
for (int i = from; i < to; i++) {
if (i == p_page) {
- Label *current = memnew(Label);
+ Button *current = memnew(Button);
current->set_text(itos(i + 1));
+ current->set_disabled(true);
+ current->set_focus_mode(Control::FOCUS_NONE);
+
hbc->add_child(current);
} else {
- LinkButton *current = memnew(LinkButton);
- current->add_color_override("font_color", gray);
- current->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
+ Button *current = memnew(Button);
current->set_text(itos(i + 1));
current->connect("pressed", this, "_search", varray(i));
@@ -961,28 +992,26 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int
}
}
+ Button *next = memnew(Button);
+ next->set_text(TTR("Next"));
if (p_page < p_page_count - 1) {
- LinkButton *next = memnew(LinkButton);
- next->set_text(TTR("next"));
- next->add_color_override("font_color", gray);
- next->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
next->connect("pressed", this, "_search", varray(p_page + 1));
-
- hbc->add_child(next);
+ } else {
+ next->set_disabled(true);
+ next->set_focus_mode(Control::FOCUS_NONE);
}
+ hbc->add_child(memnew(VSeparator));
+ hbc->add_child(next);
+ Button *last = memnew(Button);
+ last->set_text(TTR("Last"));
if (p_page != p_page_count - 1) {
- LinkButton *last = memnew(LinkButton);
- last->set_text(TTR("last"));
- last->add_color_override("font_color", gray);
- last->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
- hbc->add_child(last);
last->connect("pressed", this, "_search", varray(p_page_count - 1));
+ } else {
+ last->set_disabled(true);
+ last->set_focus_mode(Control::FOCUS_NONE);
}
-
- Label *totals = memnew(Label);
- totals->set_text("( " + itos(from * p_page_len) + " - " + itos(from * p_page_len + p_current_items - 1) + " / " + itos(p_total_items) + " )");
- hbc->add_child(totals);
+ hbc->add_child(last);
hbc->add_spacer();
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 93aeca6632..1d20c63969 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -30,7 +30,6 @@
#include "canvas_item_editor_plugin.h"
-#include "editor/animation_editor.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/plugins/animation_player_editor_plugin.h"
@@ -365,7 +364,7 @@ Object *CanvasItemEditor::_get_editor_data(Object *p_what) {
void CanvasItemEditor::_keying_changed() {
- if (AnimationPlayerEditor::singleton->get_key_editor()->is_visible_in_tree())
+ if (AnimationPlayerEditor::singleton->get_track_editor()->is_visible_in_tree())
animation_hb->show();
else
animation_hb->hide();
@@ -520,8 +519,17 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel
node = node->get_parent();
}
+ // Check if the canvas item is already in the list (for groups or scenes)
+ bool duplicate = false;
+ for (int j = 0; j < i; j++) {
+ if (r_items[j].item == canvas_item) {
+ duplicate = true;
+ break;
+ }
+ }
+
//Remove the item if invalid
- if (!canvas_item || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || (canvas_item->has_meta("_edit_lock_") && canvas_item->get_meta("_edit_lock_"))) {
+ if (!canvas_item || duplicate || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || (canvas_item->has_meta("_edit_lock_") && canvas_item->get_meta("_edit_lock_"))) {
r_items.remove(i);
i--;
} else {
@@ -530,6 +538,87 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel
}
}
+void CanvasItemEditor::_get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items) {
+ Point2 screen_pos = transform.xform(p_pos);
+
+ 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));
+
+ Vector<Vector2> bone_shape;
+ if (!_get_bone_shape(&bone_shape, NULL, E))
+ continue;
+
+ // Check if the point is inside the Polygon2D
+ if (Geometry::is_point_in_polygon(screen_pos, bone_shape)) {
+ // Check if the item is already in the list
+ bool duplicate = false;
+ for (int i = 0; i < r_items.size(); i++) {
+ if (r_items[i].item == from_node) {
+ duplicate = true;
+ break;
+ }
+ }
+ if (duplicate)
+ continue;
+
+ // Else, add it
+ _SelectResult res;
+ res.item = from_node;
+ res.z_index = from_node ? from_node->get_z_index() : 0;
+ res.has_z = from_node;
+ r_items.push_back(res);
+ }
+ }
+}
+
+bool CanvasItemEditor::_get_bone_shape(Vector<Vector2> *shape, Vector<Vector2> *outline_shape, Map<BoneKey, BoneList>::Element *bone) {
+ int bone_width = EditorSettings::get_singleton()->get("editors/2d/bone_width");
+ int bone_outline_width = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size");
+
+ Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(bone->key().from));
+ Node2D *to_node = Object::cast_to<Node2D>(ObjectDB::get_instance(bone->key().to));
+
+ if (!from_node->is_inside_tree())
+ return false; //may have been removed
+ if (!from_node)
+ return false;
+
+ if (!to_node && bone->get().length == 0)
+ return false;
+
+ Vector2 from = transform.xform(from_node->get_global_position());
+ Vector2 to;
+
+ if (to_node)
+ to = transform.xform(to_node->get_global_position());
+ else
+ to = transform.xform(from_node->get_global_transform().xform(Vector2(bone->get().length, 0)));
+
+ Vector2 rel = to - from;
+ Vector2 relt = rel.tangent().normalized() * bone_width;
+ Vector2 reln = rel.normalized();
+ Vector2 reltn = relt.normalized();
+
+ if (shape) {
+ shape->clear();
+ shape->push_back(from);
+ shape->push_back(from + rel * 0.2 + relt);
+ shape->push_back(to);
+ shape->push_back(from + rel * 0.2 - relt);
+ }
+
+ if (outline_shape) {
+ outline_shape->clear();
+ outline_shape->push_back(from + (-reln - reltn) * bone_outline_width);
+ outline_shape->push_back(from + (-reln + reltn) * bone_outline_width);
+ outline_shape->push_back(from + rel * 0.2 + relt + reltn * bone_outline_width);
+ outline_shape->push_back(to + (reln + reltn) * bone_outline_width);
+ outline_shape->push_back(to + (reln - reltn) * bone_outline_width);
+ outline_shape->push_back(from + rel * 0.2 - relt - reltn * bone_outline_width);
+ }
+ return true;
+}
+
void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
if (!p_node)
return;
@@ -629,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) {
@@ -1787,8 +1877,13 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
// Find the item to select
CanvasItem *canvas_item = NULL;
Vector<_SelectResult> selection;
+
+ // Retrieve the items
_get_canvas_items_at_pos(click, selection, editor_selection->get_selection().empty() ? 1 : 0);
+ // Retrieve the bones
+ _get_bones_at_pos(click, selection);
+
for (int i = 0; i < selection.size(); i++) {
if (editor_selection->is_selected(selection[i].item)) {
// Drag the node(s) if requested
@@ -1888,32 +1983,53 @@ bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> m = p_event;
if (m.is_valid()) {
- if (drag_type == DRAG_NONE && tool == TOOL_SELECT) {
- Point2 click = transform.affine_inverse().xform(m->get_position());
-
- //Checks if the hovered items changed, update the viewport if so
- Vector<_SelectResult> hovering_results_tmp;
- _get_canvas_items_at_pos(click, hovering_results_tmp);
- hovering_results_tmp.sort();
- bool changed = false;
- if (hovering_results.size() == hovering_results_tmp.size()) {
- for (int i = 0; i < hovering_results.size(); i++) {
- if (hovering_results[i].item != hovering_results_tmp[i].item) {
- changed = true;
- break;
- }
- }
- } else {
- changed = true;
- }
+ Point2 click = transform.affine_inverse().xform(m->get_position());
- if (changed) {
- hovering_results = hovering_results_tmp;
- viewport->update();
+ // Checks if the hovered items changed, update the viewport if so
+ Vector<_SelectResult> hovering_results_items;
+ _get_canvas_items_at_pos(click, hovering_results_items);
+ hovering_results_items.sort();
+
+ // Compute the nodes names and icon position
+ Vector<_HoverResult> hovering_results_tmp;
+ for (int i = 0; i < hovering_results_items.size(); i++) {
+ CanvasItem *canvas_item = hovering_results_items[i].item;
+
+ if (canvas_item->_edit_use_rect())
+ continue;
+
+ _HoverResult hover_result;
+ hover_result.position = canvas_item->get_global_transform_with_canvas().get_origin();
+ if (has_icon(canvas_item->get_class(), "EditorIcons"))
+ hover_result.icon = get_icon(canvas_item->get_class(), "EditorIcons");
+ else
+ hover_result.icon = get_icon("Object", "EditorIcons");
+ hover_result.name = canvas_item->get_name();
+
+ hovering_results_tmp.push_back(hover_result);
+ }
+
+ // Check if changed, if so, update.
+ bool changed = false;
+ if (hovering_results_tmp.size() == hovering_results.size()) {
+ for (int i = 0; i < hovering_results_tmp.size(); i++) {
+ _HoverResult a = hovering_results_tmp[i];
+ _HoverResult b = hovering_results[i];
+ if (a.icon != b.icon || a.name != b.name || a.position != b.position) {
+ changed = true;
+ break;
+ }
}
+ } else {
+ changed = true;
+ }
- return true;
+ if (changed) {
+ hovering_results = hovering_results_tmp;
+ viewport->update();
}
+
+ return true;
}
return false;
@@ -2375,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) {
@@ -2579,57 +2697,25 @@ void CanvasItemEditor::_draw_bones() {
RID ci = viewport->get_canvas_item();
if (skeleton_show_bones) {
- int bone_width = EditorSettings::get_singleton()->get("editors/2d/bone_width");
Color bone_color1 = EditorSettings::get_singleton()->get("editors/2d/bone_color1");
Color bone_color2 = EditorSettings::get_singleton()->get("editors/2d/bone_color2");
Color bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color");
Color bone_outline_color = EditorSettings::get_singleton()->get("editors/2d/bone_outline_color");
Color bone_selected_color = EditorSettings::get_singleton()->get("editors/2d/bone_selected_color");
- int bone_outline_size = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size");
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));
-
- if (!from_node->is_inside_tree())
- continue; //may have been removed
- if (!from_node)
+ Vector<Vector2> bone_shape;
+ Vector<Vector2> bone_shape_outline;
+ if (!_get_bone_shape(&bone_shape, &bone_shape_outline, E))
continue;
- if (!to_node && E->get().length == 0)
+ Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().from));
+ if (!from_node->is_visible_in_tree())
continue;
- Vector2 from = transform.xform(from_node->get_global_position());
- Vector2 to;
-
- if (to_node)
- to = transform.xform(to_node->get_global_position());
- else
- to = transform.xform(from_node->get_global_transform().xform(Vector2(E->get().length, 0)));
-
- Vector2 rel = to - from;
- Vector2 relt = rel.tangent().normalized() * bone_width;
- Vector2 reln = rel.normalized();
- Vector2 reltn = relt.normalized();
-
- Vector<Vector2> bone_shape;
- bone_shape.push_back(from);
- bone_shape.push_back(from + rel * 0.2 + relt);
- bone_shape.push_back(to);
- bone_shape.push_back(from + rel * 0.2 - relt);
-
- Vector<Vector2> bone_shape_outline;
- bone_shape_outline.push_back(from + (-reln - reltn) * bone_outline_size);
- bone_shape_outline.push_back(from + (-reln + reltn) * bone_outline_size);
- bone_shape_outline.push_back(from + rel * 0.2 + relt + reltn * bone_outline_size);
- bone_shape_outline.push_back(to + (reln + reltn) * bone_outline_size);
- bone_shape_outline.push_back(to + (reln - reltn) * bone_outline_size);
- bone_shape_outline.push_back(from + rel * 0.2 - relt - reltn * bone_outline_size);
-
Vector<Color> colors;
if (from_node->has_meta("_edit_ik_")) {
-
colors.push_back(bone_ik_color);
colors.push_back(bone_ik_color);
colors.push_back(bone_ik_color);
@@ -2706,26 +2792,15 @@ void CanvasItemEditor::_draw_hover() {
List<Rect2> previous_rects;
for (int i = 0; i < hovering_results.size(); i++) {
- // Draw the node's name and icon
- CanvasItem *canvas_item = hovering_results[i].item;
- if (canvas_item->_edit_use_rect())
- continue;
-
- Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
+ Ref<Texture> node_icon = hovering_results[i].icon;
+ String node_name = hovering_results[i].name;
- // Get the resources
- Ref<Texture> node_icon;
- if (has_icon(canvas_item->get_class(), "EditorIcons"))
- node_icon = get_icon(canvas_item->get_class(), "EditorIcons");
- else
- node_icon = get_icon("Object", "EditorIcons");
Ref<Font> font = get_font("font", "Label");
- String node_name = canvas_item->get_name();
Size2 node_name_size = font->get_string_size(node_name);
Size2 item_size = Size2(node_icon->get_size().x + 4 + node_name_size.x, MAX(node_icon->get_size().y, node_name_size.y - 3));
- Point2 pos = xform.get_origin() - Point2(0, item_size.y) + (Point2(node_icon->get_size().x, -node_icon->get_size().y) / 4);
+ Point2 pos = transform.xform(hovering_results[i].position) - Point2(0, item_size.y) + (Point2(node_icon->get_size().x, -node_icon->get_size().y) / 4);
// Rectify the position to avoid overlaping items
for (List<Rect2>::Element *E = previous_rects.front(); E; E = E->next()) {
if (E->get().intersects(Rect2(pos, item_size))) {
@@ -2735,8 +2810,10 @@ void CanvasItemEditor::_draw_hover() {
previous_rects.push_back(Rect2(pos, item_size));
- // Draw the node icon and name
+ // Draw icon
viewport->draw_texture(node_icon, pos, Color(1.0, 1.0, 1.0, 0.5));
+
+ // Draw name
viewport->draw_string(font, pos + Point2(node_icon->get_size().x + 4, item_size.y - 3), node_name, Color(1.0, 1.0, 1.0, 0.5));
}
}
@@ -3016,7 +3093,7 @@ void CanvasItemEditor::_notification(int p_what) {
select_sb->set_default_margin(Margin(i), 4);
}
- AnimationPlayerEditor::singleton->get_key_editor()->connect("visibility_changed", this, "_keying_changed");
+ AnimationPlayerEditor::singleton->get_track_editor()->connect("visibility_changed", this, "_keying_changed");
_keying_changed();
get_tree()->connect("node_added", this, "_tree_changed", varray());
get_tree()->connect("node_removed", this, "_tree_changed", varray());
@@ -3628,11 +3705,11 @@ void CanvasItemEditor::_popup_callback(int p_op) {
Node2D *n2d = Object::cast_to<Node2D>(canvas_item);
if (key_pos)
- AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), existing);
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), existing);
if (key_rot)
- AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(n2d, "rotation_degrees", Math::rad2deg(n2d->get_rotation()), existing);
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation_degrees", Math::rad2deg(n2d->get_rotation()), existing);
if (key_scale)
- AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), existing);
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), existing);
if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) {
//look for an IK chain
@@ -3659,11 +3736,11 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for (List<Node2D *>::Element *F = ik_chain.front(); F; F = F->next()) {
if (key_pos)
- AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(F->get(), "position", F->get()->get_position(), existing);
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "position", F->get()->get_position(), existing);
if (key_rot)
- AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(F->get(), "rotation_degrees", Math::rad2deg(F->get()->get_rotation()), existing);
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "rotation_degrees", Math::rad2deg(F->get()->get_rotation()), existing);
if (key_scale)
- AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(F->get(), "scale", F->get()->get_scale(), existing);
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "scale", F->get()->get_scale(), existing);
}
}
}
@@ -3673,11 +3750,11 @@ void CanvasItemEditor::_popup_callback(int p_op) {
Control *ctrl = Object::cast_to<Control>(canvas_item);
if (key_pos)
- AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), existing);
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), existing);
if (key_rot)
- AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation_degrees(), existing);
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation_degrees(), existing);
if (key_scale)
- AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), existing);
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), existing);
}
}
@@ -3773,7 +3850,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
ctrl->set_position(Point2());
/*
if (key_scale)
- AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size());
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size());
*/
}
}
@@ -4275,13 +4352,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
hb->add_child(snap_button);
snap_button->set_toggle_mode(true);
snap_button->connect("toggled", this, "_button_toggle_snap");
- snap_button->set_tooltip(TTR("Toggles snapping"));
- snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_snap", TTR("Use Snap"), KEY_S));
+ snap_button->set_tooltip(TTR("Toggle snapping."));
+ 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);
snap_config_menu->set_h_size_flags(SIZE_SHRINK_END);
- snap_config_menu->set_tooltip(TTR("Snapping options"));
+ snap_config_menu->set_tooltip(TTR("Snapping Options"));
PopupMenu *p = snap_config_menu->get_popup();
p->connect("id_pressed", this, "_popup_callback");
@@ -4404,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/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index a1957b892e..adc4010f39 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -257,9 +257,15 @@ class CanvasItemEditor : public VBoxContainer {
return has_z && p_rr.has_z ? p_rr.z_index < z_index : p_rr.has_z;
}
};
-
Vector<_SelectResult> selection_results;
- Vector<_SelectResult> hovering_results;
+
+ struct _HoverResult {
+
+ Point2 position;
+ Ref<Texture> icon;
+ String name;
+ };
+ Vector<_HoverResult> hovering_results;
struct BoneList {
@@ -351,6 +357,7 @@ class CanvasItemEditor : public VBoxContainer {
void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit = 0);
+ void _get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items);
void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
bool _select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append);
@@ -379,6 +386,7 @@ class CanvasItemEditor : public VBoxContainer {
UndoRedo *undo_redo;
bool _build_bones_list(Node *p_node);
+ bool _get_bone_shape(Vector<Vector2> *shape, Vector<Vector2> *outline_shape, Map<BoneKey, BoneList>::Element *bone);
List<CanvasItem *> _get_edited_canvas_items(bool retreive_locked = false, bool remove_canvas_item_if_parent_in_selection = true);
Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list);
diff --git a/editor/plugins/collision_polygon_2d_editor_plugin.cpp b/editor/plugins/collision_polygon_2d_editor_plugin.cpp
index b624d69810..672337ba2f 100644
--- a/editor/plugins/collision_polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_polygon_2d_editor_plugin.cpp
@@ -42,6 +42,7 @@ void CollisionPolygon2DEditor::_set_node(Node *p_polygon) {
CollisionPolygon2DEditor::CollisionPolygon2DEditor(EditorNode *p_editor) :
AbstractPolygon2DEditor(p_editor) {
+ node = NULL;
}
CollisionPolygon2DEditorPlugin::CollisionPolygon2DEditorPlugin(EditorNode *p_node) :
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/cube_grid_theme_editor_plugin.cpp b/editor/plugins/cube_grid_theme_editor_plugin.cpp
index 81f45b9f55..68d5ea5247 100644
--- a/editor/plugins/cube_grid_theme_editor_plugin.cpp
+++ b/editor/plugins/cube_grid_theme_editor_plugin.cpp
@@ -198,7 +198,7 @@ void MeshLibraryEditor::_menu_cbk(int p_option) {
} break;
case MENU_OPTION_REMOVE_ITEM: {
- String p = editor->get_property_editor()->get_selected_path();
+ String p = editor->get_inspector()->get_selected_path();
if (p.begins_with("/MeshLibrary/item") && p.get_slice_count("/") >= 3) {
to_erase = p.get_slice("/", 3).to_int();
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index 7c49408c35..49c54ad67d 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -91,7 +91,7 @@ void CurveEditor::set_curve(Ref<Curve> curve) {
}
Size2 CurveEditor::get_minimum_size() const {
- return Vector2(64, 64);
+ return Vector2(64, 150) * EDSCALE;
}
void CurveEditor::_notification(int p_what) {
@@ -639,7 +639,7 @@ void CurveEditor::_draw() {
Ref<Font> font = get_font("font", "Label");
float font_height = font->get_height();
- const Color text_color = get_color("font_color", "Editor");
+ Color text_color = get_color("font_color", "Editor");
{
// X axis
@@ -720,6 +720,7 @@ void CurveEditor::_draw() {
// Help text
if (_selected_point > 0 && _selected_point + 1 < curve.get_point_count()) {
+ text_color.a *= 0.4;
draw_string(font, Vector2(50, font_height), TTR("Hold Shift to edit tangents individually"), text_color);
}
}
@@ -750,87 +751,28 @@ void CurveEditor::_bind_methods() {
//---------------
-CurveEditorPlugin::CurveEditorPlugin(EditorNode *p_node) {
- _editor_node = p_node;
-
- _view = memnew(CurveEditor);
- _view->set_custom_minimum_size(Size2(100, 128 * EDSCALE));
- _view->hide();
-
- _toggle_button = _editor_node->add_bottom_panel_item(get_name(), _view);
- _toggle_button->hide();
-
- get_editor_interface()->get_resource_previewer()->add_preview_generator(memnew(CurvePreviewGenerator));
-}
-
-CurveEditorPlugin::~CurveEditorPlugin() {
-}
-
-void CurveEditorPlugin::edit(Object *p_object) {
-
- Ref<Curve> curve_ref;
-
- if (_current_ref.is_valid()) {
- CurveTexture *ct = Object::cast_to<CurveTexture>(*_current_ref);
- if (ct)
- ct->disconnect(CoreStringNames::get_singleton()->changed, this, "_curve_texture_changed");
- }
-
- if (p_object) {
- Resource *res = Object::cast_to<Resource>(p_object);
- ERR_FAIL_COND(res == NULL);
- ERR_FAIL_COND(!handles(p_object));
-
- _current_ref = Ref<Resource>(Object::cast_to<Resource>(p_object));
-
- if (_current_ref.is_valid()) {
- Curve *curve = Object::cast_to<Curve>(*_current_ref);
- if (curve)
- curve_ref = Ref<Curve>(curve);
- else {
- CurveTexture *ct = Object::cast_to<CurveTexture>(*_current_ref);
- if (ct) {
- ct->connect(CoreStringNames::get_singleton()->changed, this, "_curve_texture_changed");
- curve_ref = ct->get_curve();
- }
- }
- }
+bool EditorInspectorPluginCurve::can_handle(Object *p_object) {
- } else {
- _current_ref = Ref<Resource>();
- }
-
- _view->set_curve(curve_ref);
+ return Object::cast_to<Curve>(p_object) != NULL;
}
-bool CurveEditorPlugin::handles(Object *p_object) const {
- // Both handled so that we can keep the curve editor open
- return Object::cast_to<Curve>(p_object) || Object::cast_to<CurveTexture>(p_object);
-}
+void EditorInspectorPluginCurve::parse_begin(Object *p_object) {
-void CurveEditorPlugin::make_visible(bool p_visible) {
- if (p_visible) {
- _toggle_button->show();
- _editor_node->make_bottom_panel_item_visible(_view);
- } else {
- _toggle_button->hide();
- if (_view->is_visible_in_tree())
- _editor_node->hide_bottom_panel();
- }
-}
+ Curve *curve = Object::cast_to<Curve>(p_object);
+ ERR_FAIL_COND(!curve);
+ Ref<Curve> c(curve);
-void CurveEditorPlugin::_curve_texture_changed() {
- // If the curve is shown indirectly as a CurveTexture is edited,
- // we need to monitor when the curve property gets assigned
- CurveTexture *ct = Object::cast_to<CurveTexture>(*_current_ref);
- if (ct) {
- _view->set_curve(ct->get_curve());
- }
+ CurveEditor *editor = memnew(CurveEditor);
+ editor->set_curve(curve);
+ add_custom_control(editor);
}
-void CurveEditorPlugin::_bind_methods() {
+CurveEditorPlugin::CurveEditorPlugin(EditorNode *p_node) {
+ Ref<EditorInspectorPluginCurve> curve_plugin;
+ curve_plugin.instance();
+ EditorInspector::add_inspector_plugin(curve_plugin);
- ClassDB::bind_method(D_METHOD("_curve_texture_changed"), &CurveEditorPlugin::_curve_texture_changed);
+ get_editor_interface()->get_resource_previewer()->add_preview_generator(memnew(CurvePreviewGenerator));
}
//-----------------------------------
@@ -852,13 +794,13 @@ Ref<Texture> CurvePreviewGenerator::generate(const Ref<Resource> &p_from) {
img_ref.instance();
Image &im = **img_ref;
- im.create(thumbnail_size, thumbnail_size, 0, Image::FORMAT_RGBA8);
+ im.create(thumbnail_size, thumbnail_size / 2, 0, Image::FORMAT_RGBA8);
im.lock();
Color bg_color(0.1, 0.1, 0.1, 1.0);
for (int i = 0; i < thumbnail_size; i++) {
- for (int j = 0; j < thumbnail_size; j++) {
+ for (int j = 0; j < thumbnail_size / 2; j++) {
im.set_pixel(i, j, bg_color);
}
}
diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h
index 97f1ba2fa1..255f359ed2 100644
--- a/editor/plugins/curve_editor_plugin.h
+++ b/editor/plugins/curve_editor_plugin.h
@@ -119,28 +119,19 @@ private:
float _tangents_length;
};
+class EditorInspectorPluginCurve : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorPluginCurve, EditorInspectorPlugin)
+public:
+ virtual bool can_handle(Object *p_object);
+ virtual void parse_begin(Object *p_object);
+};
+
class CurveEditorPlugin : public EditorPlugin {
GDCLASS(CurveEditorPlugin, EditorPlugin)
public:
CurveEditorPlugin(EditorNode *p_node);
- ~CurveEditorPlugin();
String get_name() const { return "Curve"; }
- bool has_main_screen() const { return false; }
- void edit(Object *p_object);
- bool handles(Object *p_object) const;
- void make_visible(bool p_visible);
-
-private:
- static void _bind_methods();
-
- void _curve_texture_changed();
-
-private:
- CurveEditor *_view;
- Ref<Resource> _current_ref;
- EditorNode *_editor_node;
- ToolButton *_toggle_button;
};
class CurvePreviewGenerator : public EditorResourcePreviewGenerator {
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 8542296bde..0d25b3685a 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -37,6 +37,7 @@
#include "io/resource_loader.h"
#include "os/os.h"
#include "scene/resources/bit_mask.h"
+#include "scene/resources/dynamic_font.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
@@ -96,6 +97,7 @@ Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) {
if (img.is_null() || img->empty())
return Ref<Texture>();
+ img = img->duplicate();
img->clear_mipmaps();
int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
@@ -467,15 +469,6 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) {
Color text_color = EditorSettings::get_singleton()->get("text_editor/highlighting/text_color");
Color symbol_color = EditorSettings::get_singleton()->get("text_editor/highlighting/symbol_color");
- if (EditorSettings::get_singleton()->get("text_editor/theme/color_theme") == "Adaptive") {
- Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme();
-
- bg_color = tm->get_color("text_editor/highlighting/background_color", "Editor");
- keyword_color = tm->get_color("text_editor/highlighting/keyword_color", "Editor");
- text_color = tm->get_color("text_editor/highlighting/text_color", "Editor");
- symbol_color = tm->get_color("text_editor/highlighting/symbol_color", "Editor");
- }
-
img->lock();
if (bg_color.a == 0)
@@ -561,274 +554,92 @@ EditorScriptPreviewPlugin::EditorScriptPreviewPlugin() {
}
///////////////////////////////////////////////////////////////////
-// FIXME: Needs to be rewritten for AudioStream in Godot 3.0+
-#if 0
-bool EditorSamplePreviewPlugin::handles(const String& p_type) const {
+bool EditorAudioStreamPreviewPlugin::handles(const String &p_type) const {
- return ClassDB::is_parent_class(p_type,"Sample");
+ return ClassDB::is_parent_class(p_type, "AudioStream");
}
-Ref<Texture> EditorSamplePreviewPlugin::generate(const RES& p_from) {
-
- Ref<Sample> smp =p_from;
- ERR_FAIL_COND_V(smp.is_null(),Ref<Texture>());
+Ref<Texture> EditorAudioStreamPreviewPlugin::generate(const RES &p_from) {
+ Ref<AudioStream> stream = p_from;
+ ERR_FAIL_COND_V(stream.is_null(), Ref<Texture>());
int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
- thumbnail_size*=EDSCALE;
+ thumbnail_size *= EDSCALE;
PoolVector<uint8_t> img;
int w = thumbnail_size;
int h = thumbnail_size;
- img.resize(w*h*3);
+ img.resize(w * h * 3);
PoolVector<uint8_t>::Write imgdata = img.write();
- uint8_t * imgw = imgdata.ptr();
- PoolVector<uint8_t> data = smp->get_data();
- PoolVector<uint8_t>::Read sampledata = data.read();
- const uint8_t *sdata=sampledata.ptr();
-
- bool stereo = smp->is_stereo();
- bool _16=smp->get_format()==Sample::FORMAT_PCM16;
- int len = smp->get_length();
+ uint8_t *imgw = imgdata.ptr();
- if (len<1)
- return Ref<Texture>();
+ Ref<AudioStreamPlayback> playback = stream->instance_playback();
- if (smp->get_format()==Sample::FORMAT_IMA_ADPCM) {
-
- struct IMA_ADPCM_State {
-
- int16_t step_index;
- int32_t predictor;
- /* values at loop point */
- int16_t loop_step_index;
- int32_t loop_predictor;
- int32_t last_nibble;
- int32_t loop_pos;
- int32_t window_ofs;
- const uint8_t *ptr;
- } ima_adpcm;
-
- ima_adpcm.step_index=0;
- ima_adpcm.predictor=0;
- ima_adpcm.loop_step_index=0;
- ima_adpcm.loop_predictor=0;
- ima_adpcm.last_nibble=-1;
- ima_adpcm.loop_pos=0x7FFFFFFF;
- ima_adpcm.window_ofs=0;
- ima_adpcm.ptr=NULL;
-
-
- for(int i=0;i<w;i++) {
-
- float max[2]={-1e10,-1e10};
- float min[2]={1e10,1e10};
- int from = i*len/w;
- int to = (i+1)*len/w;
- if (to>=len)
- to=len-1;
-
- for(int j=from;j<to;j++) {
-
- while(j>ima_adpcm.last_nibble) {
-
- static const int16_t _ima_adpcm_step_table[89] = {
- 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
- 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
- 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
- 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
- 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
- 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
- 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
- 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
- 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
- };
-
- static const int8_t _ima_adpcm_index_table[16] = {
- -1, -1, -1, -1, 2, 4, 6, 8,
- -1, -1, -1, -1, 2, 4, 6, 8
- };
-
- int16_t nibble,diff,step;
-
- ima_adpcm.last_nibble++;
- const uint8_t *src_ptr=sdata;
-
- int ofs = ima_adpcm.last_nibble>>1;
-
- if (stereo)
- ofs*=2;
-
-
- nibble = (ima_adpcm.last_nibble&1)?
- (src_ptr[ofs]>>4):(src_ptr[ofs]&0xF);
- step=_ima_adpcm_step_table[ima_adpcm.step_index];
-
- ima_adpcm.step_index += _ima_adpcm_index_table[nibble];
- if (ima_adpcm.step_index<0)
- ima_adpcm.step_index=0;
- if (ima_adpcm.step_index>88)
- ima_adpcm.step_index=88;
-
- diff = step >> 3 ;
- if (nibble & 1)
- diff += step >> 2 ;
- if (nibble & 2)
- diff += step >> 1 ;
- if (nibble & 4)
- diff += step ;
- if (nibble & 8)
- diff = -diff ;
-
- ima_adpcm.predictor+=diff;
- if (ima_adpcm.predictor<-0x8000)
- ima_adpcm.predictor=-0x8000;
- else if (ima_adpcm.predictor>0x7FFF)
- ima_adpcm.predictor=0x7FFF;
-
-
- /* store loop if there */
- if (ima_adpcm.last_nibble==ima_adpcm.loop_pos) {
-
- ima_adpcm.loop_step_index = ima_adpcm.step_index;
- ima_adpcm.loop_predictor = ima_adpcm.predictor;
- }
+ float len_s = stream->get_length();
+ if (len_s == 0) {
+ len_s = 60; //one minute audio if no length specified
+ }
+ int frame_length = AudioServer::get_singleton()->get_mix_rate() * len_s;
- }
+ Vector<AudioFrame> frames;
+ frames.resize(frame_length);
- float v=ima_adpcm.predictor/32767.0;
- if (v>max[0])
- max[0]=v;
- if (v<min[0])
- min[0]=v;
- }
- max[0]*=0.8;
- min[0]*=0.8;
-
- for(int j=0;j<h;j++) {
- float v = (j/(float)h) * 2.0 - 1.0;
- uint8_t* imgofs = &imgw[(uint64_t(j)*w+i)*3];
- if (v>min[0] && v<max[0]) {
- imgofs[0]=255;
- imgofs[1]=150;
- imgofs[2]=80;
- } else {
- imgofs[0]=0;
- imgofs[1]=0;
- imgofs[2]=0;
- }
- }
- }
- } else {
- for(int i=0;i<w;i++) {
- // i trust gcc will optimize this loop
- float max[2]={-1e10,-1e10};
- float min[2]={1e10,1e10};
- int c=stereo?2:1;
- int from = uint64_t(i)*len/w;
- int to = (uint64_t(i)+1)*len/w;
- if (to>=len)
- to=len-1;
-
- if (_16) {
- const int16_t*src =(const int16_t*)sdata;
-
- for(int j=0;j<c;j++) {
-
- for(int k=from;k<=to;k++) {
-
- float v = src[uint64_t(k)*c+j]/32768.0;
- if (v>max[j])
- max[j]=v;
- if (v<min[j])
- min[j]=v;
- }
+ playback->start();
+ playback->mix(frames.ptrw(), 1, frames.size());
+ playback->stop();
- }
- } else {
+ for (int i = 0; i < w; i++) {
- const int8_t*src =(const int8_t*)sdata;
+ float max = -1000;
+ float min = 1000;
+ int from = uint64_t(i) * frame_length / w;
+ int to = uint64_t(i + 1) * frame_length / w;
+ to = MIN(to, frame_length);
+ from = MIN(from, frame_length - 1);
+ if (to == from) {
+ to = from + 1;
+ }
- for(int j=0;j<c;j++) {
+ for (int j = from; j < to; j++) {
- for(int k=from;k<=to;k++) {
+ max = MAX(max, frames[j].l);
+ max = MAX(max, frames[j].r);
- float v = src[uint64_t(k)*c+j]/128.0;
- if (v>max[j])
- max[j]=v;
- if (v<min[j])
- min[j]=v;
- }
+ min = MIN(min, frames[j].l);
+ min = MIN(min, frames[j].r);
+ }
- }
- }
+ int pfrom = CLAMP((min * 0.5 + 0.5) * h / 2, 0, h / 2) + h / 4;
+ int pto = CLAMP((max * 0.5 + 0.5) * h / 2, 0, h / 2) + h / 4;
- max[0]*=0.8;
- max[1]*=0.8;
- min[0]*=0.8;
- min[1]*=0.8;
-
- if (!stereo) {
- for(int j=0;j<h;j++) {
- float v = (j/(float)h) * 2.0 - 1.0;
- uint8_t* imgofs = &imgw[(j*w+i)*3];
- if (v>min[0] && v<max[0]) {
- imgofs[0]=255;
- imgofs[1]=150;
- imgofs[2]=80;
- } else {
- imgofs[0]=0;
- imgofs[1]=0;
- imgofs[2]=0;
- }
- }
+ for (int j = 0; j < h; j++) {
+ uint8_t *p = &imgw[(j * w + i) * 3];
+ if (j < pfrom || j > pto) {
+ p[0] = 100;
+ p[1] = 100;
+ p[2] = 100;
} else {
-
- for(int j=0;j<h;j++) {
-
- int half;
- float v;
- if (j<(h/2)) {
- half=0;
- v = (j/(float)(h/2)) * 2.0 - 1.0;
- } else {
- half=1;
- if( (float)(h/2) != 0 ) {
- v = ((j-(h/2))/(float)(h/2)) * 2.0 - 1.0;
- } else {
- v = ((j-(h/2))/(float)(1/2)) * 2.0 - 1.0;
- }
- }
-
- uint8_t* imgofs = &imgw[(j*w+i)*3];
- if (v>min[half] && v<max[half]) {
- imgofs[0]=255;
- imgofs[1]=150;
- imgofs[2]=80;
- } else {
- imgofs[0]=0;
- imgofs[1]=0;
- imgofs[2]=0;
- }
- }
-
+ p[0] = 180;
+ p[1] = 180;
+ p[2] = 180;
}
-
}
}
imgdata = PoolVector<uint8_t>::Write();
- post_process_preview(img);
+ //post_process_preview(img);
- Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture));
- ptex->create_from_image(Image(w,h,0,Image::FORMAT_RGB8,img),0);
+ Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture));
+ Ref<Image> image;
+ image.instance();
+ image->create(w, h, false, Image::FORMAT_RGB8, img);
+ ptex->create_from_image(image, 0);
return ptex;
-
}
-EditorSamplePreviewPlugin::EditorSamplePreviewPlugin() {
+EditorAudioStreamPreviewPlugin::EditorAudioStreamPreviewPlugin() {
}
-#endif
///////////////////////////////////////////////////////////////////////////
@@ -942,3 +753,100 @@ EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() {
VS::get_singleton()->free(camera);
VS::get_singleton()->free(scenario);
}
+
+///////////////////////////////////////////////////////////////////////////
+
+void EditorFontPreviewPlugin::_preview_done(const Variant &p_udata) {
+
+ preview_done = true;
+}
+
+void EditorFontPreviewPlugin::_bind_methods() {
+
+ ClassDB::bind_method("_preview_done", &EditorFontPreviewPlugin::_preview_done);
+}
+
+bool EditorFontPreviewPlugin::handles(const String &p_type) const {
+
+ return ClassDB::is_parent_class(p_type, "DynamicFontData");
+}
+
+Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) {
+ if (canvas.is_valid()) {
+ VS::get_singleton()->viewport_remove_canvas(viewport, canvas);
+ }
+
+ canvas = VS::get_singleton()->canvas_create();
+ canvas_item = VS::get_singleton()->canvas_item_create();
+
+ VS::get_singleton()->viewport_attach_canvas(viewport, canvas);
+ VS::get_singleton()->canvas_item_set_parent(canvas_item, canvas);
+
+ Ref<DynamicFontData> SampledFont;
+ SampledFont.instance();
+ SampledFont->set_font_path(p_path);
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
+ thumbnail_size *= EDSCALE;
+
+ Ref<DynamicFont> sampled_font;
+ sampled_font.instance();
+ sampled_font->set_size(50);
+ sampled_font->set_font_data(SampledFont);
+
+ String sampled_text = "Abg";
+ Vector2 size = sampled_font->get_string_size(sampled_text);
+
+ Vector2 pos;
+
+ pos.x = 64 - size.x / 2;
+ pos.y = 80;
+
+ Ref<Font> font = sampled_font;
+
+ font->draw(canvas_item, pos, sampled_text);
+
+ VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture
+
+ preview_done = false;
+ VS::get_singleton()->request_frame_drawn_callback(this, "_preview_done", Variant());
+
+ while (!preview_done) {
+ OS::get_singleton()->delay_usec(10);
+ }
+
+ Ref<Image> img = VS::get_singleton()->VS::get_singleton()->texture_get_data(viewport_texture);
+ ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
+
+ img->convert(Image::FORMAT_RGBA8);
+ img->resize(thumbnail_size, thumbnail_size);
+
+ post_process_preview(img);
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture));
+ ptex->create_from_image(img, 0);
+
+ return ptex;
+}
+
+Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from) {
+
+ return generate_from_path(p_from->get_path());
+}
+
+EditorFontPreviewPlugin::EditorFontPreviewPlugin() {
+
+ viewport = VS::get_singleton()->viewport_create();
+ VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_DISABLED);
+ VS::get_singleton()->viewport_set_vflip(viewport, true);
+ VS::get_singleton()->viewport_set_size(viewport, 128, 128);
+ VS::get_singleton()->viewport_set_active(viewport, true);
+ viewport_texture = VS::get_singleton()->viewport_get_texture(viewport);
+}
+
+EditorFontPreviewPlugin::~EditorFontPreviewPlugin() {
+
+ VS::get_singleton()->free(canvas_item);
+ VS::get_singleton()->free(canvas);
+ VS::get_singleton()->free(viewport);
+}
diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h
index 35b5c3a5f0..140d9f849f 100644
--- a/editor/plugins/editor_preview_plugins.h
+++ b/editor/plugins/editor_preview_plugins.h
@@ -100,17 +100,13 @@ public:
EditorScriptPreviewPlugin();
};
-// FIXME: Needs to be rewritten for AudioStream in Godot 3.0+
-#if 0
-class EditorSamplePreviewPlugin : public EditorResourcePreviewGenerator {
+class EditorAudioStreamPreviewPlugin : public EditorResourcePreviewGenerator {
public:
+ virtual bool handles(const String &p_type) const;
+ virtual Ref<Texture> generate(const RES &p_from);
- virtual bool handles(const String& p_type) const;
- virtual Ref<Texture> generate(const RES& p_from);
-
- EditorSamplePreviewPlugin();
+ EditorAudioStreamPreviewPlugin();
};
-#endif
class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator {
@@ -140,4 +136,27 @@ public:
~EditorMeshPreviewPlugin();
};
+class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator {
+
+ GDCLASS(EditorFontPreviewPlugin, EditorResourcePreviewGenerator)
+
+ RID viewport;
+ RID viewport_texture;
+ RID canvas;
+ RID canvas_item;
+ volatile bool preview_done;
+
+ void _preview_done(const Variant &p_udata);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual bool handles(const String &p_type) const;
+ virtual Ref<Texture> generate(const RES &p_from);
+ virtual Ref<Texture> generate_from_path(const String &p_path);
+
+ EditorFontPreviewPlugin();
+ ~EditorFontPreviewPlugin();
+};
#endif // EDITORPREVIEWPLUGINS_H
diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp
index e89cb68935..442bd52ea7 100644
--- a/editor/plugins/gradient_editor_plugin.cpp
+++ b/editor/plugins/gradient_editor_plugin.cpp
@@ -33,77 +33,70 @@
#include "canvas_item_editor_plugin.h"
#include "spatial_editor_plugin.h"
-GradientEditorPlugin::GradientEditorPlugin(EditorNode *p_node) {
-
- editor = p_node;
- ramp_editor = memnew(GradientEdit);
-
- add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM, ramp_editor);
-
- ramp_editor->set_custom_minimum_size(Size2(100, 48));
- ramp_editor->hide();
- ramp_editor->connect("ramp_changed", this, "ramp_changed");
+Size2 GradientEditor::get_minimum_size() const {
+ return Size2(0, 60) * EDSCALE;
}
+void GradientEditor::_gradient_changed() {
-void GradientEditorPlugin::edit(Object *p_object) {
-
- Gradient *gradient = Object::cast_to<Gradient>(p_object);
- if (!gradient)
+ if (editing)
return;
- gradient_ref = Ref<Gradient>(gradient);
- ramp_editor->set_points(gradient_ref->get_points());
-}
-bool GradientEditorPlugin::handles(Object *p_object) const {
+ editing = true;
+ Vector<Gradient::Point> points = gradient->get_points();
+ set_points(points);
+ editing = false;
+}
- return p_object->is_class("Gradient");
+void GradientEditor::_ramp_changed() {
+
+ editing = true;
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action("Gradient Edited");
+ undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets());
+ undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors());
+ undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets());
+ undo_redo->add_undo_method(gradient.ptr(), "set_colors", gradient->get_colors());
+ undo_redo->commit_action();
+ editing = false;
}
-void GradientEditorPlugin::make_visible(bool p_visible) {
+void GradientEditor::_bind_methods() {
- if (p_visible) {
- ramp_editor->show();
- } else {
- ramp_editor->hide();
- }
+ ClassDB::bind_method("_gradient_changed", &GradientEditor::_gradient_changed);
+ ClassDB::bind_method("_ramp_changed", &GradientEditor::_ramp_changed);
}
-void GradientEditorPlugin::_ramp_changed() {
-
- if (gradient_ref.is_valid()) {
+void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) {
+ gradient = p_gradient;
+ connect("ramp_changed", this, "_ramp_changed");
+ gradient->connect("changed", this, "_gradient_changed");
+ set_points(gradient->get_points());
+}
- UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+GradientEditor::GradientEditor() {
+ editing = false;
+}
- //Not sure if I should convert this data to PoolVector
- Vector<float> new_offsets = ramp_editor->get_offsets();
- Vector<Color> new_colors = ramp_editor->get_colors();
- Vector<float> old_offsets = gradient_ref->get_offsets();
- Vector<Color> old_colors = gradient_ref->get_colors();
+///////////////////////
- if (old_offsets.size() != new_offsets.size())
- ur->create_action(TTR("Add/Remove Color Ramp Point"));
- else
- ur->create_action(TTR("Modify Color Ramp"), UndoRedo::MERGE_ENDS);
- ur->add_do_method(this, "undo_redo_gradient", new_offsets, new_colors);
- ur->add_undo_method(this, "undo_redo_gradient", old_offsets, old_colors);
- ur->commit_action();
+bool EditorInspectorPluginGradient::can_handle(Object *p_object) {
- //color_ramp_ref->set_points(ramp_editor->get_points());
- }
+ return Object::cast_to<Gradient>(p_object) != NULL;
}
-void GradientEditorPlugin::_undo_redo_gradient(const Vector<float> &offsets, const Vector<Color> &colors) {
+void EditorInspectorPluginGradient::parse_begin(Object *p_object) {
- gradient_ref->set_offsets(offsets);
- gradient_ref->set_colors(colors);
- ramp_editor->set_points(gradient_ref->get_points());
- ramp_editor->update();
-}
+ Gradient *gradient = Object::cast_to<Gradient>(p_object);
+ Ref<Gradient> g(gradient);
-GradientEditorPlugin::~GradientEditorPlugin() {
+ GradientEditor *editor = memnew(GradientEditor);
+ editor->set_gradient(g);
+ add_custom_control(editor);
}
-void GradientEditorPlugin::_bind_methods() {
- ClassDB::bind_method(D_METHOD("ramp_changed"), &GradientEditorPlugin::_ramp_changed);
- ClassDB::bind_method(D_METHOD("undo_redo_gradient", "offsets", "colors"), &GradientEditorPlugin::_undo_redo_gradient);
+GradientEditorPlugin::GradientEditorPlugin(EditorNode *p_node) {
+
+ Ref<EditorInspectorPluginGradient> plugin;
+ plugin.instance();
+ add_inspector_plugin(plugin);
}
diff --git a/editor/plugins/gradient_editor_plugin.h b/editor/plugins/gradient_editor_plugin.h
index 52f4c59575..0c878b168f 100644
--- a/editor/plugins/gradient_editor_plugin.h
+++ b/editor/plugins/gradient_editor_plugin.h
@@ -35,28 +35,39 @@
#include "editor/editor_plugin.h"
#include "scene/gui/gradient_edit.h"
-class GradientEditorPlugin : public EditorPlugin {
+class GradientEditor : public GradientEdit {
+ GDCLASS(GradientEditor, GradientEdit)
- GDCLASS(GradientEditorPlugin, EditorPlugin);
+ bool editing;
+ Ref<Gradient> gradient;
- Ref<Gradient> gradient_ref;
- GradientEdit *ramp_editor;
- EditorNode *editor;
+ void _gradient_changed();
+ void _ramp_changed();
protected:
static void _bind_methods();
- void _ramp_changed();
- void _undo_redo_gradient(const Vector<float> &offsets, const Vector<Color> &colors);
+
+public:
+ virtual Size2 get_minimum_size() const;
+ void set_gradient(const Ref<Gradient> &p_gradient);
+ GradientEditor();
+};
+
+class EditorInspectorPluginGradient : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorPluginGradient, EditorInspectorPlugin)
+public:
+ virtual bool can_handle(Object *p_object);
+ virtual void parse_begin(Object *p_object);
+};
+
+class GradientEditorPlugin : public EditorPlugin {
+
+ GDCLASS(GradientEditorPlugin, EditorPlugin);
public:
virtual String get_name() const { return "ColorRamp"; }
- 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);
GradientEditorPlugin(EditorNode *p_node);
- ~GradientEditorPlugin();
};
#endif /* TOOLS_EDITOR_PLUGINS_COLOR_RAMP_EDITOR_PLUGIN_H_ */
diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp
index 8b44f672b0..f75fb0d109 100644
--- a/editor/plugins/item_list_editor_plugin.cpp
+++ b/editor/plugins/item_list_editor_plugin.cpp
@@ -388,7 +388,7 @@ ItemListEditor::ItemListEditor() {
vbc->add_child(property_editor);
property_editor->set_v_size_flags(SIZE_EXPAND_FILL);
- tree = property_editor->get_scene_tree();
+ tree = property_editor->get_property_tree();
}
ItemListEditor::~ItemListEditor() {
diff --git a/editor/plugins/line_2d_editor_plugin.cpp b/editor/plugins/line_2d_editor_plugin.cpp
index 47d5a73078..ba6452c1d1 100644
--- a/editor/plugins/line_2d_editor_plugin.cpp
+++ b/editor/plugins/line_2d_editor_plugin.cpp
@@ -64,6 +64,7 @@ void Line2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, con
Line2DEditor::Line2DEditor(EditorNode *p_editor) :
AbstractPolygon2DEditor(p_editor) {
+ node = NULL;
}
Line2DEditorPlugin::Line2DEditorPlugin(EditorNode *p_node) :
diff --git a/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp
index d1edf1ae10..0332e15b0e 100644
--- a/editor/plugins/navigation_polygon_editor_plugin.cpp
+++ b/editor/plugins/navigation_polygon_editor_plugin.cpp
@@ -123,6 +123,7 @@ void NavigationPolygonEditor::_create_resource() {
NavigationPolygonEditor::NavigationPolygonEditor(EditorNode *p_editor) :
AbstractPolygon2DEditor(p_editor) {
+ node = NULL;
}
NavigationPolygonEditorPlugin::NavigationPolygonEditorPlugin(EditorNode *p_node) :
diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp
index 7728995a99..e0325702a8 100644
--- a/editor/plugins/particles_editor_plugin.cpp
+++ b/editor/plugins/particles_editor_plugin.cpp
@@ -31,130 +31,10 @@
#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) {
-
- if (p_node == node) {
- node = NULL;
- hide();
- }
-}
-
-void ParticlesEditor::_node_selected(const NodePath &p_path) {
-
- Node *sel = get_node(p_path);
- if (!sel)
- return;
-
- VisualInstance *vi = Object::cast_to<VisualInstance>(sel);
- if (!vi) {
-
- err_dialog->set_text(TTR("Node does not contain geometry."));
- err_dialog->popup_centered_minsize();
- return;
- }
-
- geometry = vi->get_faces(VisualInstance::FACES_SOLID);
-
- if (geometry.size() == 0) {
-
- err_dialog->set_text(TTR("Node does not contain geometry (faces)."));
- err_dialog->popup_centered_minsize();
- return;
- }
-
- Transform geom_xform = node->get_global_transform().affine_inverse() * vi->get_global_transform();
-
- int gc = geometry.size();
- PoolVector<Face3>::Write w = geometry.write();
-
- for (int i = 0; i < gc; i++) {
- for (int j = 0; j < 3; j++) {
- w[i].vertex[j] = geom_xform.xform(w[i].vertex[j]);
- }
- }
-
- w = PoolVector<Face3>::Write();
-
- emission_dialog->popup_centered(Size2(300, 130));
-}
-
-void ParticlesEditor::_notification(int p_notification) {
-
- if (p_notification == NOTIFICATION_ENTER_TREE) {
- options->set_icon(options->get_popup()->get_icon("Particles", "EditorIcons"));
- }
-}
-
-void ParticlesEditor::_menu_option(int p_option) {
-
- switch (p_option) {
-
- case MENU_OPTION_GENERATE_AABB: {
- generate_aabb->popup_centered_minsize();
- } break;
- case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH: {
-
- Ref<ParticlesMaterial> material = node->get_process_material();
- if (material.is_null()) {
- EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticlesMaterial' is required."));
- return;
- }
- emission_file_dialog->popup_centered_ratio();
-
- } break;
-
- case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: {
- Ref<ParticlesMaterial> material = node->get_process_material();
- if (material.is_null()) {
- EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticlesMaterial' is required."));
- return;
- }
-
- emission_tree_dialog->popup_centered_ratio();
-
- } break;
- }
-}
-
-void ParticlesEditor::_generate_aabb() {
-
- float time = generate_seconds->get_value();
-
- float running = 0.0;
-
- EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time));
-
- AABB rect;
- while (running < time) {
-
- uint64_t ticks = OS::get_singleton()->get_ticks_usec();
- ep.step("Generating...", int(running), true);
- OS::get_singleton()->delay_usec(1000);
-
- AABB capture = node->capture_aabb();
- if (rect == AABB())
- rect = capture;
- else
- rect.merge_with(capture);
-
- running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0;
- }
-
- node->set_visibility_aabb(rect);
-}
-
-void ParticlesEditor::edit(Particles *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) {
@@ -175,7 +55,7 @@ void ParticlesEditor::_generate_emission_points() {
err_dialog->set_text(TTR("Faces contain no area!"));
err_dialog->popup_centered_minsize();
- return;
+ return false;
}
int emissor_count = emission_amount->get_value();
@@ -185,9 +65,9 @@ void ParticlesEditor::_generate_emission_points() {
float areapos = Math::random(0.0f, area_accum);
Map<float, int>::Element *E = triangle_area_map.find_closest(areapos);
- ERR_FAIL_COND(!E)
+ ERR_FAIL_COND_V(!E, false)
int index = E->get();
- ERR_FAIL_INDEX(index, geometry.size());
+ ERR_FAIL_INDEX_V(index, geometry.size(), false);
// ok FINALLY get face
Face3 face = geometry[index];
@@ -195,15 +75,11 @@ void ParticlesEditor::_generate_emission_points() {
Vector3 pos = face.get_random_point_inside();
- points.push_back(pos.x);
- points.push_back(pos.y);
- points.push_back(pos.z);
+ points.push_back(pos);
if (use_normals) {
Vector3 normal = face.get_plane().normal;
- normals.push_back(normal.x);
- normals.push_back(normal.y);
- normals.push_back(normal.z);
+ normals.push_back(normal);
}
}
} else {
@@ -214,7 +90,7 @@ void ParticlesEditor::_generate_emission_points() {
err_dialog->set_text(TTR("No faces!"));
err_dialog->popup_centered_minsize();
- return;
+ return false;
}
PoolVector<Face3>::Read r = geometry.read();
@@ -276,15 +152,210 @@ void ParticlesEditor::_generate_emission_points() {
Vector3 point = ofs + dir * val;
- points.push_back(point.x);
- points.push_back(point.y);
- points.push_back(point.z);
+ points.push_back(point);
break;
}
}
}
- int point_count = points.size() / 3;
+ return true;
+}
+
+void ParticlesEditorBase::_node_selected(const NodePath &p_path) {
+
+ Node *sel = get_node(p_path);
+ if (!sel)
+ return;
+
+ VisualInstance *vi = Object::cast_to<VisualInstance>(sel);
+ if (!vi) {
+
+ err_dialog->set_text(TTR("Node does not contain geometry."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ geometry = vi->get_faces(VisualInstance::FACES_SOLID);
+
+ if (geometry.size() == 0) {
+
+ err_dialog->set_text(TTR("Node does not contain geometry (faces)."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ Transform geom_xform = base_node->get_global_transform().affine_inverse() * vi->get_global_transform();
+
+ int gc = geometry.size();
+ PoolVector<Face3>::Write w = geometry.write();
+
+ for (int i = 0; i < gc; i++) {
+ for (int j = 0; j < 3; j++) {
+ w[i].vertex[j] = geom_xform.xform(w[i].vertex[j]);
+ }
+ }
+
+ w = PoolVector<Face3>::Write();
+
+ 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) {
+ options->set_icon(options->get_popup()->get_icon("Particles", "EditorIcons"));
+ }
+}
+
+void ParticlesEditor::_menu_option(int p_option) {
+
+ switch (p_option) {
+
+ case MENU_OPTION_GENERATE_AABB: {
+ generate_aabb->popup_centered_minsize();
+ } break;
+ case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH: {
+
+ Ref<ParticlesMaterial> material = node->get_process_material();
+ if (material.is_null()) {
+ EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticlesMaterial' is required."));
+ return;
+ }
+ emission_file_dialog->popup_centered_ratio();
+
+ } break;
+
+ case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: {
+ Ref<ParticlesMaterial> material = node->get_process_material();
+ if (material.is_null()) {
+ EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticlesMaterial' is required."));
+ return;
+ }
+
+ 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);
+
+ 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;
+ }
+}
+
+void ParticlesEditor::_generate_aabb() {
+
+ float time = generate_seconds->get_value();
+
+ float running = 0.0;
+
+ EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time));
+
+ AABB rect;
+ while (running < time) {
+
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec();
+ ep.step("Generating...", int(running), true);
+ OS::get_singleton()->delay_usec(1000);
+
+ AABB capture = node->capture_aabb();
+ if (rect == AABB())
+ rect = capture;
+ else
+ rect.merge_with(capture);
+
+ running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0;
+ }
+
+ node->set_visibility_aabb(rect);
+}
+
+void ParticlesEditor::edit(Particles *p_particles) {
+
+ base_node = p_particles;
+ node = p_particles;
+}
+
+void ParticlesEditor::_generate_emission_points() {
+
+ /// hacer codigo aca
+ PoolVector<Vector3> points;
+ PoolVector<Vector3> normals;
+
+ if (!_generate(points, normals)) {
+ return;
+ }
+
+ int point_count = points.size();
int w = 2048;
int h = (point_count / 2048) + 1;
@@ -295,8 +366,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 +384,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 +396,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 +423,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 +439,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/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index f04e0a801c..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;
@@ -1035,6 +1036,7 @@ Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const {
Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
AbstractPolygon2DEditor(p_editor) {
+ node = NULL;
snap_step = Vector2(10, 10);
use_snap = false;
snap_show_grid = false;
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 fa674e1e34..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();
@@ -1182,12 +1187,13 @@ void ScriptEditor::_notification(int p_what) {
script_forward->set_icon(get_icon("Forward", "EditorIcons"));
script_back->set_icon(get_icon("Back", "EditorIcons"));
+ members_overview_alphabeta_sort_button->set_icon(get_icon("Sort", "EditorIcons"));
} break;
case NOTIFICATION_READY: {
get_tree()->connect("tree_changed", this, "_tree_changed");
- editor->connect("request_help", this, "_request_help");
+ editor->get_inspector_dock()->connect("request_help", this, "_request_help");
editor->connect("request_help_search", this, "_help_search");
editor->connect("request_help_index", this, "_help_index");
} break;
@@ -1214,6 +1220,9 @@ void ScriptEditor::_notification(int p_what) {
script_forward->set_icon(get_icon("Forward", "EditorIcons"));
script_back->set_icon(get_icon("Back", "EditorIcons"));
+ members_overview_alphabeta_sort_button->set_icon(get_icon("Sort", "EditorIcons"));
+ filename->add_style_override("normal", editor->get_gui_base()->get_stylebox("normal", "LineEdit"));
+
recent_scripts->set_as_minsize();
} break;
@@ -1403,17 +1412,28 @@ void ScriptEditor::_update_members_overview_visibility() {
ScriptEditorBase *se = _get_current_editor();
if (!se) {
+ members_overview_alphabeta_sort_button->set_visible(false);
members_overview->set_visible(false);
+ overview_vbox->set_visible(false);
return;
}
if (members_overview_enabled && se->show_members_overview()) {
+ members_overview_alphabeta_sort_button->set_visible(true);
members_overview->set_visible(true);
+ overview_vbox->set_visible(true);
} else {
+ members_overview_alphabeta_sort_button->set_visible(false);
members_overview->set_visible(false);
+ overview_vbox->set_visible(false);
}
}
+void ScriptEditor::_toggle_members_overview_alpha_sort(bool p_alphabetic_sort) {
+ EditorSettings::get_singleton()->set("text_editor/tools/sort_members_outline_alphabetically", p_alphabetic_sort);
+ _update_members_overview();
+}
+
void ScriptEditor::_update_members_overview() {
members_overview->clear();
@@ -1423,10 +1443,19 @@ void ScriptEditor::_update_members_overview() {
}
Vector<String> functions = se->get_functions();
+ if (EditorSettings::get_singleton()->get("text_editor/tools/sort_members_outline_alphabetically")) {
+ functions.sort();
+ }
+
for (int i = 0; i < functions.size(); i++) {
members_overview->add_item(functions[i].get_slice(":", 0));
members_overview->set_item_metadata(i, functions[i].get_slice(":", 1).to_int() - 1);
}
+
+ String path = se->get_edited_script()->get_path();
+ bool built_in = !path.is_resource_file();
+ String name = built_in ? path.get_file() : se->get_name();
+ filename->set_text(name);
}
void ScriptEditor::_update_help_overview_visibility() {
@@ -1445,9 +1474,13 @@ void ScriptEditor::_update_help_overview_visibility() {
}
if (help_overview_enabled) {
+ members_overview_alphabeta_sort_button->set_visible(false);
help_overview->set_visible(true);
+ overview_vbox->set_visible(true);
+ filename->set_text(se->get_name());
} else {
help_overview->set_visible(false);
+ overview_vbox->set_visible(false);
}
}
@@ -1536,9 +1569,10 @@ void ScriptEditor::_update_script_names() {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
if (se) {
- String name = se->get_name();
Ref<Texture> icon = se->get_icon();
String path = se->get_edited_script()->get_path();
+ bool built_in = !path.is_resource_file();
+ String name = built_in ? path.get_file() : se->get_name();
_ScriptEditorItemData sd;
sd.icon = icon;
@@ -1838,6 +1872,7 @@ void ScriptEditor::save_all_scripts() {
}
_update_script_names();
+ EditorFileSystem::get_singleton()->update_script_classes();
}
void ScriptEditor::apply_scripts() const {
@@ -2562,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);
@@ -2596,6 +2632,8 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_live_auto_reload_running_scripts", &ScriptEditor::_live_auto_reload_running_scripts);
ClassDB::bind_method("_unhandled_input", &ScriptEditor::_unhandled_input);
ClassDB::bind_method("_script_list_gui_input", &ScriptEditor::_script_list_gui_input);
+ ClassDB::bind_method("_toggle_members_overview_alpha_sort", &ScriptEditor::_toggle_members_overview_alpha_sort);
+ ClassDB::bind_method("_update_members_overview", &ScriptEditor::_update_members_overview);
ClassDB::bind_method("_script_changed", &ScriptEditor::_script_changed);
ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts);
ClassDB::bind_method("_on_find_in_files_requested", &ScriptEditor::_on_find_in_files_requested);
@@ -2656,14 +2694,39 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
add_child(context_menu);
context_menu->connect("id_pressed", this, "_menu_option");
+ overview_vbox = memnew(VBoxContainer);
+ overview_vbox->set_custom_minimum_size(Size2(0, 90));
+ overview_vbox->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ list_split->add_child(overview_vbox);
+ buttons_hbox = memnew(HBoxContainer);
+ overview_vbox->add_child(buttons_hbox);
+
+ filename = memnew(Label);
+ filename->set_clip_text(true);
+ filename->set_h_size_flags(SIZE_EXPAND_FILL);
+ filename->add_style_override("normal", EditorNode::get_singleton()->get_gui_base()->get_stylebox("normal", "LineEdit"));
+ buttons_hbox->add_child(filename);
+
+ members_overview_alphabeta_sort_button = memnew(ToolButton);
+ members_overview_alphabeta_sort_button->set_tooltip(TTR("Toggle alphabetical sorting of the method list."));
+ members_overview_alphabeta_sort_button->set_toggle_mode(true);
+ members_overview_alphabeta_sort_button->set_pressed(EditorSettings::get_singleton()->get("text_editor/tools/sort_members_outline_alphabetically"));
+ members_overview_alphabeta_sort_button->connect("toggled", this, "_toggle_members_overview_alpha_sort");
+
+ buttons_hbox->add_child(members_overview_alphabeta_sort_button);
+
members_overview = memnew(ItemList);
- list_split->add_child(members_overview);
+ overview_vbox->add_child(members_overview);
+
members_overview->set_allow_reselect(true);
members_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing
members_overview->set_v_size_flags(SIZE_EXPAND_FILL);
+ members_overview->set_allow_rmb_select(true);
+ members_overview->set_drag_forwarding(this);
help_overview = memnew(ItemList);
- list_split->add_child(help_overview);
+ overview_vbox->add_child(help_overview);
help_overview->set_allow_reselect(true);
help_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing
help_overview->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -2707,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);
@@ -2814,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 9f37b18d7d..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;
@@ -199,6 +204,10 @@ class ScriptEditor : public PanelContainer {
ItemList *script_list;
HSplitContainer *script_split;
ItemList *members_overview;
+ VBoxContainer *overview_vbox;
+ HBoxContainer *buttons_hbox;
+ Label *filename;
+ ToolButton *members_overview_alphabeta_sort_button;
bool members_overview_enabled;
ItemList *help_overview;
bool help_overview_enabled;
@@ -247,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;
@@ -318,6 +328,7 @@ class ScriptEditor : public PanelContainer {
void _update_members_overview_visibility();
void _update_members_overview();
+ void _toggle_members_overview_alpha_sort(bool p_alphabetic_sort);
void _update_script_names();
bool _sort_list_on_update;
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 45f5e667fa..ffc2203475 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -1289,16 +1289,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 +1619,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 +1677,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 +1687,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 +1714,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");
@@ -1727,7 +1739,7 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/select_all", TTR("Select All"), KEY_MASK_CMD | KEY_A);
ED_SHORTCUT("script_text_editor/move_up", TTR("Move Up"), KEY_MASK_ALT | KEY_UP);
ED_SHORTCUT("script_text_editor/move_down", TTR("Move Down"), KEY_MASK_ALT | KEY_DOWN);
- ED_SHORTCUT("script_text_editor/delete_line", TTR("Delete Line"), KEY_MASK_CTRL | KEY_MASK_SHIFT | KEY_K);
+ ED_SHORTCUT("script_text_editor/delete_line", TTR("Delete Line"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_K);
//leave these at zero, same can be accomplished with tab/shift-tab, including selection
//the next/previous in history shortcut in this case makes a lot more sene.
@@ -1740,28 +1752,36 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), 0);
#ifdef OSX_ENABLED
ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C);
- ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL | KEY_SPACE);
#else
ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B);
- ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE);
#endif
- ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CTRL | KEY_MASK_ALT | KEY_T);
- ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent To Spaces"), KEY_MASK_CTRL | KEY_MASK_SHIFT | KEY_Y);
- ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent To Tabs"), KEY_MASK_CTRL | KEY_MASK_SHIFT | KEY_X);
+ ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE);
+ ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T);
+ ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent To Spaces"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Y);
+ ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent To Tabs"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_X);
ED_SHORTCUT("script_text_editor/auto_indent", TTR("Auto Indent"), KEY_MASK_CMD | KEY_I);
+#ifdef OSX_ENABLED
+ ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B);
+#else
ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9);
- ED_SHORTCUT("script_text_editor/remove_all_breakpoints", TTR("Remove All Breakpoints"), KEY_MASK_CTRL | KEY_MASK_SHIFT | KEY_F9);
- ED_SHORTCUT("script_text_editor/goto_next_breakpoint", TTR("Goto Next Breakpoint"), KEY_MASK_CTRL | KEY_PERIOD);
- ED_SHORTCUT("script_text_editor/goto_previous_breakpoint", TTR("Goto Previous Breakpoint"), KEY_MASK_CTRL | KEY_COMMA);
+#endif
+ ED_SHORTCUT("script_text_editor/remove_all_breakpoints", TTR("Remove All Breakpoints"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F9);
+ ED_SHORTCUT("script_text_editor/goto_next_breakpoint", TTR("Goto Next Breakpoint"), KEY_MASK_CMD | KEY_PERIOD);
+ ED_SHORTCUT("script_text_editor/goto_previous_breakpoint", TTR("Goto Previous Breakpoint"), KEY_MASK_CMD | KEY_COMMA);
ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Convert To Uppercase"), KEY_MASK_SHIFT | KEY_F4);
ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Convert To Lowercase"), KEY_MASK_SHIFT | KEY_F3);
ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KEY_MASK_SHIFT | KEY_F2);
ED_SHORTCUT("script_text_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F);
+#ifdef OSX_ENABLED
+ ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_MASK_CMD | KEY_G);
+ ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G);
+#else
ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_F3);
ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3);
+#endif
ED_SHORTCUT("script_text_editor/replace", TTR("Replace..."), KEY_MASK_CMD | KEY_R);
ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F);
@@ -1769,7 +1789,11 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F);
ED_SHORTCUT("script_text_editor/goto_line", TTR("Goto Line..."), KEY_MASK_CMD | KEY_L);
+#ifdef OSX_ENABLED
+ ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_MASK_SHIFT | KEY_SPACE);
+#else
ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_SHIFT | KEY_F1);
+#endif
ScriptEditor::register_create_script_editor_function(create_editor);
}
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/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp
index e372f792d6..08bfebefbd 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_2d_editor_plugin.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* skeleton_2d_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 "skeleton_2d_editor_plugin.h"
#include "canvas_item_editor_plugin.h"
diff --git a/editor/plugins/skeleton_2d_editor_plugin.h b/editor/plugins/skeleton_2d_editor_plugin.h
index bbe2a3a6f2..26ab4328b0 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.h
+++ b/editor/plugins/skeleton_2d_editor_plugin.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* skeleton_2d_editor_plugin.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 SKELETON_2D_EDITOR_PLUGIN_H
#define SKELETON_2D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index 5b713ef3c4..37b8562e96 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -32,7 +32,7 @@
#include "camera_matrix.h"
#include "core/os/input.h"
-#include "editor/animation_editor.h"
+
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/plugins/animation_player_editor_plugin.h"
@@ -217,7 +217,7 @@ bool SpatialEditorGizmo::intersect_frustum(const Camera *p_camera, const Vector<
return false;
}
-bool SpatialEditorGizmo::intersect_ray(const Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle, bool p_sec_first) {
+bool SpatialEditorGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle, bool p_sec_first) {
return false;
}
@@ -320,24 +320,20 @@ void SpatialEditorViewport::_select_clicked(bool p_append, bool p_single) {
void SpatialEditorViewport::_select(Spatial *p_node, bool p_append, bool p_single) {
if (!p_append) {
+ editor_selection->clear();
+ }
- // should not modify the selection..
+ if (editor_selection->is_selected(p_node)) {
+ //erase
+ editor_selection->remove_node(p_node);
+ } else {
- editor_selection->clear();
editor_selection->add_node(p_node);
+ }
+ if (p_single) {
if (Engine::get_singleton()->is_editor_hint())
editor->call("edit_node", p_node);
-
- } else {
-
- if (editor_selection->is_selected(p_node) && p_single) {
- //erase
- editor_selection->remove_node(p_node);
- } else {
-
- editor_selection->add_node(p_node);
- }
}
}
@@ -376,7 +372,7 @@ ObjectID SpatialEditorViewport::_select_ray(const Point2 &p_pos, bool p_append,
Vector3 normal;
int handle = -1;
- bool inters = seg->intersect_ray(camera, p_pos, point, normal, NULL, p_alt_select);
+ bool inters = seg->intersect_ray(camera, p_pos, point, normal, &handle, p_alt_select);
if (!inters)
continue;
@@ -475,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(), 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);
@@ -485,7 +485,7 @@ Vector3 SpatialEditorViewport::_get_screen_to_space(const Vector3 &p_vector3) {
camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot);
camera_transform.translate(0, 0, cursor.distance);
- return camera_transform.xform(Vector3(((p_vector3.x / get_size().width) * 2.0 - 1.0) * screen_w, ((1.0 - (p_vector3.y / get_size().height)) * 2.0 - 1.0) * screen_h, -get_znear()));
+ return camera_transform.xform(Vector3(((p_vector3.x / get_size().width) * 2.0 - 1.0) * screen_w, ((1.0 - (p_vector3.y / get_size().height)) * 2.0 - 1.0) * screen_h, -(get_znear() + p_vector3.z)));
}
void SpatialEditorViewport::_select_region() {
@@ -493,23 +493,25 @@ void SpatialEditorViewport::_select_region() {
if (cursor.region_begin == cursor.region_end)
return; //nothing really
+ float z_offset = MAX(0.0, 5.0 - get_znear());
+
Vector3 box[4] = {
Vector3(
MIN(cursor.region_begin.x, cursor.region_end.x),
MIN(cursor.region_begin.y, cursor.region_end.y),
- 0),
+ z_offset),
Vector3(
MAX(cursor.region_begin.x, cursor.region_end.x),
MIN(cursor.region_begin.y, cursor.region_end.y),
- 0),
+ z_offset),
Vector3(
MAX(cursor.region_begin.x, cursor.region_end.x),
MAX(cursor.region_begin.y, cursor.region_end.y),
- 0),
+ z_offset),
Vector3(
MIN(cursor.region_begin.x, cursor.region_end.x),
MAX(cursor.region_begin.y, cursor.region_end.y),
- 0)
+ z_offset)
};
Vector<Plane> frustum;
@@ -520,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 += 500.0;
+ 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;
@@ -544,19 +552,26 @@ void SpatialEditorViewport::_select_region() {
if (!sp)
continue;
+ Spatial *root_sp = sp;
+ while (root_sp && root_sp != edited_scene && root_sp->get_owner() != edited_scene && !edited_scene->is_editable_instance(root_sp->get_owner())) {
+ root_sp = Object::cast_to<Spatial>(root_sp->get_owner());
+ }
+
+ if (selected.find(root_sp) != -1) continue;
+
Ref<SpatialEditorGizmo> seg = sp->get_gizmo();
if (!seg.is_valid())
continue;
- Spatial *root_sp = sp;
- while (root_sp && root_sp != edited_scene && root_sp->get_owner() != edited_scene && !edited_scene->is_editable_instance(root_sp->get_owner())) {
- root_sp = Object::cast_to<Spatial>(root_sp->get_owner());
+ if (seg->intersect_frustum(camera, frustum)) {
+ selected.push_back(root_sp);
}
+ }
- if (selected.find(root_sp) == -1)
- if (seg->intersect_frustum(camera, frustum))
- _select(root_sp, true, false);
+ bool single = selected.size() == 1;
+ for (int i = 0; i < selected.size(); i++) {
+ _select(selected[i], true, single);
}
}
@@ -1170,6 +1185,9 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (cursor.region_select) {
+
+ if (!clicked_wants_append) _clear_selected();
+
_select_region();
cursor.region_select = false;
surface->update();
@@ -1279,7 +1297,6 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (cursor.region_select && nav_mode == NAVIGATION_NONE) {
-
cursor.region_end = m->get_position();
surface->update();
return;
@@ -1829,7 +1846,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (!get_selected_count() || _edit.mode != TRANSFORM_NONE)
return;
- if (!AnimationPlayerEditor::singleton->get_key_editor()->has_keying()) {
+ if (!AnimationPlayerEditor::singleton->get_track_editor()->has_keying()) {
set_message(TTR("Keying is disabled (no key inserted)."));
return;
}
@@ -2153,10 +2170,7 @@ void SpatialEditorViewport::_notification(int p_what) {
VisualInstance *vi = Object::cast_to<VisualInstance>(sp);
- if (se->aabb.has_no_surface()) {
-
- se->aabb = vi ? vi->get_aabb() : AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
- }
+ se->aabb = vi ? vi->get_aabb() : AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
Transform t = sp->get_global_gizmo_transform();
t.translate(se->aabb.position);
diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
index 7736db67b1..637926a913 100644
--- a/editor/plugins/spatial_editor_plugin.h
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -62,7 +62,7 @@ public:
virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
virtual bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum);
- virtual bool intersect_ray(const Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false);
+ 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);
SpatialEditorGizmo();
};
diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp
index 49816fe2ae..66673cca00 100644
--- a/editor/plugins/sprite_editor_plugin.cpp
+++ b/editor/plugins/sprite_editor_plugin.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* sprite_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 "sprite_editor_plugin.h"
#include "canvas_item_editor_plugin.h"
diff --git a/editor/plugins/sprite_editor_plugin.h b/editor/plugins/sprite_editor_plugin.h
index 17aa3eb1f9..238227e4a0 100644
--- a/editor/plugins/sprite_editor_plugin.h
+++ b/editor/plugins/sprite_editor_plugin.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* sprite_editor_plugin.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 SPRITE_EDITOR_PLUGIN_H
#define SPRITE_EDITOR_PLUGIN_H
diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp
index f4faab69ed..f6d98cb4c7 100644
--- a/editor/plugins/style_box_editor_plugin.cpp
+++ b/editor/plugins/style_box_editor_plugin.cpp
@@ -30,7 +30,26 @@
#include "style_box_editor_plugin.h"
-void StyleBoxEditor::edit(const Ref<StyleBox> &p_stylebox) {
+bool EditorInspectorPluginStyleBox::can_handle(Object *p_object) {
+
+ return Object::cast_to<StyleBox>(p_object) != NULL;
+}
+
+void EditorInspectorPluginStyleBox::parse_begin(Object *p_object) {
+
+ Ref<StyleBox> sb = Ref<StyleBox>(Object::cast_to<StyleBox>(p_object));
+
+ StyleBoxPreview *preview = memnew(StyleBoxPreview);
+ preview->edit(sb);
+ add_custom_control(preview);
+}
+bool EditorInspectorPluginStyleBox::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) {
+ return false; //do not want
+}
+void EditorInspectorPluginStyleBox::parse_end() {
+}
+
+void StyleBoxPreview::edit(const Ref<StyleBox> &p_stylebox) {
if (stylebox.is_valid())
stylebox->disconnect("changed", this, "_sb_changed");
@@ -39,71 +58,33 @@ void StyleBoxEditor::edit(const Ref<StyleBox> &p_stylebox) {
preview->add_style_override("panel", stylebox);
stylebox->connect("changed", this, "_sb_changed");
}
+ _sb_changed();
}
-void StyleBoxEditor::_sb_changed() {
+void StyleBoxPreview::_sb_changed() {
preview->update();
+ if (stylebox.is_valid()) {
+ Size2 ms = stylebox->get_minimum_size() * 4 / 3;
+ ms.height = MAX(ms.height, 150 * EDSCALE);
+ preview->set_custom_minimum_size(ms);
+ }
}
-void StyleBoxEditor::_bind_methods() {
+void StyleBoxPreview::_bind_methods() {
- ClassDB::bind_method("_sb_changed", &StyleBoxEditor::_sb_changed);
- //ClassDB::bind_method("_import",&StyleBoxEditor::_import);
- //ClassDB::bind_method("_import_accept",&StyleBoxEditor::_import_accept);
- //ClassDB::bind_method("_preview_text_changed",&StyleBoxEditor::_preview_text_changed);
+ ClassDB::bind_method("_sb_changed", &StyleBoxPreview::_sb_changed);
}
-StyleBoxEditor::StyleBoxEditor() {
-
- panel = memnew(Panel);
- add_child(panel);
- panel->set_anchors_and_margins_preset(Control::PRESET_WIDE);
-
- Label *l = memnew(Label);
- l->set_text(TTR("StyleBox Preview:"));
- l->set_position(Point2(5, 5));
- panel->add_child(l);
+StyleBoxPreview::StyleBoxPreview() {
preview = memnew(Panel);
- panel->add_child(preview);
- preview->set_position(Point2(50, 50));
- preview->set_size(Size2(200, 100));
-}
-
-void StyleBoxEditorPlugin::edit(Object *p_node) {
-
- if (Object::cast_to<StyleBox>(p_node)) {
- stylebox_editor->edit(Object::cast_to<StyleBox>(p_node));
- stylebox_editor->show();
- } else
- stylebox_editor->hide();
-}
-
-bool StyleBoxEditorPlugin::handles(Object *p_node) const {
-
- return p_node->is_class("StyleBox");
-}
-
-void StyleBoxEditorPlugin::make_visible(bool p_visible) {
-
- if (p_visible) {
- button->show();
- EditorNode::get_singleton()->make_bottom_panel_item_visible(stylebox_editor);
-
- } else {
- if (stylebox_editor->is_visible_in_tree())
- EditorNode::get_singleton()->hide_bottom_panel();
- button->hide();
- }
+ add_margin_child(TTR("Preview:"), preview);
}
StyleBoxEditorPlugin::StyleBoxEditorPlugin(EditorNode *p_node) {
- stylebox_editor = memnew(StyleBoxEditor);
- stylebox_editor->set_custom_minimum_size(Size2(0, 250));
-
- //p_node->get_viewport()->add_child(stylebox_editor);
- button = p_node->add_bottom_panel_item(TTR("StyleBox"), stylebox_editor);
- button->hide();
+ Ref<EditorInspectorPluginStyleBox> inspector_plugin;
+ inspector_plugin.instance();
+ add_inspector_plugin(inspector_plugin);
}
diff --git a/editor/plugins/style_box_editor_plugin.h b/editor/plugins/style_box_editor_plugin.h
index 34d8caaeb6..6b0d7e57a8 100644
--- a/editor/plugins/style_box_editor_plugin.h
+++ b/editor/plugins/style_box_editor_plugin.h
@@ -31,18 +31,17 @@
#ifndef STYLE_BOX_EDITOR_PLUGIN_H
#define STYLE_BOX_EDITOR_PLUGIN_H
+#include "editor/editor_inspector.h"
#include "editor/editor_node.h"
#include "scene/gui/option_button.h"
#include "scene/gui/texture_rect.h"
#include "scene/resources/style_box.h"
-class StyleBoxEditor : public Control {
+class StyleBoxPreview : public VBoxContainer {
- GDCLASS(StyleBoxEditor, Control);
+ GDCLASS(StyleBoxPreview, VBoxContainer);
- Panel *panel;
Panel *preview;
-
Ref<StyleBox> stylebox;
void _sb_changed();
@@ -53,23 +52,24 @@ protected:
public:
void edit(const Ref<StyleBox> &p_stylebox);
- StyleBoxEditor();
+ StyleBoxPreview();
+};
+
+class EditorInspectorPluginStyleBox : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorPluginStyleBox, 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 StyleBoxEditorPlugin : public EditorPlugin {
GDCLASS(StyleBoxEditorPlugin, EditorPlugin);
- StyleBoxEditor *stylebox_editor;
- EditorNode *editor;
- Button *button;
-
public:
virtual String get_name() const { return "StyleBox"; }
- bool has_main_screen() const { return false; }
- virtual void edit(Object *p_node);
- virtual bool handles(Object *p_node) const;
- virtual void make_visible(bool p_visible);
StyleBoxEditorPlugin(EditorNode *p_node);
};
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index 5ba3931689..e4fdd1f251 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -601,6 +601,17 @@ void TextureRegionEditor::apply_rect(const Rect2 &rect) {
void TextureRegionEditor::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_PROCESS: {
+ if (node_sprite) {
+ if (node_sprite->is_region()) {
+
+ set_process(false);
+ EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+ }
+ } else {
+ set_process(false);
+ }
+ } break;
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_READY: {
zoom_out->set_icon(get_icon("ZoomLess", "EditorIcons"));
@@ -640,6 +651,23 @@ void TextureRegionEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_zoom_out"), &TextureRegionEditor::_zoom_out);
}
+bool TextureRegionEditor::is_stylebox() {
+ return obj_styleBox.is_valid();
+}
+
+bool TextureRegionEditor::is_atlas_texture() {
+
+ return atlas_tex.is_valid();
+}
+
+bool TextureRegionEditor::is_ninepatch() {
+ return node_ninepatch != NULL;
+}
+
+Sprite *TextureRegionEditor::get_sprite() {
+ return node_sprite;
+}
+
void TextureRegionEditor::edit(Object *p_obj) {
if (node_sprite)
node_sprite->remove_change_receptor(this);
@@ -670,6 +698,12 @@ void TextureRegionEditor::edit(Object *p_obj) {
tile_set = Ref<TileSet>(NULL);
}
edit_draw->update();
+ if (node_sprite && !node_sprite->is_region()) {
+ set_process(true);
+ }
+ if (!p_obj) {
+ set_process(false);
+ }
}
void TextureRegionEditor::_changed_callback(Object *p_changed, const char *p_prop) {
@@ -932,8 +966,12 @@ bool TextureRegionEditorPlugin::handles(Object *p_object) const {
void TextureRegionEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
texture_region_button->show();
- if (texture_region_button->is_pressed())
- region_editor->show();
+ if (region_editor->is_stylebox() || region_editor->is_atlas_texture() || region_editor->is_ninepatch() || (region_editor->get_sprite() && region_editor->get_sprite()->is_region())) {
+ editor->make_bottom_panel_item_visible(region_editor);
+ } else {
+ if (texture_region_button->is_pressed())
+ region_editor->show();
+ }
} else {
texture_region_button->hide();
region_editor->edit(NULL);
@@ -989,10 +1027,10 @@ TextureRegionEditorPlugin::TextureRegionEditorPlugin(EditorNode *p_node) {
editor = p_node;
region_editor = memnew(TextureRegionEditor(p_node));
- texture_region_button = p_node->add_bottom_panel_item(TTR("Texture Region"), region_editor);
+ texture_region_button = p_node->add_bottom_panel_item(TTR("TextureRegion"), region_editor);
texture_region_button->set_tooltip(TTR("Texture Region Editor"));
- region_editor->set_custom_minimum_size(Size2(0, 200));
+ region_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
region_editor->hide();
texture_region_button->hide();
}
diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h
index 1244953a3f..eeba1987a6 100644
--- a/editor/plugins/texture_region_editor_plugin.h
+++ b/editor/plugins/texture_region_editor_plugin.h
@@ -131,6 +131,10 @@ public:
void _region_draw();
void _region_input(const Ref<InputEvent> &p_input);
void _scroll_changed(float);
+ bool is_stylebox();
+ bool is_atlas_texture();
+ bool is_ninepatch();
+ Sprite *get_sprite();
void edit(Object *p_obj);
TextureRegionEditor(EditorNode *p_editor);
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index 72b3af5a09..19646f37b5 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -35,6 +35,7 @@
#include "editor/editor_settings.h"
#include "os/input.h"
#include "os/keyboard.h"
+#include "scene/gui/split_container.h"
void TileMapEditor::_notification(int p_what) {
@@ -132,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();
@@ -168,6 +167,13 @@ void TileMapEditor::_menu_option(int p_option) {
}
}
+void TileMapEditor::_palette_selected(int index) {
+
+ if (manual_autotile) {
+ _update_palette();
+ }
+}
+
void TileMapEditor::_canvas_mouse_enter() {
mouse_over = true;
@@ -180,41 +186,129 @@ 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;
- int item = palette->get_current();
+ 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;
- if (item == -1)
- return TileMap::INVALID_CELL;
+ 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;
- return palette->get_item_metadata(item);
+ undo_redo->add_undo_method(node, "set_celld", p_vec, cell_old);
+ undo_redo->add_do_method(node, "set_celld", p_vec, cell_new);
}
-void TileMapEditor::set_selected_tile(int p_tile) {
+void TileMapEditor::_start_undo(const String &p_action) {
- int idx = palette->find_metadata(p_tile);
+ undo_data.clear();
+ undo_redo->create_action(p_action);
+}
+
+void TileMapEditor::_finish_undo() {
- if (idx >= 0) {
- palette->select(idx, true);
- palette->ensure_current_is_visible();
+ 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()));
+ }
+
+ 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);
bool prev_flip_v = node->is_cell_y_flipped(p_pos.x, p_pos.y);
bool prev_transpose = node->is_cell_transposed(p_pos.x, p_pos.y);
+ Vector2 prev_position = node->get_cell_autotile_coord(p_pos.x, p_pos.y);
- if (p_value == prev_val && p_flip_h == prev_flip_h && p_flip_v == prev_flip_v && p_transpose == prev_transpose)
+ Vector2 position;
+ int current = manual_palette->get_current();
+ if (current != -1) {
+ position = manual_palette->get_item_metadata(current);
+ } else {
+ // if there is no manual tile selected, that either means that
+ // autotiling is enabled, or the given tile is not autotiling. Either
+ // way, the coordinate of the tile does not matter, so assigning it to
+ // the coordinate of the existing tile works fine.
+ position = prev_position;
+ }
+
+ 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);
- node->update_bitmask_area(Point2(p_pos));
+ if (manual_autotile) {
+ if (current != -1) {
+ node->set_cell_autotile_coord(p_pos.x, p_pos.y, position);
+ }
+ } else {
+ // manually placing tiles should not update bitmasks
+ node->update_bitmask_area(Point2(p_pos));
+ }
+}
+
+void TileMapEditor::_manual_toggled(bool p_enabled) {
+ manual_autotile = p_enabled;
+ _update_palette();
}
void TileMapEditor::_text_entered(const String &p_text) {
@@ -259,8 +353,10 @@ 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();
Ref<TileSet> tileset = node->get_tileset();
if (tileset.is_null())
@@ -268,7 +364,6 @@ void TileMapEditor::_update_palette() {
List<int> tiles;
tileset->get_tile_list(&tiles);
-
if (tiles.empty())
return;
@@ -284,6 +379,9 @@ void TileMapEditor::_update_palette() {
palette->set_fixed_icon_size(Size2(min_size, min_size));
palette->set_fixed_column_width(min_size * MAX(size_slider->get_value(), 1));
+ palette->set_same_column_width(true);
+ manual_palette->set_fixed_icon_size(Size2(min_size, min_size));
+ manual_palette->set_same_column_width(true);
String filter = search_box->get_text().strip_edges();
@@ -344,12 +442,54 @@ void TileMapEditor::_update_palette() {
palette->set_item_metadata(palette->get_item_count() - 1, entries[i].id);
}
- palette->set_same_column_width(true);
-
- 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(sel_tile) == TileSet::AUTO_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()) {
+ entries.push_back(E->key());
+ }
+ entries.sort();
+
+ Ref<Texture> tex = tileset->tile_get_texture(sel_tile);
+
+ for (int i = 0; i < entries.size(); i++) {
+
+ manual_palette->add_item(String());
+
+ if (tex.is_valid()) {
+
+ 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())
+ manual_palette->set_item_icon_region(manual_palette->get_item_count() - 1, region);
+
+ manual_palette->set_item_icon(manual_palette->get_item_count() - 1, tex);
+ }
+
+ manual_palette->set_item_metadata(manual_palette->get_item_count() - 1, entries[i]);
+ }
+ }
+
+ if (manual_palette->get_item_count() > 0) {
+ // Only show the manual palette if at least tile exists in it
+ int selected = manual_palette->get_current();
+ if (selected == -1) selected = 0;
+ manual_palette->set_current(selected);
+ manual_palette->show();
+ }
}
void TileMapEditor::_pick_tile(const Point2 &p_pos) {
@@ -365,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));
@@ -378,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();
@@ -479,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();
@@ -498,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);
}
}
@@ -533,9 +679,17 @@ void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h
Rect2 r = node->get_tileset()->tile_get_region(p_cell);
if (node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::AUTO_TILE) {
+ Vector2 offset;
+ int selected = manual_palette->get_current();
+ if (manual_autotile && selected != -1) {
+ offset = manual_palette->get_item_metadata(selected);
+ } else {
+ offset = node->get_tileset()->autotile_get_icon_coordinate(p_cell);
+ }
+
int spacing = node->get_tileset()->autotile_get_spacing(p_cell);
r.size = node->get_tileset()->autotile_get_size(p_cell);
- r.position += (r.size + Vector2(spacing, spacing)) * node->get_tileset()->autotile_get_icon_coordinate(p_cell);
+ r.position += (r.size + Vector2(spacing, spacing)) * offset;
}
Size2 sc = p_xform.get_scale();
@@ -754,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) {
@@ -780,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();
@@ -811,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();
@@ -847,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;
@@ -880,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
@@ -942,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
@@ -961,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;
@@ -970,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();
@@ -983,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);
@@ -1032,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) {
@@ -1042,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;
@@ -1058,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;
@@ -1073,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);
@@ -1095,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();
@@ -1105,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) {
@@ -1113,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);
}
}
@@ -1125,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);
}
}
}
@@ -1382,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) {
@@ -1440,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);
}
}
}
@@ -1503,12 +1658,14 @@ void TileMapEditor::_tileset_settings_changed() {
void TileMapEditor::_icon_size_changed(float p_value) {
if (node) {
palette->set_icon_scale(p_value);
+ manual_palette->set_icon_scale(p_value);
_update_palette();
}
}
void TileMapEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_manual_toggled"), &TileMapEditor::_manual_toggled);
ClassDB::bind_method(D_METHOD("_text_entered"), &TileMapEditor::_text_entered);
ClassDB::bind_method(D_METHOD("_text_changed"), &TileMapEditor::_text_changed);
ClassDB::bind_method(D_METHOD("_sbox_input"), &TileMapEditor::_sbox_input);
@@ -1517,6 +1674,7 @@ void TileMapEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_canvas_mouse_exit"), &TileMapEditor::_canvas_mouse_exit);
ClassDB::bind_method(D_METHOD("_tileset_settings_changed"), &TileMapEditor::_tileset_settings_changed);
ClassDB::bind_method(D_METHOD("_update_transform_buttons"), &TileMapEditor::_update_transform_buttons);
+ ClassDB::bind_method(D_METHOD("_palette_selected"), &TileMapEditor::_palette_selected);
ClassDB::bind_method(D_METHOD("_fill_points"), &TileMapEditor::_fill_points);
ClassDB::bind_method(D_METHOD("_erase_points"), &TileMapEditor::_erase_points);
@@ -1534,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;
}
@@ -1574,6 +1733,8 @@ void TileMapEditor::_update_transform_buttons(Object *p_button) {
TileMapEditor::TileMapEditor(EditorNode *p_editor) {
node = NULL;
+ manual_autotile = false;
+ manual_position = Vector2(0, 0);
canvas_item_editor = NULL;
editor = p_editor;
undo_redo = editor->get_undo_redo();
@@ -1590,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);
@@ -1601,6 +1765,11 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
HBoxContainer *tool_hb2 = memnew(HBoxContainer);
add_child(tool_hb2);
+ manual_button = memnew(CheckBox);
+ manual_button->set_text("Disable Autotile");
+ manual_button->connect("toggled", this, "_manual_toggled");
+ add_child(manual_button);
+
search_box = memnew(LineEdit);
search_box->set_h_size_flags(SIZE_EXPAND_FILL);
search_box->connect("text_entered", this, "_text_entered");
@@ -1619,14 +1788,31 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
int mw = EDITOR_DEF("editors/tile_map/palette_min_width", 80);
+ VSplitContainer *palette_container = memnew(VSplitContainer);
+ palette_container->set_v_size_flags(SIZE_EXPAND_FILL);
+ palette_container->set_custom_minimum_size(Size2(mw, 0));
+ add_child(palette_container);
+
// Add tile palette
palette = memnew(ItemList);
+ palette->set_h_size_flags(SIZE_EXPAND_FILL);
palette->set_v_size_flags(SIZE_EXPAND_FILL);
- palette->set_custom_minimum_size(Size2(mw, 0));
palette->set_max_columns(0);
palette->set_icon_mode(ItemList::ICON_MODE_TOP);
palette->set_max_text_lines(2);
- add_child(palette);
+ palette->set_select_mode(ItemList::SELECT_MULTI);
+ palette->connect("item_selected", this, "_palette_selected");
+ palette_container->add_child(palette);
+
+ // Add autotile override palette
+ manual_palette = memnew(ItemList);
+ manual_palette->set_h_size_flags(SIZE_EXPAND_FILL);
+ manual_palette->set_v_size_flags(SIZE_EXPAND_FILL);
+ manual_palette->set_max_columns(0);
+ manual_palette->set_icon_mode(ItemList::ICON_MODE_TOP);
+ manual_palette->set_max_text_lines(2);
+ manual_palette->hide();
+ palette_container->add_child(manual_palette);
// Add menu items
toolbar = memnew(HBoxContainer);
diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h
index 642870aec0..b8443ca962 100644
--- a/editor/plugins/tile_map_editor_plugin.h
+++ b/editor/plugins/tile_map_editor_plugin.h
@@ -35,6 +35,7 @@
#include "editor/editor_plugin.h"
#include "scene/2d/tile_map.h"
+#include "scene/gui/check_box.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/menu_button.h"
@@ -77,6 +78,8 @@ class TileMapEditor : public VBoxContainer {
};
TileMap *node;
+ bool manual_autotile;
+ Vector2 manual_position;
EditorNode *editor;
UndoRedo *undo_redo;
@@ -85,6 +88,7 @@ class TileMapEditor : public VBoxContainer {
LineEdit *search_box;
HSlider *size_slider;
ItemList *palette;
+ ItemList *manual_palette;
HBoxContainer *toolbar;
@@ -97,6 +101,7 @@ class TileMapEditor : public VBoxContainer {
ToolButton *rotate_90;
ToolButton *rotate_180;
ToolButton *rotate_270;
+ CheckBox *manual_button;
Tool tool;
@@ -124,6 +129,7 @@ class TileMapEditor : public VBoxContainer {
bool xf;
bool yf;
bool tr;
+ Vector2 ac;
CellOp() :
idx(TileMap::INVALID_CELL),
@@ -150,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);
@@ -165,16 +174,21 @@ 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);
void _text_changed(const String &p_text);
void _sbox_input(const Ref<InputEvent> &p_ie);
void _update_palette();
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/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
index 385fa24ad8..087c4293f1 100644
--- a/editor/plugins/tile_set_editor_plugin.cpp
+++ b/editor/plugins/tile_set_editor_plugin.cpp
@@ -123,10 +123,10 @@ void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) {
for (List<uint32_t>::Element *E = shapes.front(); E; E = E->next()) {
if (sb->is_shape_owner_disabled(E->get())) continue;
- Transform2D shape_transform = sb->shape_owner_get_transform(E->get());
+ Transform2D shape_transform = sb->get_transform() * sb->shape_owner_get_transform(E->get());
bool one_way = sb->is_shape_owner_one_way_collision_enabled(E->get());
- shape_transform.set_origin(shape_transform.get_origin() - phys_offset);
+ shape_transform[2] -= phys_offset;
for (int k = 0; k < sb->shape_owner_get_shape_count(E->get()); k++) {
@@ -667,7 +667,7 @@ void TileSetEditor::_on_workspace_draw() {
if (mask & TileSet::BIND_BOTTOMRIGHT) {
workspace->draw_rect(Rect2(anchor + size / 2, size / 2), c);
}
- } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
+ } else {
if (mask & TileSet::BIND_TOPLEFT) {
workspace->draw_rect(Rect2(anchor, size / 3), c);
}
@@ -821,7 +821,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
bit = TileSet::BIND_BOTTOMRIGHT;
}
}
- } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
+ } else {
if (pos.x < size.x / 3) {
if (pos.y < size.y / 3) {
bit = TileSet::BIND_TOPLEFT;
@@ -884,7 +884,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
bit = TileSet::BIND_BOTTOMRIGHT;
}
}
- } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
+ } else {
if (pos.x < size.x / 3) {
if (pos.y < size.y / 3) {
bit = TileSet::BIND_TOPLEFT;
@@ -1849,7 +1849,7 @@ void TileSetEditorHelper::_get_property_list(List<PropertyInfo> *p_list) const {
if (selected_tile < 0 || tileset.is_null())
return;
- p_list->push_back(PropertyInfo(Variant::INT, "bitmask_mode", PROPERTY_HINT_ENUM, "2x2,3x3"));
+ p_list->push_back(PropertyInfo(Variant::INT, "bitmask_mode", PROPERTY_HINT_ENUM, "2x2,3x3 (minimal),3x3"));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "layout/tile_size"));
p_list->push_back(PropertyInfo(Variant::INT, "layout/spacing", PROPERTY_HINT_RANGE, "0,256,1"));
}
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
new file mode 100644
index 0000000000..682ca744ff
--- /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_manager.cpp b/editor/project_manager.cpp
index 666f08cb2d..0d06b71420 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -199,6 +199,7 @@ private:
sp = TTR("Imported Project");
project_name->set_text(sp);
+ _text_changed(sp);
}
}
@@ -222,6 +223,7 @@ private:
}
String sp = p.simplify_path();
project_path->set_text(sp);
+ _path_text_changed(sp);
get_ok()->call_deferred("grab_focus");
}
@@ -230,6 +232,7 @@ private:
String p = p_path;
String sp = p.simplify_path();
project_path->set_text(sp);
+ _path_text_changed(sp);
get_ok()->call_deferred("grab_focus");
}
@@ -263,7 +266,9 @@ private:
if (d->make_dir(project_name->get_text()) == OK) {
d->change_dir(project_name->get_text());
- project_path->set_text(d->get_current_dir());
+ String dir_str = d->get_current_dir();
+ project_path->set_text(dir_str);
+ _path_text_changed(dir_str);
created_folder_path = d->get_current_dir();
create_dir->set_disabled(true);
} else {
@@ -475,7 +480,9 @@ private:
_remove_created_folder();
project_path->clear();
+ _path_text_changed("");
project_name->clear();
+ _text_changed("");
if (status_rect->get_texture() == get_icon("StatusError", "EditorIcons"))
msg->show();
@@ -540,7 +547,9 @@ public:
msg->show();
get_ok()->set_disabled(true);
} else if (current->has_setting("application/config/name")) {
- project_name->set_text(current->get("application/config/name"));
+ String proj = current->get("application/config/name");
+ project_name->set_text(proj);
+ _text_changed(proj);
}
project_name->call_deferred("grab_focus");
@@ -559,7 +568,9 @@ public:
fdialog->set_current_dir(d->get_current_dir());
memdelete(d);
}
- project_name->set_text(TTR("New Game Project"));
+ String proj = TTR("New Game Project");
+ project_name->set_text(proj);
+ _text_changed(proj);
project_path->set_editable(true);
browse->set_disabled(false);
@@ -725,9 +736,11 @@ void ProjectManager::_update_project_buttons() {
}
}
- erase_btn->set_disabled(selected_list.size() < 1);
- open_btn->set_disabled(selected_list.size() < 1);
- rename_btn->set_disabled(selected_list.size() < 1);
+ bool empty_selection = selected_list.empty();
+ erase_btn->set_disabled(empty_selection);
+ open_btn->set_disabled(empty_selection);
+ rename_btn->set_disabled(empty_selection);
+ run_btn->set_disabled(empty_selection);
}
void ProjectManager::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 24081279ea..7e4e589bb4 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -149,53 +149,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 +255,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: {
@@ -431,8 +443,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: {
@@ -443,10 +453,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;
@@ -459,8 +469,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: {
@@ -483,14 +491,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: {
@@ -513,8 +513,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: {}
}
@@ -682,19 +680,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++) {
@@ -763,10 +768,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"));
}
}
@@ -796,7 +802,7 @@ void ProjectSettingsEditor::popup_project_settings() {
void ProjectSettingsEditor::_item_selected() {
- TreeItem *ti = globals_editor->get_property_editor()->get_scene_tree()->get_selected();
+ TreeItem *ti = globals_editor->get_property_editor()->get_property_tree()->get_selected();
if (!ti)
return;
if (!ti->get_parent())
@@ -905,7 +911,7 @@ void ProjectSettingsEditor::_action_check(String p_action) {
if (!_validate_action_name(p_action)) {
- action_add_error->set_text(TTR("Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or '\"'"));
+ action_add_error->set_text(TTR("Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or '\"'."));
action_add_error->show();
action_add->set_disabled(true);
return;
@@ -1738,7 +1744,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
//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_scene_tree()->connect("cell_selected", this, "_item_selected");
+ 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");
@@ -1801,6 +1807,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");
@@ -1857,14 +1871,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..0ced88d7f6 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -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;
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index e0063925b1..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");
@@ -4212,7 +4213,7 @@ void PropertyEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property")));
}
-Tree *PropertyEditor::get_scene_tree() {
+Tree *PropertyEditor::get_property_tree() {
return tree;
}
@@ -4695,7 +4696,7 @@ SectionedPropertyEditor::SectionedPropertyEditor() {
editor->set_v_size_flags(SIZE_EXPAND_FILL);
right_vb->add_child(editor, true);
- editor->get_scene_tree()->set_column_titles_visible(false);
+ editor->get_property_tree()->set_column_titles_visible(false);
editor->hide_top_label();
diff --git a/editor/property_editor.h b/editor/property_editor.h
index 017a190adb..56743822d2 100644
--- a/editor/property_editor.h
+++ b/editor/property_editor.h
@@ -275,7 +275,7 @@ public:
String get_selected_path() const;
- Tree *get_scene_tree();
+ Tree *get_property_tree();
Label *get_top_label();
void hide_top_label();
void update_tree();
@@ -309,6 +309,7 @@ public:
void collapse_all_folding();
void expand_all_folding();
+
PropertyEditor();
~PropertyEditor();
};
diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp
index 3e95064ead..d927e07976 100644
--- a/editor/property_selector.cpp
+++ b/editor/property_selector.cpp
@@ -120,33 +120,33 @@ void PropertySelector::_update_search() {
bool found = false;
Ref<Texture> type_icons[Variant::VARIANT_MAX] = {
- Control::get_icon("MiniVariant", "EditorIcons"),
- Control::get_icon("MiniBoolean", "EditorIcons"),
- Control::get_icon("MiniInteger", "EditorIcons"),
- Control::get_icon("MiniFloat", "EditorIcons"),
- Control::get_icon("MiniString", "EditorIcons"),
- Control::get_icon("MiniVector2", "EditorIcons"),
- Control::get_icon("MiniRect2", "EditorIcons"),
- Control::get_icon("MiniVector3", "EditorIcons"),
- Control::get_icon("MiniMatrix2", "EditorIcons"),
- Control::get_icon("MiniPlane", "EditorIcons"),
- Control::get_icon("MiniQuat", "EditorIcons"),
- Control::get_icon("MiniAabb", "EditorIcons"),
- Control::get_icon("MiniMatrix3", "EditorIcons"),
- Control::get_icon("MiniTransform", "EditorIcons"),
- Control::get_icon("MiniColor", "EditorIcons"),
- Control::get_icon("MiniPath", "EditorIcons"),
- Control::get_icon("MiniRid", "EditorIcons"),
- Control::get_icon("MiniObject", "EditorIcons"),
- Control::get_icon("MiniDictionary", "EditorIcons"),
- Control::get_icon("MiniArray", "EditorIcons"),
- Control::get_icon("MiniRawArray", "EditorIcons"),
- Control::get_icon("MiniIntArray", "EditorIcons"),
- Control::get_icon("MiniFloatArray", "EditorIcons"),
- Control::get_icon("MiniStringArray", "EditorIcons"),
- Control::get_icon("MiniVector2Array", "EditorIcons"),
- Control::get_icon("MiniVector3Array", "EditorIcons"),
- Control::get_icon("MiniColorArray", "EditorIcons")
+ Control::get_icon("Variant", "EditorIcons"),
+ Control::get_icon("bool", "EditorIcons"),
+ Control::get_icon("int", "EditorIcons"),
+ Control::get_icon("float", "EditorIcons"),
+ Control::get_icon("String", "EditorIcons"),
+ Control::get_icon("Vector2", "EditorIcons"),
+ Control::get_icon("Rect2", "EditorIcons"),
+ Control::get_icon("Vector3", "EditorIcons"),
+ Control::get_icon("Transform2D", "EditorIcons"),
+ Control::get_icon("Plane", "EditorIcons"),
+ Control::get_icon("Quat", "EditorIcons"),
+ Control::get_icon("AABB", "EditorIcons"),
+ Control::get_icon("Basis", "EditorIcons"),
+ Control::get_icon("Transform", "EditorIcons"),
+ Control::get_icon("Color", "EditorIcons"),
+ Control::get_icon("Path", "EditorIcons"),
+ Control::get_icon("RID", "EditorIcons"),
+ Control::get_icon("Object", "EditorIcons"),
+ Control::get_icon("Dictionary", "EditorIcons"),
+ Control::get_icon("Array", "EditorIcons"),
+ Control::get_icon("PoolByteArray", "EditorIcons"),
+ Control::get_icon("PoolIntArray", "EditorIcons"),
+ Control::get_icon("PoolRealArray", "EditorIcons"),
+ Control::get_icon("PoolStringArray", "EditorIcons"),
+ Control::get_icon("PoolVector2Array", "EditorIcons"),
+ Control::get_icon("PoolVector3Array", "EditorIcons"),
+ Control::get_icon("PoolColorArray", "EditorIcons")
};
for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
@@ -175,6 +175,10 @@ void PropertySelector::_update_search() {
if (search_box->get_text() != String() && E->get().name.find(search_box->get_text()) == -1)
continue;
+
+ if (type_filter.size() && type_filter.find(E->get().type) == -1)
+ continue;
+
TreeItem *item = search_options->create_item(category ? category : root);
item->set_text(0, E->get().name);
item->set_metadata(0, E->get().name);
@@ -534,6 +538,10 @@ void PropertySelector::select_property_from_instance(Object *p_instance, const S
_update_search();
}
+void PropertySelector::set_type_filter(const Vector<Variant::Type> &p_type_filter) {
+ type_filter = p_type_filter;
+}
+
void PropertySelector::_bind_methods() {
ClassDB::bind_method(D_METHOD("_text_changed"), &PropertySelector::_text_changed);
diff --git a/editor/property_selector.h b/editor/property_selector.h
index d9b1aee422..f5b34d210e 100644
--- a/editor/property_selector.h
+++ b/editor/property_selector.h
@@ -60,6 +60,8 @@ class PropertySelector : public ConfirmationDialog {
void _item_selected();
+ Vector<Variant::Type> type_filter;
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -75,6 +77,8 @@ public:
void select_property_from_basic_type(Variant::Type p_type, const String &p_current = "");
void select_property_from_instance(Object *p_instance, const String &p_current = "");
+ void set_type_filter(const Vector<Variant::Type> &p_type_filter);
+
PropertySelector();
};
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 8b99a3d503..2ffaa0ca12 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -33,7 +33,7 @@
#include "core/io/resource_saver.h"
#include "core/os/keyboard.h"
#include "core/project_settings.h"
-#include "editor/animation_editor.h"
+
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/multi_node_edit.h"
@@ -75,6 +75,8 @@ void SceneTreeDock::_unhandled_key_input(Ref<InputEvent> p_event) {
if (ED_IS_SHORTCUT("scene_tree/batch_rename", p_event)) {
_tool_selected(TOOL_BATCH_RENAME);
+ } else if (ED_IS_SHORTCUT("scene_tree/rename", p_event)) {
+ _tool_selected(TOOL_RENAME);
} else if (ED_IS_SHORTCUT("scene_tree/add_child_node", p_event)) {
_tool_selected(TOOL_NEW);
} else if (ED_IS_SHORTCUT("scene_tree/instance_scene", p_event)) {
@@ -111,7 +113,7 @@ void SceneTreeDock::instance(const String &p_file) {
Node *parent = scene_tree->get_selected();
if (!parent) {
- Node *parent = edited_scene;
+ parent = edited_scene;
};
if (!edited_scene) {
@@ -293,6 +295,13 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
rename_dialog->popup_centered();
}
} break;
+ case TOOL_RENAME: {
+ Tree *tree = scene_tree->get_scene_tree();
+ if (tree->is_anything_selected()) {
+ tree->grab_focus();
+ tree->edit_selected();
+ }
+ } break;
case TOOL_NEW: {
String preferred = "";
@@ -542,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();
@@ -732,7 +767,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (node) {
node->set_scene_inherited_state(Ref<SceneState>());
scene_tree->update_tree();
- EditorNode::get_singleton()->get_property_editor()->update_tree();
+ EditorNode::get_singleton()->get_inspector()->update_tree();
}
}
} break;
@@ -748,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) {
@@ -794,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: {
@@ -811,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);
}
}
@@ -1239,7 +1351,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
path_renames[ni].second = fixed_node_path;
}
- editor_data->get_undo_redo().add_do_method(sed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, -1);
+ editor_data->get_undo_redo().add_do_method(sed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, p_position_in_parent + inc);
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)) + "/" + new_name), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index());
if (p_keep_global_xform) {
@@ -1253,8 +1365,8 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
editor_data->get_undo_redo().add_do_method(this, "_set_owners", edited_scene, owners);
- if (AnimationPlayerEditor::singleton->get_key_editor()->get_root() == node)
- editor_data->get_undo_redo().add_do_method(AnimationPlayerEditor::singleton->get_key_editor(), "set_root", node);
+ if (AnimationPlayerEditor::singleton->get_track_editor()->get_root() == node)
+ editor_data->get_undo_redo().add_do_method(AnimationPlayerEditor::singleton->get_track_editor(), "set_root", node);
editor_data->get_undo_redo().add_undo_method(new_parent, "remove_child", node);
editor_data->get_undo_redo().add_undo_method(node, "set_name", former_names[ni]);
@@ -1281,8 +1393,8 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
editor_data->get_undo_redo().add_undo_method(node->get_parent(), "add_child", node);
editor_data->get_undo_redo().add_undo_method(node->get_parent(), "move_child", node, child_pos);
editor_data->get_undo_redo().add_undo_method(this, "_set_owners", edited_scene, owners);
- if (AnimationPlayerEditor::singleton->get_key_editor()->get_root() == node)
- editor_data->get_undo_redo().add_undo_method(AnimationPlayerEditor::singleton->get_key_editor(), "set_root", node);
+ if (AnimationPlayerEditor::singleton->get_track_editor()->get_root() == node)
+ editor_data->get_undo_redo().add_undo_method(AnimationPlayerEditor::singleton->get_track_editor(), "set_root", node);
if (p_keep_global_xform) {
if (Object::cast_to<Node2D>(node))
@@ -1383,8 +1495,8 @@ void SceneTreeDock::_delete_confirm() {
editor_data->get_undo_redo().add_do_method(n->get_parent(), "remove_child", n);
editor_data->get_undo_redo().add_undo_method(n->get_parent(), "add_child", n);
editor_data->get_undo_redo().add_undo_method(n->get_parent(), "move_child", n, n->get_index());
- if (AnimationPlayerEditor::singleton->get_key_editor()->get_root() == n)
- editor_data->get_undo_redo().add_undo_method(AnimationPlayerEditor::singleton->get_key_editor(), "set_root", n);
+ if (AnimationPlayerEditor::singleton->get_track_editor()->get_root() == n)
+ editor_data->get_undo_redo().add_undo_method(AnimationPlayerEditor::singleton->get_track_editor(), "set_root", n);
editor_data->get_undo_redo().add_undo_method(this, "_set_owners", edited_scene, owners);
editor_data->get_undo_redo().add_undo_reference(n);
@@ -1870,7 +1982,6 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->clear();
- menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/batch_rename"), TOOL_BATCH_RENAME);
if (selection.size() == 1) {
subresources.clear();
@@ -1886,6 +1997,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT);
menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT);
menu->add_separator();
+ menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME);
}
menu->add_icon_shortcut(get_icon("Reload", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE);
menu->add_separator();
@@ -1895,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);
@@ -1925,6 +2039,12 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT);
menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT);
}
+
+ if (selection.size() > 1) {
+ //this is not a commonly used action, it makes no sense for it to be where it was nor always present.
+ menu->add_separator();
+ menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/batch_rename"), TOOL_BATCH_RENAME);
+ }
menu->add_separator();
menu->add_icon_shortcut(get_icon("Remove", "EditorIcons"), ED_SHORTCUT("scene_tree/delete", TTR("Delete Node(s)"), KEY_DELETE), TOOL_ERASE);
menu->set_size(Size2(1, 1));
@@ -2064,6 +2184,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
filter_hbc->add_constant_override("separate", 0);
ToolButton *tb;
+ ED_SHORTCUT("scene_tree/rename", TTR("Rename"));
ED_SHORTCUT("scene_tree/batch_rename", TTR("Batch Rename"), KEY_MASK_CMD | KEY_F2);
ED_SHORTCUT("scene_tree/add_child_node", TTR("Add Child Node"), KEY_MASK_CMD | KEY_A);
ED_SHORTCUT("scene_tree/instance_scene", TTR("Instance Child Scene"));
@@ -2074,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);
@@ -2137,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);
@@ -2211,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 fd7d616a80..fd74611bde 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -59,6 +59,7 @@ class SceneTreeDock : public VBoxContainer {
TOOL_NEW,
TOOL_INSTANCE,
+ TOOL_RENAME,
TOOL_BATCH_RENAME,
TOOL_REPLACE,
TOOL_ATTACH_SCRIPT,
@@ -67,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,
@@ -79,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 {
@@ -133,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);
@@ -214,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 64d278c0c5..88d614ab89 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -30,6 +30,7 @@
#include "scene_tree_editor.h"
+#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor_node.h"
#include "message_queue.h"
@@ -70,8 +71,18 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
} else if (p_id == BUTTON_VISIBILITY) {
undo_redo->create_action(TTR("Toggle Visible"));
- undo_redo->add_do_method(this, "toggle_visible", n);
- undo_redo->add_undo_method(this, "toggle_visible", n);
+ _toggle_visible(n);
+ List<Node *> selection = editor_selection->get_selected_node_list();
+ if (selection.size() > 1) {
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+ Node *nv = E->get();
+ ERR_FAIL_COND(!nv);
+ if (nv == n) {
+ continue;
+ }
+ _toggle_visible(nv);
+ }
+ }
undo_redo->commit_action();
} else if (p_id == BUTTON_LOCK) {
@@ -80,6 +91,12 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
_update_tree();
emit_signal("node_changed");
}
+ } else if (p_id == BUTTON_PIN) {
+
+ if (n->is_class("AnimationPlayer")) {
+ AnimationPlayerEditor::singleton->unpin();
+ _update_tree();
+ }
} else if (p_id == BUTTON_GROUP) {
if (n->is_class("CanvasItem")) {
@@ -118,33 +135,13 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
}
}
void SceneTreeEditor::_toggle_visible(Node *p_node) {
- if (p_node->is_class("Spatial")) {
- bool v = bool(p_node->call("is_visible"));
- p_node->call("set_visible", !v);
- } else if (p_node->is_class("CanvasItem")) {
+ if (p_node->has_method("is_visible") && p_node->has_method("set_visible")) {
bool v = bool(p_node->call("is_visible"));
- if (v) {
- p_node->call("hide");
- } else {
- p_node->call("show");
- }
+ undo_redo->add_do_method(p_node, "set_visible", !v);
+ undo_redo->add_undo_method(p_node, "set_visible", v);
}
}
-void SceneTreeEditor::toggle_visible(Node *p_node) {
- _toggle_visible(p_node);
- List<Node *> selection = editor_selection->get_selected_node_list();
- if (selection.size() > 1) {
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
- Node *nv = E->get();
- ERR_FAIL_COND(!nv);
- if (nv == p_node) {
- continue;
- }
- _toggle_visible(nv);
- }
- }
-}
bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
if (!p_node)
@@ -169,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);
@@ -199,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)) {
@@ -229,7 +229,7 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
bool has_groups = p_node->has_persistent_groups();
if (has_connections && has_groups) {
- item->add_button(0, get_icon("SignalsAndGroups", "EditorIcons"), BUTTON_SIGNALS, false, TTR("Node has connection(s) and group(s)\nClick to show signals dock."));
+ item->add_button(0, get_icon("SignalsAndGroups", "EditorIcons"), BUTTON_SIGNALS, false, TTR("Node has connection(s) and group(s).\nClick to show signals dock."));
} else if (has_connections) {
item->add_button(0, get_icon("Signals", "EditorIcons"), BUTTON_SIGNALS, false, TTR("Node has connections.\nClick to show signals dock."));
} else if (has_groups) {
@@ -255,18 +255,18 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
if (!p_node->get_script().is_null()) {
- item->add_button(0, get_icon("Script", "EditorIcons"), BUTTON_SCRIPT, false, TTR("Open script"));
+ item->add_button(0, get_icon("Script", "EditorIcons"), BUTTON_SCRIPT, false, TTR("Open Script"));
}
if (p_node->is_class("CanvasItem")) {
bool is_locked = p_node->has_meta("_edit_lock_"); //_edit_group_
if (is_locked)
- item->add_button(0, get_icon("Lock", "EditorIcons"), BUTTON_LOCK, false, TTR("Node is locked.\nClick to unlock"));
+ item->add_button(0, get_icon("Lock", "EditorIcons"), BUTTON_LOCK, false, TTR("Node is locked.\nClick to unlock it."));
bool is_grouped = p_node->has_meta("_edit_group_");
if (is_grouped)
- item->add_button(0, get_icon("Group", "EditorIcons"), BUTTON_GROUP, false, TTR("Children are not selectable.\nClick to make selectable"));
+ item->add_button(0, get_icon("Group", "EditorIcons"), BUTTON_GROUP, false, TTR("Children are not selectable.\nClick to make selectable."));
bool v = p_node->call("is_visible");
if (v)
@@ -282,7 +282,7 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
bool is_locked = p_node->has_meta("_edit_lock_");
if (is_locked)
- item->add_button(0, get_icon("Lock", "EditorIcons"), BUTTON_LOCK, false, TTR("Node is locked.\nClick to unlock"));
+ item->add_button(0, get_icon("Lock", "EditorIcons"), BUTTON_LOCK, false, TTR("Node is locked.\nClick to unlock it."));
bool v = p_node->call("is_visible");
if (v)
@@ -294,6 +294,13 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
p_node->connect("visibility_changed", this, "_node_visibility_changed", varray(p_node));
_update_visibility_color(p_node, item);
+ } else if (p_node->is_class("AnimationPlayer")) {
+
+ bool is_pinned = AnimationPlayerEditor::singleton->get_player() == p_node && AnimationPlayerEditor::singleton->is_pinned();
+
+ if (is_pinned) {
+ item->add_button(0, get_icon("Pin", "EditorIcons"), BUTTON_PIN, false, TTR("AnimationPlayer is pinned.\nClick to unpin."));
+ }
}
}
@@ -319,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;
@@ -712,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;
@@ -968,8 +995,6 @@ void SceneTreeEditor::_bind_methods() {
ClassDB::bind_method("_cell_collapsed", &SceneTreeEditor::_cell_collapsed);
ClassDB::bind_method("_rmb_select", &SceneTreeEditor::_rmb_select);
ClassDB::bind_method("_warning_changed", &SceneTreeEditor::_warning_changed);
- ClassDB::bind_method("_toggle_visible", &SceneTreeEditor::_toggle_visible);
- ClassDB::bind_method("toggle_visible", &SceneTreeEditor::toggle_visible);
ClassDB::bind_method("_node_script_changed", &SceneTreeEditor::_node_script_changed);
ClassDB::bind_method("_node_visibility_changed", &SceneTreeEditor::_node_visibility_changed);
diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h
index b63eb2a1f0..c4f63f5736 100644
--- a/editor/scene_tree_editor.h
+++ b/editor/scene_tree_editor.h
@@ -55,6 +55,7 @@ class SceneTreeEditor : public Control {
BUTTON_WARNING = 5,
BUTTON_SIGNALS = 6,
BUTTON_GROUPS = 7,
+ BUTTON_PIN = 8,
};
Tree *tree;
@@ -70,8 +71,6 @@ class SceneTreeEditor : public Control {
void _compute_hash(Node *p_node, uint64_t &hash);
- void toggle_visible(Node *p_node);
-
bool _add_nodes(Node *p_node, TreeItem *p_parent);
void _test_update_tree();
void _update_tree();
@@ -132,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;
@@ -148,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 a83de1627d..62848a6035 100644
--- a/editor/script_editor_debugger.cpp
+++ b/editor/script_editor_debugger.cpp
@@ -872,7 +872,7 @@ void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType
reason->add_color_override("font_color", get_color("success_color", "Editor"));
}
reason->set_text(p_reason);
- reason->set_tooltip(p_reason);
+ reason->set_tooltip(p_reason.word_wrap(80));
}
void ScriptEditorDebugger::_performance_select() {
@@ -1851,7 +1851,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
ppeer = Ref<PacketPeerStream>(memnew(PacketPeerStream));
ppeer->set_input_buffer_max_size(1024 * 1024 * 8); //8mb should be enough
editor = p_editor;
- editor->get_property_editor()->connect("object_id_selected", this, "_scene_tree_property_select_object");
+ editor->get_inspector()->connect("object_id_selected", this, "_scene_tree_property_select_object");
tabs = memnew(TabContainer);
tabs->set_tab_align(TabContainer::ALIGN_LEFT);
@@ -1877,6 +1877,9 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
reason->set_text("");
hbc->add_child(reason);
reason->set_h_size_flags(SIZE_EXPAND_FILL);
+ reason->set_autowrap(true);
+ reason->set_max_lines_visible(3);
+ reason->set_mouse_filter(Control::MOUSE_FILTER_PASS);
hbc->add_child(memnew(VSeparator));
@@ -1936,7 +1939,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
inspector = memnew(PropertyEditor);
inspector->set_h_size_flags(SIZE_EXPAND_FILL);
inspector->hide_top_label();
- inspector->get_scene_tree()->set_column_title(0, TTR("Variable"));
+ inspector->get_property_tree()->set_column_title(0, TTR("Variable"));
inspector->set_enable_capitalize_paths(false);
inspector->set_read_only(true);
inspector->connect("object_id_selected", this, "_scene_tree_property_select_object");
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp
index c3e9e4ab62..ae88b3a035 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/settings_config_dialog.cpp
@@ -61,7 +61,7 @@ void EditorSettingsDialog::_settings_property_edited(const String &p_name) {
if (full_name == "text_editor/theme/color_theme") {
property_editor->get_property_editor()->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", 6); // 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");
}
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index 2652b09763..3b0ac8864a 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -36,6 +36,7 @@
#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"
@@ -201,6 +202,9 @@ 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) * 100.0f, Vector3(selectable_icon_size, selectable_icon_size, selectable_icon_size) * 200.0f));
+
ins.mesh = mesh;
ins.unscaled = true;
ins.billboard = true;
@@ -209,13 +213,13 @@ 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;
+
instances.push_back(ins);
}
-void EditorSpatialGizmo::add_collision_triangles(const Ref<TriangleMesh> &p_tmesh, const AABB &p_bounds) {
-
+void EditorSpatialGizmo::add_collision_triangles(const Ref<TriangleMesh> &p_tmesh) {
collision_mesh = p_tmesh;
- collision_mesh_bounds = p_bounds;
}
void EditorSpatialGizmo::add_collision_segments(const Vector<Vector3> &p_lines) {
@@ -332,64 +336,74 @@ bool EditorSpatialGizmo::intersect_frustum(const Camera *p_camera, const Vector<
ERR_FAIL_COND_V(!spatial_node, false);
ERR_FAIL_COND_V(!valid, false);
- if (collision_segments.size()) {
+ if (selectable_icon_size > 0.0f) {
+ Vector3 origin = spatial_node->get_global_transform().get_origin();
const Plane *p = p_frustum.ptr();
int fc = p_frustum.size();
- int vc = collision_segments.size();
- const Vector3 *vptr = collision_segments.ptr();
- Transform t = spatial_node->get_global_transform();
+ bool any_out = false;
- for (int i = 0; i < vc / 2; i++) {
+ for (int j = 0; j < fc; j++) {
- Vector3 a = t.xform(vptr[i * 2 + 0]);
- Vector3 b = t.xform(vptr[i * 2 + 1]);
+ if (p[j].is_point_over(origin)) {
+ any_out = true;
+ break;
+ }
+ }
+
+ if (!any_out)
+ return true;
+ return false;
+ }
+
+ if (collision_segments.size()) {
- bool any_out = false;
- for (int j = 0; j < fc; j++) {
+ const Plane *p = p_frustum.ptr();
+ int fc = p_frustum.size();
- if (p[j].distance_to(a) > 0 && p[j].distance_to(b) > 0) {
+ int vc = collision_segments.size();
+ const Vector3 *vptr = collision_segments.ptr();
+ Transform t = spatial_node->get_global_transform();
+ bool any_out = false;
+ for (int j = 0; j < fc; j++) {
+ for (int i = 0; i < vc; i++) {
+ Vector3 v = t.xform(vptr[i]);
+ if (p[j].is_point_over(v)) {
any_out = true;
break;
}
}
-
- if (!any_out)
- return true;
+ if (any_out) break;
}
- return false;
+ if (!any_out) return true;
}
- if (collision_mesh_bounds.size != Vector3(0.0, 0.0, 0.0)) {
+ if (collision_mesh.is_valid()) {
Transform t = spatial_node->get_global_transform();
- const Plane *p = p_frustum.ptr();
- int fc = p_frustum.size();
-
- Vector3 mins = t.xform(collision_mesh_bounds.get_position());
- Vector3 max = t.xform(collision_mesh_bounds.get_position() + collision_mesh_bounds.get_size());
- bool any_out = false;
+ Vector3 mesh_scale = t.get_basis().get_scale();
+ t.orthonormalize();
- for (int j = 0; j < fc; j++) {
+ Transform it = t.affine_inverse();
- if (p[j].distance_to(mins) > 0 || p[j].distance_to(max) > 0) {
+ Vector<Plane> transformed_frustum;
- any_out = true;
- break;
- }
+ for (int i = 0; i < 4; i++) {
+ transformed_frustum.push_back(it.xform(p_frustum[i]));
}
- if (!any_out)
+ if (collision_mesh->inside_convex_shape(transformed_frustum.ptr(), transformed_frustum.size(), mesh_scale)) {
return true;
+ }
}
return false;
}
-bool EditorSpatialGizmo::intersect_ray(const Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle, bool p_sec_first) {
+bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle, bool p_sec_first) {
ERR_FAIL_COND_V(!spatial_node, false);
ERR_FAIL_COND_V(!valid, false);
@@ -453,6 +467,44 @@ bool EditorSpatialGizmo::intersect_ray(const Camera *p_camera, const Point2 &p_p
}
}
+ if (selectable_icon_size > 0.0f) {
+
+ Transform t = spatial_node->get_global_transform();
+ t.orthonormalize();
+ t.set_look_at(t.origin, p_camera->get_camera_transform().origin, Vector3(0, 1, 0));
+
+ float scale = t.origin.distance_to(p_camera->get_camera_transform().origin);
+
+ if (p_camera->get_projection() == Camera::PROJECTION_ORTHOGONAL) {
+ 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);
+
+ Transform oct = p_camera->get_camera_transform();
+
+ p_camera->look_at(t.origin, Vector3(0, 1, 0));
+ Vector3 c0 = t.xform(Vector3(selectable_icon_size, selectable_icon_size, 0) * scale);
+ Vector3 c1 = t.xform(Vector3(-selectable_icon_size, -selectable_icon_size, 0) * scale);
+
+ Point2 p0 = p_camera->unproject_position(c0);
+ Point2 p1 = p_camera->unproject_position(c1);
+
+ p_camera->set_global_transform(oct);
+
+ Rect2 rect(p0, p1 - p0);
+
+ rect.set_position(center - rect.get_size() / 2.0);
+
+ if (rect.has_point(p_point)) {
+ return true;
+ }
+
+ return false;
+ }
+
if (collision_segments.size()) {
Plane camp(p_camera->get_transform().origin, (-p_camera->get_transform().basis.get_axis(2)).normalized());
@@ -664,7 +716,7 @@ void EditorSpatialGizmo::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_lines", "lines", "material", "billboard"), &EditorSpatialGizmo::add_lines, DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_mesh", "mesh", "billboard", "skeleton"), &EditorSpatialGizmo::add_mesh, DEFVAL(false), DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("add_collision_segments", "segments"), &EditorSpatialGizmo::add_collision_segments);
- ClassDB::bind_method(D_METHOD("add_collision_triangles", "triangles", "bounds"), &EditorSpatialGizmo::add_collision_triangles);
+ ClassDB::bind_method(D_METHOD("add_collision_triangles", "triangles"), &EditorSpatialGizmo::add_collision_triangles);
ClassDB::bind_method(D_METHOD("add_unscaled_billboard", "material", "default_scale"), &EditorSpatialGizmo::add_unscaled_billboard, DEFVAL(1));
ClassDB::bind_method(D_METHOD("add_handles", "handles", "billboard", "secondary"), &EditorSpatialGizmo::add_handles, DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_spatial_node", "node"), &EditorSpatialGizmo::_set_spatial_node);
@@ -1272,14 +1324,15 @@ bool MeshInstanceSpatialGizmo::can_draw() const {
}
void MeshInstanceSpatialGizmo::redraw() {
+ clear();
+
Ref<Mesh> m = mesh->get_mesh();
if (!m.is_valid())
return; //none
Ref<TriangleMesh> tm = m->generate_triangle_mesh();
if (tm.is_valid()) {
- AABB aabb;
- add_collision_triangles(tm, aabb);
+ add_collision_triangles(tm);
}
}
@@ -1291,6 +1344,27 @@ MeshInstanceSpatialGizmo::MeshInstanceSpatialGizmo(MeshInstance *p_mesh) {
/////
+bool Sprite3DSpatialGizmo::can_draw() const {
+ return true;
+}
+void Sprite3DSpatialGizmo::redraw() {
+
+ clear();
+
+ Ref<TriangleMesh> tm = sprite->generate_triangle_mesh();
+ if (tm.is_valid()) {
+ add_collision_triangles(tm);
+ }
+}
+
+Sprite3DSpatialGizmo::Sprite3DSpatialGizmo(SpriteBase3D *p_sprite) {
+
+ sprite = p_sprite;
+ set_spatial_node(p_sprite);
+}
+
+///
+
void Position3DSpatialGizmo::redraw() {
clear();
@@ -1861,6 +1935,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";
@@ -1892,6 +1971,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;
@@ -1972,6 +2057,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();
@@ -2033,6 +2136,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;
@@ -2209,6 +2337,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;
@@ -2540,8 +2729,9 @@ void ParticlesGizmo::redraw() {
}
//add_unscaled_billboard(SpatialEditorGizmos::singleton->visi,0.05);
- add_unscaled_billboard(icon, 0.05);
+
add_handles(handles);
+ add_unscaled_billboard(icon, 0.05);
}
ParticlesGizmo::ParticlesGizmo(Particles *p_particles) {
diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h
index c5dc36cb22..924f82dc16 100644
--- a/editor/spatial_editor_gizmos.h
+++ b/editor/spatial_editor_gizmos.h
@@ -49,6 +49,7 @@
#include "scene/3d/ray_cast.h"
#include "scene/3d/reflection_probe.h"
#include "scene/3d/room_instance.h"
+#include "scene/3d/sprite_3d.h"
#include "scene/3d/vehicle_body.h"
#include "scene/3d/visibility_notifier.h"
@@ -80,7 +81,6 @@ class EditorSpatialGizmo : public SpatialEditorGizmo {
Vector<Vector3> collision_segments;
Ref<TriangleMesh> collision_mesh;
- AABB collision_mesh_bounds;
struct Handle {
Vector3 pos;
@@ -89,6 +89,7 @@ class EditorSpatialGizmo : public SpatialEditorGizmo {
Vector<Vector3> handles;
Vector<Vector3> secondary_handles;
+ float selectable_icon_size = -1.0f;
bool billboard_handle;
bool valid;
@@ -102,7 +103,7 @@ protected:
void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false);
void add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard = false, const RID &p_skeleton = RID());
void add_collision_segments(const Vector<Vector3> &p_lines);
- void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh, const AABB &p_bounds = AABB());
+ void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh);
void add_unscaled_billboard(const Ref<Material> &p_material, float p_scale = 1);
void add_handles(const Vector<Vector3> &p_handles, bool p_billboard = false, bool p_secondary = false);
void add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3());
@@ -118,7 +119,7 @@ protected:
public:
virtual Vector3 get_handle_pos(int p_idx) const;
virtual bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum);
- virtual bool intersect_ray(const Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false);
+ 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);
void clear();
void create();
@@ -192,6 +193,18 @@ public:
MeshInstanceSpatialGizmo(MeshInstance *p_mesh = NULL);
};
+class Sprite3DSpatialGizmo : public EditorSpatialGizmo {
+
+ GDCLASS(Sprite3DSpatialGizmo, EditorSpatialGizmo);
+
+ SpriteBase3D *sprite;
+
+public:
+ virtual bool can_draw() const;
+ void redraw();
+ Sprite3DSpatialGizmo(SpriteBase3D *p_sprite = NULL);
+};
+
class Position3DSpatialGizmo : public EditorSpatialGizmo {
GDCLASS(Position3DSpatialGizmo, EditorSpatialGizmo);
diff --git a/editor/translations/af.po b/editor/translations/af.po
index 8644e20317..c5853bbb2f 100644
--- a/editor/translations/af.po
+++ b/editor/translations/af.po
@@ -502,7 +502,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Koppel '%s' aan '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "Koppel..."
#: editor/connections_dialog.cpp
@@ -928,11 +928,11 @@ msgid "Move Audio Bus"
msgstr "Skuif Oudio-Bus"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "Stoor Oudio-Bus Uitleg As..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr "Ligging van Nuwe Uitleg..."
#: editor/editor_audio_buses.cpp
@@ -1071,11 +1071,11 @@ msgid "Updating Scene"
msgstr "Toneel word Opgedateer"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr "Plaaslike veranderinge word gebêre..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr "Toneel word opgedateer..."
#: editor/editor_data.cpp
@@ -1146,7 +1146,7 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr ""
#: editor/editor_file_dialog.cpp
@@ -1416,12 +1416,12 @@ msgid "Error saving resource!"
msgstr "Fout tydens storing van hulpbron!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "Stoor Hulpbron As..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr "Ek sien..."
#: editor/editor_node.cpp
@@ -1626,11 +1626,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1642,7 +1642,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr ""
#: editor/editor_node.cpp
@@ -1694,7 +1694,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1839,7 +1839,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1851,11 +1851,11 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1875,15 +1875,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2128,7 +2128,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2237,7 +2237,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2388,7 +2388,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2464,7 +2464,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2481,7 +2481,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2495,7 +2495,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2632,11 +2632,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2649,16 +2649,16 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Dupliseer"
#: editor/filesystem_dock.cpp
@@ -2684,7 +2684,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2750,7 +2750,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2762,7 +2762,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2778,7 +2778,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2798,7 +2798,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3213,7 +3213,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3221,7 +3221,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3290,7 +3290,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3357,7 +3357,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3544,6 +3544,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3965,7 +3966,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4170,7 +4171,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4532,7 +4533,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4629,7 +4630,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4835,15 +4836,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5294,11 +5295,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5551,7 +5548,7 @@ msgid "Remove All"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5619,7 +5616,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5808,7 +5805,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5899,6 +5896,11 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Ongeldige naam."
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "Kon nie vouer skep nie."
@@ -6087,8 +6089,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6116,7 +6118,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6300,7 +6302,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6396,11 +6398,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6571,7 +6573,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
diff --git a/editor/translations/ar.po b/editor/translations/ar.po
index a57dc0f0cc..ccf2b97d9a 100644
--- a/editor/translations/ar.po
+++ b/editor/translations/ar.po
@@ -3,6 +3,7 @@
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
#
+# Adel <dragonhunter250@gmail.com>, 2018.
# athomield <athomield@hotmail.com>, 2017.
# Basil Al-Khateeb <basil.y.alkhateeb@gmail.com>, 2017.
# Jamal Alyafei <jamal.qassim@gmail.com>, 2017.
@@ -13,14 +14,15 @@
# noureldin sharaf <sharaf.noureldin@yahoo.com>, 2017.
# omar anwar aglan <omar.aglan91@yahoo.com>, 2017-2018.
# OWs Tetra <owstetra@gmail.com>, 2017.
+# Rached Noureddine <rached.noureddine@gmail.com>, 2018.
# Rex_sa <asd1234567890m@gmail.com>, 2017.
# Wajdi Feki <wajdi.feki@gmail.com>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-01-30 16:34+0000\n"
-"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n"
+"PO-Revision-Date: 2018-05-28 18:34+0000\n"
+"Last-Translator: Rached Noureddine <rached.noureddine@gmail.com>\n"
"Language-Team: Arabic <https://hosted.weblate.org/projects/godot-engine/"
"godot/ar/>\n"
"Language: ar\n"
@@ -28,7 +30,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
-"X-Generator: Weblate 2.19-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -508,7 +510,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "قطع إتصال'%s' من '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "يتصل..."
#: editor/connections_dialog.cpp
@@ -928,11 +930,11 @@ msgid "Move Audio Bus"
msgstr "تحريك بيوس الصوت"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "إحÙظ نسق بيوس الصوت كـ..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr "المكان للنسق الجديد..."
#: editor/editor_audio_buses.cpp
@@ -1068,11 +1070,11 @@ msgid "Updating Scene"
msgstr "ÙŠÙحدث المشهد"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "جاري تخزين التعديلات المحلية.."
+msgid "Storing local changes..."
+msgstr "جاري تخزين التعديلات المحلية..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr "ÙŠÙحدث المشهد..."
#: editor/editor_data.cpp
@@ -1141,8 +1143,8 @@ msgid "Show In File Manager"
msgstr "أظهر ÙÙŠ مدير الملÙات"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "مجلد جديد.."
+msgid "New Folder..."
+msgstr "مجلد جديد..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1403,20 +1405,20 @@ 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!"
msgstr "خطأ ÙÙŠ Ø­Ùظ المورد!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "Ø­Ùظ المورد باسم..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "أنا أري.."
+msgid "I see..."
+msgstr "أنا أري..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1640,11 +1642,11 @@ msgid "Open Base Scene"
msgstr "Ùتح مشهد أساسي"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr "Ùتح سريع للمشهد..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr "Ùتح سريع للكود..."
#: editor/editor_node.cpp
@@ -1656,8 +1658,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "هل تريد Ø­Ùظ التغييرات إلي'%s' قبل الإغلاق؟"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Ø­Ùظ المشهد كـ.."
+msgid "Save Scene As..."
+msgstr "Ø­Ùظ المشهد كـ..."
#: editor/editor_node.cpp
msgid "No"
@@ -1708,7 +1710,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "هذا الÙعل لا يمكن إرجاعة. إرجاع علي أية حال؟"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr "تشغيل مشهد بسرعة..."
#: editor/editor_node.cpp
@@ -1865,8 +1867,8 @@ msgid "Previous tab"
msgstr "التبويب السابق"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Ùلتر الملÙات.."
+msgid "Filter Files..."
+msgstr "Ùلتر الملÙات..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1877,12 +1879,12 @@ msgid "New Scene"
msgstr "مشهد جديد"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "مشهد مورث جديد.."
+msgid "New Inherited Scene..."
+msgstr "مشهد مورث جديد..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "اÙتح مشهد.."
+msgid "Open Scene..."
+msgstr "اÙتح مشهد..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1901,16 +1903,16 @@ msgid "Open Recent"
msgstr "ÙÙتح مؤخراً"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "تحويل الي.."
+msgid "Convert To..."
+msgstr "تحويل الي..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "مكتبة الميش.."
+msgid "MeshLibrary..."
+msgstr "مكتبة الميش..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "مجموعة البلاط.."
+msgid "TileSet..."
+msgstr "مجموعة البلاط..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2171,7 +2173,7 @@ msgid "Save the currently edited resource."
msgstr "Ø­Ùظ المورد الذي يتم تعديله حاليا."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr "Ø­Ùظ باسم..."
#: editor/editor_node.cpp
@@ -2280,8 +2282,8 @@ msgid "Creating Mesh Previews"
msgstr "ÙŠÙنشئ مستعرضات الميش"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "الصورة المصغرة.."
+msgid "Thumbnail..."
+msgstr "الصورة المصغرة..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2433,8 +2435,8 @@ msgid "(Current)"
msgstr "(الحالي)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "يستقبل المرايا، من Ùضلك إنتظر.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "يستقبل المرايا، من Ùضلك إنتظر..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2511,8 +2513,8 @@ msgid "Error requesting url: "
msgstr "خطأ ÙÙŠ طلب الرابط: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "يتصل بالسرÙر.."
+msgid "Connecting to Mirror..."
+msgstr "يتصل بالسرÙر..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2528,7 +2530,7 @@ msgstr "لا يمكن الحل"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr "جاري الإتصال..."
#: editor/export_template_manager.cpp
@@ -2541,7 +2543,7 @@ msgstr "متصل"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "جار الطلب..."
#: editor/export_template_manager.cpp
@@ -2674,12 +2676,12 @@ msgid "Collapse all"
msgstr "طوي الكل"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "إعادة تسمية.."
+msgid "Rename..."
+msgstr "إعادة تسمية..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "تحريك إلي.."
+msgid "Move To..."
+msgstr "تحريك إلي..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2690,15 +2692,15 @@ msgid "Instance"
msgstr "نموذج"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "تعديل التبعيات.."
+msgid "Edit Dependencies..."
+msgstr "تعديل التبعيات..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "أظهر المÙلاك.."
+msgid "View Owners..."
+msgstr "أظهر المÙلاك..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "تكرير..."
#: editor/filesystem_dock.cpp
@@ -2724,10 +2726,10 @@ msgstr "نمذج المشهد(المشاهد) المحددة كطÙÙ„ للعقد
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"ÙŠÙحص الملÙات،\n"
-"من Ùضلك إنتظر.."
+"من Ùضلك إنتظر..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2792,8 +2794,8 @@ msgid "Import Scene"
msgstr "إستيراد مشهد"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "حاري إستيراد المشهد.."
+msgid "Importing Scene..."
+msgstr "حاري إستيراد المشهد..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2804,8 +2806,8 @@ msgid "Generating for Mesh: "
msgstr "انشاء من اجل الميش: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "تشغيل الكود المÙخصص.."
+msgid "Running Custom Script..."
+msgstr "تشغيل الكود المÙخصص..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2820,8 +2822,8 @@ msgid "Error running post-import script:"
msgstr "خطأ ÙÙŠ تشغيل الكود الملصق- المستورد:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "جاري الحÙظ.."
+msgid "Saving..."
+msgstr "جاري الحÙظ..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2840,8 +2842,8 @@ msgid "Import As:"
msgstr "إستيراد كـ:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "إعداد Ù…Ùسبق.."
+msgid "Preset..."
+msgstr "إعداد Ù…Ùسبق..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3258,16 +3260,16 @@ msgid "Transition Node"
msgstr "عقدة التنقل"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "إستيراد الحركة.."
+msgid "Import Animations..."
+msgstr "إستيراد الحركة..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "تعديل مصاÙÙŠ العقد"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "الÙلترة.."
+msgid "Filters..."
+msgstr "الÙلترة..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3334,7 +3336,7 @@ msgid "Fetching:"
msgstr "يجلب:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr "جاري الحل..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3401,7 +3403,7 @@ msgid "Site:"
msgstr "الموقع:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr "الدعم..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3595,6 +3597,7 @@ msgid "Use Rotation Snap"
msgstr "إستعمال كبس التدوير"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "تعديل الكبس..."
@@ -3691,14 +3694,12 @@ msgid "Show Guides"
msgstr "أظهر الموجهات"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "إظهار الشبكة"
+msgstr "إظهار المركز"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "أظهر المساعدات"
+msgstr "أظهر الشاشة"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -3758,11 +3759,11 @@ msgstr "إضاÙØ© %s..."
#: editor/plugins/canvas_item_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Ok"
-msgstr ""
+msgstr "حسنا"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Cannot instantiate multiple nodes without root."
-msgstr ""
+msgstr "لا يمكن إنشاء عقد متعددة بدون العقدة الجذر."
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
@@ -3991,7 +3992,7 @@ msgstr "الميش ليس لديه سطح لكي ينشئ حدود منه!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "شبكة بسيطة ليست PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4022,8 +4023,8 @@ msgid "Create Convex Collision Sibling"
msgstr "إنشاء متصادم محدب قريب"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "إنشاء شبكة الخطوط العريضة .."
+msgid "Create Outline Mesh..."
+msgstr "إنشاء شبكة الخطوط العريضة ..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4171,7 +4172,7 @@ msgstr "إنشاء مجال الإرتÙاع..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Marking walkable triangles..."
-msgstr "تعليم مثلثات التحرك.."
+msgstr "تعليم مثلثات التحرك..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Constructing compact heightfield..."
@@ -4216,101 +4217,101 @@ msgstr "إنشاء Ù…Ùضلع التنقل"
#: editor/plugins/particles_2d_editor_plugin.cpp
#: editor/plugins/particles_editor_plugin.cpp
msgid "Generating AABB"
-msgstr "ÙŠÙنشئ AABB"
+msgstr "توليد AABB"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Can only set point into a ParticlesMaterial process material"
-msgstr ""
+msgstr "لا يمكن إنشاء سوى نقطة وحيدة داخل ParticlesMaterial معالج المواد"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Error loading image:"
-msgstr ""
+msgstr "خطأ تحميل الصورة:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr ""
+msgid "No pixels with transparency > 128 in image..."
+msgstr "لا بيكسل بشÙاÙية > 128 ÙÙŠ الصورة..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
-msgstr ""
+msgstr "توليد Rect الرؤية"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Load Emission Mask"
-msgstr ""
+msgstr "حمل قناع الانبعاث"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Clear Emission Mask"
-msgstr ""
+msgstr "إمسح قناع الانبعاث"
#: editor/plugins/particles_2d_editor_plugin.cpp
#: editor/plugins/particles_editor_plugin.cpp
msgid "Particles"
-msgstr ""
+msgstr "جسيمات"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generated Point Count:"
-msgstr ""
+msgstr "عدد النقاط المولدة:"
#: editor/plugins/particles_2d_editor_plugin.cpp
#: editor/plugins/particles_editor_plugin.cpp
msgid "Generation Time (sec):"
-msgstr ""
+msgstr "وقت التوليد (تانية):"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Emission Mask"
-msgstr ""
+msgstr "قناع الانبعاث"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Capture from Pixel"
-msgstr ""
+msgstr "التقط من البيكسل"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Emission Colors"
-msgstr ""
+msgstr "الوان الانبعاث"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Node does not contain geometry."
-msgstr ""
+msgstr "العقدة لا تحتوي على هندسة."
#: editor/plugins/particles_editor_plugin.cpp
msgid "Node does not contain geometry (faces)."
-msgstr ""
+msgstr "العقدة لا تحتوي على هندسة (الوجوه)."
#: editor/plugins/particles_editor_plugin.cpp
msgid "A processor material of type 'ParticlesMaterial' is required."
-msgstr ""
+msgstr "معالج المواد من نوع 'ParticlesMaterial' مطلوب."
#: editor/plugins/particles_editor_plugin.cpp
msgid "Faces contain no area!"
-msgstr ""
+msgstr "الوجوه لا تحتوي على منطقة!"
#: editor/plugins/particles_editor_plugin.cpp
msgid "No faces!"
-msgstr ""
+msgstr "لا وجوه!"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Generate AABB"
-msgstr ""
+msgstr "ولد AABB"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Mesh"
-msgstr ""
+msgstr "أنشئ نقاط إنبعاث من الشبكة"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Node"
-msgstr ""
+msgstr "أنشئ نقاط إنبعاث من العقدة"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emitter"
-msgstr ""
+msgstr "أنشئ باعث"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Emission Points:"
-msgstr ""
+msgstr "نقاط الانبعاث:"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Surface Points"
-msgstr ""
+msgstr "نقاط المساحة"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Surface Points+Normal (Directed)"
@@ -4318,15 +4319,15 @@ msgstr ""
#: editor/plugins/particles_editor_plugin.cpp
msgid "Volume"
-msgstr ""
+msgstr "حجم"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Emission Source: "
-msgstr ""
+msgstr "مصدر الانبعاث: "
#: editor/plugins/particles_editor_plugin.cpp
msgid "Generate Visibility AABB"
-msgstr ""
+msgstr "ولد رؤية AABB"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Remove Point from Curve"
@@ -4338,39 +4339,39 @@ msgstr ""
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Remove In-Control from Curve"
-msgstr ""
+msgstr "أزل In-Control من المنحنى"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Add Point to Curve"
-msgstr ""
+msgstr "أض٠نقطة للمنحنى"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Move Point in Curve"
-msgstr ""
+msgstr "حرك النقطة داخل المنحنى"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Move In-Control in Curve"
-msgstr ""
+msgstr "حرك In-Control داخل المنحنى"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Move Out-Control in Curve"
-msgstr ""
+msgstr "حرك Out-Control داخل المنحنى"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Select Points"
-msgstr ""
+msgstr "إختر النقاط"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Shift+Drag: Select Control Points"
-msgstr ""
+msgstr "Shift+سحب: إختر نقاط التحكم"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Click: Add Point"
-msgstr ""
+msgstr "إظغط: أض٠نقطة"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
@@ -4588,7 +4589,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4685,7 +4686,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4891,15 +4892,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5350,11 +5351,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5607,7 +5604,7 @@ msgid "Remove All"
msgstr "مسح الكل"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5635,33 +5632,39 @@ msgid "Create From Current Editor Theme"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
msgid "CheckBox Radio1"
-msgstr ""
+msgstr "صندوق تأشير ١"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
msgid "CheckBox Radio2"
-msgstr ""
+msgstr "صندوق تأشير٢"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
msgid "Item"
-msgstr ""
+msgstr "عنصر"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
msgid "Check Item"
-msgstr ""
+msgstr "اختار العنصر"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
msgid "Checked Item"
-msgstr ""
+msgstr "عنصر مَضْبÙوط"
#: editor/plugins/theme_editor_plugin.cpp
#, fuzzy
msgid "Radio Item"
-msgstr "إضاÙØ© عنصر"
+msgstr "عنصر انتقاء"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
msgid "Checked Radio Item"
-msgstr ""
+msgstr "عنصر انتقاء مَضْبÙوط"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5672,24 +5675,26 @@ msgid "Many"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
+#, fuzzy
msgid "Options"
-msgstr ""
+msgstr "الخيارات"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr ""
+#, fuzzy
+msgid "Has,Many,Options"
+msgstr "بكثير، خيارات عديدة،!"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
-msgstr ""
+msgstr "علامة التبويب 1"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 2"
-msgstr ""
+msgstr "علامة التبويب 2"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 3"
-msgstr ""
+msgstr "علامة التبويب 3"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Data Type:"
@@ -5864,7 +5869,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5954,6 +5959,11 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+#, fuzzy
+msgid "Invalid Project Name."
+msgstr "اسم غير صالح."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "لا يمكن إنشاء المجلد."
@@ -6140,8 +6150,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6169,7 +6179,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6353,7 +6363,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6449,11 +6459,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6624,7 +6634,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -8068,6 +8078,9 @@ msgstr ""
msgid "Invalid font size."
msgstr ""
+#~ msgid "Next"
+#~ msgstr "التالي"
+
#~ msgid "Can't contain '/' or ':'"
#~ msgstr "لا يمكن أن يحتوي علي '/' أو ':'"
@@ -8080,9 +8093,6 @@ msgstr ""
#~ msgid "Can't write file."
#~ msgstr "لا يمكن كتابة الملÙ."
-#~ msgid "Next"
-#~ msgstr "التالي"
-
#~ msgid "Not found!"
#~ msgstr "لم يوجد!"
@@ -8131,8 +8141,8 @@ msgstr ""
#~ msgid "Exporting for %s"
#~ msgstr "التصدير كـ %s"
-#~ msgid "Setting Up.."
-#~ msgstr "جاري الإعداد.."
+#~ msgid "Setting Up..."
+#~ msgstr "جاري الإعداد..."
#~ msgid "The quick brown fox jumps over the lazy dog."
#~ msgstr "أبجد هوز حطي كلمن صعÙص قرشت ثخذ ضظغ."
diff --git a/editor/translations/bg.po b/editor/translations/bg.po
index 741f6ab209..9f366b3d2f 100644
--- a/editor/translations/bg.po
+++ b/editor/translations/bg.po
@@ -497,7 +497,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr ""
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -907,11 +907,11 @@ msgid "Move Audio Bus"
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1048,12 +1048,12 @@ msgid "Updating Scene"
msgstr "ОбновÑване на Ñцената"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "ОбновÑване на Ñцената.."
+msgid "Updating scene..."
+msgstr "ОбновÑване на Ñцената..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1121,8 +1121,8 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Ðова папка.."
+msgid "New Folder..."
+msgstr "Ðова папка..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1384,12 +1384,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1594,11 +1594,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Бързо отварÑне на Ñцена.."
+msgid "Quick Open Scene..."
+msgstr "Бързо отварÑне на Ñцена..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1610,8 +1610,8 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Запазване на Ñцената като.."
+msgid "Save Scene As..."
+msgstr "Запазване на Ñцената като..."
#: editor/editor_node.cpp
msgid "No"
@@ -1662,8 +1662,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Бързо пуÑкане на Ñцена.."
+msgid "Quick Run Scene..."
+msgstr "Бързо пуÑкане на Ñцена..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1812,7 +1812,7 @@ msgid "Previous tab"
msgstr "Предишен подпрозорец"
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1824,12 +1824,12 @@ msgid "New Scene"
msgstr "Ðова Ñцена"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "ОтварÑне на Ñцена.."
+msgid "Open Scene..."
+msgstr "ОтварÑне на Ñцена..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1848,15 +1848,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2101,7 +2101,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2211,7 +2211,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2363,7 +2363,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2420,7 +2420,7 @@ msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
msgid "Request Failed."
-msgstr "Запитване.."
+msgstr "Запитване..."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -2443,8 +2443,8 @@ msgstr "Имаше грешка при внаÑÑнето:"
#: editor/export_template_manager.cpp
#, fuzzy
-msgid "Connecting to Mirror.."
-msgstr "Свързване.."
+msgid "Connecting to Mirror..."
+msgstr "Свързване..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2460,8 +2460,8 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Свързване.."
+msgid "Connecting..."
+msgstr "Свързване..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2475,8 +2475,8 @@ msgstr "ИзрÑзване на възелите"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Запитване.."
+msgid "Requesting..."
+msgstr "Запитване..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2485,7 +2485,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#, fuzzy
msgid "Connection Error"
-msgstr "Свързване.."
+msgstr "Свързване..."
#: editor/export_template_manager.cpp
msgid "SSL Handshake Error"
@@ -2616,11 +2616,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2633,15 +2633,15 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2667,7 +2667,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2690,12 +2690,12 @@ msgstr ""
#: editor/import/resource_importer_scene.cpp
#, fuzzy
msgid "Import as Single Scene"
-msgstr "ВнаÑÑне на Ñцената.."
+msgstr "ВнаÑÑне на Ñцената..."
#: editor/import/resource_importer_scene.cpp
#, fuzzy
msgid "Import with Separate Animations"
-msgstr "ВнаÑÑне на анимации.."
+msgstr "ВнаÑÑне на анимации..."
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Materials"
@@ -2736,8 +2736,8 @@ msgid "Import Scene"
msgstr "ВнаÑÑне на Ñцена"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "ВнаÑÑне на Ñцената.."
+msgid "Importing Scene..."
+msgstr "ВнаÑÑне на Ñцената..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2748,7 +2748,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2764,7 +2764,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2785,7 +2785,7 @@ msgid "Import As:"
msgstr "ВнаÑÑне като:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3203,15 +3203,15 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "ВнаÑÑне на анимации.."
+msgid "Import Animations..."
+msgstr "ВнаÑÑне на анимации..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3280,7 +3280,7 @@ msgid "Fetching:"
msgstr "ИзтеглÑне:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3348,7 +3348,7 @@ msgid "Site:"
msgstr "ÐœÑÑто:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr "Поддръжка"
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3538,6 +3538,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3960,7 +3961,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4167,7 +4168,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4529,7 +4530,7 @@ msgid "Import Theme"
msgstr "ВнаÑÑне на тема"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4627,7 +4628,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4835,15 +4836,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5299,11 +5300,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5558,7 +5555,7 @@ msgid "Remove All"
msgstr "ЗатварÑне на вÑичко"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5626,7 +5623,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5817,7 +5814,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5911,6 +5908,11 @@ msgstr "ВнеÑен проект"
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Име:"
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "ÐеуÑпешно Ñъздаване на папка."
@@ -6104,8 +6106,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6133,7 +6135,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6317,7 +6319,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6414,11 +6416,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6592,7 +6594,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -8139,8 +8141,8 @@ msgstr ""
#~ "Status: Needs Re-Import"
#~ msgstr "Запазване и повторно внаÑÑне"
-#~ msgid "Re-Import.."
-#~ msgstr "Повторно внаÑÑне.."
+#~ msgid "Re-Import..."
+#~ msgstr "Повторно внаÑÑне..."
#~ msgid "Font Import"
#~ msgstr "ВнаÑÑне на шрифт"
@@ -8229,5 +8231,5 @@ msgstr ""
#~ msgid "Export all files in the project directory."
#~ msgstr "ИзнаÑÑне на вÑички файлове в папката на проекта."
-#~ msgid "Export.."
-#~ msgstr "ИзнаÑÑне.."
+#~ msgid "Export..."
+#~ msgstr "ИзнаÑÑне..."
diff --git a/editor/translations/bn.po b/editor/translations/bn.po
index b8cd30b562..3d00e3450c 100644
--- a/editor/translations/bn.po
+++ b/editor/translations/bn.po
@@ -502,8 +502,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "'%s' à¦à¦° সাথে '%s' সংযà§à¦•à§à¦¤ করà§à¦¨"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "সংযোগ.."
+msgid "Connect..."
+msgstr "সংযোগ..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -927,12 +927,12 @@ msgid "Move Audio Bus"
msgstr "অডিও বাস মà§à¦­ করà§à¦¨"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "অডিও বাস লেআউট সেভ করà§à¦¨.."
+msgid "Save Audio Bus Layout As..."
+msgstr "অডিও বাস লেআউট সেভ করà§à¦¨..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "নতà§à¦¨ লেআউট লোকেশন.."
+msgid "Location for New Layout..."
+msgstr "নতà§à¦¨ লেআউট লোকেশন..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1073,12 +1073,12 @@ msgid "Updating Scene"
msgstr "দৃশà§à¦¯ হাল নাগাদ হচà§à¦›à§‡"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "সà§à¦¥à¦¾à¦¨à§€à§Ÿ পরিবরà§à¦¤à¦¨-সমূহ সংরকà§à¦·à¦¿à¦¤ হচà§à¦›à§‡.."
+msgid "Storing local changes..."
+msgstr "সà§à¦¥à¦¾à¦¨à§€à§Ÿ পরিবরà§à¦¤à¦¨-সমূহ সংরকà§à¦·à¦¿à¦¤ হচà§à¦›à§‡..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "দৃশà§à¦¯ হাল নাগাদ হচà§à¦›à§‡.."
+msgid "Updating scene..."
+msgstr "দৃশà§à¦¯ হাল নাগাদ হচà§à¦›à§‡..."
#: editor/editor_data.cpp
#, fuzzy
@@ -1151,7 +1151,7 @@ msgstr "ফাইল-মà§à¦¯à¦¾à¦¨à§‡à¦œà¦¾à¦°à§‡ দেখà§à¦¨"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
#, fuzzy
-msgid "New Folder.."
+msgid "New Folder..."
msgstr "ফোলà§à¦¡à¦¾à¦° তৈরি করà§à¦¨"
#: editor/editor_file_dialog.cpp
@@ -1437,13 +1437,13 @@ msgid "Error saving resource!"
msgstr "রিসোরà§à¦¸ সংরকà§à¦·à¦£à§‡ সমসà§à¦¯à¦¾ হয়েছে!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "রিসোরà§à¦¸ à¦à¦‡à¦°à§‚পে সংরকà§à¦·à¦£ করà§à¦¨.."
+msgid "Save Resource As..."
+msgstr "রিসোরà§à¦¸ à¦à¦‡à¦°à§‚পে সংরকà§à¦·à¦£ করà§à¦¨..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "বà§à¦à¦²à¦¾à¦®.."
+msgid "I see..."
+msgstr "বà§à¦à¦²à¦¾à¦®..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1680,12 +1680,12 @@ msgid "Open Base Scene"
msgstr "গোড়ার দৃশà§à¦¯ খà§à¦²à§à¦¨"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "দà§à¦°à§à¦¤ দৃশà§à¦¯ খà§à¦²à§à¦¨.."
+msgid "Quick Open Scene..."
+msgstr "দà§à¦°à§à¦¤ দৃশà§à¦¯ খà§à¦²à§à¦¨..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "দà§à¦°à§à¦¤ সà§à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ খà§à¦²à§à¦¨.."
+msgid "Quick Open Script..."
+msgstr "দà§à¦°à§à¦¤ সà§à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ খà§à¦²à§à¦¨..."
#: editor/editor_node.cpp
#, fuzzy
@@ -1697,8 +1697,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "'%s' বনà§à¦§ করার পূরà§à¦¬à§‡ পরিবরà§à¦¤à¦¨à¦¸à¦®à§‚হ সংরকà§à¦·à¦£ করবেন?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "দৃশà§à¦¯ à¦à¦‡à¦°à§‚পে সংরকà§à¦·à¦£ করà§à¦¨.."
+msgid "Save Scene As..."
+msgstr "দৃশà§à¦¯ à¦à¦‡à¦°à§‚পে সংরকà§à¦·à¦£ করà§à¦¨..."
#: editor/editor_node.cpp
#, fuzzy
@@ -1752,8 +1752,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "à¦à¦‡ কাজটি অসমà§à¦ªà¦¾à¦¦à¦¿à¦¤ করা সমà§à¦­à¦¬ হবে না। তবà§à¦“ পà§à¦°à¦¤à§à¦¯à¦¾à¦¬à¦°à§à¦¤à¦¨ করবেন?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "দà§à¦°à§à¦¤ দৃশà§à¦¯ চালান.."
+msgid "Quick Run Scene..."
+msgstr "দà§à¦°à§à¦¤ দৃশà§à¦¯ চালান..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1915,8 +1915,8 @@ msgstr "পূরà§à¦¬à§‡à¦° টà§à¦¯à¦¾à¦¬"
#: editor/editor_node.cpp
#, fuzzy
-msgid "Filter Files.."
-msgstr "দà§à¦°à§à¦¤ ফাইলসমূহ ফিলà§à¦Ÿà¦¾à¦° করà§à¦¨.."
+msgid "Filter Files..."
+msgstr "দà§à¦°à§à¦¤ ফাইলসমূহ ফিলà§à¦Ÿà¦¾à¦° করà§à¦¨..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1927,12 +1927,12 @@ msgid "New Scene"
msgstr "নতà§à¦¨ দৃশà§à¦¯"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "নতà§à¦¨ উতà§à¦¤à¦°à¦¾à¦§à¦¿à¦•à¦¾à¦°à§€ দৃশà§à¦¯.."
+msgid "New Inherited Scene..."
+msgstr "নতà§à¦¨ উতà§à¦¤à¦°à¦¾à¦§à¦¿à¦•à¦¾à¦°à§€ দৃশà§à¦¯..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "দৃশà§à¦¯ খà§à¦²à§à¦¨.."
+msgid "Open Scene..."
+msgstr "দৃশà§à¦¯ খà§à¦²à§à¦¨..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1951,16 +1951,16 @@ msgid "Open Recent"
msgstr "সামà§à¦ªà§à¦°à¦¤à¦¿à¦•à¦¸à¦®à§‚হ খà§à¦²à§à¦¨"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "à¦à¦¤à§‡ রূপানà§à¦¤à¦° করà§à¦¨.."
+msgid "Convert To..."
+msgstr "à¦à¦¤à§‡ রূপানà§à¦¤à¦° করà§à¦¨..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshLibrary (মেস-লাইবà§à¦°à§‡à¦°à¦¿).."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary (মেস-লাইবà§à¦°à§‡à¦°à¦¿)..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet (টাইল-সেট).."
+msgid "TileSet..."
+msgstr "TileSet (টাইল-সেট)..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2228,8 +2228,8 @@ msgid "Save the currently edited resource."
msgstr "à¦à¦‡-মà§à¦¹à§‚রà§à¦¤à§‡ সমà§à¦ªà¦¾à¦¦à¦¿à¦¤ রিসোরà§à¦¸à¦Ÿà¦¿ সংরকà§à¦·à¦£ করà§à¦¨à¥¤"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "à¦à¦‡à¦°à§‚পে সংরকà§à¦·à¦£ করà§à¦¨.."
+msgid "Save As..."
+msgstr "à¦à¦‡à¦°à§‚পে সংরকà§à¦·à¦£ করà§à¦¨..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2300,7 +2300,7 @@ msgstr "à¦à¦•à¦Ÿà¦¿ সà§à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ খà§à¦²à§à¦¨ à¦à¦¬à¦‚ চà¦
#: editor/editor_node.cpp
#, fuzzy
msgid "New Inherited"
-msgstr "নতà§à¦¨ উতà§à¦¤à¦°à¦¾à¦§à¦¿à¦•à¦¾à¦°à§€ দৃশà§à¦¯.."
+msgstr "নতà§à¦¨ উতà§à¦¤à¦°à¦¾à¦§à¦¿à¦•à¦¾à¦°à§€ দৃশà§à¦¯..."
#: editor/editor_node.cpp
msgid "Load Errors"
@@ -2346,8 +2346,8 @@ msgid "Creating Mesh Previews"
msgstr "মেস লাইবà§à¦°à§‡à¦°à¦¿ তৈরি হচà§à¦›à§‡"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "থামà§à¦¬à¦¨à§‡à¦‡à¦².."
+msgid "Thumbnail..."
+msgstr "থামà§à¦¬à¦¨à§‡à¦‡à¦²..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2507,8 +2507,8 @@ msgid "(Current)"
msgstr "বরà§à¦¤à¦®à¦¾à¦¨:"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "মিরর রিটà§à¦°à¦¾à¦‡à¦­ করা হচà§à¦›à§‡, দযা করে অপেকà§à¦·à¦¾ করà§à¦¨.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "মিরর রিটà§à¦°à¦¾à¦‡à¦­ করা হচà§à¦›à§‡, দযা করে অপেকà§à¦·à¦¾ করà§à¦¨..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2557,7 +2557,7 @@ msgstr "সমসà§à¦¯à¦¾ সমাধানে বà§à¦¯à¦°à§à¦¥à¥¤"
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
msgid "Can't connect."
-msgstr "সংযোগ.."
+msgstr "সংযোগ..."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -2592,8 +2592,8 @@ msgstr "à¦à¦Ÿà¦²à¦¾à¦¸/মানচিতà§à¦°à¦¾à¦¬à¦²à§€ সংরকà§à¦·à
#: editor/export_template_manager.cpp
#, fuzzy
-msgid "Connecting to Mirror.."
-msgstr "সংযোগ.."
+msgid "Connecting to Mirror..."
+msgstr "সংযোগ..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2603,7 +2603,7 @@ msgstr "সংযোগ বিচà§à¦›à¦¿à¦¨à§à¦¨ করà§à¦¨"
#: editor/export_template_manager.cpp
#, fuzzy
msgid "Resolving"
-msgstr "সংরকà§à¦·à¦¿à¦¤ হচà§à¦›à§‡.."
+msgstr "সংরকà§à¦·à¦¿à¦¤ হচà§à¦›à§‡..."
#: editor/export_template_manager.cpp
msgid "Can't Resolve"
@@ -2612,13 +2612,13 @@ msgstr "কাংখিত সমাধানে বà§à¦¯à¦°à§à¦¥"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
-msgid "Connecting.."
-msgstr "সংযোগ.."
+msgid "Connecting..."
+msgstr "সংযোগ..."
#: editor/export_template_manager.cpp
#, fuzzy
msgid "Can't Connect"
-msgstr "সংযোগ.."
+msgstr "সংযোগ..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2628,7 +2628,7 @@ msgstr "সংযোগ"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "পরীকà§à¦·à¦¾à¦®à§‚লক উৎস"
#: editor/export_template_manager.cpp
@@ -2639,7 +2639,7 @@ msgstr "নীচে"
#: editor/export_template_manager.cpp
#, fuzzy
msgid "Connection Error"
-msgstr "সংযোগ.."
+msgstr "সংযোগ..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2747,7 +2747,7 @@ msgstr "বà§à¦¯à¦¬à¦¹à§ƒà¦¤ নামে অগà§à¦°à¦¹à¦£à¦¯à§‹à¦—à§à¦¯ অ
#: editor/filesystem_dock.cpp
#, fuzzy
msgid "No name provided."
-msgstr "পà§à¦¨à¦ƒà¦¨à¦¾à¦®à¦•à¦°à¦£ করà§à¦¨ অথবা সরান.."
+msgstr "পà§à¦¨à¦ƒà¦¨à¦¾à¦®à¦•à¦°à¦£ করà§à¦¨ অথবা সরান..."
#: editor/filesystem_dock.cpp
#, fuzzy
@@ -2790,12 +2790,12 @@ msgstr "কলাপà§à¦¸ করà§à¦¨"
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Rename.."
+msgid "Rename..."
msgstr "পà§à¦¨à¦ƒà¦¨à¦¾à¦®à¦•à¦°à¦£ করà§à¦¨"
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "à¦à¦–ানে সরান.."
+msgid "Move To..."
+msgstr "à¦à¦–ানে সরান..."
#: editor/filesystem_dock.cpp
#, fuzzy
@@ -2807,16 +2807,16 @@ msgid "Instance"
msgstr "ইনসà§à¦Ÿà§à¦¯à¦¾à¦¨à§à¦¸"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "নিরà§à¦­à¦°à¦¤à¦¾à¦¸à¦®à§‚হ সমà§à¦ªà¦¾à¦¦à¦¨ করà§à¦¨.."
+msgid "Edit Dependencies..."
+msgstr "নিরà§à¦­à¦°à¦¤à¦¾à¦¸à¦®à§‚হ সমà§à¦ªà¦¾à¦¦à¦¨ করà§à¦¨..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "সà§à¦¬à¦¤à§à¦¬à¦¾à¦§à¦¿à¦•à¦¾à¦°à§€à¦¦à§‡à¦° দেখà§à¦¨.."
+msgid "View Owners..."
+msgstr "সà§à¦¬à¦¤à§à¦¬à¦¾à¦§à¦¿à¦•à¦¾à¦°à§€à¦¦à§‡à¦° দেখà§à¦¨..."
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "ডà§à¦ªà§à¦²à¦¿à¦•à§‡à¦Ÿ"
#: editor/filesystem_dock.cpp
@@ -2842,10 +2842,10 @@ msgstr "নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ দৃশà§à¦¯(সমূহ)-কে নিà
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"ফাইল সà§à¦•à§à¦¯à¦¾à¦¨ করা হচà§à¦›à§‡,\n"
-"অনà§à¦—à§à¦°à¦¹à¦ªà§‚রà§à¦¬à¦• অপেকà§à¦·à¦¾ করà§à¦¨.."
+"অনà§à¦—à§à¦°à¦¹à¦ªà§‚রà§à¦¬à¦• অপেকà§à¦·à¦¾ করà§à¦¨..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2867,12 +2867,12 @@ msgstr "গà§à¦°à§à¦ª/দল হতে অপসারণ করà§à¦¨"
#: editor/import/resource_importer_scene.cpp
#, fuzzy
msgid "Import as Single Scene"
-msgstr "দৃশà§à¦¯ ইমà§à¦ªà§‹à¦°à§à¦Ÿ করা হচà§à¦›à§‡.."
+msgstr "দৃশà§à¦¯ ইমà§à¦ªà§‹à¦°à§à¦Ÿ করা হচà§à¦›à§‡..."
#: editor/import/resource_importer_scene.cpp
#, fuzzy
msgid "Import with Separate Animations"
-msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨à¦¸à¦®à§‚হ ইমà§à¦ªà§‹à¦°à§à¦Ÿ করà§à¦¨.."
+msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨à¦¸à¦®à§‚হ ইমà§à¦ªà§‹à¦°à§à¦Ÿ করà§à¦¨..."
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Materials"
@@ -2913,8 +2913,8 @@ msgid "Import Scene"
msgstr "দৃশà§à¦¯ ইমà§à¦ªà§‹à¦°à§à¦Ÿ করà§à¦¨"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "দৃশà§à¦¯ ইমà§à¦ªà§‹à¦°à§à¦Ÿ করা হচà§à¦›à§‡.."
+msgid "Importing Scene..."
+msgstr "দৃশà§à¦¯ ইমà§à¦ªà§‹à¦°à§à¦Ÿ করা হচà§à¦›à§‡..."
#: editor/import/resource_importer_scene.cpp
#, fuzzy
@@ -2927,8 +2927,8 @@ msgid "Generating for Mesh: "
msgstr "AABB উৎপনà§à¦¨ করà§à¦¨"
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "সà§à¦¬à¦¨à¦¿à¦°à§à¦®à¦¿à¦¤ সà§à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ চালানো হচà§à¦›à§‡.."
+msgid "Running Custom Script..."
+msgstr "সà§à¦¬à¦¨à¦¿à¦°à§à¦®à¦¿à¦¤ সà§à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ চালানো হচà§à¦›à§‡..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2943,8 +2943,8 @@ msgid "Error running post-import script:"
msgstr "ইমà§à¦ªà§‹à¦°à§à¦Ÿ-পরবরà§à¦¤à§€ সà§à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ চালানোয় সমসà§à¦¯à¦¾ হয়েছে:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "সংরকà§à¦·à¦¿à¦¤ হচà§à¦›à§‡.."
+msgid "Saving..."
+msgstr "সংরকà§à¦·à¦¿à¦¤ হচà§à¦›à§‡..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2965,8 +2965,8 @@ msgid "Import As:"
msgstr "ইমà§à¦ªà§‹à¦°à§à¦Ÿ"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "পà§à¦°à¦¿à¦¸à§‡à¦Ÿ.."
+msgid "Preset..."
+msgstr "পà§à¦°à¦¿à¦¸à§‡à¦Ÿ..."
#: editor/import_dock.cpp
#, fuzzy
@@ -3392,16 +3392,16 @@ msgid "Transition Node"
msgstr "টà§à¦°à§à¦¯à¦¾à¦¨à¦œà¦¿à¦¶à¦¨ নোড"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨à¦¸à¦®à§‚হ ইমà§à¦ªà§‹à¦°à§à¦Ÿ করà§à¦¨.."
+msgid "Import Animations..."
+msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨à¦¸à¦®à§‚হ ইমà§à¦ªà§‹à¦°à§à¦Ÿ করà§à¦¨..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "নোড ফিলà§à¦Ÿà¦¾à¦°à¦¸à¦®à§‚হ সমà§à¦ªà¦¾à¦¦à¦¨ করà§à¦¨"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "ফিলà§à¦Ÿà¦¾à¦°à¦¸à¦®à§‚হ.."
+msgid "Filters..."
+msgstr "ফিলà§à¦Ÿà¦¾à¦°à¦¸à¦®à§‚হ..."
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
@@ -3474,8 +3474,8 @@ msgstr "খà§à¦à¦œà§‡ আনার চেসà§à¦Ÿà¦¾ চলছে:"
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
-msgid "Resolving.."
-msgstr "সংরকà§à¦·à¦¿à¦¤ হচà§à¦›à§‡.."
+msgid "Resolving..."
+msgstr "সংরকà§à¦·à¦¿à¦¤ হচà§à¦›à§‡..."
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
@@ -3543,8 +3543,8 @@ msgid "Site:"
msgstr "ওয়েবসাইট:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "সমরà§à¦¥à¦¨.."
+msgid "Support..."
+msgstr "সমরà§à¦¥à¦¨..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3743,9 +3743,10 @@ msgid "Use Rotation Snap"
msgstr "ঘূরà§à¦£à¦¨ সà§à¦¨à§à¦¯à¦¾à¦ª বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
#, fuzzy
msgid "Configure Snap..."
-msgstr "সà§à¦¨à§à¦¯à¦¾à¦ª কনফিগার করà§à¦¨.."
+msgstr "সà§à¦¨à§à¦¯à¦¾à¦ª কনফিগার করà§à¦¨..."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap Relative"
@@ -4189,8 +4190,8 @@ msgid "Create Convex Collision Sibling"
msgstr "কনভেকà§à¦¸ কলিশ়ন সহোদর তৈরি করà§à¦¨"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "পà§à¦°à¦¾à¦¨à§à¦¤à¦°à§‡à¦–া মেস তৈরি করà§à¦¨.."
+msgid "Create Outline Mesh..."
+msgstr "পà§à¦°à¦¾à¦¨à§à¦¤à¦°à§‡à¦–া মেস তৈরি করà§à¦¨..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
#, fuzzy
@@ -4334,7 +4335,7 @@ msgstr "কনফিগারেশন তৈরি করা হচà§à¦›à§‡...
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Calculating grid size..."
-msgstr "গà§à¦°à¦¿à¦¡ সাইজ হিসাব করা হচà§à¦›à§‡.."
+msgstr "গà§à¦°à¦¿à¦¡ সাইজ হিসাব করা হচà§à¦›à§‡..."
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
@@ -4344,7 +4345,7 @@ msgstr "লাইটের ওকটà§à¦°à§€ (octree) তৈরি করা à¦
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
msgid "Marking walkable triangles..."
-msgstr "অনà§à¦¬à¦¾à¦¦-সমà§à¦­à¦¬ শবà§à¦¦à¦®à¦¾à¦²à¦¾/বাকà§à¦¯-সমূহ.."
+msgstr "অনà§à¦¬à¦¾à¦¦-সমà§à¦­à¦¬ শবà§à¦¦à¦®à¦¾à¦²à¦¾/বাকà§à¦¯-সমূহ..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Constructing compact heightfield..."
@@ -4367,7 +4368,7 @@ msgstr "ওকটà§à¦°à§€ (octree) গঠনবিনà§à¦¯à¦¾à¦¸ তৈরি
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
msgid "Creating polymesh..."
-msgstr "পà§à¦°à¦¾à¦¨à§à¦¤à¦°à§‡à¦–া মেস তৈরি করà§à¦¨.."
+msgstr "পà§à¦°à¦¾à¦¨à§à¦¤à¦°à§‡à¦–া মেস তৈরি করà§à¦¨..."
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
@@ -4406,8 +4407,8 @@ msgid "Error loading image:"
msgstr "ছবি লোডে সমসà§à¦¯à¦¾ হয়েছে:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "সà§à¦¬à¦šà§à¦›à¦¤à¦¾à¦¸à¦¹ কোনো পিকà§à¦¸à§‡à¦² নেই > ছবিতে ১২৮.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "সà§à¦¬à¦šà§à¦›à¦¤à¦¾à¦¸à¦¹ কোনো পিকà§à¦¸à§‡à¦² নেই > ছবিতে ১২৮..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4790,8 +4791,8 @@ msgid "Import Theme"
msgstr "থিম ইমà§à¦ªà§‹à¦°à§à¦Ÿ করà§à¦¨"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "থিম à¦à¦‡à¦°à§‚পে সংরকà§à¦·à¦£ করà§à¦¨.."
+msgid "Save Theme As..."
+msgstr "থিম à¦à¦‡à¦°à§‚পে সংরকà§à¦·à¦£ করà§à¦¨..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4891,8 +4892,8 @@ msgstr "ফেবরিট/পà§à¦°à¦¿à¦¯à¦¼-সমূহ অদলবদল/à¦
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "খà§à¦à¦œà§à¦¨.."
+msgid "Find..."
+msgstr "খà§à¦à¦œà§à¦¨..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -5095,28 +5096,28 @@ msgstr "পূরà§à¦¬à§‡à¦° বিরতিবিনà§à¦¦à§à¦¤à§‡ যান"
#: editor/plugins/script_text_editor.cpp
#, fuzzy
msgid "Convert To Uppercase"
-msgstr "à¦à¦¤à§‡ রূপানà§à¦¤à¦° করà§à¦¨.."
+msgstr "à¦à¦¤à§‡ রূপানà§à¦¤à¦° করà§à¦¨..."
#: editor/plugins/script_text_editor.cpp
#, fuzzy
msgid "Convert To Lowercase"
-msgstr "à¦à¦¤à§‡ রূপানà§à¦¤à¦° করà§à¦¨.."
+msgstr "à¦à¦¤à§‡ রূপানà§à¦¤à¦° করà§à¦¨..."
#: editor/plugins/script_text_editor.cpp
msgid "Find Previous"
msgstr "পূরà§à¦¬à§‡ খà§à¦à¦œà§à¦¨"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨.."
+msgid "Replace..."
+msgstr "পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "ফাংশনে যান.."
+msgid "Goto Function..."
+msgstr "ফাংশনে যান..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "লাইনে যান.."
+msgid "Goto Line..."
+msgstr "লাইনে যান..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5590,12 +5591,8 @@ msgid "Transform"
msgstr "রà§à¦ªà¦¾à¦¨à§à¦¤à¦°"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "সà§à¦¨à§à¦¯à¦¾à¦ª কনফিগার করà§à¦¨.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "রà§à¦ªà¦¾à¦¨à§à¦¤à¦°à§‡à¦° à¦à¦° সংলাপ.."
+msgid "Transform Dialog..."
+msgstr "রà§à¦ªà¦¾à¦¨à§à¦¤à¦°à§‡à¦° à¦à¦° সংলাপ..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5854,8 +5851,8 @@ msgid "Remove All"
msgstr "অপসারণ করà§à¦¨"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "থিম à¦à¦¡à¦¿à¦Ÿ করà§à¦¨.."
+msgid "Edit theme..."
+msgstr "থিম à¦à¦¡à¦¿à¦Ÿ করà§à¦¨..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5925,7 +5922,8 @@ msgid "Options"
msgstr "সিদà§à¦§à¦¾à¦¨à§à¦¤à¦¸à¦®à§‚হ"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+#, fuzzy
+msgid "Has,Many,Options"
msgstr "আছে,অনেক,à¦à¦•à¦¾à¦§à¦¿à¦•,সিদà§à¦§à¦¾à¦¨à§à¦¤à¦¸à¦®à§‚হ!"
#: editor/plugins/theme_editor_plugin.cpp
@@ -6055,7 +6053,7 @@ msgstr "দৃশà§à¦¯ হতে à¦à¦•à¦¤à§à¦°à¦¿à¦¤ করবেন?"
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
msgid "Tile Set"
-msgstr "TileSet (টাইল-সেট).."
+msgstr "TileSet (টাইল-সেট)..."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -6123,11 +6121,11 @@ msgstr ""
#: editor/project_export.cpp
#, fuzzy
msgid "Presets"
-msgstr "পà§à¦°à¦¿à¦¸à§‡à¦Ÿ.."
+msgstr "পà§à¦°à¦¿à¦¸à§‡à¦Ÿ..."
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "সংযোগ.."
+msgid "Add..."
+msgstr "সংযোগ..."
#: editor/project_export.cpp
msgid "Resources"
@@ -6236,6 +6234,11 @@ msgstr "পà§à¦°à¦•à¦²à§à¦ª ইমà§à¦ªà§‹à¦°à§à¦Ÿ করা হয়েছে
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "পà§à¦°à¦•à¦²à§à¦ªà§‡à¦° নাম:"
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "ফোলà§à¦¡à¦¾à¦° তৈরী করা সমà§à¦­à¦¬ হয়নি।"
@@ -6332,7 +6335,7 @@ 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?"
@@ -6418,7 +6421,7 @@ msgstr "পà§à¦¨à¦°à¦¾à¦°à¦®à§à¦­ (সেঃ):"
#: editor/project_manager.cpp
#, fuzzy
msgid "Can't run project"
-msgstr "সংযোগ.."
+msgstr "সংযোগ..."
#: editor/project_manager.cpp
msgid ""
@@ -6444,8 +6447,8 @@ msgstr "মাউসের বোতাম"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6473,8 +6476,8 @@ msgid "Control+"
msgstr "কনà§à¦Ÿà§à¦°à§‹à¦²+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "যেকোনো কী/চাবি চাপà§à¦¨.."
+msgid "Press a Key..."
+msgstr "যেকোনো কী/চাবি চাপà§à¦¨..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6667,8 +6670,8 @@ msgid "Property:"
msgstr "পà§à¦°à¦ªà¦¾à¦°à§à¦Ÿà¦¿:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
-msgstr "ওভাররাইড.."
+msgid "Override For..."
+msgstr "ওভাররাইড..."
#: editor/project_settings_editor.cpp
msgid "Input Map"
@@ -6768,12 +6771,12 @@ msgid "Easing Out-In"
msgstr "গমন-আগমন সহজ/আলগা করন"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "ফাইল.."
+msgid "File..."
+msgstr "ফাইল..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "পথ.."
+msgid "Dir..."
+msgstr "পথ..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6805,7 +6808,7 @@ msgstr "ফাইলসিসà§à¦Ÿà§‡à¦®"
#: editor/property_editor.cpp
#, fuzzy
msgid "Convert To %s"
-msgstr "à¦à¦¤à§‡ রূপানà§à¦¤à¦° করà§à¦¨.."
+msgstr "à¦à¦¤à§‡ রূপানà§à¦¤à¦° করà§à¦¨..."
#: editor/property_editor.cpp
msgid "Error loading file: Not a resource!"
@@ -6953,8 +6956,8 @@ msgid "This operation can't be done on instanced scenes."
msgstr "ইনà§à¦¸à¦Ÿà§à¦¯à¦¾à¦¨à§à¦¸ করা দৃশà§à¦¯à§‡ à¦à¦Ÿà¦¿ করা সমà§à¦­à¦¬ হবে না।"
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "নতà§à¦¨ দৃশà§à¦¯ à¦à¦‡à¦°à§‚পে সংরকà§à¦·à¦£ করà§à¦¨.."
+msgid "Save New Scene As..."
+msgstr "নতà§à¦¨ দৃশà§à¦¯ à¦à¦‡à¦°à§‚পে সংরকà§à¦·à¦£ করà§à¦¨..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
@@ -7497,12 +7500,12 @@ msgstr ""
#: modules/gdnative/gdnative_library_editor_plugin.cpp
#, fuzzy
msgid "Platform"
-msgstr "পà§à¦²à¦¾à¦Ÿà¦«à¦°à§à¦®à§‡ পà§à¦°à¦¤à¦¿à¦²à¦¿à¦ªà¦¿ করà§à¦¨.."
+msgstr "পà§à¦²à¦¾à¦Ÿà¦«à¦°à§à¦®à§‡ পà§à¦°à¦¤à¦¿à¦²à¦¿à¦ªà¦¿ করà§à¦¨..."
#: modules/gdnative/gdnative_library_editor_plugin.cpp
#, fuzzy
msgid "Dynamic Library"
-msgstr "MeshLibrary (মেস-লাইবà§à¦°à§‡à¦°à¦¿).."
+msgstr "MeshLibrary (মেস-লাইবà§à¦°à§‡à¦°à¦¿)..."
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Add an architecture entry"
@@ -7516,7 +7519,7 @@ msgstr "জিডিনà§à¦¯à¦¾à¦Ÿà¦¿à¦­"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
#, fuzzy
msgid "Library"
-msgstr "MeshLibrary (মেস-লাইবà§à¦°à§‡à¦°à¦¿).."
+msgstr "MeshLibrary (মেস-লাইবà§à¦°à§‡à¦°à¦¿)..."
#: modules/gdnative/gdnative_library_singleton_editor.cpp
#, fuzzy
@@ -8544,6 +8547,13 @@ msgstr "ফনà§à¦Ÿ তà§à¦²à¦¤à§‡/লোডে সমসà§à¦¯à¦¾ হয়েà¦
msgid "Invalid font size."
msgstr "ফনà§à¦Ÿà§‡à¦° আকার অগà§à¦°à¦¹à¦¨à¦¯à§‹à¦—à§à¦¯à¥¤"
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "পূরà§à¦¬à§‡à¦° টà§à¦¯à¦¾à¦¬"
+
+#~ msgid "Next"
+#~ msgstr "পরবরà§à¦¤à§€"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "অকারà§à¦¯à¦•à¦° অà§à¦¯à¦¾à¦•à¦¶à¦¨ ('/' বা ':' ছাড়া কিছà§à¦‡ যাবে না)।"
@@ -8573,9 +8583,6 @@ msgstr "ফনà§à¦Ÿà§‡à¦° আকার অগà§à¦°à¦¹à¦¨à¦¯à§‹à¦—à§à¦¯à¥¤"
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "পà§à¦°à¦•à¦²à§à¦ªà§‡à¦° পথে engine.cfg তৈরি করা সমà§à¦­à¦¬ হয়নি।"
-#~ msgid "Next"
-#~ msgstr "পরবরà§à¦¤à§€"
-
#~ msgid "Not found!"
#~ msgstr "খà§à¦à¦œà§‡ পাওয়া যায়নি!"
@@ -8717,8 +8724,8 @@ msgstr "ফনà§à¦Ÿà§‡à¦° আকার অগà§à¦°à¦¹à¦¨à¦¯à§‹à¦—à§à¦¯à¥¤"
#~ msgid "Exporting for %s"
#~ msgstr "%s à¦à¦° জনà§à¦¯ à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ (export) হচà§à¦›à§‡"
-#~ msgid "Setting Up.."
-#~ msgstr "সà§à¦¥à¦¾à¦ªà¦¿à¦¤/বিনà§à¦¯à¦¸à§à¦¤ হচà§à¦›à§‡.."
+#~ msgid "Setting Up..."
+#~ msgstr "সà§à¦¥à¦¾à¦ªà¦¿à¦¤/বিনà§à¦¯à¦¸à§à¦¤ হচà§à¦›à§‡..."
#~ msgid "Error loading scene."
#~ msgstr "দৃশà§à¦¯ লোডে সমসà§à¦¯à¦¾ হয়েছে।"
@@ -8772,8 +8779,8 @@ msgstr "ফনà§à¦Ÿà§‡à¦° আকার অগà§à¦°à¦¹à¦¨à¦¯à§‹à¦—à§à¦¯à¥¤"
#~ msgid "Info"
#~ msgstr "তথà§à¦¯"
-#~ msgid "Re-Import.."
-#~ msgstr "পà§à¦¨-ইমà§à¦ªà§‹à¦°à§à¦Ÿ.."
+#~ msgid "Re-Import..."
+#~ msgstr "পà§à¦¨-ইমà§à¦ªà§‹à¦°à§à¦Ÿ..."
#~ msgid "No bit masks to import!"
#~ msgstr "ইমà§à¦ªà§‹à¦°à§à¦Ÿ করার জনà§à¦¯ কোনো বিট মাসà§à¦• নেই!"
@@ -9170,14 +9177,14 @@ msgstr "ফনà§à¦Ÿà§‡à¦° আকার অগà§à¦°à¦¹à¦¨à¦¯à§‹à¦—à§à¦¯à¥¤"
#~ msgid "Zoom (%):"
#~ msgstr "জà§à¦®à§ (%):"
-#~ msgid "Skeleton.."
-#~ msgstr "সà§à¦•à§‡à¦²à§‡à¦Ÿà¦¨/কাঠাম.."
+#~ msgid "Skeleton..."
+#~ msgstr "সà§à¦•à§‡à¦²à§‡à¦Ÿà¦¨/কাঠাম..."
#~ msgid "Zoom Reset"
#~ msgstr "জà§à¦®à§ পà§à¦¨:সà§à¦¥à¦¾à¦ªà¦¨ করà§à¦¨"
-#~ msgid "Zoom Set.."
-#~ msgstr "জà§à¦®à§ নিরà§à¦§à¦¾à¦°à¦£ করà§à¦¨.."
+#~ msgid "Zoom Set..."
+#~ msgstr "জà§à¦®à§ নিরà§à¦§à¦¾à¦°à¦£ করà§à¦¨..."
#~ msgid "Set a Value"
#~ msgstr "à¦à¦•à¦Ÿà¦¿ মান নিরà§à¦§à¦¾à¦°à¦£ করà§à¦¨"
@@ -9642,8 +9649,8 @@ msgstr "ফনà§à¦Ÿà§‡à¦° আকার অগà§à¦°à¦¹à¦¨à¦¯à§‹à¦—à§à¦¯à¥¤"
#~ msgid "Export Project PCK"
#~ msgstr "পà§à¦°à¦•à¦²à§à¦ªà§‡à¦° PCK à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ করà§à¦¨"
-#~ msgid "Export.."
-#~ msgstr "à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ.."
+#~ msgid "Export..."
+#~ msgstr "à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ..."
#~ msgid "Project Export"
#~ msgstr "à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ পà§à¦°à¦•à¦²à§à¦ª"
diff --git a/editor/translations/ca.po b/editor/translations/ca.po
index a1f92b5316..d2bffb0f84 100644
--- a/editor/translations/ca.po
+++ b/editor/translations/ca.po
@@ -2,14 +2,13 @@
# 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-03-10 03:34+0000\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"
@@ -17,7 +16,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 2.20-dev\n"
+"X-Generator: Weblate 3.0\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -498,8 +497,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Desconnecta '%s' de '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "Connecta.."
+msgid "Connect..."
+msgstr "Connecta..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -919,11 +918,11 @@ msgid "Move Audio Bus"
msgstr "Mou el Bus d'Àudio"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "Anomena i Desa el Disseny del Bus d'Àudio..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr "Ubicació del Nou Disseny..."
#: editor/editor_audio_buses.cpp
@@ -1065,12 +1064,12 @@ msgid "Updating Scene"
msgstr "Actualitzant l'Escena"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr "Emmagatzemant canvis locals..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "S'està actualitzant l'escena.."
+msgid "Updating scene..."
+msgstr "S'està actualitzant l'escena..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1138,8 +1137,8 @@ msgid "Show In File Manager"
msgstr "Mostra en el Gestor de Fitxers"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Nou Directori.."
+msgid "New Folder..."
+msgstr "Nou Directori..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1400,19 +1399,19 @@ msgstr "Buida la Sortida"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "L'exportació del projecte ha fallat amb el codi d'error %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Error en desar recurs!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "Anomena i Desa el Recurs..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr "Vaja..."
#: editor/editor_node.cpp
@@ -1642,11 +1641,11 @@ msgid "Open Base Scene"
msgstr "Obre una Escena Base"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr "Obertura Ràpida d'Escenes..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr "Obertura Ràpida d'Scripts..."
#: editor/editor_node.cpp
@@ -1658,7 +1657,7 @@ msgid "Save changes to '%s' before closing?"
msgstr "Desar els canvis a '%s' abans de tancar?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr "Anomena i Desa l'Escena..."
#: editor/editor_node.cpp
@@ -1711,7 +1710,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Aquesta acció no es pot desfer. N'esteu segur?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr "Execució Ràpida de l'Escena..."
#: editor/editor_node.cpp
@@ -1872,7 +1871,7 @@ msgid "Previous tab"
msgstr "Pestanya Anterior"
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr "Filtrat de Fitxers..."
#: editor/editor_node.cpp
@@ -1884,11 +1883,11 @@ msgid "New Scene"
msgstr "Nova Escena"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr "Nova Escena heretada..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr "Obre Escena..."
#: editor/editor_node.cpp
@@ -1908,15 +1907,15 @@ msgid "Open Recent"
msgstr "Obre Recent"
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr "Converteix a..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr "Biblioteca de Models (MeshLibrary)..."
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2181,7 +2180,7 @@ msgid "Save the currently edited resource."
msgstr "Desa el recurs editat ara."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr "Anomena i Desa..."
#: editor/editor_node.cpp
@@ -2290,8 +2289,8 @@ msgid "Creating Mesh Previews"
msgstr "Creant Previsualitzacions de Malles"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Miniatura.."
+msgid "Thumbnail..."
+msgstr "Miniatura..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2443,7 +2442,7 @@ msgid "(Current)"
msgstr "(Actual)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr "S'estan buscant rèpliques..."
#: editor/export_template_manager.cpp
@@ -2490,7 +2489,7 @@ msgstr "No es pot resoldre."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Can't connect."
-msgstr "No es pot connectar.."
+msgstr "No es pot connectar..."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -2521,7 +2520,7 @@ msgid "Error requesting url: "
msgstr "Error en la sol·licitud de l'url: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr "Connexió amb la Rèplica..."
#: editor/export_template_manager.cpp
@@ -2538,7 +2537,7 @@ msgstr "No es pot resoldre"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr "Connexió en marxa..."
#: editor/export_template_manager.cpp
@@ -2551,7 +2550,7 @@ msgstr "Connectat"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "Sol·licitud en marxa..."
#: editor/export_template_manager.cpp
@@ -2685,11 +2684,11 @@ msgid "Collapse all"
msgstr "Col·lapsar tot"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Reanomena.."
+msgid "Rename..."
+msgstr "Reanomena..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr "Mou cap a..."
#: editor/filesystem_dock.cpp
@@ -2701,16 +2700,16 @@ msgid "Instance"
msgstr "Instància"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr "Edita Dependències..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr "Mostra Propietaris..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Duplica.."
+msgid "Duplicate..."
+msgstr "Duplica..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2735,7 +2734,7 @@ msgstr "Instancia les escenes seleccionades com a filles del node seleccionat."
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr "Analitzant Fitxers..."
#: editor/filesystem_dock.cpp
@@ -2801,7 +2800,7 @@ msgid "Import Scene"
msgstr "Importa Escena"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr "Important Escena..."
#: editor/import/resource_importer_scene.cpp
@@ -2813,7 +2812,7 @@ msgid "Generating for Mesh: "
msgstr "S'està generant per a la Malla: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr "Executant Script Personalitzat..."
#: editor/import/resource_importer_scene.cpp
@@ -2829,7 +2828,7 @@ msgid "Error running post-import script:"
msgstr "Error en l'execució de l'Script de post-importació:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr "Desant..."
#: editor/import_dock.cpp
@@ -2849,8 +2848,8 @@ msgid "Import As:"
msgstr "Importar com a:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Configuració.."
+msgid "Preset..."
+msgstr "Configuració..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3268,7 +3267,7 @@ msgid "Transition Node"
msgstr "Node de Transició"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr "Importa animacions..."
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3276,7 +3275,7 @@ msgid "Edit Node Filters"
msgstr "Edita els filtres de Node"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr "Filtres..."
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3344,8 +3343,8 @@ msgid "Fetching:"
msgstr "Recollida:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "s'està resolent.."
+msgid "Resolving..."
+msgstr "s'està resolent..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3411,7 +3410,7 @@ msgid "Site:"
msgstr "Lloc:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr "Suport..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3610,6 +3609,7 @@ msgid "Use Rotation Snap"
msgstr "Rotació alineada"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "Configura l'Alineament..."
@@ -3706,14 +3706,12 @@ msgid "Show Guides"
msgstr "Mostra les guies"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
msgstr "Mostra l'Origen"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 Vista"
+msgstr "Mostra el Viewport"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -4006,7 +4004,7 @@ msgstr "La Malla manca d'una superfície on delinear-hi els contorns!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "El tipus primitiu de Mesh no és PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4037,7 +4035,7 @@ msgid "Create Convex Collision Sibling"
msgstr "Crea col·lisions convexes entre nodes germans"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr "Crea una malla de contorn..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4242,7 +4240,7 @@ msgid "Error loading image:"
msgstr "Error en carregar la imatge:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr "Cap píxel amb transparència > 128 en la imatge..."
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4603,7 +4601,7 @@ msgid "Import Theme"
msgstr "Importa un Tema"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr "Desa el Tema com a..."
#: editor/plugins/script_editor_plugin.cpp
@@ -4700,7 +4698,7 @@ msgstr "Panell d'Scripts"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr "Cerca..."
#: editor/plugins/script_editor_plugin.cpp
@@ -4910,15 +4908,15 @@ msgid "Find Previous"
msgstr "Cerca l'Anterior"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr "Substitueix..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Vés a la Funció.."
+msgid "Goto Function..."
+msgstr "Vés a la Funció..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr "Vés a la Línia..."
#: editor/plugins/script_text_editor.cpp
@@ -5372,11 +5370,7 @@ msgid "Transform"
msgstr "Transforma"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Configura l'Alineament..."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr "Diàleg de Transformació..."
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5629,7 +5623,7 @@ msgid "Remove All"
msgstr "Treu-los tots"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr "Edita el Tema..."
#: editor/plugins/theme_editor_plugin.cpp
@@ -5677,14 +5671,12 @@ msgid "Checked Item"
msgstr "Element validat"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "Afegeix un Element"
+msgstr "Element de ràdio"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "Element validat"
+msgstr "Element de ràdio validat"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5699,8 +5691,8 @@ msgid "Options"
msgstr "Opcions"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr "Tens,Moltes,Diverses,Opcions!"
+msgid "Has,Many,Options"
+msgstr "Té,Moltes,Opcions"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5892,7 +5884,7 @@ msgid "Presets"
msgstr "Configuracions prestablertes"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr "Afegeix..."
#: editor/project_export.cpp
@@ -5986,6 +5978,10 @@ msgid "Imported Project"
msgstr "Project importat"
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "El nom del Projecte no és vàlid."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "No s'ha pogut crear el directori."
@@ -6185,9 +6181,11 @@ msgstr "Botó del ratolí"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
+"Nom d'acció no vàlid. No pot estar buit ni contenir '/', ':', '=', '\\' o "
+"'\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6214,8 +6212,8 @@ msgid "Control+"
msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "Premeu una Tecla.."
+msgid "Press a Key..."
+msgstr "Premeu una Tecla..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6398,7 +6396,7 @@ msgid "Property:"
msgstr "Propietat:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr "Substitutiu per a..."
#: editor/project_settings_editor.cpp
@@ -6494,11 +6492,11 @@ msgid "Easing Out-In"
msgstr "Esmorteeix Sortida-Entrada"
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr "Fitxer..."
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr "Directori..."
#: editor/property_editor.cpp
@@ -6671,7 +6669,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Aquesta operació no es pot dur a terme en escenes instanciadas."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr "Anomena i Desa la Nova Escena..."
#: editor/scene_tree_dock.cpp
@@ -7390,7 +7388,7 @@ msgstr "Trieu la distància:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "El nom de la classe no pot ser una paraula clau reservada"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -8101,7 +8099,7 @@ msgstr "Cal que la propietat Camí assenyali cap a un node Spatial vàlid."
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "WorldEnvironment necessita un recurs Ambiental."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8115,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 ""
+"Aquest WorldEnvironment s'ignora. Afegiu una càmera (per a escenes 3D) o "
+"configureu el Background Mode a Canvas (per a escenes 2D)."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8213,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 ':')."
@@ -8240,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!"
@@ -8375,8 +8379,8 @@ msgstr "La mida de la lletra no és vàlida."
#~ msgid "Exporting for %s"
#~ msgstr "Exportació per a %s"
-#~ msgid "Setting Up.."
-#~ msgstr "Instal·lant.."
+#~ msgid "Setting Up..."
+#~ msgstr "Instal·lant..."
#~ msgid "Error loading scene."
#~ msgstr "No s'ha pogut carregar l'escena."
@@ -8433,7 +8437,7 @@ msgstr "La mida de la lletra no és vàlida."
#~ msgid "Info"
#~ msgstr "Informació"
-#~ msgid "Re-Import.."
+#~ msgid "Re-Import..."
#~ msgstr "ReImporta..."
#~ msgid "No bit masks to import!"
diff --git a/editor/translations/cs.po b/editor/translations/cs.po
index ce26418cbf..1066bbad94 100644
--- a/editor/translations/cs.po
+++ b/editor/translations/cs.po
@@ -6,6 +6,7 @@
# Fadex <vitekpaulik@gmail.com>, 2017.
# Jan 'spl!te' Kondelík <j.kondelik@centrum.cz>, 2016.
# Jiri Hysek <contact@jirihysek.com>, 2017.
+# Josef KuchaÅ™ <josef.kuchar267@gmail.com>, 2018.
# Luděk Novotný <gladosicek@gmail.com>, 2016, 2018.
# Martin Novák <maidx@seznam.cz>, 2017.
# zxey <r.hozak@seznam.cz>, 2018.
@@ -13,15 +14,15 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-01-24 12:07+0000\n"
-"Last-Translator: zxey <r.hozak@seznam.cz>\n"
+"PO-Revision-Date: 2018-05-21 12:36+0000\n"
+"Last-Translator: Josef KuchaÅ™ <josef.kuchar267@gmail.com>\n"
"Language-Team: Czech <https://hosted.weblate.org/projects/godot-engine/godot/"
"cs/>\n"
"Language: cs\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.19-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -32,9 +33,8 @@ msgid "All Selection"
msgstr "Všechny vybrané"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Keyframe Time"
-msgstr "Animace: změna hodnoty"
+msgstr "Animace: ZmÄ›nit Äas klíÄového snímku"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
@@ -45,9 +45,8 @@ msgid "Anim Change Transform"
msgstr "Animace: změna transformace"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Keyframe Value"
-msgstr "Animace: změna hodnoty"
+msgstr "Animace: ZmÄ›nit hodnotu klíÄového snímku"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
@@ -92,7 +91,7 @@ msgstr "Animace: změna typu hodnot"
#: editor/animation_editor.cpp
#, fuzzy
msgid "Anim Track Change Wrap Mode"
-msgstr "ZmÄ›na režimu opakování animaÄní stopy"
+msgstr "Animace: ZmÄ›na režimu opakování animaÄní stopy"
#: editor/animation_editor.cpp
msgid "Edit Node Curve"
@@ -230,7 +229,7 @@ msgstr "Změnit opakování animace"
#: editor/animation_editor.cpp
msgid "Anim Create Typed Value Key"
-msgstr ""
+msgstr "Animace: VytvoÅ™it typovaný klíÄ"
#: editor/animation_editor.cpp
msgid "Anim Insert"
@@ -326,7 +325,7 @@ msgstr "Přechod"
#: editor/animation_editor.cpp
msgid "Scale Ratio:"
-msgstr "Poměr měřítka:"
+msgstr "Poměr zvětšení:"
#: editor/animation_editor.cpp
msgid "Call Functions in Which Node?"
@@ -504,8 +503,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Odpojit '%s' od '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "Připojit.."
+msgid "Connect..."
+msgstr "Připojit..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -876,7 +875,7 @@ msgstr "Ztlumit"
#: editor/editor_audio_buses.cpp
msgid "Bypass"
-msgstr ""
+msgstr "Obejít"
#: editor/editor_audio_buses.cpp
msgid "Bus options"
@@ -924,12 +923,12 @@ msgid "Move Audio Bus"
msgstr "Přesunout Audio Bus"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Uložit rozložení Audio Busu jako.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Uložit rozložení Audio Busu jako..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Umístění pro nové rozložení.."
+msgid "Location for New Layout..."
+msgstr "Umístění pro nové rozložení..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1066,12 +1065,12 @@ msgid "Updating Scene"
msgstr "Aktualizuji scénu"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Ukládám lokální změny.."
+msgid "Storing local changes..."
+msgstr "Ukládám lokální změny..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Aktualizuji scénu.."
+msgid "Updating scene..."
+msgstr "Aktualizuji scénu..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1139,8 +1138,8 @@ msgid "Show In File Manager"
msgstr "Ukázat ve správci souborů"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Nová složka.."
+msgid "New Folder..."
+msgstr "Nová složka..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1209,14 +1208,12 @@ msgid "Focus Path"
msgstr ""
#: editor/editor_file_dialog.cpp
-#, fuzzy
msgid "Move Favorite Up"
-msgstr "Přesunout oblíbenou položku o úroveň výš"
+msgstr "Přesunout oblíbenou položku nahoru"
#: editor/editor_file_dialog.cpp
-#, fuzzy
msgid "Move Favorite Down"
-msgstr "Přesunout oblíbenou položku o úroveň níž"
+msgstr "Přesunout oblíbenou položku dolů"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Go to parent folder"
@@ -1245,7 +1242,7 @@ msgstr ""
#: editor/editor_file_system.cpp
msgid "(Re)Importing Assets"
-msgstr ""
+msgstr "(Re)Importování assetů"
#: editor/editor_help.cpp editor/editor_node.cpp
#: editor/plugins/script_editor_plugin.cpp
@@ -1410,13 +1407,13 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "Chápu.."
+msgid "I see..."
+msgstr "Chápu..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1622,12 +1619,12 @@ msgid "Open Base Scene"
msgstr "Otevřít základní scénu"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Rychlé otevření scény.."
+msgid "Quick Open Scene..."
+msgstr "Rychle otevřít scénu..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Rychlé otevření skriptu.."
+msgid "Quick Open Script..."
+msgstr "Rychlé otevření skriptu..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1638,8 +1635,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Uložit změny '%s' před zavřením?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Uložit scénu jako.."
+msgid "Save Scene As..."
+msgstr "Uložit scénu jako..."
#: editor/editor_node.cpp
msgid "No"
@@ -1690,7 +1687,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Tuto akci nelze vrátit zpÄ›t. PokraÄovat?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr "Rychlé spuštění scény..."
#: editor/editor_node.cpp
@@ -1845,7 +1842,7 @@ msgid "Previous tab"
msgstr "Předchozí záložka"
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr "Filtrovat soubory..."
#: editor/editor_node.cpp
@@ -1857,12 +1854,12 @@ msgid "New Scene"
msgstr "Nová scéna"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Nová odvozená scéna.."
+msgid "New Inherited Scene..."
+msgstr "Nová odvozená scéna..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Otevřít scénu.."
+msgid "Open Scene..."
+msgstr "Otevřít scénu..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1881,16 +1878,16 @@ msgid "Open Recent"
msgstr "Otevřít nedávné"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Konvertovat na.."
+msgid "Convert To..."
+msgstr "Konvertovat na..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshLibrary.."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet.."
+msgid "TileSet..."
+msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2065,9 +2062,8 @@ msgid "Q&A"
msgstr "Q&A"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Issue Tracker"
-msgstr "Správa chyb"
+msgstr "Issue Tracker"
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
msgid "Community"
@@ -2115,7 +2111,7 @@ msgstr "Přehrát vlastní scénu"
#: editor/editor_node.cpp
msgid "Play Custom Scene"
-msgstr "Přehrát vlastní scénu"
+msgstr "Spustit vlastní scénu"
#: editor/editor_node.cpp
msgid "Spins when the editor window repaints!"
@@ -2150,8 +2146,8 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Uložit jako.."
+msgid "Save As..."
+msgstr "Uložit jako..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2259,8 +2255,8 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Náhled.."
+msgid "Thumbnail..."
+msgstr "Náhled..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2304,14 +2300,12 @@ msgid "Average Time (sec)"
msgstr "PrůmÄ›rný Äas (sek.)"
#: editor/editor_profiler.cpp
-#, fuzzy
msgid "Frame %"
-msgstr "% snímku"
+msgstr "Snímek %"
#: editor/editor_profiler.cpp
-#, fuzzy
msgid "Physics Frame %"
-msgstr "% fyzikálního snímku"
+msgstr "Fyzikální snímek %"
#: editor/editor_profiler.cpp editor/script_editor_debugger.cpp
msgid "Time:"
@@ -2326,7 +2320,6 @@ msgid "Self"
msgstr ""
#: editor/editor_profiler.cpp
-#, fuzzy
msgid "Frame #:"
msgstr "Snímek Ä.:"
@@ -2416,7 +2409,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2492,9 +2485,8 @@ msgid "Error requesting url: "
msgstr "Chyba požadavku o url: "
#: editor/export_template_manager.cpp
-#, fuzzy
-msgid "Connecting to Mirror.."
-msgstr "Připojuji se k mirroru.."
+msgid "Connecting to Mirror..."
+msgstr "Připojuji se k zrcadlu..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2510,22 +2502,21 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Připojuji.."
+msgid "Connecting..."
+msgstr "Připojuji..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
msgstr "Nelze se připojit"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Connected"
msgstr "Připojeno"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Posílá se žádost.."
+msgid "Requesting..."
+msgstr "Posílá se žádost..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2653,44 +2644,43 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid "Collapse all"
-msgstr ""
+msgstr "Sbalit vše"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr ""
+msgid "Rename..."
+msgstr "Přejmenovat..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr ""
+msgid "Move To..."
+msgstr "Přesunout do..."
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Open Scene(s)"
-msgstr "Otevřít scénu"
+msgstr "Otevřít scénu(y)"
#: editor/filesystem_dock.cpp
msgid "Instance"
-msgstr ""
+msgstr "Instance"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr ""
+msgid "Edit Dependencies..."
+msgstr "Upravit závislosti..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr ""
+msgid "View Owners..."
+msgstr "Zobrazit vlastníky..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Duplikovat.."
+msgid "Duplicate..."
+msgstr "Duplikovat..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
-msgstr ""
+msgstr "Předchozí adresář"
#: editor/filesystem_dock.cpp
msgid "Next Directory"
-msgstr ""
+msgstr "Následující adresář"
#: editor/filesystem_dock.cpp
msgid "Re-Scan Filesystem"
@@ -2707,7 +2697,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2773,7 +2763,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2785,7 +2775,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2801,7 +2791,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2821,8 +2811,8 @@ msgid "Import As:"
msgstr "Importovat jako:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Preset.."
+msgid "Preset..."
+msgstr "Předvolba..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -2878,7 +2868,6 @@ msgid ""
msgstr ""
#: editor/plugins/abstract_polygon_2d_editor.cpp
-#, fuzzy
msgid "Delete points"
msgstr "Odstranit body"
@@ -2983,7 +2972,7 @@ msgstr "Přehrát vybranou animaci od vybrané pozice. (D)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation position (in seconds)."
-msgstr ""
+msgstr "Pozice animace (v sekundách)."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Scale animation playback globally for the node."
@@ -3034,17 +3023,14 @@ msgid "Enable Onion Skinning"
msgstr ""
#: editor/plugins/animation_player_editor_plugin.cpp
-#, fuzzy
msgid "Directions"
msgstr "Směry"
#: editor/plugins/animation_player_editor_plugin.cpp
-#, fuzzy
msgid "Past"
-msgstr "Minulý"
+msgstr "Předcházející"
#: editor/plugins/animation_player_editor_plugin.cpp
-#, fuzzy
msgid "Future"
msgstr "Budoucí"
@@ -3159,7 +3145,6 @@ msgid "Amount:"
msgstr "Množství:"
#: editor/plugins/animation_tree_editor_plugin.cpp
-#, fuzzy
msgid "Blend:"
msgstr "Prolínání:"
@@ -3241,21 +3226,20 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Importovat animace.."
+msgid "Import Animations..."
+msgstr "Importovat animace..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "Filtry.."
+msgid "Filters..."
+msgstr "Filtry..."
#: editor/plugins/animation_tree_editor_plugin.cpp
-#, fuzzy
msgid "AnimationTree"
-msgstr "Přiblížení animace."
+msgstr "Strom animací"
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
@@ -3319,8 +3303,8 @@ msgid "Fetching:"
msgstr "Stahuji:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "Zjišťování.."
+msgid "Resolving..."
+msgstr "Zjišťování..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3386,8 +3370,8 @@ msgid "Site:"
msgstr "Web:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Podpora.."
+msgid "Support..."
+msgstr "Podpora..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3435,28 +3419,28 @@ msgstr "Nastavení přichycování"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Grid Offset:"
-msgstr ""
+msgstr "Offset mřížky:"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Grid Step:"
-msgstr ""
+msgstr "Krok mřížky:"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Rotation Offset:"
-msgstr ""
+msgstr "Offset rotace:"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Rotation Step:"
-msgstr ""
+msgstr "Krok rotace:"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Pivot"
-msgstr ""
+msgstr "Přemístit střed"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Action"
-msgstr ""
+msgstr "Přesunout akci"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move vertical guide"
@@ -3487,23 +3471,20 @@ msgid "Create new horizontal and vertical guides"
msgstr "Vytvořit nové vodorovné a svislé vodítka"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Edit IK Chain"
msgstr "Upravit IK řetězec"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Edit CanvasItem"
-msgstr ""
+msgstr "Upravit CanvasItem"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Anchors only"
msgstr "Pouze kotvy"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Change Anchors and Margins"
-msgstr "Upravit kotvy a okraje"
+msgstr "Změnit kotvy a okraje"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Change Anchors"
@@ -3519,19 +3500,20 @@ msgstr "Režim výběru"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Drag: Rotate"
-msgstr ""
+msgstr "Táhnutí: OtoÄit"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Alt+Drag: Move"
-msgstr ""
+msgstr "Alt+Táhnutí: Přemístit"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving)."
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
msgid "Alt+RMB: Depth list selection"
-msgstr ""
+msgstr "Alt+Pravé tlaÄíko myÅ¡i:"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Mode"
@@ -3569,7 +3551,6 @@ msgid "Snapping options"
msgstr "Možnosti přichytávání"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Snap to grid"
msgstr "Přichytit k mřížce"
@@ -3578,6 +3559,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "Nastavení přichytávání..."
@@ -3591,11 +3573,11 @@ msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Smart snapping"
-msgstr ""
+msgstr "Chytré přichytávání"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to parent"
-msgstr ""
+msgstr "PÅ™ichytit k rodiÄovi"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node anchor"
@@ -3611,7 +3593,7 @@ msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to guides"
-msgstr ""
+msgstr "Přichytit k vodítkům"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
@@ -3633,15 +3615,15 @@ msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Make Bones"
-msgstr ""
+msgstr "Vytvořit kosti"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Clear Bones"
-msgstr ""
+msgstr "Vymazat kosti"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Bones"
-msgstr ""
+msgstr "Zobrazit kosti"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Make IK Chain"
@@ -3662,9 +3644,8 @@ msgid "Show Grid"
msgstr "Zobrazit mřížku"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Helpers"
-msgstr "Zobrazit pomocné"
+msgstr "Zobrazit pomocníky"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Rulers"
@@ -3675,9 +3656,8 @@ msgid "Show Guides"
msgstr "Zobrazit vodítka"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "Zobrazit mřížku"
+msgstr "Zobrazit poÄátek"
#: editor/plugins/canvas_item_editor_plugin.cpp
#, fuzzy
@@ -3685,25 +3665,22 @@ msgid "Show Viewport"
msgstr "Zobrazit pomocné"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Center Selection"
msgstr "Vycentrovat výběr"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Frame Selection"
-msgstr ""
+msgstr "Výběr snímku"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Layout"
msgstr "Rozložení"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Insert Keys"
msgstr "Vložit klíÄe"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Insert Key"
msgstr "Vložit klíÄ"
@@ -3724,9 +3701,8 @@ msgid "Drag pivot from mouse position"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Set pivot at mouse position"
-msgstr "Odstranit signál"
+msgstr "Nastavit střed na pozici myši"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Multiply grid step by 2"
@@ -3755,7 +3731,7 @@ msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Create Node"
-msgstr ""
+msgstr "Vytvořit uzel"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
@@ -3774,7 +3750,7 @@ msgstr ""
#: editor/plugins/collision_polygon_editor_plugin.cpp
msgid "Create Poly3D"
-msgstr ""
+msgstr "Vytvořit Poly3D"
#: editor/plugins/collision_shape_2d_editor_plugin.cpp
msgid "Set Handle"
@@ -3782,17 +3758,17 @@ msgstr ""
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Remove item %d?"
-msgstr ""
+msgstr "Odstranit %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 "Přidat položku"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Remove Selected Item"
-msgstr ""
+msgstr "Odstranit vybranou položku"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Import from Scene"
@@ -3804,11 +3780,11 @@ msgstr "Aktualizovat ze scény"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Flat0"
-msgstr ""
+msgstr "Flat0"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Flat1"
-msgstr ""
+msgstr "Flat1"
#: editor/plugins/curve_editor_plugin.cpp
#, fuzzy
@@ -3883,7 +3859,7 @@ msgstr ""
#: editor/plugins/item_list_editor_plugin.cpp
msgid "Item %d"
-msgstr ""
+msgstr "Položka %d"
#: editor/plugins/item_list_editor_plugin.cpp
msgid "Items"
@@ -3891,17 +3867,19 @@ msgstr "Položky"
#: editor/plugins/item_list_editor_plugin.cpp
msgid "Item List Editor"
-msgstr ""
+msgstr "Editor seznamu položek"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid ""
"No OccluderPolygon2D resource on this node.\n"
"Create and assign one?"
msgstr ""
+"Na tomto uzlu není žádný OccluderPolygon2D.\n"
+"Vytvořit a přiřadit k tomuto uzlu?"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Create Occluder Polygon"
-msgstr ""
+msgstr "Vytvořit Occluder Polygon"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Create a new polygon from scratch."
@@ -3937,19 +3915,19 @@ msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "This doesn't work on scene root!"
-msgstr ""
+msgstr "Toto v kořenu scény nefunguje!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Trimesh Shape"
-msgstr ""
+msgstr "Vytvořit Trimesh Shape"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Convex Shape"
-msgstr ""
+msgstr "Vytvořit Convex Shape"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Navigation Mesh"
-msgstr ""
+msgstr "Vytvořit Navigation Mesh"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Contained Mesh is not of type ArrayMesh."
@@ -4008,7 +3986,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4020,7 +3998,6 @@ msgid "View UV2"
msgstr "Zobrazit UV2"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-#, fuzzy
msgid "Unwrap UV2 for Lightmap/AO"
msgstr "Rozbalit UV2 pro Lightmapu/AO"
@@ -4103,15 +4080,15 @@ msgstr ""
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "X-Axis"
-msgstr ""
+msgstr "Osa X"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Y-Axis"
-msgstr ""
+msgstr "Osa Y"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Z-Axis"
-msgstr ""
+msgstr "Osa Z"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Mesh Up Axis:"
@@ -4127,11 +4104,11 @@ msgstr ""
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Random Scale:"
-msgstr ""
+msgstr "Náhodné měřítko:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Populate"
-msgstr ""
+msgstr "Naplnit"
#: editor/plugins/navigation_mesh_editor_plugin.cpp
msgid "Bake!"
@@ -4215,7 +4192,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4278,7 +4255,7 @@ msgstr ""
#: editor/plugins/particles_editor_plugin.cpp
msgid "Generate AABB"
-msgstr ""
+msgstr "Vygenerovat AABB"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Mesh"
@@ -4335,7 +4312,7 @@ msgstr ""
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Move Point in Curve"
-msgstr ""
+msgstr "Přesunout bod v křivce"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Move In-Control in Curve"
@@ -4348,17 +4325,18 @@ msgstr ""
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Select Points"
-msgstr ""
+msgstr "Vybrat body"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
+#, fuzzy
msgid "Shift+Drag: Select Control Points"
-msgstr ""
+msgstr "Shift+Táhnutí:"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Click: Add Point"
-msgstr ""
+msgstr "Kliknutí: Přidat bod"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
@@ -4382,16 +4360,16 @@ msgstr ""
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Delete Point"
-msgstr ""
+msgstr "Odstranit bod"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Close Curve"
-msgstr ""
+msgstr "Uzavřít křivku"
#: editor/plugins/path_editor_plugin.cpp
msgid "Curve Point #"
-msgstr ""
+msgstr "Bod křivky #"
#: editor/plugins/path_editor_plugin.cpp
#, fuzzy
@@ -4399,9 +4377,8 @@ msgid "Set Curve Point Position"
msgstr "Odstranit signál"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Set Curve In Position"
-msgstr "Odstranit signál"
+msgstr "Nastavit křivku na pozici"
#: editor/plugins/path_editor_plugin.cpp
#, fuzzy
@@ -4410,11 +4387,11 @@ msgstr "Odstranit signál"
#: editor/plugins/path_editor_plugin.cpp
msgid "Split Path"
-msgstr ""
+msgstr "Rozdělit cestu"
#: editor/plugins/path_editor_plugin.cpp
msgid "Remove Path Point"
-msgstr ""
+msgstr "Odstranit bod cesty"
#: editor/plugins/path_editor_plugin.cpp
#, fuzzy
@@ -4431,7 +4408,7 @@ msgstr "Vytvořit UV mapu"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Transform UV Map"
-msgstr ""
+msgstr "Transformovat UV mapu"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Polygon 2D UV Editor"
@@ -4443,16 +4420,15 @@ msgstr "Přesunout bod"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Ctrl: Rotate"
-msgstr ""
+msgstr "Ctrl: OtoÄit"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Shift: Move All"
msgstr "Shift: Přesunout vše"
#: editor/plugins/polygon_2d_editor_plugin.cpp
-#, fuzzy
msgid "Shift+Ctrl: Scale"
-msgstr "Shift+Ctrl: Zvětšení"
+msgstr "Shift+Ctrl: Změnit měřítko"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Move Polygon"
@@ -4464,7 +4440,7 @@ msgstr "OtoÄit polygon"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Scale Polygon"
-msgstr ""
+msgstr "Změnit měřítko mnohoúhelníku"
#: editor/plugins/polygon_2d_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4523,18 +4499,18 @@ msgstr "Schránka zdroje je prázdná!"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/scene_tree_dock.cpp editor/scene_tree_editor.cpp
msgid "Open in Editor"
-msgstr ""
+msgstr "Otevřít v editoru"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/scene_tree_editor.cpp
msgid "Instance:"
-msgstr ""
+msgstr "Instance:"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp
#: editor/scene_tree_editor.cpp editor/script_editor_debugger.cpp
msgid "Type:"
-msgstr ""
+msgstr "Typ:"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
@@ -4581,13 +4557,12 @@ msgid "Import Theme"
msgstr "Importovat motiv"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Uložit motiv jako.."
+msgid "Save Theme As..."
+msgstr "Uložit motiv jako..."
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid " Class Reference"
-msgstr " ReferenÄní třídy"
+msgstr " Reference třídy"
#: editor/plugins/script_editor_plugin.cpp
msgid "Sort"
@@ -4627,12 +4602,11 @@ msgstr "Uložit vše"
#: editor/plugins/script_editor_plugin.cpp
msgid "Soft Reload Script"
-msgstr ""
+msgstr "Lehký restart skriptu"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Copy Script Path"
-msgstr "Zkopírovat uzly"
+msgstr "Zkopírovat cestu ke skriptu"
#: editor/plugins/script_editor_plugin.cpp
msgid "Show In File System"
@@ -4680,8 +4654,8 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Najít.."
+msgid "Find..."
+msgstr "Najít..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4699,7 +4673,7 @@ msgstr "Vstoupit"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Break"
-msgstr ""
+msgstr "Přerušit"
#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp
#: editor/script_editor_debugger.cpp
@@ -4708,7 +4682,7 @@ msgstr "PokraÄovat"
#: editor/plugins/script_editor_plugin.cpp
msgid "Keep Debugger Open"
-msgstr ""
+msgstr "Nechat ladící program otevřený"
#: editor/plugins/script_editor_plugin.cpp
msgid "Debug with external editor"
@@ -4821,12 +4795,11 @@ msgstr "Odsadit zprava"
#: editor/plugins/script_text_editor.cpp
msgid "Toggle Comment"
-msgstr ""
+msgstr "Přepnout komentář"
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Fold/Unfold Line"
-msgstr "Běž na řádek"
+msgstr "Složit/Rozložit řádek"
#: editor/plugins/script_text_editor.cpp
msgid "Fold All Lines"
@@ -4890,16 +4863,16 @@ msgid "Find Previous"
msgstr "Najít předchozí"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Nahradit.."
+msgid "Replace..."
+msgstr "Nahradit..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Přejít na funkci.."
+msgid "Goto Function..."
+msgstr "Přejít na funkci..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Přejít na řádek.."
+msgid "Goto Line..."
+msgstr "Přejít na řádek..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -4947,7 +4920,7 @@ msgstr "Změnit skalární funkci"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Function"
-msgstr ""
+msgstr "Změnit vektorovou funkci"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Uniform"
@@ -5043,19 +5016,19 @@ msgstr "Perspektivní"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Aborted."
-msgstr ""
+msgstr "Transformace zrušena."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "X-Axis Transform."
-msgstr ""
+msgstr "Změnit osu X."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Y-Axis Transform."
-msgstr ""
+msgstr "Změnit osu Y."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Z-Axis Transform."
-msgstr ""
+msgstr "Změnit osu Z."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Plane Transform."
@@ -5063,7 +5036,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scaling: "
-msgstr ""
+msgstr "Škálování: "
#: editor/plugins/spatial_editor_plugin.cpp
#, fuzzy
@@ -5080,7 +5053,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Animation Key Inserted."
-msgstr ""
+msgstr "AnimaÄní klÃ­Ä vložen."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Objects Drawn"
@@ -5103,9 +5076,8 @@ msgid "Draw Calls"
msgstr "Vykreslovací volání"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Vertices"
-msgstr "Vertexy"
+msgstr "Vrcholy"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "FPS"
@@ -5188,9 +5160,8 @@ msgid "Display Unshaded"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "View Environment"
-msgstr "Zobrazení prostředí"
+msgstr "Zobrazit prostředí"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Gizmos"
@@ -5247,7 +5218,7 @@ msgstr "Rychlost volného pohledu"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "XForm Dialog"
-msgstr ""
+msgstr "XForm Dialog"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Select Mode (Q)"
@@ -5259,6 +5230,9 @@ msgid ""
"Alt+Drag: Move\n"
"Alt+RMB: Depth list selection"
msgstr ""
+"Táhnutí: OtoÄit\n"
+"Alt+Táhnutí: Přemístit\n"
+"Alt+Pravé tlaÄíko myÅ¡i: VýbÄ›r seznamu hloubky"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Move Mode (W)"
@@ -5314,19 +5288,19 @@ msgstr "Přepnout perspektivní/ortogonální pohled"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Insert Animation Key"
-msgstr ""
+msgstr "Vložit animaÄní klíÄ"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Focus Origin"
-msgstr ""
+msgstr "Zaměřit poÄátek"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Focus Selection"
-msgstr ""
+msgstr "Zaměřit výběr"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Align Selection With View"
-msgstr ""
+msgstr "Zarovnat výběr s pohledem"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Tool Select"
@@ -5353,44 +5327,40 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Nastavit přichycení.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
-msgstr ""
+msgstr "1 výřez"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "2 Viewports"
-msgstr ""
+msgstr "2 výřezy"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "2 Viewports (Alt)"
-msgstr ""
+msgstr "2 výřezy (Alt)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "3 Viewports"
-msgstr ""
+msgstr "3 výřezy"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "3 Viewports (Alt)"
-msgstr ""
+msgstr "3 výřezy (Alt)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "4 Viewports"
-msgstr ""
+msgstr "4 výřezy"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Origin"
-msgstr ""
+msgstr "Zobrazit poÄátek"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Grid"
-msgstr ""
+msgstr "Zobrazit mřížku"
#: editor/plugins/spatial_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
@@ -5444,11 +5414,11 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotate (deg.):"
-msgstr ""
+msgstr "OtoÄit (stupnÄ›):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scale (ratio):"
-msgstr ""
+msgstr "Změnit měřítko (poměr):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Type"
@@ -5456,11 +5426,11 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Pre"
-msgstr ""
+msgstr "Před"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Post"
-msgstr ""
+msgstr "Po"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "ERROR: Couldn't load frame resource!"
@@ -5468,7 +5438,7 @@ msgstr ""
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Add Frame"
-msgstr ""
+msgstr "Přidat snímek"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Resource clipboard is empty or not a texture!"
@@ -5476,7 +5446,7 @@ msgstr ""
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Paste Frame"
-msgstr ""
+msgstr "Vložit snímek"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Add Empty"
@@ -5488,27 +5458,27 @@ msgstr ""
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Change Animation FPS"
-msgstr ""
+msgstr "Změnit FPS animace"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "(empty)"
-msgstr ""
+msgstr "(prázdný)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Animations"
-msgstr ""
+msgstr "Animace"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Speed (FPS):"
-msgstr ""
+msgstr "Rychlost (FPS):"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Loop"
-msgstr ""
+msgstr "SmyÄka"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Animation Frames"
-msgstr ""
+msgstr "Snímky animace"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Insert Empty (Before)"
@@ -5525,7 +5495,7 @@ msgstr "Zkopírovat uzly"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Move (After)"
-msgstr ""
+msgstr "Přemístit (za)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "SpriteFrames"
@@ -5549,7 +5519,7 @@ msgstr ""
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "<None>"
-msgstr ""
+msgstr "<Žádné>"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Pixel Snap"
@@ -5566,25 +5536,25 @@ msgstr ""
#: editor/plugins/texture_region_editor_plugin.cpp
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Offset:"
-msgstr ""
+msgstr "Offset:"
#: editor/plugins/texture_region_editor_plugin.cpp
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Step:"
-msgstr ""
+msgstr "Krok:"
#: editor/plugins/texture_region_editor_plugin.cpp
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Separation:"
-msgstr ""
+msgstr "Oddělení:"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Texture Region"
-msgstr ""
+msgstr "Oblast textury"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Texture Region Editor"
-msgstr ""
+msgstr "Editor oblasti textury"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Can't save theme to file:"
@@ -5592,29 +5562,28 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
msgid "Add All Items"
-msgstr ""
+msgstr "Přidat všechny položky"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Add All"
-msgstr ""
+msgstr "Přidat vše"
#: editor/plugins/theme_editor_plugin.cpp
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Remove Item"
-msgstr ""
+msgstr "Odstranit položku"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Remove All Items"
-msgstr "Odstranit výběr"
+msgstr "Odstranit všechny položky"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Remove All"
msgstr "Odebrat vše"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr ""
+msgid "Edit theme..."
+msgstr "Editovat téma..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5622,15 +5591,15 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
msgid "Add Class Items"
-msgstr ""
+msgstr "Přidat položky třídy"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Remove Class Items"
-msgstr ""
+msgstr "Odstranit položky třídy"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Create Empty Template"
-msgstr ""
+msgstr "Vytvořit prázdnou šablonu"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Create Empty Editor Template"
@@ -5642,19 +5611,20 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
msgid "CheckBox Radio1"
-msgstr ""
+msgstr "CheckBox Radio1"
#: editor/plugins/theme_editor_plugin.cpp
msgid "CheckBox Radio2"
-msgstr ""
+msgstr "CheckBox Radio2"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Item"
-msgstr ""
+msgstr "Položka"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
msgid "Check Item"
-msgstr ""
+msgstr "Zkontrolovat položku"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Checked Item"
@@ -5678,61 +5648,60 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
msgid "Options"
-msgstr ""
+msgstr "Možnosti"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr ""
+#, fuzzy
+msgid "Has,Many,Options"
+msgstr "Možnosti"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
-msgstr ""
+msgstr "Tab 1"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 2"
-msgstr ""
+msgstr "Tab 2"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 3"
-msgstr ""
+msgstr "Tab 3"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Data Type:"
-msgstr ""
+msgstr "Datový typ:"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Icon"
-msgstr ""
+msgstr "Ikona"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Style"
-msgstr ""
+msgstr "Styl"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Font"
-msgstr ""
+msgstr "Font"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Color"
-msgstr ""
+msgstr "Barva"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme"
msgstr ""
#: editor/plugins/tile_map_editor_plugin.cpp
-#, fuzzy
msgid "Erase Selection"
-msgstr "Změnit měřítko výběru"
+msgstr "Vymazat oznaÄené"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Paint TileMap"
msgstr ""
#: editor/plugins/tile_map_editor_plugin.cpp
-#, fuzzy
msgid "Line Draw"
-msgstr "Lineární"
+msgstr "Nakreslit Äáru"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rectangle Paint"
@@ -5760,11 +5729,11 @@ msgstr ""
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Mirror X"
-msgstr ""
+msgstr "Zrcadlit X"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Mirror Y"
-msgstr ""
+msgstr "Zrcadlit Y"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Paint Tile"
@@ -5776,19 +5745,19 @@ msgstr ""
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 0 degrees"
-msgstr ""
+msgstr "OtoÄit o 0 stupňů"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 90 degrees"
-msgstr ""
+msgstr "OtoÄit o 90 stupňů"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 180 degrees"
-msgstr ""
+msgstr "OtoÄit o 180 stupňů"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 270 degrees"
-msgstr ""
+msgstr "OtoÄit o 270 stupňů"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Could not find tile:"
@@ -5796,15 +5765,15 @@ msgstr ""
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Item name or ID:"
-msgstr ""
+msgstr "Název položky nebo ID:"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from scene?"
-msgstr ""
+msgstr "Vytvořit ze scény?"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Merge from scene?"
-msgstr ""
+msgstr "SlouÄit ze scény?"
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
@@ -5813,15 +5782,15 @@ msgstr "Soubor:"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
-msgstr ""
+msgstr "Vytvořit ze scény"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Merge from Scene"
-msgstr ""
+msgstr "SlouÄit ze scény"
#: editor/plugins/tile_set_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Error"
-msgstr ""
+msgstr "Chyba"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Autotiles"
@@ -5864,7 +5833,7 @@ msgstr "Odstranit"
#: editor/project_export.cpp
msgid "Delete preset '%s'?"
-msgstr "Odstranit preset '%s'?"
+msgstr "Odstranit předvolbu '%s'?"
#: editor/project_export.cpp
msgid "Export templates for this platform are missing/corrupted: "
@@ -5872,35 +5841,35 @@ msgstr "Exportní šablony pro tuto platformu chybí nebo jsou poškozené: "
#: editor/project_export.cpp
msgid "Presets"
-msgstr ""
+msgstr "Předvolby"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr ""
+msgid "Add..."
+msgstr "Přidat..."
#: editor/project_export.cpp
msgid "Resources"
-msgstr ""
+msgstr "Zdroje"
#: editor/project_export.cpp
msgid "Export all resources in the project"
-msgstr ""
+msgstr "Exportovat vÄechny zdroje tohoto projektu"
#: editor/project_export.cpp
msgid "Export selected scenes (and dependencies)"
-msgstr ""
+msgstr "Exportovat vybrané scény (a závislosti)"
#: editor/project_export.cpp
msgid "Export selected resources (and dependencies)"
-msgstr ""
+msgstr "Exportovat vybrané zdroje (a závislosti)"
#: editor/project_export.cpp
msgid "Export Mode:"
-msgstr ""
+msgstr "Expertní režim:"
#: editor/project_export.cpp
msgid "Resources to export:"
-msgstr ""
+msgstr "Zdroje k exportu:"
#: editor/project_export.cpp
msgid ""
@@ -5966,6 +5935,11 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Jméno projektu:"
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "Nelze vytvořit složku."
@@ -6013,11 +5987,11 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Import & Edit"
-msgstr ""
+msgstr "Importovat a upravit"
#: editor/project_manager.cpp
msgid "Create New Project"
-msgstr ""
+msgstr "Vytvořit nový projekt"
#: editor/project_manager.cpp
msgid "Create & Edit"
@@ -6025,7 +5999,7 @@ msgstr "Vytvořit a editovat"
#: editor/project_manager.cpp
msgid "Install Project:"
-msgstr ""
+msgstr "Instalovat projekt:"
#: editor/project_manager.cpp
msgid "Install & Edit"
@@ -6033,7 +6007,7 @@ msgstr "Instalovat a editovat"
#: editor/project_manager.cpp
msgid "Project Name:"
-msgstr ""
+msgstr "Jméno projektu:"
#: editor/project_manager.cpp
msgid "Create folder"
@@ -6041,15 +6015,15 @@ msgstr "Vytvořit složku"
#: editor/project_manager.cpp
msgid "Project Path:"
-msgstr ""
+msgstr "Cesta k projektu:"
#: editor/project_manager.cpp
msgid "Browse"
-msgstr ""
+msgstr "Procházet"
#: editor/project_manager.cpp
msgid "Unnamed Project"
-msgstr ""
+msgstr "Nepojmenovaný projekt"
#: editor/project_manager.cpp
msgid "Can't open project"
@@ -6057,7 +6031,7 @@ msgstr "Nelze otevřít projekt"
#: editor/project_manager.cpp
msgid "Are you sure to open more than one project?"
-msgstr ""
+msgstr "Jste si jisti, že chcete otevřit více než jeden projekt?"
#: editor/project_manager.cpp
msgid ""
@@ -6074,11 +6048,11 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Are you sure to run more than one project?"
-msgstr ""
+msgstr "Jste si jisti, že chcete spustit více než jeden projekt?"
#: editor/project_manager.cpp
msgid "Remove project from the list? (Folder contents will not be modified)"
-msgstr ""
+msgstr "Odstranit projekt ze seznamu? (Obsah složky zůstane nedotÄen)"
#: editor/project_manager.cpp
msgid ""
@@ -6102,15 +6076,15 @@ msgstr "Seznam projektů"
#: editor/project_manager.cpp
msgid "Scan"
-msgstr ""
+msgstr "Skenovat"
#: editor/project_manager.cpp
msgid "Select a Folder to Scan"
-msgstr ""
+msgstr "Vyberte složku pro skenování"
#: editor/project_manager.cpp
msgid "New Project"
-msgstr ""
+msgstr "Nový projekt"
#: editor/project_manager.cpp
msgid "Templates"
@@ -6118,11 +6092,11 @@ msgstr "Å ablony"
#: editor/project_manager.cpp
msgid "Exit"
-msgstr ""
+msgstr "UkonÄit"
#: editor/project_manager.cpp
msgid "Restart Now"
-msgstr ""
+msgstr "Restartovat nyní"
#: editor/project_manager.cpp
msgid "Can't run project"
@@ -6148,17 +6122,20 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Mouse Button"
-msgstr ""
+msgstr "TlaÄítko myÅ¡i"
#: editor/project_settings_editor.cpp
+#, fuzzy
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
+"Neplatné jméno akce. Nesmí být prázdné nebo obsahovat '/', ':', '=', '\\' "
+"nebo '\"'"
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
-msgstr ""
+msgstr "Akce '%s' již existuje!"
#: editor/project_settings_editor.cpp
msgid "Rename Input Action Event"
@@ -6178,51 +6155,51 @@ msgstr "Alt+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "Control+"
-msgstr ""
+msgstr "Ctrl+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr ""
+msgid "Press a Key..."
+msgstr "Stiskněte klávesu..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
-msgstr ""
+msgstr "Index tlaÄítka myÅ¡i:"
#: editor/project_settings_editor.cpp
msgid "Left Button"
-msgstr ""
+msgstr "Levé tlaÄítko"
#: editor/project_settings_editor.cpp
msgid "Right Button"
-msgstr ""
+msgstr "Pravé tlaÄítko"
#: editor/project_settings_editor.cpp
msgid "Middle Button"
-msgstr ""
+msgstr "ProstÅ™ední tlaÄítko"
#: editor/project_settings_editor.cpp
msgid "Wheel Up Button"
-msgstr ""
+msgstr "KoleÄko nahoru"
#: editor/project_settings_editor.cpp
msgid "Wheel Down Button"
-msgstr ""
+msgstr "KoleÄko dolů"
#: editor/project_settings_editor.cpp
msgid "Button 6"
-msgstr ""
+msgstr "TlaÄítko Ä. 6"
#: editor/project_settings_editor.cpp
msgid "Button 7"
-msgstr ""
+msgstr "TlaÄítko Ä. 7"
#: editor/project_settings_editor.cpp
msgid "Button 8"
-msgstr ""
+msgstr "TlaÄítko Ä. 8"
#: editor/project_settings_editor.cpp
msgid "Button 9"
-msgstr ""
+msgstr "TlaÄítko Ä. 9"
#: editor/project_settings_editor.cpp
msgid "Joypad Axis Index:"
@@ -6247,7 +6224,7 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Add Event"
-msgstr ""
+msgstr "Přidat akci"
#: editor/project_settings_editor.cpp
msgid "Device"
@@ -6287,7 +6264,7 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "No property '%s' exists."
-msgstr ""
+msgstr "Vlastnost '%s' neexistuje."
#: editor/project_settings_editor.cpp
msgid "Setting '%s' is internal, and it can't be deleted."
@@ -6299,7 +6276,7 @@ msgstr "Odstranit položku"
#: editor/project_settings_editor.cpp
msgid "Already existing"
-msgstr ""
+msgstr "Již existující"
#: editor/project_settings_editor.cpp
msgid "Add Input Action"
@@ -6307,11 +6284,11 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Error saving settings."
-msgstr ""
+msgstr "Chyba při ukládání nastavení."
#: editor/project_settings_editor.cpp
msgid "Settings saved OK."
-msgstr ""
+msgstr "Nastavení úspěšně uloženo."
#: editor/project_settings_editor.cpp
msgid "Override for Feature"
@@ -6319,11 +6296,11 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Add Translation"
-msgstr ""
+msgstr "Přidat překlad"
#: editor/project_settings_editor.cpp
msgid "Remove Translation"
-msgstr ""
+msgstr "Odstranit překlad"
#: editor/project_settings_editor.cpp
msgid "Add Remapped Path"
@@ -6360,14 +6337,14 @@ msgstr "Nastavení projektu (project.godot)"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "General"
-msgstr ""
+msgstr "Všeobecné"
#: editor/project_settings_editor.cpp editor/property_editor.cpp
msgid "Property:"
-msgstr ""
+msgstr "Vlastnost:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6376,35 +6353,35 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action:"
-msgstr ""
+msgstr "Akce:"
#: editor/project_settings_editor.cpp
msgid "Device:"
-msgstr ""
+msgstr "Zařízení:"
#: editor/project_settings_editor.cpp
msgid "Index:"
-msgstr ""
+msgstr "Index:"
#: editor/project_settings_editor.cpp
msgid "Localization"
-msgstr ""
+msgstr "Lokalizace"
#: editor/project_settings_editor.cpp
msgid "Translations"
-msgstr ""
+msgstr "Překlady"
#: editor/project_settings_editor.cpp
msgid "Translations:"
-msgstr ""
+msgstr "Překlady:"
#: editor/project_settings_editor.cpp
msgid "Remaps"
-msgstr ""
+msgstr "Přemapování"
#: editor/project_settings_editor.cpp
msgid "Resources:"
-msgstr ""
+msgstr "Zdroje:"
#: editor/project_settings_editor.cpp
msgid "Remaps by Locale:"
@@ -6452,7 +6429,7 @@ msgstr ""
#: editor/property_editor.cpp
msgid "Zero"
-msgstr ""
+msgstr "Nula"
#: editor/property_editor.cpp
msgid "Easing In-Out"
@@ -6463,37 +6440,36 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
-msgstr ""
+msgid "File..."
+msgstr "Soubor..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr ""
+msgid "Dir..."
+msgstr "Složka..."
#: editor/property_editor.cpp
msgid "Assign"
-msgstr ""
+msgstr "Přiřadit"
#: editor/property_editor.cpp
-#, fuzzy
msgid "Select Node"
-msgstr "Vybrat vše"
+msgstr "Vybrat uzel"
#: editor/property_editor.cpp
msgid "New Script"
-msgstr ""
+msgstr "Nový skript"
#: editor/property_editor.cpp
msgid "New %s"
-msgstr ""
+msgstr "Nový %s"
#: editor/property_editor.cpp
msgid "Make Unique"
-msgstr ""
+msgstr "Vytvořit unikátní"
#: editor/property_editor.cpp
msgid "Show in File System"
-msgstr ""
+msgstr "Zobrazit v souborovém systému"
#: editor/property_editor.cpp
msgid "Convert To %s"
@@ -6508,9 +6484,8 @@ msgid "Selected node is not a Viewport!"
msgstr ""
#: editor/property_editor.cpp
-#, fuzzy
msgid "Pick a Node"
-msgstr "Vložit uzly"
+msgstr "Vybrat uzel"
#: editor/property_editor.cpp
msgid "Bit %d, val %d."
@@ -6522,20 +6497,19 @@ msgstr ""
#: editor/property_editor.cpp
msgid "[Empty]"
-msgstr ""
+msgstr "[Prázdné]"
#: editor/property_editor.cpp modules/visual_script/visual_script_editor.cpp
msgid "Set"
-msgstr ""
+msgstr "Nastavit"
#: editor/property_editor.cpp
msgid "Properties:"
-msgstr ""
+msgstr "Vlastnosti:"
#: editor/property_selector.cpp
-#, fuzzy
msgid "Select Property"
-msgstr "Přidat vlastnost setter"
+msgstr "Vybrat vlastnost"
#: editor/property_selector.cpp
msgid "Select Virtual Method"
@@ -6575,15 +6549,15 @@ msgstr ""
#: editor/run_settings_dialog.cpp
msgid "Current Scene"
-msgstr ""
+msgstr "Aktuální scéna"
#: editor/run_settings_dialog.cpp
msgid "Main Scene"
-msgstr ""
+msgstr "Hlavní scéna"
#: editor/run_settings_dialog.cpp
msgid "Main Scene Arguments:"
-msgstr ""
+msgstr "Argumenty hlavní scény:"
#: editor/run_settings_dialog.cpp
msgid "Scene Run Settings"
@@ -6600,7 +6574,7 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Error loading scene from %s"
-msgstr ""
+msgstr "Chyba pÅ™i naÄítání scény z %s"
#: editor/scene_tree_dock.cpp
msgid ""
@@ -6618,19 +6592,19 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Move Node In Parent"
-msgstr ""
+msgstr "PÅ™esunout uzel v rodiÄi"
#: editor/scene_tree_dock.cpp
msgid "Move Nodes In Parent"
-msgstr ""
+msgstr "PÅ™esunout uzly v rodiÄi"
#: editor/scene_tree_dock.cpp
msgid "Duplicate Node(s)"
-msgstr ""
+msgstr "Duplikovat uzel/uzly"
#: editor/scene_tree_dock.cpp
msgid "Delete Node(s)?"
-msgstr ""
+msgstr "Odstranit uzel/uzly?"
#: editor/scene_tree_dock.cpp
msgid "Can not perform with the root node."
@@ -6641,8 +6615,8 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr ""
+msgid "Save New Scene As..."
+msgstr "Uložit novou scénu jako..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
@@ -6658,7 +6632,7 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Makes Sense!"
-msgstr ""
+msgstr "Dává smysl!"
#: editor/scene_tree_dock.cpp
msgid "Can't operate on nodes from a foreign scene!"
@@ -6670,7 +6644,7 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Remove Node(s)"
-msgstr ""
+msgstr "Odstranit uzel/uzly"
#: editor/scene_tree_dock.cpp
msgid ""
@@ -6680,7 +6654,7 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Error saving scene."
-msgstr ""
+msgstr "Chyba při ukládání scény."
#: editor/scene_tree_dock.cpp
msgid "Error duplicating scene to save it."
@@ -6696,11 +6670,11 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Delete Node(s)"
-msgstr ""
+msgstr "Odstranit uzel/uzly"
#: editor/scene_tree_dock.cpp
msgid "Add Child Node"
-msgstr ""
+msgstr "Přidat podřízený uzel"
#: editor/scene_tree_dock.cpp
msgid "Instance Child Scene"
@@ -6708,11 +6682,11 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Change Type"
-msgstr ""
+msgstr "Změnit typ"
#: editor/scene_tree_dock.cpp
msgid "Attach Script"
-msgstr ""
+msgstr "Připojit skript"
#: editor/scene_tree_dock.cpp
msgid "Clear Script"
@@ -6720,16 +6694,15 @@ msgstr "Vymazat skript"
#: editor/scene_tree_dock.cpp
msgid "Merge From Scene"
-msgstr ""
+msgstr "SlouÄit ze scény"
#: editor/scene_tree_dock.cpp
msgid "Save Branch as Scene"
-msgstr ""
+msgstr "Uložit větev jako scénu"
#: editor/scene_tree_dock.cpp
-#, fuzzy
msgid "Copy Node Path"
-msgstr "Kopírovat cestu uzlu"
+msgstr "Kopírovat cestu k uzlu"
#: editor/scene_tree_dock.cpp
msgid "Delete (No Confirm)"
@@ -6737,7 +6710,7 @@ msgstr "Odstranit (bez potvrzení)"
#: editor/scene_tree_dock.cpp
msgid "Add/Create a New Node"
-msgstr ""
+msgstr "Přidat/Vytvořit nový uzel"
#: editor/scene_tree_dock.cpp
msgid ""
@@ -6746,26 +6719,24 @@ msgid ""
msgstr ""
#: editor/scene_tree_dock.cpp
-#, fuzzy
msgid "Filter nodes"
msgstr "Filtrovat uzly"
#: editor/scene_tree_dock.cpp
msgid "Attach a new or existing script for the selected node."
-msgstr ""
+msgstr "Připojit nový, nebo existující skript k vybranému uzlu."
#: editor/scene_tree_dock.cpp
msgid "Clear a script for the selected node."
msgstr ""
#: editor/scene_tree_dock.cpp
-#, fuzzy
msgid "Remote"
msgstr "Vzdálený"
#: editor/scene_tree_dock.cpp
msgid "Local"
-msgstr ""
+msgstr "Místní"
#: editor/scene_tree_dock.cpp
msgid "Clear Inheritance? (No Undo!)"
@@ -6823,7 +6794,7 @@ msgstr ""
#: editor/scene_tree_editor.cpp
msgid "Toggle Visibility"
-msgstr ""
+msgstr "Přepnout viditelnost"
#: editor/scene_tree_editor.cpp
msgid "Invalid node name, the following characters are not allowed:"
@@ -6831,11 +6802,11 @@ msgstr ""
#: editor/scene_tree_editor.cpp
msgid "Rename Node"
-msgstr ""
+msgstr "Přejmenovat uzel"
#: editor/scene_tree_editor.cpp
msgid "Scene Tree (Nodes):"
-msgstr ""
+msgstr "Strom scény (uzly):"
#: editor/scene_tree_editor.cpp
msgid "Node Configuration Warning!"
@@ -6843,7 +6814,7 @@ msgstr ""
#: editor/scene_tree_editor.cpp
msgid "Select a Node"
-msgstr ""
+msgstr "Vybrat uzel"
#: editor/script_create_dialog.cpp
msgid "Error loading template '%s'"
@@ -6859,36 +6830,35 @@ msgstr "Chyba nahrávání skriptu z %s"
#: editor/script_create_dialog.cpp
msgid "N/A"
-msgstr ""
+msgstr "N/A"
#: editor/script_create_dialog.cpp
msgid "Path is empty"
-msgstr ""
+msgstr "Cesta je prázdná"
#: editor/script_create_dialog.cpp
msgid "Path is not local"
-msgstr ""
+msgstr "Cesta není místní"
#: editor/script_create_dialog.cpp
msgid "Invalid base path"
-msgstr ""
+msgstr "Neplatná základní cesta"
#: editor/script_create_dialog.cpp
msgid "Directory of the same name exists"
-msgstr ""
+msgstr "Složka se stejným jménem již existuje"
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "File exists, will be reused"
-msgstr "Soubor už existuje. Přepsat?"
+msgstr "Soubor již existuje, bude znovu použit"
#: editor/script_create_dialog.cpp
msgid "Invalid extension"
-msgstr ""
+msgstr "Neplatná přípona"
#: editor/script_create_dialog.cpp
msgid "Wrong extension chosen"
-msgstr ""
+msgstr "Vybrána špatná přípona"
#: editor/script_create_dialog.cpp
msgid "Invalid Path"
@@ -6896,7 +6866,7 @@ msgstr "Neplatná cesta"
#: editor/script_create_dialog.cpp
msgid "Invalid class name"
-msgstr ""
+msgstr "Neplatné jméno třídy"
#: editor/script_create_dialog.cpp
#, fuzzy
@@ -6905,11 +6875,11 @@ msgstr "Neplatné jméno vlastnosti."
#: editor/script_create_dialog.cpp
msgid "Script valid"
-msgstr ""
+msgstr "Skript je validní"
#: editor/script_create_dialog.cpp
msgid "Allowed: a-z, A-Z, 0-9 and _"
-msgstr ""
+msgstr "Povoleno: a-z, A-Z, 0-9 a _"
#: editor/script_create_dialog.cpp
msgid "Built-in script (into scene file)"
@@ -6925,15 +6895,15 @@ msgstr ""
#: editor/script_create_dialog.cpp
msgid "Language"
-msgstr ""
+msgstr "Jazyk"
#: editor/script_create_dialog.cpp
msgid "Inherits"
-msgstr ""
+msgstr "Dědí"
#: editor/script_create_dialog.cpp
msgid "Class Name"
-msgstr ""
+msgstr "Jméno třídy"
#: editor/script_create_dialog.cpp
msgid "Template"
@@ -6948,29 +6918,28 @@ msgid "Attach Node Script"
msgstr ""
#: editor/script_editor_debugger.cpp
-#, fuzzy
msgid "Remote "
msgstr "Vzdálený "
#: editor/script_editor_debugger.cpp
msgid "Bytes:"
-msgstr ""
+msgstr "Bajtů:"
#: editor/script_editor_debugger.cpp
msgid "Warning"
-msgstr ""
+msgstr "Varování"
#: editor/script_editor_debugger.cpp
msgid "Error:"
-msgstr ""
+msgstr "Chyba:"
#: editor/script_editor_debugger.cpp
msgid "Source:"
-msgstr ""
+msgstr "Zdroj:"
#: editor/script_editor_debugger.cpp
msgid "Function:"
-msgstr ""
+msgstr "Funkce:"
#: editor/script_editor_debugger.cpp
msgid "Pick one or more items from the list to display the graph."
@@ -6978,16 +6947,15 @@ msgstr ""
#: editor/script_editor_debugger.cpp modules/mono/editor/mono_bottom_panel.cpp
msgid "Errors"
-msgstr ""
+msgstr "Chyby"
#: editor/script_editor_debugger.cpp
msgid "Child Process Connected"
msgstr ""
#: editor/script_editor_debugger.cpp
-#, fuzzy
msgid "Copy Error"
-msgstr "Připojit.."
+msgstr "Kopírovat chybu"
#: editor/script_editor_debugger.cpp
msgid "Inspect Previous Instance"
@@ -7003,11 +6971,11 @@ msgstr ""
#: editor/script_editor_debugger.cpp
msgid "Variable"
-msgstr ""
+msgstr "Proměnná"
#: editor/script_editor_debugger.cpp
msgid "Errors:"
-msgstr ""
+msgstr "Chyby:"
#: editor/script_editor_debugger.cpp
msgid "Stack Trace (if applicable):"
@@ -7023,7 +6991,7 @@ msgstr ""
#: editor/script_editor_debugger.cpp
msgid "Value"
-msgstr ""
+msgstr "Hodnota"
#: editor/script_editor_debugger.cpp
msgid "Monitors"
@@ -7035,7 +7003,7 @@ msgstr ""
#: editor/script_editor_debugger.cpp
msgid "Total:"
-msgstr ""
+msgstr "Celkem:"
#: editor/script_editor_debugger.cpp
msgid "Video Mem"
@@ -7043,23 +7011,23 @@ msgstr ""
#: editor/script_editor_debugger.cpp
msgid "Resource Path"
-msgstr ""
+msgstr "Cesta ke zdroji"
#: editor/script_editor_debugger.cpp
msgid "Type"
-msgstr ""
+msgstr "Typ"
#: editor/script_editor_debugger.cpp
msgid "Format"
-msgstr ""
+msgstr "Formát"
#: editor/script_editor_debugger.cpp
msgid "Usage"
-msgstr ""
+msgstr "Používání"
#: editor/script_editor_debugger.cpp
msgid "Misc"
-msgstr ""
+msgstr "Různé"
#: editor/script_editor_debugger.cpp
msgid "Clicked Control:"
@@ -7079,7 +7047,7 @@ msgstr ""
#: editor/settings_config_dialog.cpp
msgid "Shortcuts"
-msgstr ""
+msgstr "Zkratky"
#: editor/settings_config_dialog.cpp
msgid "Binding"
@@ -7087,7 +7055,7 @@ msgstr ""
#: editor/spatial_editor_gizmos.cpp
msgid "Change Light Radius"
-msgstr ""
+msgstr "Změnit rádius světla"
#: editor/spatial_editor_gizmos.cpp
msgid "Change AudioStreamPlayer3D Emission Angle"
@@ -7095,11 +7063,11 @@ msgstr ""
#: editor/spatial_editor_gizmos.cpp
msgid "Change Camera FOV"
-msgstr ""
+msgstr "Změnit zorné pole kamery"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Camera Size"
-msgstr ""
+msgstr "Změnit velikost kamery"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Sphere Shape Radius"
@@ -7152,15 +7120,15 @@ msgstr ""
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Platform:"
-msgstr ""
+msgstr "Platforma:"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Platform"
-msgstr ""
+msgstr "Platforma"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Dynamic Library"
-msgstr ""
+msgstr "Dynamická knihovna"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Add an architecture entry"
@@ -7168,23 +7136,23 @@ msgstr ""
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "GDNativeLibrary"
-msgstr ""
+msgstr "GDNativeLibrary"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
msgid "Library"
-msgstr ""
+msgstr "Knihovna"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
msgid "Status"
-msgstr ""
+msgstr "Status"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
msgid "Libraries: "
-msgstr ""
+msgstr "Knihovny: "
#: modules/gdnative/register_types.cpp
msgid "GDNative"
-msgstr ""
+msgstr "GDNative"
#: modules/gdscript/gdscript_functions.cpp
#: modules/visual_script/visual_script_builtin_funcs.cpp
@@ -7260,14 +7228,12 @@ msgid "Floor:"
msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "GridMap Delete Selection"
-msgstr "Smazat vybraný"
+msgstr "GridMap Smazat výběr"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "GridMap Duplicate Selection"
-msgstr "Duplikovat výběr"
+msgstr "GridMap Duplikovat výběr"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Grid Map"
@@ -7292,15 +7258,15 @@ msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Edit X Axis"
-msgstr ""
+msgstr "Editovat osu X"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Edit Y Axis"
-msgstr ""
+msgstr "Editovat osu Y"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Edit Z Axis"
-msgstr ""
+msgstr "Editovat osu Z"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Cursor Rotate X"
@@ -7331,9 +7297,8 @@ msgid "Cursor Clear Rotation"
msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Create Area"
-msgstr "Vytvořit nový"
+msgstr "Vytvořit plochu"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Create Exterior Connector"
@@ -7341,15 +7306,13 @@ msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Erase Area"
-msgstr ""
+msgstr "Vymazat oblast"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Clear Selection"
-msgstr "Změnit měřítko výběru"
+msgstr "Vymazat výběr"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "GridMap Settings"
msgstr "Nastavení GridMap"
@@ -7363,25 +7326,23 @@ msgstr ""
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
-msgstr ""
+msgstr "Generování řešení..."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating C# project..."
-msgstr ""
+msgstr "Generování C# projektu..."
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Failed to create solution."
msgstr "Nepodařilo se vytvořit řešení."
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Failed to save solution."
msgstr "Nepodařilo se uložit řešení."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Done"
-msgstr ""
+msgstr "Hotovo"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Failed to create C# project."
@@ -7389,14 +7350,13 @@ msgstr ""
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Mono"
-msgstr ""
+msgstr "Mono"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "About C# support"
-msgstr ""
+msgstr "O podpoře C#"
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Create C# solution"
msgstr "Vytvořit C# řešení"
@@ -7410,7 +7370,7 @@ msgstr "Sestavit projekt"
#: modules/mono/editor/mono_bottom_panel.cpp
msgid "Warnings"
-msgstr ""
+msgstr "Varování"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
@@ -7458,9 +7418,8 @@ msgid "Change Signal Arguments"
msgstr "Upravit argumenty signálu"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Change Argument Type"
-msgstr "Změnit typ hodnot pole"
+msgstr "Změnit typ argumentu"
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Argument name"
@@ -7515,29 +7474,25 @@ msgid "Add Signal"
msgstr "Přidat signál"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Change Expression"
-msgstr "Animace: změna přechodu"
+msgstr "Změnit výraz"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Node"
msgstr "Přidat uzel"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Remove VisualScript Nodes"
-msgstr "Odstranit neplatné klíÄe"
+msgstr "Odstranit uzly VisualScriptu"
#: modules/visual_script/visual_script_editor.cpp
msgid "Duplicate VisualScript Nodes"
msgstr ""
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Hold %s to drop a Getter. Hold Shift to drop a generic signature."
msgstr ""
-"Podržte Meta k uvolnění getteru. Podržte Shift k uvolnění generického "
-"podpisu."
+"Podržte %s k uvolnění getteru. Podržte Shift k uvolnění generického podpisu."
#: modules/visual_script/visual_script_editor.cpp
#, fuzzy
@@ -7547,7 +7502,6 @@ msgstr ""
"podpisu."
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Hold %s to drop a simple reference to the node."
msgstr "Podržte %s k uvolnění jednoduché reference na uzel."
@@ -7627,11 +7581,11 @@ msgstr "Zavolat"
#: modules/visual_script/visual_script_editor.cpp
msgid "Get"
-msgstr ""
+msgstr "Získat"
#: modules/visual_script/visual_script_editor.cpp
msgid "Script already has function '%s'"
-msgstr ""
+msgstr "Script již má funkci '%s'"
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Input Value"
@@ -7643,7 +7597,7 @@ msgstr ""
#: modules/visual_script/visual_script_editor.cpp
msgid "Clipboard is empty!"
-msgstr ""
+msgstr "Schránka je prázdná!"
#: modules/visual_script/visual_script_editor.cpp
msgid "Paste VisualScript Nodes"
@@ -7775,11 +7729,11 @@ msgstr ""
#: platform/javascript/export/export.cpp
msgid "Run in Browser"
-msgstr ""
+msgstr "Spustit v prohlížeÄi"
#: platform/javascript/export/export.cpp
msgid "Run exported HTML in the system's default browser."
-msgstr ""
+msgstr "Spustit vyexportované HTML ve výchozím prohlížeÄi."
#: platform/javascript/export/export.cpp
msgid "Could not write file:"
@@ -7845,7 +7799,7 @@ msgstr ""
#: scene/2d/collision_polygon_2d.cpp
msgid "An empty CollisionPolygon2D has no effect on collision."
-msgstr "Prázdný CollisionPolygon2D nemá žádný efekt na kolizích."
+msgstr "Prázdný CollisionPolygon2D nemá při kolizi žádný efekt."
#: scene/2d/collision_shape_2d.cpp
msgid ""
@@ -7930,6 +7884,8 @@ msgid ""
"VisibilityEnable2D works best when used with the edited scene root directly "
"as parent."
msgstr ""
+"VisibilityEnable2D funguje nejlépe, když je nastaven jako rodiÄ editované "
+"scény."
#: scene/3d/arvr_nodes.cpp
msgid "ARVRCamera must have an ARVROrigin node as its parent"
@@ -7961,11 +7917,11 @@ msgstr ""
#: scene/3d/baked_lightmap.cpp
msgid "%d%%"
-msgstr ""
+msgstr "%d%%"
#: scene/3d/baked_lightmap.cpp
msgid "(Time Left: %d:%02d s)"
-msgstr ""
+msgstr "(Zbývající Äas: %d:%02d s)"
#: scene/3d/baked_lightmap.cpp
msgid "Plotting Meshes: "
@@ -8092,11 +8048,11 @@ msgstr ""
#: scene/gui/color_picker.cpp
msgid "Raw Mode"
-msgstr ""
+msgstr "RAW mód"
#: scene/gui/color_picker.cpp
msgid "Add current color as a preset"
-msgstr ""
+msgstr "Přidat aktuální barvu jako předvolbu"
#: scene/gui/dialogs.cpp
msgid "Alert!"
@@ -8104,7 +8060,7 @@ msgstr "Pozor!"
#: scene/gui/dialogs.cpp
msgid "Please Confirm..."
-msgstr "PotvrÄte prosím.."
+msgstr "PotvrÄte prosím..."
#: scene/gui/file_dialog.cpp
msgid "Select this Folder"
@@ -8168,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 ':'"
@@ -8181,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 349706f6e0..3b5854334a 100644
--- a/editor/translations/da.po
+++ b/editor/translations/da.po
@@ -6,14 +6,14 @@
# Dankse Memes <purplelops@gmail.com>, 2018.
# David Lamhauge <davidlamhauge@gmail.com>, 2016.
# Esben Damkjær Sørensen <esben@damkjaergaard.com>, 2018.
-# Kim Nielsen <kimmowich@stofanet.dk>, 2017.
+# Kim Nielsen <kimmowich@stofanet.dk>, 2017, 2018.
# Michael Madsen <mim@michael-madsen.dk>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-04-22 07:35+0000\n"
-"Last-Translator: Dankse Memes <purplelops@gmail.com>\n"
+"PO-Revision-Date: 2018-05-17 19:35+0000\n"
+"Last-Translator: Kim Nielsen <kimmowich@stofanet.dk>\n"
"Language-Team: Danish <https://hosted.weblate.org/projects/godot-engine/"
"godot/da/>\n"
"Language: da\n"
@@ -501,7 +501,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Afbryd '%s' fra '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "Forbind..."
#: editor/connections_dialog.cpp
@@ -829,9 +829,8 @@ msgid "Rename Audio Bus"
msgstr "Omdøb Audio Bus"
#: editor/editor_audio_buses.cpp
-#, fuzzy
msgid "Change Audio Bus Volume"
-msgstr "Skifter Audio Bus Solo"
+msgstr "Skift Audio Bus Volume"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Solo"
@@ -923,12 +922,12 @@ msgid "Move Audio Bus"
msgstr "Flyt Audio Bus"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Gem Audio Bus Layout Som.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Gem Audio Bus Layout Som..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Placering for Ny Layout.."
+msgid "Location for New Layout..."
+msgstr "Placering for Ny Layout..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1069,12 +1068,12 @@ msgid "Updating Scene"
msgstr "Opdatere Scene"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Gemmer lokale ændringer.."
+msgid "Storing local changes..."
+msgstr "Gemmer lokale ændringer..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Opdatere scene.."
+msgid "Updating scene..."
+msgstr "Opdatere scene..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1142,8 +1141,8 @@ msgid "Show In File Manager"
msgstr "Vis I Fil Manager"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Opret mappe.."
+msgid "New Folder..."
+msgstr "Opret mappe..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1335,18 +1334,16 @@ msgid "Description"
msgstr "Beskrivelse"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Online Tutorials:"
-msgstr "Online Dokumentation:"
+msgstr "Online Undervisning:"
#: 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 ""
-"Der er i øjeblikket ingen beskrivelse af denne metode. Det vil være en stor "
+"Der er i øjeblikket ingen beskrivelse af denne klasse. Det vil være en stor "
"hjælp, hvis du kan [color=$color][url=$url]bidrage[/url][/color] med en "
"beskrivelse!"
@@ -1404,30 +1401,28 @@ msgid "Clear"
msgstr "Clear"
#: editor/editor_log.cpp
-#, fuzzy
msgid "Clear Output"
-msgstr "Output"
+msgstr "Ryd Output"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Projekt eksport fejlede med fejlkode %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Fejl, kan ikke gemme ressource!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Gem Ressource Som.."
+msgid "Save Resource As..."
+msgstr "Gem Ressource Som..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
#, fuzzy
-msgid "I see.."
-msgstr "Jeg ser.."
+msgid "I see..."
+msgstr "Jeg ser..."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Can't open file for writing:"
msgstr "Kan ikke åbne fil til skrivning:"
@@ -1448,7 +1443,6 @@ msgid "Error while parsing '%s'."
msgstr "Error ved parsing af '%s'."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Unexpected end of file '%s'."
msgstr "Uventet afslutning af fil '%s'."
@@ -1478,12 +1472,11 @@ msgid "This operation can't be done without a tree root."
msgstr "Denne handling kan ikke foretages uden tree root"
#: editor/editor_node.cpp
-#, fuzzy
msgid ""
"Couldn't save scene. Likely dependencies (instances or inheritance) couldn't "
"be satisfied."
msgstr ""
-"Kunne ikke gemme scene. Der er nogle afhængigheder (forekomster) some ikke "
+"Kunne ikke gemme scene. Der er nogle afhængigheder (forekomster) som ikke "
"kunne opfyldes."
#: editor/editor_node.cpp
@@ -1660,12 +1653,12 @@ msgid "Open Base Scene"
msgstr "Ã…bn Grund Scene"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Hurtig Ã…bn Scene.."
+msgid "Quick Open Scene..."
+msgstr "Hurtig Ã…bn Scene..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Hurtig Ã…bn Script.."
+msgid "Quick Open Script..."
+msgstr "Hurtig Ã…bn Script..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1676,8 +1669,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Gem ændringer til '%s' før lukning?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Gem Scene Som.."
+msgid "Save Scene As..."
+msgstr "Gem Scene Som..."
#: editor/editor_node.cpp
msgid "No"
@@ -1728,8 +1721,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Denne handling kan ikke fortrydes. Vend tilbage alligevel?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Hurtig Kør Scene.."
+msgid "Quick Run Scene..."
+msgstr "Hurtig Kør Scene..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1887,8 +1880,8 @@ msgid "Previous tab"
msgstr "Forrige fane"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Filtrer filer.."
+msgid "Filter Files..."
+msgstr "Filtrer filer..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1900,12 +1893,12 @@ msgstr "Ny Scene"
#: editor/editor_node.cpp
#, fuzzy
-msgid "New Inherited Scene.."
-msgstr "Ny Nedarvet Scene.."
+msgid "New Inherited Scene..."
+msgstr "Ny Nedarvet Scene..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Ã…bn Scene.."
+msgid "Open Scene..."
+msgstr "Ã…bn Scene..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1924,16 +1917,16 @@ msgid "Open Recent"
msgstr "Ã…ben Seneste"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Konverter Til.."
+msgid "Convert To..."
+msgstr "Konverter Til..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MaskeBibliotek.."
+msgid "MeshLibrary..."
+msgstr "MaskeBibliotek..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet.."
+msgid "TileSet..."
+msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2199,8 +2192,8 @@ msgid "Save the currently edited resource."
msgstr "Gem den aktuelt redigerede ressource."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Gem Som.."
+msgid "Save As..."
+msgstr "Gem Som..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2308,8 +2301,8 @@ msgid "Creating Mesh Previews"
msgstr "Opretter Maske Forhåndsvisninger"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Miniature.."
+msgid "Thumbnail..."
+msgstr "Miniature..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2465,8 +2458,8 @@ msgid "(Current)"
msgstr "(Nuværende)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "Henter spejle, vent venligst .."
+msgid "Retrieving mirrors, please wait..."
+msgstr "Henter spejle, vent venligst ..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2548,7 +2541,7 @@ msgstr "Fejl i anmodning url: "
#: editor/export_template_manager.cpp
#, fuzzy
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr "Forbinder..."
#: editor/export_template_manager.cpp
@@ -2566,8 +2559,8 @@ msgstr "Kan ikke Løses"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Forbinder.."
+msgid "Connecting..."
+msgstr "Forbinder..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2580,8 +2573,8 @@ msgstr "Tilsluttet"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Anmoder.."
+msgid "Requesting..."
+msgstr "Anmoder..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2722,12 +2715,12 @@ msgid "Collapse all"
msgstr "Klap alle sammen"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Omdøb.."
+msgid "Rename..."
+msgstr "Omdøb..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "Flyt Til.."
+msgid "Move To..."
+msgstr "Flyt Til..."
#: editor/filesystem_dock.cpp
#, fuzzy
@@ -2739,16 +2732,16 @@ msgid "Instance"
msgstr "Instans"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Rediger Afhængigheder.."
+msgid "Edit Dependencies..."
+msgstr "Rediger Afhængigheder..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Vis Ejere.."
+msgid "View Owners..."
+msgstr "Vis Ejere..."
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Duplikere"
#: editor/filesystem_dock.cpp
@@ -2774,10 +2767,10 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Scanner Filer,\n"
-"Vent Venligst.."
+"Vent Venligst..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2842,20 +2835,20 @@ msgid "Import Scene"
msgstr "Importer Scene"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Importerer Scene.."
+msgid "Importing Scene..."
+msgstr "Importerer Scene..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
-msgstr ""
+msgstr "Generering af lightmaps"
#: editor/import/resource_importer_scene.cpp
msgid "Generating for Mesh: "
-msgstr ""
+msgstr "Generering til Mesh: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "Kører Brugerdefineret Script.."
+msgid "Running Custom Script..."
+msgstr "Kører Brugerdefineret Script..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2870,8 +2863,8 @@ msgid "Error running post-import script:"
msgstr "Fejl ved kørsel af efter-import script:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Gemmer.."
+msgid "Saving..."
+msgstr "Gemmer..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2890,8 +2883,8 @@ msgid "Import As:"
msgstr "Importer Som:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Forudindstillet.."
+msgid "Preset..."
+msgstr "Forudindstillet..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -2981,21 +2974,21 @@ msgstr "Fjern Animation"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: Invalid animation name!"
-msgstr ""
+msgstr "FEJL: Ugyldig animationsnavn!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: Animation name already exists!"
-msgstr ""
+msgstr "FEJL: Animationsnavn eksisterer allerede!"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Rename Animation"
-msgstr ""
+msgstr "Omdøb animation"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Add Animation"
-msgstr ""
+msgstr "Tilføj animation"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Blend Next Changed"
@@ -3007,19 +3000,19 @@ msgstr ""
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Load Animation"
-msgstr ""
+msgstr "Indlæs animation"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Duplicate Animation"
-msgstr ""
+msgstr "Lav en kopi af animation"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: No animation to copy!"
-msgstr ""
+msgstr "FEJL: Der er ingen animation der kan kopieres!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: No animation resource on clipboard!"
-msgstr ""
+msgstr "FEJL: Ingen animationsressource i udklipsholder!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Pasted Animation"
@@ -3031,31 +3024,31 @@ msgstr ""
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: No animation to edit!"
-msgstr ""
+msgstr "FEJL: Der er ingen animation som kan redigeres!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation backwards from current pos. (A)"
-msgstr ""
+msgstr "Afspil valgte animation baglæns fra nuværende position. (A)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation backwards from end. (Shift+A)"
-msgstr ""
+msgstr "Afspil valgt animation baglæns fra slutningen. (Shift+A)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Stop animation playback. (S)"
-msgstr ""
+msgstr "Stop animation afspilning. (S)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation from start. (Shift+D)"
-msgstr ""
+msgstr "Afspil valgt animation fra start. (Shift+D)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation from current pos. (D)"
-msgstr ""
+msgstr "Afspil valgt animation fra nuværende position. (D)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation position (in seconds)."
-msgstr ""
+msgstr "Animationsposition (i sekunder)."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Scale animation playback globally for the node."
@@ -3310,7 +3303,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3318,7 +3311,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3387,7 +3380,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3454,7 +3447,7 @@ msgid "Site:"
msgstr "Websted:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr "Støtte..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3642,6 +3635,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -4065,7 +4059,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4272,7 +4266,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4638,7 +4632,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4737,7 +4731,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4944,15 +4938,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5405,11 +5399,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5662,7 +5652,7 @@ msgid "Remove All"
msgstr "Fjern Alt"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5730,7 +5720,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5856,7 +5846,7 @@ msgstr ""
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
msgid "Tile Set"
-msgstr "TileSet.."
+msgstr "TileSet..."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -5920,7 +5910,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -6011,6 +6001,11 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Ugyldigt navn."
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "Kunne ikke oprette mappe."
@@ -6201,8 +6196,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6230,7 +6225,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6415,7 +6410,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6511,11 +6506,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6686,7 +6681,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -8197,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 ':'"
@@ -8210,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 2087c7f4b6..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.
@@ -12,10 +11,13 @@
# CitrusEdition <mariankloesler@web.de>, 2017.
# danjo <atze@libra.uberspace.de>, 2016.
# Eurocloud KnowHow <tobias.kloy@werde-volunteer.info>, 2017.
+# HugeGameArt <hugegameartgd@gmail.com>, 2018.
# hyperglow <greensoma@web.de>, 2016.
# Jan Groß <jan@grossit.de>, 2016.
# 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.
@@ -25,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-04-18 15:38+0000\n"
-"Last-Translator: Neicul <neicul@gmx.de>\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"
@@ -39,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"
@@ -330,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"
@@ -520,8 +522,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "'%s' von '%s' trennen"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "Verbinden.."
+msgid "Connect..."
+msgstr "Verbinden..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -946,12 +948,12 @@ msgid "Move Audio Bus"
msgstr "Audiobus verschieben"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Audiobus-Layout speichern als.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Audiobus-Layout speichern als..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Speicherort für neues Layout.."
+msgid "Location for New Layout..."
+msgstr "Speicherort für neues Layout..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1092,11 +1094,11 @@ msgid "Updating Scene"
msgstr "Aktualisiere Szene"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Speichere lokale Änderungen.."
+msgid "Storing local changes..."
+msgstr "Speichere lokale Änderungen..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr "Aktualisiere Szene..."
#: editor/editor_data.cpp
@@ -1165,8 +1167,8 @@ msgid "Show In File Manager"
msgstr "Zeige im Dateimanager"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Neuer Ordner.."
+msgid "New Folder..."
+msgstr "Neuer Ordner..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1427,19 +1429,19 @@ msgstr "Ausgabe löschen"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Projekt-Export ist fehlgeschlagen mit Fehlercode %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Fehler beim speichern der Ressource!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Speichere Ressource als.."
+msgid "Save Resource As..."
+msgstr "Speichere Ressource als..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr "Verstehe..."
#: editor/editor_node.cpp
@@ -1671,12 +1673,12 @@ msgid "Open Base Scene"
msgstr "Basisszene öffnen"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Schnell Szenen öffnen.."
+msgid "Quick Open Scene..."
+msgstr "Schnell Szenen öffnen..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Schnell Skripte öffnen.."
+msgid "Quick Open Script..."
+msgstr "Schnell Skripte öffnen..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1687,8 +1689,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Änderungen in ‚%s‘ vor dem Schließen speichern?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Szene speichern als.."
+msgid "Save Scene As..."
+msgstr "Szene speichern als..."
#: editor/editor_node.cpp
msgid "No"
@@ -1741,8 +1743,8 @@ msgstr ""
"Diese Aktion kann nicht rückgängig gemacht werden. Trotzdem zurücksetzen?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Schnell Szene starten.."
+msgid "Quick Run Scene..."
+msgstr "Schnell Szene starten..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1905,8 +1907,8 @@ msgid "Previous tab"
msgstr "Vorheriger Tab"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Dateien filtern.."
+msgid "Filter Files..."
+msgstr "Dateien filtern..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1917,12 +1919,12 @@ msgid "New Scene"
msgstr "Neue Szene"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Neue gererbte Szene.."
+msgid "New Inherited Scene..."
+msgstr "Neue geerbte Szene..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Szene öffnen.."
+msgid "Open Scene..."
+msgstr "Szene öffnen..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1941,16 +1943,16 @@ msgid "Open Recent"
msgstr "Zuletzt benutzte Szenen"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Umwandeln zu.."
+msgid "Convert To..."
+msgstr "Umwandeln zu..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshLibrary.."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet.."
+msgid "TileSet..."
+msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2214,8 +2216,8 @@ msgid "Save the currently edited resource."
msgstr "Speichere die so eben bearbeitete Ressource."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Speichern als.."
+msgid "Save As..."
+msgstr "Speichern als..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2323,8 +2325,8 @@ msgid "Creating Mesh Previews"
msgstr "Mesh-Vorschauen erzeugen"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Vorschau.."
+msgid "Thumbnail..."
+msgstr "Vorschau..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2476,12 +2478,12 @@ msgid "(Current)"
msgstr "(Aktuell)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
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."
@@ -2554,8 +2556,8 @@ msgid "Error requesting url: "
msgstr "Fehler beim Abrufen der URL: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "Verbinde mit Mirror.."
+msgid "Connecting to Mirror..."
+msgstr "Verbinde mit Mirror..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2571,8 +2573,8 @@ msgstr "Kann nicht aufgelöst werden"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Verbinde.."
+msgid "Connecting..."
+msgstr "Verbinde..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2584,8 +2586,8 @@ msgstr "Verbunden"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Frage an.."
+msgid "Requesting..."
+msgstr "Frage an..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2721,12 +2723,12 @@ msgid "Collapse all"
msgstr "Alle einklappen"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Umbenennen.."
+msgid "Rename..."
+msgstr "Umbenennen..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "Verschiebe zu.."
+msgid "Move To..."
+msgstr "Verschiebe zu..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2737,16 +2739,16 @@ msgid "Instance"
msgstr "Instanz"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Abhängigkeiten bearbeiten.."
+msgid "Edit Dependencies..."
+msgstr "Abhängigkeiten bearbeiten..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Zeige Besitzer.."
+msgid "View Owners..."
+msgstr "Zeige Besitzer..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Duplizieren.."
+msgid "Duplicate..."
+msgstr "Duplizieren..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2771,10 +2773,10 @@ msgstr "Instantiiere gewählte Szene(n) als Unterobjekt des ausgewählten Nodes.
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Lese Dateien,\n"
-"Bitte warten.."
+"Bitte warten..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2839,8 +2841,8 @@ msgid "Import Scene"
msgstr "Szene importieren"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Szene wird importiert.."
+msgid "Importing Scene..."
+msgstr "Szene wird importiert..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2851,8 +2853,8 @@ msgid "Generating for Mesh: "
msgstr "Generierung für Mesh: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "Angepasstes Skript wird ausgeführt.."
+msgid "Running Custom Script..."
+msgstr "Angepasstes Skript wird ausgeführt..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2867,8 +2869,8 @@ msgid "Error running post-import script:"
msgstr "Fehler beim ausführen des Post-Import Skripts:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Speichere.."
+msgid "Saving..."
+msgstr "Speichere..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2887,8 +2889,8 @@ msgid "Import As:"
msgstr "Importiere als:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Voreinstellungen.."
+msgid "Preset..."
+msgstr "Voreinstellungen..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3305,16 +3307,16 @@ msgid "Transition Node"
msgstr "Ãœbergangs-Node"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Animationen importieren.."
+msgid "Import Animations..."
+msgstr "Animationen importieren..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "Nodefilter bearbeiten"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "Filter.."
+msgid "Filters..."
+msgstr "Filter..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3381,8 +3383,8 @@ msgid "Fetching:"
msgstr "Hole:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "Löse auf.."
+msgid "Resolving..."
+msgstr "Löse auf..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3448,8 +3450,8 @@ msgid "Site:"
msgstr "Seite:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Stabilität.."
+msgid "Support..."
+msgstr "Stabilität..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3648,6 +3650,7 @@ msgid "Use Rotation Snap"
msgstr "Rotationsraster benutzen"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "Einrasten konfigurieren..."
@@ -3745,14 +3748,12 @@ msgid "Show Guides"
msgstr "Hilfslinien anzeigen"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
msgstr "Zeige Ursprung"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "Eine Ansicht"
+msgstr "Zeige Ansichtsfenster (Viewport)"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -4046,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 ""
+msgstr "Der Mesh-Grundtyp ist nicht ist nicht PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4077,8 +4078,8 @@ msgid "Create Convex Collision Sibling"
msgstr "Konvexes Kollisionselement erzeugen"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "Umriss-Mesh erzeugen.."
+msgid "Create Outline Mesh..."
+msgstr "Umriss-Mesh erzeugen..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4285,8 +4286,8 @@ msgid "Error loading image:"
msgstr "Fehler beim Laden des Bilds:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Keine Pixel mit einer Transparenz > 128 im Bild.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Keine Pixel mit einer Transparenz > 128 im Bild..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4646,8 +4647,8 @@ msgid "Import Theme"
msgstr "Motiv importieren"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Motiv speichern als.."
+msgid "Save Theme As..."
+msgstr "Motiv speichern als..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4743,8 +4744,8 @@ msgstr "Seitenleiste umschalten"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Finde.."
+msgid "Find..."
+msgstr "Finde..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4953,16 +4954,16 @@ msgid "Find Previous"
msgstr "Finde Vorheriges"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Ersetzen.."
+msgid "Replace..."
+msgstr "Ersetzen..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Springe zu Funktion.."
+msgid "Goto Function..."
+msgstr "Springe zu Funktion..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Springe zu Zeile.."
+msgid "Goto Line..."
+msgstr "Springe zu Zeile..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5415,12 +5416,8 @@ msgid "Transform"
msgstr "Transformation"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Einrasten konfigurieren.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "Transformationsdialog.."
+msgid "Transform Dialog..."
+msgstr "Transformationsdialog..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5672,8 +5669,8 @@ msgid "Remove All"
msgstr "Alles entfernen"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "Thema bearbeiten.."
+msgid "Edit theme..."
+msgstr "Thema bearbeiten..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5720,14 +5717,12 @@ msgid "Checked Item"
msgstr "Überprüftes Element"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "Element hinzufügen"
+msgstr "Element der Auswahl"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "Überprüftes Element"
+msgstr "Markiertes Element der Auswahl"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5742,8 +5737,8 @@ msgid "Options"
msgstr "Optionen"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr "Enthalten,Viele,Einige,Optionen!"
+msgid "Has,Many,Options"
+msgstr "Einstellungen"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5934,8 +5929,8 @@ msgid "Presets"
msgstr "Vorlagen"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "Hinzufügen.."
+msgid "Add..."
+msgstr "Hinzufügen..."
#: editor/project_export.cpp
msgid "Resources"
@@ -6028,6 +6023,10 @@ msgid "Imported Project"
msgstr "Importiertes Projekt"
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "Ungültiger Projektname."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "Ordner konnte nicht erstellt werden."
@@ -6230,9 +6229,11 @@ msgstr "Maustaste"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
+"Ungültiger Aktionsname. Er kann weder leer sein noch ‚/‘, ‚:‘, ‚=‘, ‘\\‘ "
+"oder ‚\"‘ enthalten."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6259,8 +6260,8 @@ msgid "Control+"
msgstr "Steuerung+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "Drücke eine Taste.."
+msgid "Press a Key..."
+msgstr "Drücke eine Taste..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6443,8 +6444,8 @@ msgid "Property:"
msgstr "Eigenschaft:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
-msgstr "Überschreiben für.."
+msgid "Override For..."
+msgstr "Überschreiben für..."
#: editor/project_settings_editor.cpp
msgid "Input Map"
@@ -6539,12 +6540,12 @@ msgid "Easing Out-In"
msgstr "Glätten Aus-Ein"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "Datei.."
+msgid "File..."
+msgstr "Datei..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "Verzeichnis.."
+msgid "Dir..."
+msgstr "Verzeichnis..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6719,8 +6720,8 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Diese Aktion kann nicht auf instantiierten Szenen ausgeführt werden."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "Speichere neue Szene als.."
+msgid "Save New Scene As..."
+msgstr "Speichere neue Szene als..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
@@ -7439,7 +7440,7 @@ msgstr "Auswahlradius:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "Der Klassenname kann nicht ein reserviertes Schlüsselwort sein"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -8164,7 +8165,7 @@ msgstr "Die Pfad-Eigenschaft muss auf ein gültiges Spatial-Node verweisen."
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "Ein WorldEnvironment benötigt eine Environment-Ressource."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8178,6 +8179,9 @@ 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 ""
+"Dieses WorldEnvironment wird ignoriert. Entweder füge eine Kamera (für 3D-"
+"Szenen) hinzu oder setze den Hintergrund-Modus des Environments nach Canvas "
+"(für 2D-Szenen)."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8280,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)."
@@ -8306,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!"
@@ -8457,7 +8465,7 @@ msgstr "Ungültige Schriftgröße."
#~ msgid "Exporting for %s"
#~ msgstr "Exportiere für %s"
-#~ msgid "Setting Up.."
+#~ msgid "Setting Up..."
#~ msgstr "Bereite vor..."
#~ msgid "Error loading scene."
@@ -8520,8 +8528,8 @@ msgstr "Ungültige Schriftgröße."
#~ msgid "Info"
#~ msgstr "Info"
-#~ msgid "Re-Import.."
-#~ msgstr "Neuimport.."
+#~ msgid "Re-Import..."
+#~ msgstr "Neuimport..."
#~ msgid "No bit masks to import!"
#~ msgstr "Keine Bitmasken zu importieren!"
@@ -8915,14 +8923,14 @@ msgstr "Ungültige Schriftgröße."
#~ msgid "Zoom (%):"
#~ msgstr "Vergrößerung (%):"
-#~ msgid "Skeleton.."
-#~ msgstr "Skelett.."
+#~ msgid "Skeleton..."
+#~ msgstr "Skelett..."
#~ msgid "Zoom Reset"
#~ msgstr "Vergrößerung zurücksetzen"
-#~ msgid "Zoom Set.."
-#~ msgstr "Vergrößerung setzen.."
+#~ msgid "Zoom Set..."
+#~ msgstr "Vergrößerung setzen..."
#~ msgid "Set a Value"
#~ msgstr "Einen Wert setzen"
@@ -9392,8 +9400,8 @@ msgstr "Ungültige Schriftgröße."
#~ msgid "Export Project PCK"
#~ msgstr "Exportiere Projekt-PCK"
-#~ msgid "Export.."
-#~ msgstr "Exportieren.."
+#~ msgid "Export..."
+#~ msgstr "Exportieren..."
#~ msgid "Project Export"
#~ msgstr "Projekt exportieren"
@@ -9458,8 +9466,8 @@ msgstr "Ungültige Schriftgröße."
#~ msgid "Method In Node:"
#~ msgstr "Methode in Node:"
-#~ msgid "Edit Connections.."
-#~ msgstr "Bearbeite Verbindungen.."
+#~ msgid "Edit Connections..."
+#~ msgstr "Bearbeite Verbindungen..."
#~ msgid "Plugin List:"
#~ msgstr "Plugin Liste:"
diff --git a/editor/translations/de_CH.po b/editor/translations/de_CH.po
index ea942bb7c2..26f824bc4b 100644
--- a/editor/translations/de_CH.po
+++ b/editor/translations/de_CH.po
@@ -495,7 +495,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr ""
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -916,11 +916,11 @@ msgid "Move Audio Bus"
msgstr "Bild bewegen/einfügen"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1056,11 +1056,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1130,7 +1130,7 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr ""
#: editor/editor_file_dialog.cpp
@@ -1394,12 +1394,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1608,11 +1608,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1625,7 +1625,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr ""
#: editor/editor_node.cpp
@@ -1680,7 +1680,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1828,7 +1828,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1840,11 +1840,11 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1865,15 +1865,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2123,7 +2123,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2235,7 +2235,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2386,7 +2386,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2465,7 +2465,7 @@ msgstr "Szene kann nicht gespeichert werden."
#: editor/export_template_manager.cpp
#, fuzzy
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr "Connections editieren"
#: editor/export_template_manager.cpp
@@ -2483,7 +2483,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
-msgid "Connecting.."
+msgid "Connecting..."
msgstr "Connections editieren"
#: editor/export_template_manager.cpp
@@ -2498,7 +2498,7 @@ msgstr "Verbindung zu Node:"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2639,11 +2639,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2656,16 +2656,16 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Node(s) duplizieren"
#: editor/filesystem_dock.cpp
@@ -2691,7 +2691,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2757,7 +2757,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2769,7 +2769,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2785,7 +2785,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2806,7 +2806,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3231,7 +3231,7 @@ msgid "Transition Node"
msgstr "Transition-Node"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3239,7 +3239,7 @@ msgid "Edit Node Filters"
msgstr "Node Filter editieren"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3310,7 +3310,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3378,7 +3378,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3571,6 +3571,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3998,7 +3999,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4206,7 +4207,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4575,7 +4576,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4672,7 +4673,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4881,15 +4882,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5347,11 +5348,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5607,7 +5604,7 @@ msgid "Remove All"
msgstr "Ungültige Bilder löschen"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5675,7 +5672,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5865,7 +5862,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5961,6 +5958,11 @@ msgstr "Importierte Projekte"
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Projektname:"
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "Node erstellen"
@@ -6157,8 +6159,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6186,8 +6188,8 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "Taste drücken.."
+msgid "Press a Key..."
+msgstr "Taste drücken..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6373,7 +6375,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6470,11 +6472,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6651,7 +6653,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Das funktioniert nicht bei einer instanzierten Szene."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr "Neue Szene speichern als..."
#: editor/scene_tree_dock.cpp
diff --git a/editor/translations/editor.pot b/editor/translations/editor.pot
index 61d67d7089..1cb31e0ee9 100644
--- a/editor/translations/editor.pot
+++ b/editor/translations/editor.pot
@@ -3261,19 +3261,19 @@ msgid "Download for this asset is already in progress!"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "first"
+msgid "First"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "prev"
+msgid "Previous"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "next"
+msgid "Next"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "last"
+msgid "Last"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3491,6 +3491,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -5240,10 +5241,6 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap..."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Dialog..."
msgstr ""
@@ -5565,7 +5562,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5843,6 +5840,10 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr ""
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -6029,8 +6030,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
diff --git a/editor/translations/el.po b/editor/translations/el.po
index 2bf8d790ab..b3275b4647 100644
--- a/editor/translations/el.po
+++ b/editor/translations/el.po
@@ -8,7 +8,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-03-05 16:04+0000\n"
+"PO-Revision-Date: 2018-05-20 09:37+0000\n"
"Last-Translator: George Tsiamasiotis <gtsiam@windowslive.com>\n"
"Language-Team: Greek <https://hosted.weblate.org/projects/godot-engine/godot/"
"el/>\n"
@@ -16,7 +16,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 2.20-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -498,8 +498,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "ΑποσÏνδεση του '%s' απο το '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "ΣÏνδεση.."
+msgid "Connect..."
+msgstr "ΣÏνδεση..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -919,12 +919,12 @@ msgid "Move Audio Bus"
msgstr "Μετακίνηση διαÏλου ήχου"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Αποθήκευση διάταξης διαÏλων ήχου ÏŽÏ‚.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Αποθήκευση διάταξης διαÏλων ήχου ÏŽÏ‚..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Τοποθεσία για νέα διάταξη.."
+msgid "Location for New Layout..."
+msgstr "Τοποθεσία για νέα διάταξη..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1061,12 +1061,12 @@ msgid "Updating Scene"
msgstr "ΕνημέÏωση σκηνής"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Αποθήκευση τοπικών αλλαγών.."
+msgid "Storing local changes..."
+msgstr "Αποθήκευση τοπικών αλλαγών..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "ΕνημέÏωση σκηνής.."
+msgid "Updating scene..."
+msgstr "ΕνημέÏωση σκηνής..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1134,7 +1134,7 @@ msgid "Show In File Manager"
msgstr "Εμφάνιση στη διαχείÏιση αÏχείων"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr "Îέος φάκελος"
#: editor/editor_file_dialog.cpp
@@ -1396,20 +1396,20 @@ 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!"
msgstr "Σφάλμα κατά την αποθήκευση πόÏου!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Αποθήκευση πόÏου ως.."
+msgid "Save Resource As..."
+msgstr "Αποθήκευση πόÏου ως..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "Εντάξει.."
+msgid "I see..."
+msgstr "Εντάξει..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1643,11 +1643,11 @@ msgid "Open Base Scene"
msgstr "Άνοιγμα σκηνής βάσης"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr "ΓÏήγοÏο άνοιγμα σκηνής..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr "ΓÏήγοÏη άνοιγμα δεσμής ενεÏγειών..."
#: editor/editor_node.cpp
@@ -1659,7 +1659,7 @@ msgid "Save changes to '%s' before closing?"
msgstr "Αποθήκευση αλλαγών στο '%s' Ï€Ïιν το κλείσιμο;"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr "Αποθήκευση σκηνή ως..."
#: editor/editor_node.cpp
@@ -1714,7 +1714,7 @@ msgstr ""
"επαναφοÏά;"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr "ΓÏήγοÏη εκτέλεση σκηνής..."
#: editor/editor_node.cpp
@@ -1877,7 +1877,7 @@ msgid "Previous tab"
msgstr "ΠÏοηγοÏμενη καÏτέλα"
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr "ΦιλτÏάÏισμα αÏχείων..."
#: editor/editor_node.cpp
@@ -1889,12 +1889,12 @@ msgid "New Scene"
msgstr "Îέα σκηνή"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Îέα κληÏονομημένη σκηνή.."
+msgid "New Inherited Scene..."
+msgstr "Îέα κληÏονομημένη σκηνή..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Άνοιγμα σκηνής.."
+msgid "Open Scene..."
+msgstr "Άνοιγμα σκηνής..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1913,15 +1913,15 @@ msgid "Open Recent"
msgstr "Άνοιγμα Ï€Ïόσφατων"
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr "ΜετατÏοπή σε..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr "Βιβλιοθήκη πλεγμάτων..."
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2186,7 +2186,7 @@ msgid "Save the currently edited resource."
msgstr "Αποθήκευσε το Ï„Ïέχων επεξεÏγαζόμενο πόÏο."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr "Αποθήκευση ως..."
#: editor/editor_node.cpp
@@ -2295,8 +2295,8 @@ msgid "Creating Mesh Previews"
msgstr "ΔημιουÏγία Ï€Ïοεπισκοπήσεων πλεγμάτων"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "ΜικÏογÏαφία.."
+msgid "Thumbnail..."
+msgstr "ΜικÏογÏαφία..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2448,8 +2448,8 @@ msgid "(Current)"
msgstr "(ΤÏέχων)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "Ανάκτηση δεδοένων κατοπτÏισμοÏ, παÏακαλώ πεÏιμένετε.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "Ανάκτηση δεδοένων κατοπτÏισμοÏ, παÏακαλώ πεÏιμένετε..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2526,8 +2526,8 @@ msgid "Error requesting url: "
msgstr "Σφάλμα κατά Ï„o αίτημα για διεÏθηνση url: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "ΣÏνδεση σε διακομιστή κατοπτÏισμοÏ.."
+msgid "Connecting to Mirror..."
+msgstr "ΣÏνδεση σε διακομιστή κατοπτÏισμοÏ..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2543,8 +2543,8 @@ msgstr "Δεν είναι δυνατή η επίλυση"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "ΣÏνδεση.."
+msgid "Connecting..."
+msgstr "ΣÏνδεση..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2556,8 +2556,8 @@ msgstr "Συνδέθηκε"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Γίνεται αίτημα.."
+msgid "Requesting..."
+msgstr "Γίνεται αίτημα..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2693,11 +2693,11 @@ msgid "Collapse all"
msgstr "ΣÏμπτηξη όλων"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr "Μετονομασία..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr "Μετακίνηση σε"
#: editor/filesystem_dock.cpp
@@ -2709,15 +2709,15 @@ msgid "Instance"
msgstr "Στιγμιότυπο"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr "ΕπεξεÏγασία εξαÏτήσεων"
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr "ΠÏοβολή ιδιοκτητών"
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "ΑναπαÏαγωγή"
#: editor/filesystem_dock.cpp
@@ -2745,10 +2745,10 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"ΣάÏωση αÏχείων,\n"
-"ΠαÏακαλώ πεÏιμένετε.."
+"ΠαÏακαλώ πεÏιμένετε..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2813,7 +2813,7 @@ msgid "Import Scene"
msgstr "Εισαγωγή σκηνής"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr "Εισαγωγή σκηνής..."
#: editor/import/resource_importer_scene.cpp
@@ -2825,7 +2825,7 @@ msgid "Generating for Mesh: "
msgstr "ΔημιουÏία για πλέγμα: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr "Εκτέλεση Ï€ÏοσαÏμοσμένης δέσμης ενεÏγειών..."
#: editor/import/resource_importer_scene.cpp
@@ -2843,7 +2843,7 @@ msgid "Error running post-import script:"
msgstr "Σφάλμα κατά την εκτέλεση της δέσμης ενεÏγειών μετ-εισαγωγής:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr "Αποθήκευση..."
#: editor/import_dock.cpp
@@ -2863,7 +2863,7 @@ msgid "Import As:"
msgstr "Εισαγωγή ώς:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr "ΔιαμόÏφωση..."
#: editor/import_dock.cpp
@@ -3281,16 +3281,16 @@ msgid "Transition Node"
msgstr "Κόμβος μετάβασης"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Εισαγωγή κινήσεων.."
+msgid "Import Animations..."
+msgstr "Εισαγωγή κινήσεων..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "ΕπεξεÏγασία φίλτÏων κόμβων"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "ΦίλτÏα.."
+msgid "Filters..."
+msgstr "ΦίλτÏα..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3358,7 +3358,7 @@ msgid "Fetching:"
msgstr "Λήψη:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr "Επίλυση..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3425,8 +3425,8 @@ msgid "Site:"
msgstr "ΔιεÏθυνση:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "ΥποστήÏιξη.."
+msgid "Support..."
+msgstr "ΥποστήÏιξη..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3624,6 +3624,7 @@ msgid "Use Rotation Snap"
msgstr "ΧÏήση κουμπώματος πεÏιστÏοφής"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "ΔιαμόÏφωση κουμπώματος..."
@@ -3720,14 +3721,12 @@ msgid "Show Guides"
msgstr "Εμφάνιση οδηγών"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "ΠÏοβολή ΑÏχής"
+msgstr "ΠÏοβολή πηγής"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 Οπτική γωνία"
+msgstr "ΠÏοβολή οπτικής γωνίας"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -4020,7 +4019,7 @@ msgstr "Το πλέγμα δεν έχει επιφάνει από την οποÎ
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "O Ï€ÏωταÏχικός Ï„Ïπος δεν είναι PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4051,8 +4050,8 @@ msgid "Create Convex Collision Sibling"
msgstr "ΔημιουÏγία Î±Î´ÎµÎ»Ï†Î¿Ï ÏƒÏγκÏουσης κυÏÏ„Î¿Ï ÏƒÏŽÎ¼Î±Ï„Î¿Ï‚"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "ΔημιουÏγία πλέγματος πεÏιγÏάμματος.."
+msgid "Create Outline Mesh..."
+msgstr "ΔημιουÏγία πλέγματος πεÏιγÏάμματος..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4260,8 +4259,8 @@ msgid "Error loading image:"
msgstr "Σφάλμα κατά την φόÏτωση εικόνας:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Δεν υπάÏχουν εικονοστοιχεία με διαφάνεια >128 στην εικόνα.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Δεν υπάÏχουν εικονοστοιχεία με διαφάνεια >128 στην εικόνα..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4621,8 +4620,8 @@ msgid "Import Theme"
msgstr "Εισαγωγή θέματος"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Αποθήκευση θέματος ως.."
+msgid "Save Theme As..."
+msgstr "Αποθήκευση θέματος ως..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4718,8 +4717,8 @@ msgstr "Εναλλαγή πλαισίου δεσμών ενεÏγειών"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "ΕÏÏεση.."
+msgid "Find..."
+msgstr "ΕÏÏεση..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4928,16 +4927,16 @@ msgid "Find Previous"
msgstr "ΈυÏεση Ï€ÏοηγοÏμενου"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Αντικατάσταση.."
+msgid "Replace..."
+msgstr "Αντικατάσταση..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Πήγαινε σε συνάÏτηση.."
+msgid "Goto Function..."
+msgstr "Πήγαινε σε συνάÏτηση..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Πήγαινε σε γÏαμμή.."
+msgid "Goto Line..."
+msgstr "Πήγαινε σε γÏαμμή..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5392,12 +5391,8 @@ msgid "Transform"
msgstr "Μετασχηματισμός"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "ΔιαμόÏφωση κουμπώματος.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "Διάλογος μετασχηματισμοÏ.."
+msgid "Transform Dialog..."
+msgstr "Διάλογος μετασχηματισμοÏ..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5649,8 +5644,8 @@ msgid "Remove All"
msgstr "ΑφαίÏεση όλων"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "ΕπεξεÏγασία θέματος.."
+msgid "Edit theme..."
+msgstr "ΕπεξεÏγασία θέματος..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5697,14 +5692,12 @@ msgid "Checked Item"
msgstr "Επιλεγμένο στοιχείο"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "ΠÏοσθήκη στοιχείου"
+msgstr "Στοιχείο επιλογής"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "Επιλεγμένο στοιχείο"
+msgstr "Επιλεγμένο στοιχείο επιλογής"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5719,7 +5712,8 @@ msgid "Options"
msgstr "Επιλογές"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+#, fuzzy
+msgid "Has,Many,Options"
msgstr "Έχει,ΠάÏα,Πολλές,Επιλογές!"
#: editor/plugins/theme_editor_plugin.cpp
@@ -5912,8 +5906,8 @@ msgid "Presets"
msgstr "ΔιαμοÏφώσεις"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "ΠÏοσθήκη.."
+msgid "Add..."
+msgstr "ΠÏοσθήκη..."
#: editor/project_export.cpp
msgid "Resources"
@@ -6007,6 +6001,11 @@ msgid "Imported Project"
msgstr "Εισαγμένο έÏγο"
#: editor/project_manager.cpp
+#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Όνομα έÏγου:"
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "ΑδÏνατη η δημιουÏγία φακέλου."
@@ -6189,7 +6188,7 @@ msgid ""
"Would you like to explore the official example projects in the Asset Library?"
msgstr ""
"Δεν έχετε κανένα έÏγο.\n"
-"Θα θέλατε να εξεÏευνήσετε μεÏικά παÏαδείγματα στην βιβλιοθήκη πόÏων;"
+"Θέλετε να εξεÏευνήσετε μεÏικά παÏαδείγματα στην βιβλιοθήκη πόÏων;"
#: editor/project_settings_editor.cpp
msgid "Key "
@@ -6208,10 +6207,13 @@ msgid "Mouse Button"
msgstr "Κουμπί ποντικιοÏ"
#: editor/project_settings_editor.cpp
+#, fuzzy
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
+"ΆκυÏο όνομα ενέÏγειας. Δεν μποÏεί να είναι άδειο ή να πεÏιέχει '/', ':', "
+"'=', '\\' ή '\"'"
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6238,8 +6240,8 @@ msgid "Control+"
msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "Πατήστε ένα κουμπί.."
+msgid "Press a Key..."
+msgstr "Πατήστε ένα κουμπί..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6422,7 +6424,7 @@ msgid "Property:"
msgstr "Ιδιότητα:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr "ΠαÏάκαμψη για..."
#: editor/project_settings_editor.cpp
@@ -6518,12 +6520,12 @@ msgid "Easing Out-In"
msgstr "Ομαλή κίνηση από έξω Ï€Ïος τα μέσα"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "ΑÏχείο.."
+msgid "File..."
+msgstr "ΑÏχείο..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "Κατάλογος.."
+msgid "Dir..."
+msgstr "Κατάλογος..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6699,8 +6701,8 @@ msgstr ""
"δημιουÏγηθεί στιγμιότυπα."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "Αποθήκευση νέας σκηνής ως.."
+msgid "Save New Scene As..."
+msgstr "Αποθήκευση νέας σκηνής ως..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
@@ -7422,7 +7424,7 @@ msgstr "Επιλογή απόστασης:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "Το όνομα της κλάσης δεν μποÏεί να είναι λέξη-κλειδί"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -8144,7 +8146,7 @@ msgstr ""
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "Το WorldEnvironment χÏειάζεται έναν πόÏο Environment."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8158,6 +8160,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 ""
+"Αυτό το WorldEnvironment θα αγνοηθεί. ΠÏοσθέστε μια κάμεÏα (για 3d) ή οÏίστε "
+"το Background Mode Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… πεÏιβάλλοντος σε Canvas (για 2d)."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8256,6 +8260,13 @@ msgstr "Σφάλμα κατά την φόÏτωση της γÏαμματοσεÎ
msgid "Invalid font size."
msgstr "Μη έγκυÏο μέγεθος γÏαμματοσειÏάς."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "ΠÏοηγοÏμενη καÏτέλα"
+
+#~ msgid "Next"
+#~ msgstr "Επόμενο"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Μη έγκυÏη ενέÏγεια (Όλα επιτÏέποντα εκτός από το '/' και το ':')."
@@ -8283,9 +8294,6 @@ msgstr "Μη έγκυÏο μέγεθος γÏαμματοσειÏάς."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "Δεν βÏέθηκε το project.godot στη διαδÏομή του έÏγου."
-#~ msgid "Next"
-#~ msgstr "Επόμενο"
-
#~ msgid "Not found!"
#~ msgstr "Δεν βÏέθηκε!"
@@ -8431,8 +8439,8 @@ msgstr "Μη έγκυÏο μέγεθος γÏαμματοσειÏάς."
#~ msgid "Exporting for %s"
#~ msgstr "Εξαγωγή για %s"
-#~ msgid "Setting Up.."
-#~ msgstr "ΑÏχικοποίηση.."
+#~ msgid "Setting Up..."
+#~ msgstr "ΑÏχικοποίηση..."
#~ msgid "Error loading scene."
#~ msgstr "Σφάλμα κατά τη φόÏτωση σκηνής."
@@ -8494,7 +8502,7 @@ msgstr "Μη έγκυÏο μέγεθος γÏαμματοσειÏάς."
#~ msgid "Info"
#~ msgstr "ΠληÏοφοÏίες"
-#~ msgid "Re-Import.."
+#~ msgid "Re-Import..."
#~ msgstr "Εκ νέου εισαγωγή..."
#~ msgid "No bit masks to import!"
@@ -8894,14 +8902,14 @@ msgstr "Μη έγκυÏο μέγεθος γÏαμματοσειÏάς."
#~ msgid "Zoom (%):"
#~ msgstr "Μεγέθυνση (%):"
-#~ msgid "Skeleton.."
-#~ msgstr "Σκελετός.."
+#~ msgid "Skeleton..."
+#~ msgstr "Σκελετός..."
#~ msgid "Zoom Reset"
#~ msgstr "ΕπαναφοÏά μεγέθυνσης"
-#~ msgid "Zoom Set.."
-#~ msgstr "ΟÏισμός μεγέθυνσης.."
+#~ msgid "Zoom Set..."
+#~ msgstr "ΟÏισμός μεγέθυνσης..."
#~ msgid "Set a Value"
#~ msgstr "ΟÏισμός τιμής"
diff --git a/editor/translations/es.po b/editor/translations/es.po
index 86188201c1..89118d2501 100644
--- a/editor/translations/es.po
+++ b/editor/translations/es.po
@@ -2,9 +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.
-#
# Addiel Lucena Perez <addiell2017@gmail.com>, 2017.
-# Aleix Sanchis <aleixsanchis@hotmail.com>, 2017.
+# Aleix Sanchis <aleixsanchis@hotmail.com>, 2017, 2018.
# Alejandro Alvarez <eliluminado00@gmail.com>, 2017.
# Avocado <avocadosan42@gmail.com>, 2018.
# BLaDoM GUY <simplybladom@gmail.com>, 2017.
@@ -13,27 +12,29 @@
# David Couto <davidcouto@gmail.com>, 2017.
# Dharkael <izhe@hotmail.es>, 2017.
# Diego López <diegodario21@gmail.com>, 2017.
+# eon-s <emanuel.segretin@gmail.com>, 2018.
# Gustavo Leon <gleondiaz@gmail.com>, 2017-2018.
# Javier Ocampos <xavier.ocampos@gmail.com>, 2018.
+# Jose Maria Martinez <josemar1992@hotmail.com>, 2018.
# Juan Quiroga <juanquiroga9@gmail.com>, 2017.
# Kiji Pixel <raccoon.fella@gmail.com>, 2017.
# Lisandro Lorea <lisandrolorea@gmail.com>, 2016-2017.
# Lonsfor <lotharw@protonmail.com>, 2017-2018.
# Mario Nachbaur <manachbaur@gmail.com>, 2018.
# Oscar Carballal <oscar.carballal@protonmail.com>, 2017-2018.
-# Rabid Orange <theorangerabid@gmail.com>, 2017.
+# 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-05-03 02:11+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"
@@ -41,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"
@@ -53,11 +54,11 @@ msgstr "Toda la Selección"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Time"
-msgstr "Cambiar el tiempo de la clave de animación"
+msgstr "Cambiar el tiempo del Fotograma Clave de Animación"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr "Cambiar transición de animación"
+msgstr "Cambiar Transición de Animación"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
@@ -65,76 +66,76 @@ msgstr "Cambiar transformación de animación"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Value"
-msgstr "Cambiar valor de la clave de animación"
+msgstr "Cambiar valor del Fotograma Clave de Animación"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
-msgstr "Cambiar llamada de animación"
+msgstr "Cambiar Llamada de Animación"
#: editor/animation_editor.cpp
msgid "Anim Add Track"
-msgstr "Añadir pista de animación"
+msgstr "Añadir Pista de Animación"
#: editor/animation_editor.cpp
msgid "Anim Duplicate Keys"
-msgstr "Duplicar claves de animación"
+msgstr "Duplicar Claves de Animación"
#: editor/animation_editor.cpp
msgid "Move Anim Track Up"
-msgstr "Subir pista de animación"
+msgstr "Subir Pista de Animación"
#: editor/animation_editor.cpp
msgid "Move Anim Track Down"
-msgstr "Bajar pista de animación"
+msgstr "Bajar Pista de Animación"
#: editor/animation_editor.cpp
msgid "Remove Anim Track"
-msgstr "Quitar pista de animación"
+msgstr "Quitar Pista de Animación"
#: editor/animation_editor.cpp
msgid "Set Transitions to:"
-msgstr "Establecer transiciones en:"
+msgstr "Establecer Transiciones en:"
#: editor/animation_editor.cpp
msgid "Anim Track Rename"
-msgstr "Renombrar pista de animación"
+msgstr "Renombrar Pista de Animación"
#: editor/animation_editor.cpp
msgid "Anim Track Change Interpolation"
-msgstr "Cambiar interpolación de pista de animación"
+msgstr "Cambiar Interpolación de Pista de Animación"
#: editor/animation_editor.cpp
msgid "Anim Track Change Value Mode"
-msgstr "Cambiar modo de valor de pista de animación"
+msgstr "Cambiar Modo de Valor de Pista de Animación"
#: editor/animation_editor.cpp
msgid "Anim Track Change Wrap Mode"
-msgstr "Cambiar modo de ciclo de pista de animación"
+msgstr "Cambiar Modo de Ciclo de Pista de Animación"
#: editor/animation_editor.cpp
msgid "Edit Node Curve"
-msgstr "Editar nodo de curva"
+msgstr "Editar Nodo de Curva"
#: editor/animation_editor.cpp
msgid "Edit Selection Curve"
-msgstr "Editar curva de selección"
+msgstr "Editar Curva de Selección"
#: editor/animation_editor.cpp
msgid "Anim Delete Keys"
-msgstr "Borrar claves de animación"
+msgstr "Borrar Claves de Animación"
#: editor/animation_editor.cpp editor/plugins/tile_map_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Duplicate Selection"
-msgstr "Duplicar selección"
+msgstr "Duplicar Selección"
#: editor/animation_editor.cpp
msgid "Duplicate Transposed"
-msgstr "Duplicar transpuesto"
+msgstr "Duplicar Transpuesto"
#: editor/animation_editor.cpp
msgid "Remove Selection"
-msgstr "Quitar selección"
+msgstr "Quitar Selección"
#: editor/animation_editor.cpp
msgid "Continuous"
@@ -150,15 +151,15 @@ msgstr "Trigger"
#: editor/animation_editor.cpp
msgid "Anim Add Key"
-msgstr "Añadir clave de animación"
+msgstr "Añadir Clave de Animación"
#: editor/animation_editor.cpp
msgid "Anim Move Keys"
-msgstr "Mover claves de animación"
+msgstr "Mover Claves de Animación"
#: editor/animation_editor.cpp
msgid "Scale Selection"
-msgstr "Escalar selección"
+msgstr "Escalar Selección"
#: editor/animation_editor.cpp
msgid "Scale From Cursor"
@@ -166,11 +167,11 @@ msgstr "Escalar desde cursor"
#: editor/animation_editor.cpp
msgid "Goto Next Step"
-msgstr "Ir al siguiente paso"
+msgstr "Ir al Siguiente Paso"
#: editor/animation_editor.cpp
msgid "Goto Prev Step"
-msgstr "Ir al paso anterior"
+msgstr "Ir al Paso Anterior"
#: editor/animation_editor.cpp editor/plugins/curve_editor_plugin.cpp
#: editor/property_editor.cpp
@@ -203,11 +204,11 @@ msgstr "Transiciones"
#: editor/animation_editor.cpp
msgid "Optimize Animation"
-msgstr "Optimizar animación"
+msgstr "Optimizar Animación"
#: editor/animation_editor.cpp
msgid "Clean-Up Animation"
-msgstr "Limpiar animación"
+msgstr "Limpiar Animación"
#: editor/animation_editor.cpp
msgid "Create NEW track for %s and insert key?"
@@ -235,19 +236,19 @@ msgstr "Insertar Pista y Clave de Animación"
#: editor/animation_editor.cpp
msgid "Anim Insert Key"
-msgstr "Insertar clave de Animación"
+msgstr "Insertar Clave de Animación"
#: editor/animation_editor.cpp
msgid "Change Anim Len"
-msgstr "Cambiar duración de Animación"
+msgstr "Cambiar Duración de Animación"
#: editor/animation_editor.cpp
msgid "Change Anim Loop"
-msgstr "Cambiar bucle de Animación"
+msgstr "Cambiar Bucle de Animación"
#: editor/animation_editor.cpp
msgid "Anim Create Typed Value Key"
-msgstr "Crear clave de valor tipado para Animación"
+msgstr "Crear Clave de Valor Tipado para Animación"
#: editor/animation_editor.cpp
msgid "Anim Insert"
@@ -267,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)."
@@ -275,7 +276,7 @@ msgstr "Duración de la Animación (en segundos)."
#: editor/animation_editor.cpp
msgid "Step (s):"
-msgstr "Paso (s):"
+msgstr "Paso(s):"
#: editor/animation_editor.cpp
msgid "Cursor step snap (in seconds)."
@@ -307,7 +308,7 @@ msgstr "Herramientas de pistas"
#: editor/animation_editor.cpp
msgid "Enable editing of individual keys by clicking them."
-msgstr "Editar claves individuales al hacer clic."
+msgstr "Habilitar la edición de claves individuales al hacer clic."
#: editor/animation_editor.cpp
msgid "Anim. Optimizer"
@@ -345,11 +346,11 @@ msgstr "Transición"
#: editor/animation_editor.cpp
msgid "Scale Ratio:"
-msgstr "Relación de Escalado:"
+msgstr "Relación de Escala:"
#: editor/animation_editor.cpp
msgid "Call Functions in Which Node?"
-msgstr "¿Desde que nodo quieres realizar llamadas a funciones?"
+msgstr "¿Desde que Nodo quieres realizar Llamadas a Funciones?"
#: editor/animation_editor.cpp
msgid "Remove invalid keys"
@@ -441,19 +442,19 @@ msgstr "Columna:"
#: editor/connections_dialog.cpp
msgid "Method in target Node must be specified!"
-msgstr "¡Debes establecer un método en el nodo seleccionado!"
+msgstr "¡Debes establecer un método en el Nodo seleccionado!"
#: editor/connections_dialog.cpp
msgid ""
"Target method not found! Specify a valid method or attach a script to target "
"Node."
msgstr ""
-"No se ha encontrado el método objetivo. Especifica un método válido o "
-"adjunta un script en el Nodo objetivo."
+"No se encontró el método del objetivo! Especifica un método válido o adjunta "
+"un script al Nodo objetivo."
#: editor/connections_dialog.cpp
msgid "Connect To Node:"
-msgstr "Conectar a nodo:"
+msgstr "Conectar a Nodo:"
#: editor/connections_dialog.cpp editor/editor_autoload_settings.cpp
#: editor/groups_editor.cpp editor/plugins/item_list_editor_plugin.cpp
@@ -478,7 +479,7 @@ msgstr "Argumentos extras de llamada:"
#: editor/connections_dialog.cpp
msgid "Path to Node:"
-msgstr "Ruta al nodo:"
+msgstr "Ruta al Nodo:"
#: editor/connections_dialog.cpp
msgid "Make Function"
@@ -490,7 +491,7 @@ msgstr "Diferido"
#: editor/connections_dialog.cpp
msgid "Oneshot"
-msgstr "Una vez"
+msgstr "OneShot"
#: editor/connections_dialog.cpp editor/dependency_editor.cpp
#: editor/export_template_manager.cpp
@@ -516,15 +517,15 @@ msgstr "Conectar «%s» a «%s»"
#: editor/connections_dialog.cpp
msgid "Connecting Signal:"
-msgstr "Conectando señal:"
+msgstr "Conectando Señal:"
#: editor/connections_dialog.cpp
msgid "Disconnect '%s' from '%s'"
msgstr "Desconectar '%s' de '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "Conectar.."
+msgid "Connect..."
+msgstr "Conectar..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -546,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
@@ -625,7 +626,7 @@ msgstr "Arreglar rota(s)"
#: editor/dependency_editor.cpp
msgid "Dependency Editor"
-msgstr "Editor de dependencias"
+msgstr "Editor de Dependencias"
#: editor/dependency_editor.cpp
msgid "Search Replacement Resource:"
@@ -716,7 +717,7 @@ msgstr "Eliminar"
#: editor/dictionary_property_edit.cpp
msgid "Change Dictionary Key"
-msgstr "Cambiar Clave de Diccionario"
+msgstr "Cambiar Clave del Diccionario"
#: editor/dictionary_property_edit.cpp
msgid "Change Dictionary Value"
@@ -736,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"
@@ -756,11 +757,11 @@ msgstr "Autores"
#: editor/editor_about.cpp
msgid "Platinum Sponsors"
-msgstr "Patrocinadores Platino"
+msgstr "Patrocinadores Platinum"
#: editor/editor_about.cpp
msgid "Gold Sponsors"
-msgstr "Patrocinadores Oro"
+msgstr "Patrocinadores Gold"
#: editor/editor_about.cpp
msgid "Mini Sponsors"
@@ -768,11 +769,11 @@ msgstr "Mini Patrocinadores"
#: editor/editor_about.cpp
msgid "Gold Donors"
-msgstr "Donantes Oro"
+msgstr "Donantes Gold"
#: editor/editor_about.cpp
msgid "Silver Donors"
-msgstr "Donantes Plata"
+msgstr "Donantes Silver"
#: editor/editor_about.cpp
msgid "Bronze Donors"
@@ -946,12 +947,12 @@ msgid "Move Audio Bus"
msgstr "Mover Bus de Audio"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "Guardar configuración de los Buses de Audio como..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Ruta para nueva configuración.."
+msgid "Location for New Layout..."
+msgstr "Ubicación para Nueva Configuración..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1047,19 +1048,19 @@ msgstr "¡El fichero «%s» ya existe!"
#: editor/editor_autoload_settings.cpp
msgid "Rename Autoload"
-msgstr "Renombrar «Autoload»"
+msgstr "Renombrar Autoload"
#: editor/editor_autoload_settings.cpp
msgid "Toggle AutoLoad Globals"
-msgstr "Des/activar globales de «Autoload»"
+msgstr "Des/Activar Globales de Autoload"
#: editor/editor_autoload_settings.cpp
msgid "Move Autoload"
-msgstr "Mover «Autoload»"
+msgstr "Mover Autoload"
#: editor/editor_autoload_settings.cpp
msgid "Remove Autoload"
-msgstr "Quitar «Autoload»"
+msgstr "Quitar Autoload"
#: editor/editor_autoload_settings.cpp
msgid "Enable"
@@ -1067,7 +1068,7 @@ msgstr "Activar"
#: editor/editor_autoload_settings.cpp
msgid "Rearrange Autoloads"
-msgstr "Reordenar «Autoloads»"
+msgstr "Reordenar Autoloads"
#: editor/editor_autoload_settings.cpp editor/editor_file_dialog.cpp
#: scene/gui/file_dialog.cpp
@@ -1076,7 +1077,7 @@ msgstr "Ruta:"
#: editor/editor_autoload_settings.cpp
msgid "Node Name:"
-msgstr "Nombre del nodo:"
+msgstr "Nombre del Nodo:"
#: editor/editor_autoload_settings.cpp editor/editor_profiler.cpp
#: editor/project_manager.cpp editor/settings_config_dialog.cpp
@@ -1085,19 +1086,19 @@ msgstr "Nombre"
#: editor/editor_autoload_settings.cpp
msgid "Singleton"
-msgstr "«Singleton»"
+msgstr "Singleton"
#: editor/editor_data.cpp
msgid "Updating Scene"
-msgstr "Actualizando escena"
+msgstr "Actualizando Escena"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Guardando cambios locales.."
+msgid "Storing local changes..."
+msgstr "Guardando cambios locales..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Actualizando escena.."
+msgid "Updating scene..."
+msgstr "Actualizando escena..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1109,7 +1110,7 @@ msgstr "[no guardado]"
#: editor/editor_dir_dialog.cpp
msgid "Please select a base directory first"
-msgstr "Por favor, primero seleccione un directorio base"
+msgstr "Por favor, selecciona primero un directorio base"
#: editor/editor_dir_dialog.cpp
msgid "Choose a Directory"
@@ -1165,7 +1166,7 @@ msgid "Show In File Manager"
msgstr "Mostrar en el navegador de archivos"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr "Nueva carpeta..."
#: editor/editor_file_dialog.cpp
@@ -1427,20 +1428,20 @@ msgstr "Borrar salida"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "La exportación del proyecto falló con el código de error %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "¡Hubo un error al guardar el recurso!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Guardar recurso como.."
+msgid "Save Resource As..."
+msgstr "Guardar Recurso Como..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "Ya veo.."
+msgid "I see..."
+msgstr "Ya veo..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1476,7 +1477,7 @@ msgstr "Error al cargar '%s'."
#: editor/editor_node.cpp
msgid "Saving Scene"
-msgstr "Guardar escena"
+msgstr "Guardar Escena"
#: editor/editor_node.cpp
msgid "Analyzing"
@@ -1589,7 +1590,7 @@ msgstr "Expandir todas las propiedades"
#: editor/editor_node.cpp
msgid "Collapse all properties"
-msgstr "Colapsar todo"
+msgstr "Ocultar todas las propiedades"
#: editor/editor_node.cpp
msgid "Copy Params"
@@ -1613,7 +1614,7 @@ msgstr "Convertirlo en integrado"
#: editor/editor_node.cpp
msgid "Make Sub-Resources Unique"
-msgstr "Hacer sub-recursos únicos"
+msgstr "Creación de Subrecursos Únicos"
#: editor/editor_node.cpp
msgid "Open in Help"
@@ -1665,31 +1666,31 @@ msgstr "¡No se pudo comenzar el subproceso!"
#: editor/editor_node.cpp
msgid "Open Scene"
-msgstr "Abrir escena"
+msgstr "Abrir Escena"
#: editor/editor_node.cpp
msgid "Open Base Scene"
-msgstr "Abrir escena base"
+msgstr "Abrir Escena Base"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Apertura rápida de escena.."
+msgid "Quick Open Scene..."
+msgstr "Apertura Rápida de Escena..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Apertura rápida de script.."
+msgid "Quick Open Script..."
+msgstr "Apertura Rápida de Script..."
#: editor/editor_node.cpp
msgid "Save & Close"
-msgstr "Guardar & Cerrar"
+msgstr "Guardar y Cerrar"
#: editor/editor_node.cpp
msgid "Save changes to '%s' before closing?"
msgstr "¿Guardar cambios de '%s' antes de cerrar?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Guardar escena como.."
+msgid "Save Scene As..."
+msgstr "Guardar Escena Como..."
#: editor/editor_node.cpp
msgid "No"
@@ -1743,8 +1744,8 @@ msgstr ""
"modos?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Ejecución rápida de escena.."
+msgid "Quick Run Scene..."
+msgstr "Ejecución Rápida de Escena..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1782,7 +1783,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Pick a Main Scene"
-msgstr "Elige una escena principal"
+msgstr "Elige una Escena Principal"
#: editor/editor_node.cpp
msgid "Unable to enable addon plugin at: '%s' parsing of config failed."
@@ -1845,11 +1846,11 @@ msgstr "Limpiar Escenas Recientes"
#: editor/editor_node.cpp
msgid "Save Layout"
-msgstr "Guardar ajustes"
+msgstr "Guardar Ajustes"
#: editor/editor_node.cpp
msgid "Delete Layout"
-msgstr "Borrar ajustes"
+msgstr "Borrar Ajustes"
#: editor/editor_node.cpp editor/import_dock.cpp
#: editor/script_create_dialog.cpp
@@ -1858,7 +1859,7 @@ msgstr "Predeterminado"
#: editor/editor_node.cpp
msgid "Switch Scene Tab"
-msgstr "Cambiar pestaña de escena"
+msgstr "Cambiar Pestaña de Escena"
#: editor/editor_node.cpp
msgid "%d more files or folders"
@@ -1886,11 +1887,11 @@ msgstr "Alternar modo sin distracciones."
#: editor/editor_node.cpp
msgid "Add a new scene."
-msgstr "Añadir nueva Escena."
+msgstr "Añadir nueva escena."
#: editor/editor_node.cpp
msgid "Scene"
-msgstr "Escena"
+msgstr "Escenas"
#: editor/editor_node.cpp
msgid "Go to previously opened scene."
@@ -1905,8 +1906,8 @@ msgid "Previous tab"
msgstr "Pestaña anterior"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Filtrado de archivos.."
+msgid "Filter Files..."
+msgstr "Filtrado de Archivos..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1914,19 +1915,19 @@ msgstr "Operaciones con archivos de escena."
#: editor/editor_node.cpp
msgid "New Scene"
-msgstr "Nueva escena"
+msgstr "Nueva Escena"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Nueva escena heredada.."
+msgid "New Inherited Scene..."
+msgstr "Nueva Escena Heredada..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Abrir escena.."
+msgid "Open Scene..."
+msgstr "Abrir Escena..."
#: editor/editor_node.cpp
msgid "Save Scene"
-msgstr "Guardar escena"
+msgstr "Guardar Escena"
#: editor/editor_node.cpp
msgid "Save all Scenes"
@@ -1941,16 +1942,16 @@ msgid "Open Recent"
msgstr "Abrir reciente"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Convertir a.."
+msgid "Convert To..."
+msgstr "Convertir a..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "Librería de mallas.."
+msgid "MeshLibrary..."
+msgstr "Librería de mallas..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "\"TileSet\".."
+msgid "TileSet..."
+msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -1964,7 +1965,7 @@ msgstr "Rehacer"
#: editor/editor_node.cpp
msgid "Revert Scene"
-msgstr "Revertir escena"
+msgstr "Revertir Escena"
#: editor/editor_node.cpp
msgid "Miscellaneous project or scene-wide tools."
@@ -1976,19 +1977,19 @@ msgstr "Proyecto"
#: editor/editor_node.cpp
msgid "Project Settings"
-msgstr "Ajustes del proyecto"
+msgstr "Ajustes del Proyecto"
#: editor/editor_node.cpp
msgid "Run Script"
-msgstr "Ejecutar script"
+msgstr "Ejecutar Script"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Export"
-msgstr "Export"
+msgstr "Exportar"
#: editor/editor_node.cpp
msgid "Tools"
-msgstr "Herramientas"
+msgstr "Tools"
#: editor/editor_node.cpp
msgid "Quit to Project List"
@@ -2091,11 +2092,11 @@ msgstr "Editor"
#: editor/editor_node.cpp editor/settings_config_dialog.cpp
msgid "Editor Settings"
-msgstr "Ajustes del editor"
+msgstr "Ajustes del Editor"
#: editor/editor_node.cpp
msgid "Editor Layout"
-msgstr "Ajustes de diseño del editor"
+msgstr "Ajustes de Diseño del Editor"
#: editor/editor_node.cpp
msgid "Toggle Fullscreen"
@@ -2142,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"
@@ -2213,8 +2214,8 @@ msgid "Save the currently edited resource."
msgstr "Guardar el recurso editado actualmente."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Guardar como.."
+msgid "Save As..."
+msgstr "Guardar Como..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2243,11 +2244,11 @@ msgstr "Importar"
#: editor/editor_node.cpp
msgid "Node"
-msgstr "Nodo"
+msgstr "Nodos"
#: editor/editor_node.cpp
msgid "FileSystem"
-msgstr "SistDeArchivos"
+msgstr "Sistema de Archivos"
#: editor/editor_node.cpp
msgid "Output"
@@ -2263,7 +2264,7 @@ msgstr "Importar plantillas desde un archivo ZIP"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Export Project"
-msgstr "Exportar proyecto"
+msgstr "Exportar Proyecto"
#: editor/editor_node.cpp
msgid "Export Library"
@@ -2283,7 +2284,7 @@ msgstr "Abrir y ejecutar un script"
#: editor/editor_node.cpp
msgid "New Inherited"
-msgstr "Nueva escena heredada"
+msgstr "Nueva Escena Heredada"
#: editor/editor_node.cpp
msgid "Load Errors"
@@ -2322,8 +2323,8 @@ msgid "Creating Mesh Previews"
msgstr "Creando vistas previas de las mallas"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Miniatura.."
+msgid "Thumbnail..."
+msgstr "Miniatura..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2425,7 +2426,7 @@ msgstr "No se pudo instanciar el script:"
#: editor/editor_run_script.cpp
msgid "Did you forget the 'tool' keyword?"
-msgstr "Has olvidado la palabra clave 'tool'?"
+msgstr "¿Olvidaste la palabra clave 'tool'?"
#: editor/editor_run_script.cpp
msgid "Couldn't run script:"
@@ -2437,19 +2438,19 @@ msgstr "Te olvidaste del método '_run'?"
#: editor/editor_settings.cpp
msgid "Default (Same as Editor)"
-msgstr "Predeterminado (Igual que el editor)"
+msgstr "Predeterminado (Igual que el Editor)"
#: editor/editor_sub_scene.cpp
msgid "Select Node(s) to Import"
-msgstr "Selecciona nodos a importar"
+msgstr "Selecciona Nodos a importar"
#: editor/editor_sub_scene.cpp
msgid "Scene Path:"
-msgstr "Ruta a la escena:"
+msgstr "Ruta de la Escena:"
#: editor/editor_sub_scene.cpp
msgid "Import From Node:"
-msgstr "Importar desde nodo:"
+msgstr "Importar desde Nodo:"
#: editor/export_template_manager.cpp
msgid "Re-Download"
@@ -2476,7 +2477,7 @@ msgid "(Current)"
msgstr "(Actual)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr "Obteniendo mirrors, por favor espere..."
#: editor/export_template_manager.cpp
@@ -2554,8 +2555,8 @@ msgid "Error requesting url: "
msgstr "Error al solicitar url: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "Intentando conexión alternativa.."
+msgid "Connecting to Mirror..."
+msgstr "Intentando conexión alternativa..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2571,8 +2572,8 @@ msgstr "No se puede resolver"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Conectando.."
+msgid "Connecting..."
+msgstr "Conectando..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2584,8 +2585,8 @@ msgstr "Conectado"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Solicitando.."
+msgid "Requesting..."
+msgstr "Solicitando..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2722,32 +2723,32 @@ msgid "Collapse all"
msgstr "Colapsar todo"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Renombrar.."
+msgid "Rename..."
+msgstr "Renombrar..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "Mover a.."
+msgid "Move To..."
+msgstr "Mover a..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
-msgstr "Abrir escena/s"
+msgstr "Abrir Escena(s)"
#: editor/filesystem_dock.cpp
msgid "Instance"
msgstr "Instanciar"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Editar dependencias.."
+msgid "Edit Dependencies..."
+msgstr "Editar Dependencias..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Ver propietarios.."
+msgid "View Owners..."
+msgstr "Ver Propietarios..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Duplicar.."
+msgid "Duplicate..."
+msgstr "Duplicar..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2759,7 +2760,7 @@ msgstr "Carpeta siguiente"
#: editor/filesystem_dock.cpp
msgid "Re-Scan Filesystem"
-msgstr "Reanalizar sistema de archivos"
+msgstr "Reanalizar Sistema de Archivos"
#: editor/filesystem_dock.cpp
msgid "Toggle folder status as Favorite"
@@ -2773,7 +2774,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Escaneando archivos,\n"
"Por favor, espere..."
@@ -2841,8 +2842,8 @@ msgid "Import Scene"
msgstr "Importar escena"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Importando escena.."
+msgid "Importing Scene..."
+msgstr "Importando Escena..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2853,8 +2854,8 @@ msgid "Generating for Mesh: "
msgstr "Generando para modelo: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "Ejecutando script personalizado.."
+msgid "Running Custom Script..."
+msgstr "Ejecutando Script Personalizado..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2870,8 +2871,8 @@ msgid "Error running post-import script:"
msgstr "Error ejecutando el script de posimportacion:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Guardando.."
+msgid "Saving..."
+msgstr "Guardando..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2890,8 +2891,8 @@ msgid "Import As:"
msgstr "Importar como:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Ajuste.."
+msgid "Preset..."
+msgstr "Ajuste..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -2907,7 +2908,7 @@ msgstr "Grupos"
#: editor/node_dock.cpp
msgid "Select a Node to edit Signals and Groups."
-msgstr "Selecciona un nodo para editar señales y grupos."
+msgstr "Selecciona un Nodo para editar Señales y Grupos."
#: editor/plugins/abstract_polygon_2d_editor.cpp
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
@@ -2946,9 +2947,9 @@ msgid ""
"RMB: Erase Point."
msgstr ""
"Editar polígono existente:\n"
-"Click izquierdo: Mover punto.\n"
-"Control + Click izquierdo: Dividir segmento.\n"
-"Click derecho: Borrar punto."
+"Clic izquierdo: Mover punto.\n"
+"Control + Clic izquierdo: Dividir segmento.\n"
+"Clic derecho: Borrar punto."
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Delete points"
@@ -3085,7 +3086,7 @@ msgstr "Mostrar la lista de animaciones en el reproductor."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Autoplay on Load"
-msgstr "Autoreproducir al cargar"
+msgstr "Autoreproducir al Cargar"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Edit Target Blend Times"
@@ -3206,7 +3207,7 @@ msgstr "Mezcla"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Mix"
-msgstr "Mezclar"
+msgstr "Mix"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Auto Restart:"
@@ -3251,7 +3252,7 @@ msgstr "Actual:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Add Input"
-msgstr "Añadir entrada"
+msgstr "Añadir Entrada"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Clear Auto-Advance"
@@ -3263,7 +3264,7 @@ msgstr "Establecer autoavanzar"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Delete Input"
-msgstr "Eliminar entrada"
+msgstr "Eliminar Entrada"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Animation tree is valid."
@@ -3275,15 +3276,15 @@ msgstr "El árbol de animación no es correcto."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Animation Node"
-msgstr "Nodo de animación"
+msgstr "Nodo de Animación"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "OneShot Node"
-msgstr "Nodo UnaVez"
+msgstr "Nodo OneShot"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Mix Node"
-msgstr "Nodo Mezcla"
+msgstr "Nodo Mix"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend2 Node"
@@ -3299,7 +3300,7 @@ msgstr "Nodo Blend4"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "TimeScale Node"
-msgstr "Nodo TimeScale (Escala de tiempo)"
+msgstr "Nodo TimeScale"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "TimeSeek Node"
@@ -3307,19 +3308,19 @@ msgstr "Nodo TimeSeek"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Transition Node"
-msgstr "Nodo de transición"
+msgstr "Nodo Transition"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Importar animaciones.."
+msgid "Import Animations..."
+msgstr "Importar Animaciones..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
-msgstr "Editar filtros de nodo"
+msgstr "Editar Filtros de Nodo"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "Filtros.."
+msgid "Filters..."
+msgstr "Filtros..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3386,7 +3387,7 @@ msgid "Fetching:"
msgstr "Buscando:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr "Resolviendo..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3453,8 +3454,8 @@ msgid "Site:"
msgstr "Sitio:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Soporte.."
+msgid "Support..."
+msgstr "Soporte..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3494,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
@@ -3508,20 +3509,20 @@ msgstr "Configurar ajuste"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Grid Offset:"
-msgstr "Desplazamiento de rejilla:"
+msgstr "Desplazamiento de Cuadrícula:"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Grid Step:"
-msgstr "Pasos de rejilla:"
+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:"
-msgstr "Cantidad de rotaciones:"
+msgstr "Cantidad de Rotaciones:"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Pivot"
@@ -3603,7 +3604,7 @@ msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Alt+RMB: Depth list selection"
-msgstr "Alt+Click Der.: Selección en listado de solapamientos"
+msgstr "Alt + Clic Derecho: Selección en listado de solapamientos"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Mode"
@@ -3619,12 +3620,13 @@ msgid ""
"Show a list of all objects at the position clicked\n"
"(same as Alt+RMB in select mode)."
msgstr ""
-"Mostrar una lista de todos los objetos en la posición cliqueada\n"
-"(igual que Alt+Click Der. en modo selección)."
+"Mostrar una lista de todos los objetos en la posición en la que se ha hecho "
+"clic\n"
+"(igual que Alt + Clic Derecho en modo selección)."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Click to change object's rotation pivot."
-msgstr "Click para cambiar el pivote de rotación de un objeto."
+msgstr "Haz clic para cambiar el pivote de rotación de un objeto."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Pan Mode"
@@ -3651,8 +3653,9 @@ msgid "Use Rotation Snap"
msgstr "Ajustar rotación"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
-msgstr "Configurar Cuadrícula..."
+msgstr "Configurar Ajuste..."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap Relative"
@@ -3672,7 +3675,7 @@ msgstr "Ajustar al padre"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node anchor"
-msgstr "Ajustar al anclado del nodo"
+msgstr "Ajustar al anclaje del nodo"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node sides"
@@ -3732,7 +3735,7 @@ msgstr "Ver"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Show Grid"
-msgstr "Mostrar rejilla"
+msgstr "Mostrar Cuadrícula"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Helpers"
@@ -3747,14 +3750,12 @@ msgid "Show Guides"
msgstr "Mostrar guías"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "Ver origen"
+msgstr "Ver Origen"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 visor"
+msgstr "Ver Viewport"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -3774,11 +3775,11 @@ msgstr "Insertar claves"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Insert Key"
-msgstr "Insertar clave"
+msgstr "Insertar Clave"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Insert Key (Existing Tracks)"
-msgstr "Insertar clave (pistas existentes)"
+msgstr "Insertar Clave (Pistas Existentes)"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Copy Pose"
@@ -3823,7 +3824,7 @@ msgstr "No se pueden instanciar varios nodos sin un nodo raíz."
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Create Node"
-msgstr "Crear nodo"
+msgstr "Crear Nodo"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
@@ -4049,7 +4050,7 @@ msgstr "¡La malla no tiene superficie de la que crear contornos!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "El tipo de la malla primitiva no es PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4080,8 +4081,8 @@ msgid "Create Convex Collision Sibling"
msgstr "Crear colisión hermanada convexa"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "Crear contorno de malla.."
+msgid "Create Outline Mesh..."
+msgstr "Crear Contorno de Malla..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4227,7 +4228,7 @@ msgstr "Calculando tamaño de cuadrícula..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Creating heightfield..."
-msgstr "Creando octree de luces (\"heigfield\")..."
+msgstr "Creando heightfield..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Marking walkable triangles..."
@@ -4251,7 +4252,7 @@ msgstr "Creando contornos..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Creating polymesh..."
-msgstr "Crear malla 3D de contorno (\"polymesh\")..."
+msgstr "Crear polymesh..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Converting to native navigation mesh..."
@@ -4263,7 +4264,7 @@ msgstr "Configuración del Generador de Mallas de Navegación:"
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Parsing Geometry..."
-msgstr "Analizando geometría..."
+msgstr "Analizando Geometría..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Done!"
@@ -4288,8 +4289,8 @@ msgid "Error loading image:"
msgstr "Error al cargar la imagen:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "No hay píxeles con transparencia > 128 en la imagen.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "No hay píxeles con transparencia > 128 en la imagen..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4331,11 +4332,11 @@ msgstr "Colores de emisión"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Node does not contain geometry."
-msgstr "El nodo no contiene geometría."
+msgstr "El nodo no posee geometría."
#: editor/plugins/particles_editor_plugin.cpp
msgid "Node does not contain geometry (faces)."
-msgstr "El nodo no contiene geometría (caras)."
+msgstr "El nodo no posee geometría (caras)."
#: editor/plugins/particles_editor_plugin.cpp
msgid "A processor material of type 'ParticlesMaterial' is required."
@@ -4359,7 +4360,7 @@ msgstr "Crear puntos de emisión desde malla"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Node"
-msgstr "Crear puntos de emisión desde el nodo"
+msgstr "Crear Puntos de Emisión desde el Nodo"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emitter"
@@ -4431,12 +4432,12 @@ msgstr "Mayús + arrastrar: Seleccionar puntos de control"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Click: Add Point"
-msgstr "Click: Añadir punto"
+msgstr "Clic: Añadir Punto"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Right Click: Delete Point"
-msgstr "Clic derecho: Eliminar punto"
+msgstr "Clic Derecho: Eliminar Punto"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Select Control Points (Shift+Drag)"
@@ -4565,7 +4566,7 @@ msgstr "Habilitar fijado"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Grid"
-msgstr "Rejilla"
+msgstr "Cuadrícula"
#: editor/plugins/resource_preloader_editor_plugin.cpp
msgid "ERROR: Couldn't load resource!"
@@ -4591,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
@@ -4649,8 +4650,8 @@ msgid "Import Theme"
msgstr "Importar tema"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Guardar tema como.."
+msgid "Save Theme As..."
+msgstr "Guardar Tema Como..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4746,8 +4747,8 @@ msgstr "Alternar panel de scripts"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Buscar.."
+msgid "Find..."
+msgstr "Buscar..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4856,7 +4857,7 @@ msgstr "Minúscula"
#: editor/plugins/script_text_editor.cpp
msgid "Capitalize"
-msgstr "Letra Capital"
+msgstr "Poner en mayúsculas"
#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp
#: scene/gui/text_edit.cpp
@@ -4920,7 +4921,7 @@ msgstr "Convertir Indentación a Espacios"
#: editor/plugins/script_text_editor.cpp
msgid "Convert Indent To Tabs"
-msgstr "Convertir indentación a tabuladores"
+msgstr "Convertir Indentación a Tabuladores"
#: editor/plugins/script_text_editor.cpp
msgid "Auto Indent"
@@ -4956,16 +4957,16 @@ msgid "Find Previous"
msgstr "Buscar anterior"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Reemplazar.."
+msgid "Replace..."
+msgstr "Reemplazar..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Ir a función.."
+msgid "Goto Function..."
+msgstr "Ir a función..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Ir a línea.."
+msgid "Goto Line..."
+msgstr "Ir a línea..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -4973,7 +4974,7 @@ msgstr "Ayuda contextual"
#: editor/plugins/shader_editor_plugin.cpp
msgid "Shader"
-msgstr "\"Shader\""
+msgstr "Shader"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Constant"
@@ -5073,11 +5074,11 @@ msgstr "Desconectar Nodos Gráficos"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Remove Shader Graph Node"
-msgstr "Borrar Nodo Gráfico de Shader"
+msgstr "Eliminar el Nodo Gráfico del Shader"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Move Shader Graph Node"
-msgstr "Mover Nodo Gráfico de Shader"
+msgstr "Mover el Nodo Gráfico del Shader"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Duplicate Graph Node(s)"
@@ -5085,7 +5086,7 @@ msgstr "Duplicar Nodo(s) Gráfico"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Delete Shader Graph Node(s)"
-msgstr "Borrar Nodo(s) Gráfico(s) de Shader"
+msgstr "Eliminar Nodo(s) Gráfico(s) del Shader"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Error: Cyclic Connection Link"
@@ -5097,7 +5098,7 @@ msgstr "Error: Conexiones de Entrada Faltantes"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Add Shader Graph Node"
-msgstr "Añadir nodo gráfico de Shader"
+msgstr "Añadir Nodo Gráfico del Shader"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orthogonal"
@@ -5125,7 +5126,7 @@ msgstr "Transformación en el eje Z."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Plane Transform."
-msgstr "Ver transformación en plano."
+msgstr "Ver Transformación de Plano."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scaling: "
@@ -5145,7 +5146,7 @@ msgstr "Insertar claves está desactivado (no se insertaron claves)."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Animation Key Inserted."
-msgstr "Clave de animación insertada."
+msgstr "Clave de Animación Insertada."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Objects Drawn"
@@ -5157,7 +5158,7 @@ msgstr "Cambios del material"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Shader Changes"
-msgstr "Cambios del shader"
+msgstr "Cambios del Shader"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Surface Changes"
@@ -5177,11 +5178,11 @@ msgstr "FPS"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Top View."
-msgstr "Vista superior."
+msgstr "Vista Superior."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Bottom View."
-msgstr "Vista inferior."
+msgstr "Vista Inferior."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Bottom"
@@ -5189,7 +5190,7 @@ msgstr "Fondo"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Left View."
-msgstr "Vista izquierda."
+msgstr "Vista Izquierda."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Left"
@@ -5197,7 +5198,7 @@ msgstr "Izquierda"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right View."
-msgstr "Vista derecha."
+msgstr "Vista Derecha."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right"
@@ -5205,7 +5206,7 @@ msgstr "Derecha"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Front View."
-msgstr "Vista frontal."
+msgstr "Vista Frontal."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Front"
@@ -5213,7 +5214,7 @@ msgstr "Frente"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rear View."
-msgstr "Vista anterior."
+msgstr "Vista Posterior."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rear"
@@ -5281,31 +5282,31 @@ msgstr "Activar Doppler"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Left"
-msgstr "Vista libre izquierda"
+msgstr "Vista Libre Izquierda"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Right"
-msgstr "Vista libre derecha"
+msgstr "Vista Libre Derecha"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Forward"
-msgstr "Vista libre frente"
+msgstr "Vista Libre Frontal"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Backwards"
-msgstr "Vista libre atrás"
+msgstr "Vista Libre Posterior"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Up"
-msgstr "Vista libre arriba"
+msgstr "Vista Libre Arriba"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Down"
-msgstr "Vista libre abajo"
+msgstr "Vista Libre Abajo"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Speed Modifier"
-msgstr "Modificador de velocidad de \"vista libre\""
+msgstr "Modificador de Velocidad de Vista Libre"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "XForm Dialog"
@@ -5323,7 +5324,7 @@ msgid ""
msgstr ""
"Arrastrar: Rotar\n"
"Alt + Arrastrar: Mover\n"
-"Alt + Click derecho: Selección en la lista de superposición"
+"Alt + Clic Derecho: Selección en la lista de superposición"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Move Mode (W)"
@@ -5339,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)"
@@ -5351,35 +5352,35 @@ msgstr "Modo de ajuste (%s)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Bottom View"
-msgstr "Vista inferior"
+msgstr "Vista Inferior"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Top View"
-msgstr "Vista superior"
+msgstr "Vista Superior"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rear View"
-msgstr "Vista anterior"
+msgstr "Vista Posterior"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Front View"
-msgstr "Vista frontal"
+msgstr "Vista Frontal"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Left View"
-msgstr "Vista izquierda"
+msgstr "Vista Izquierda"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right View"
-msgstr "Vista derecha"
+msgstr "Vista Derecha"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal view"
-msgstr "Intercambiar entre vista de perspectiva y ortogonal"
+msgstr "Intercambiar vista Perspectiva/Ortogonal"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Insert Animation Key"
-msgstr "Insertar clave de animación"
+msgstr "Insertar Clave de Animación"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Focus Origin"
@@ -5411,23 +5412,19 @@ msgstr "Escalar"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Toggle Freelook"
-msgstr "Alternar vista libre"
+msgstr "Activar Vista Libre"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform"
-msgstr "Transformar"
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Configurar ajuste.."
+msgstr "Transform"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "Ventana de transformación.."
+msgid "Transform Dialog..."
+msgstr "Ventana de transformación..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
-msgstr "1 visor"
+msgstr "1 Viewport"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "2 Viewports"
@@ -5455,7 +5452,7 @@ msgstr "Ver origen"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Grid"
-msgstr "Ver rejilla"
+msgstr "Ver Cuadrícula"
#: editor/plugins/spatial_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
@@ -5484,7 +5481,7 @@ msgstr "Ajuste de escala (%):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Viewport Settings"
-msgstr "Ajustes del visor"
+msgstr "Ajustes del Viewport"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Perspective FOV (deg.):"
@@ -5592,7 +5589,7 @@ msgstr "Mover (Después)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "SpriteFrames"
-msgstr "Fotogramas del sprite"
+msgstr "SpriteFrames"
#: editor/plugins/style_box_editor_plugin.cpp
msgid "StyleBox Preview:"
@@ -5620,7 +5617,7 @@ msgstr "Ajustar a píxeles"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Grid Snap"
-msgstr "Ajustar a cuadrícula"
+msgstr "Ajustar a Cuadrícula"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Auto Slice"
@@ -5675,8 +5672,8 @@ msgid "Remove All"
msgstr "Quitar todos"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "Editar tema.."
+msgid "Edit theme..."
+msgstr "Editar tema..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5723,14 +5720,12 @@ msgid "Checked Item"
msgstr "Casilla de verificación activa"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "Añadir elemento"
+msgstr "Radio Item"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "Casilla de verificación activa"
+msgstr "Ratio Item Activo"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5745,8 +5740,8 @@ msgid "Options"
msgstr "Opciones"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr "¡Tienes,Muchas,Y,Variadas,Opciones!"
+msgid "Has,Many,Options"
+msgstr "Tienes, Muchas, Opciones"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5870,7 +5865,7 @@ msgstr "¿Mezclar desde escena?"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Tile Set"
-msgstr "\"Tile Set\""
+msgstr "Tile Set"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -5886,7 +5881,7 @@ msgstr "Error"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Autotiles"
-msgstr "\"Autotiles\""
+msgstr "Autotiles"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid ""
@@ -5901,8 +5896,8 @@ msgid ""
"LMB: set bit on.\n"
"RMB: set bit off."
msgstr ""
-"Click izquierdo: habilitar bit.\n"
-"Click derecho: deshabilitar bit."
+"Clic Izquierdo: habilitar bit.\n"
+"Clic Derecho: deshabilitar bit."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Select current edited sub-tile."
@@ -5938,8 +5933,8 @@ msgid "Presets"
msgstr "Preajustes"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "Añadir.."
+msgid "Add..."
+msgstr "Añadir..."
#: editor/project_export.cpp
msgid "Resources"
@@ -6014,7 +6009,7 @@ msgstr ""
#: editor/project_export.cpp
msgid "Export With Debug"
-msgstr "Exportar con depuración"
+msgstr "Exportar con Depuración"
#: editor/project_manager.cpp
msgid "The path does not exist."
@@ -6030,7 +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
+msgid "Invalid Project Name."
+msgstr "Nombre de Proyecto Inválido."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6074,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"
@@ -6086,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"
@@ -6094,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"
@@ -6102,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"
@@ -6110,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"
@@ -6118,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"
@@ -6135,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 ""
@@ -6153,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 ""
@@ -6191,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"
@@ -6214,8 +6213,9 @@ msgid ""
"You don't currently have any projects.\n"
"Would you like to explore the official example projects in the Asset Library?"
msgstr ""
-"Ahora mismo no tiene ningún proyecto.\n"
-"¿Le gustaría explorar los proyectos ejemplo oficiales del Asset Library?"
+"Actualmente no tienes ningún proyecto.\n"
+"¿Quieres explorar los proyectos de ejemplo oficiales en la Biblioteca de "
+"Assets?"
#: editor/project_settings_editor.cpp
msgid "Key "
@@ -6235,9 +6235,11 @@ msgstr "Botón del ratón"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
+"Nombre de acción inválido. No puede estar vacío ni contener '/', ':', '=', "
+"'\\' o '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6245,11 +6247,11 @@ msgstr "¡La acción «%s» ya existe!"
#: editor/project_settings_editor.cpp
msgid "Rename Input Action Event"
-msgstr "Renombrar evento de acción de entrada"
+msgstr "Renombrar Evento de Acción de Entrada"
#: editor/project_settings_editor.cpp
msgid "Add Input Action Event"
-msgstr "Añadir evento de acción de entrada"
+msgstr "Añadir Evento de Acción de Entrada"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "Shift+"
@@ -6264,8 +6266,8 @@ msgid "Control+"
msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "Presiona una tecla.."
+msgid "Press a Key..."
+msgstr "Presiona una tecla..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6321,11 +6323,11 @@ msgstr "Ãndice de boton del mando:"
#: editor/project_settings_editor.cpp
msgid "Erase Input Action"
-msgstr "Borrar \"Input Action\""
+msgstr "Borrar Acción de Entrada"
#: editor/project_settings_editor.cpp
msgid "Erase Input Action Event"
-msgstr "Borrar evento de acción de entrada"
+msgstr "Borrar Evento de Acción de Entrada"
#: editor/project_settings_editor.cpp
msgid "Add Event"
@@ -6385,7 +6387,7 @@ msgstr "Ya existe"
#: editor/project_settings_editor.cpp
msgid "Add Input Action"
-msgstr "Añadir acción de entrada"
+msgstr "Añadir Acción de Entrada"
#: editor/project_settings_editor.cpp
msgid "Error saving settings."
@@ -6397,7 +6399,7 @@ msgstr "Los ajustes se han guardado correctamente."
#: editor/project_settings_editor.cpp
msgid "Override for Feature"
-msgstr "Sobreescribir para esta característica"
+msgstr "Sobrescribir la Característica"
#: editor/project_settings_editor.cpp
msgid "Add Translation"
@@ -6448,12 +6450,12 @@ msgid "Property:"
msgstr "Propiedad:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
-msgstr "Sobre escribir por.."
+msgid "Override For..."
+msgstr "Sustituir por..."
#: editor/project_settings_editor.cpp
msgid "Input Map"
-msgstr "Mapa de entradas"
+msgstr "Mapa de Entradas"
#: editor/project_settings_editor.cpp
msgid "Action:"
@@ -6521,7 +6523,7 @@ msgstr "AutoLoad"
#: editor/property_editor.cpp
msgid "Pick a Viewport"
-msgstr "Selecciona un visor"
+msgstr "Selecciona un Viewport"
#: editor/property_editor.cpp
msgid "Ease In"
@@ -6544,12 +6546,12 @@ msgid "Easing Out-In"
msgstr "Transición salida-entrada"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "Archivo.."
+msgid "File..."
+msgstr "Archivo..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "Directorio.."
+msgid "Dir..."
+msgstr "Directorio..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6557,7 +6559,7 @@ msgstr "Asignar"
#: editor/property_editor.cpp
msgid "Select Node"
-msgstr "Seleccionar nodo"
+msgstr "Seleccionar Nodo"
#: editor/property_editor.cpp
msgid "New Script"
@@ -6585,11 +6587,11 @@ msgstr "Error al cargar el archivo: ¡No es un recurso!"
#: editor/property_editor.cpp
msgid "Selected node is not a Viewport!"
-msgstr "¡El nodo seleccionado no es un visor!"
+msgstr "¡El nodo seleccionado no es un Viewport!"
#: editor/property_editor.cpp
msgid "Pick a Node"
-msgstr "Selecciona un nodo"
+msgstr "Selecciona un Nodo"
#: editor/property_editor.cpp
msgid "Bit %d, val %d."
@@ -6634,7 +6636,7 @@ msgstr ""
#: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp
msgid "Reparent Node"
-msgstr "Reemparentar nodo"
+msgstr "Reemparentar Nodo"
#: editor/reparent_dialog.cpp
msgid "Reparent Location (Select new Parent):"
@@ -6707,11 +6709,11 @@ msgstr "Mover Nodos Dentro del Padre"
#: editor/scene_tree_dock.cpp
msgid "Duplicate Node(s)"
-msgstr "Duplicar nodos"
+msgstr "Duplicar Nodo(s)"
#: editor/scene_tree_dock.cpp
msgid "Delete Node(s)?"
-msgstr "¿Quieres borrar los nodos?"
+msgstr "¿Eliminar Nodo(s)?"
#: editor/scene_tree_dock.cpp
msgid "Can not perform with the root node."
@@ -6722,20 +6724,20 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Esta operación no puede realizarse en escenas instanciadas."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "Guardar nueva escena como.."
+msgid "Save New Scene As..."
+msgstr "Guardar Nueva Escena Como..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
-msgstr "Hijos editables"
+msgstr "Hijos Editables"
#: editor/scene_tree_dock.cpp
msgid "Load As Placeholder"
-msgstr "Cargar como temporal"
+msgstr "Cargar como Temporal"
#: editor/scene_tree_dock.cpp
msgid "Discard Instancing"
-msgstr "Descartar instancia"
+msgstr "Descartar Instancia"
#: editor/scene_tree_dock.cpp
msgid "Makes Sense!"
@@ -6751,7 +6753,7 @@ msgstr "¡No se puede operar sobre los nodos heredados por la escena actual!"
#: editor/scene_tree_dock.cpp
msgid "Remove Node(s)"
-msgstr "Borrar nodos"
+msgstr "Eliminar Nodo(s)"
#: editor/scene_tree_dock.cpp
msgid ""
@@ -6771,27 +6773,27 @@ msgstr "Error al duplicar escena para guardarla."
#: editor/scene_tree_dock.cpp
msgid "Sub-Resources"
-msgstr "Sub-recursos"
+msgstr "Sub-Recursos"
#: editor/scene_tree_dock.cpp
msgid "Clear Inheritance"
-msgstr "Limpiar heredado"
+msgstr "Limpiar Heredado"
#: editor/scene_tree_dock.cpp
msgid "Delete Node(s)"
-msgstr "Borrar nodos"
+msgstr "Eliminar Nodo(s)"
#: editor/scene_tree_dock.cpp
msgid "Add Child Node"
-msgstr "Añadir nodo hijo"
+msgstr "Añadir Nodo Hijo"
#: editor/scene_tree_dock.cpp
msgid "Instance Child Scene"
-msgstr "Instanciar escena hija"
+msgstr "Instanciar Escena Hija"
#: editor/scene_tree_dock.cpp
msgid "Change Type"
-msgstr "Cambiar tipo"
+msgstr "Cambiar Tipo"
#: editor/scene_tree_dock.cpp
msgid "Attach Script"
@@ -6803,7 +6805,7 @@ msgstr "Quitar script"
#: editor/scene_tree_dock.cpp
msgid "Merge From Scene"
-msgstr "Unir desde escena"
+msgstr "Unir Desde Escena"
#: editor/scene_tree_dock.cpp
msgid "Save Branch as Scene"
@@ -6811,22 +6813,22 @@ msgstr "Guardar Rama como Escena"
#: editor/scene_tree_dock.cpp
msgid "Copy Node Path"
-msgstr "Copiar ruta del nodo"
+msgstr "Copiar Ruta del Nodo"
#: editor/scene_tree_dock.cpp
msgid "Delete (No Confirm)"
-msgstr "Eliminar (sin confirmar)"
+msgstr "Eliminar (Sin Confirmar)"
#: editor/scene_tree_dock.cpp
msgid "Add/Create a New Node"
-msgstr "Añadir/crear nodo nuevo"
+msgstr "Añadir/Crear un Nuevo Nodo"
#: editor/scene_tree_dock.cpp
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
@@ -6839,7 +6841,7 @@ msgstr "Añadir un script nuevo o existente al nodo seleccionado."
#: editor/scene_tree_dock.cpp
msgid "Clear a script for the selected node."
-msgstr "Borra el script del nodo seleccionado."
+msgstr "Borrar el script del nodo seleccionado."
#: editor/scene_tree_dock.cpp
msgid "Remote"
@@ -6874,8 +6876,8 @@ msgid ""
"Node has connection(s) and group(s)\n"
"Click to show signals dock."
msgstr ""
-"El nodo tiene conexión/es y grupo/s\n"
-"Haz click para mostrar el panel de señales."
+"El nodo tiene conexión(es) y grupo(s)\n"
+"Haz clic para mostrar el panel de señales."
#: editor/scene_tree_editor.cpp
msgid ""
@@ -6883,7 +6885,7 @@ msgid ""
"Click to show signals dock."
msgstr ""
"El nodo tiene conexiones.\n"
-"Haz click para mostrar el panel de señales."
+"Haz clic para mostrar el panel de señales."
#: editor/scene_tree_editor.cpp
msgid ""
@@ -6891,7 +6893,7 @@ msgid ""
"Click to show groups dock."
msgstr ""
"El nodo está en el/los grupo(s).\n"
-"Click para mostrar el panel de grupos."
+"Haz clic para mostrar el panel de grupos."
#: editor/scene_tree_editor.cpp
msgid "Open script"
@@ -6903,7 +6905,7 @@ msgid ""
"Click to unlock"
msgstr ""
"El nodo está bloqueado.\n"
-"Click para desbloquear"
+"Haz clic para desbloquear"
#: editor/scene_tree_editor.cpp
msgid ""
@@ -6911,7 +6913,7 @@ msgid ""
"Click to make selectable"
msgstr ""
"Los hijos no son seleccionables.\n"
-"Haz click para hacerlos seleccionables"
+"Haz clic para hacerlos seleccionables"
#: editor/scene_tree_editor.cpp
msgid "Toggle Visibility"
@@ -6924,7 +6926,7 @@ msgstr ""
#: editor/scene_tree_editor.cpp
msgid "Rename Node"
-msgstr "Renombrar nodo"
+msgstr "Renombrar Nodo"
#: editor/scene_tree_editor.cpp
msgid "Scene Tree (Nodes):"
@@ -6932,11 +6934,11 @@ msgstr "Ãrbol de escenas (nodos):"
#: editor/scene_tree_editor.cpp
msgid "Node Configuration Warning!"
-msgstr "¡Alerta de configuración de nodos!"
+msgstr "¡Alerta de Configuración de Nodos!"
#: editor/scene_tree_editor.cpp
msgid "Select a Node"
-msgstr "Selecciona un nodo"
+msgstr "Selecciona un Nodo"
#: editor/script_create_dialog.cpp
msgid "Error loading template '%s'"
@@ -6944,7 +6946,7 @@ msgstr "Error al cargar la plantilla '%s'"
#: editor/script_create_dialog.cpp
msgid "Error - Could not create script in filesystem."
-msgstr "Error - No se pudo crear script en el sistema."
+msgstr "Error - No se pudo crear script en el sistema de archivos."
#: editor/script_create_dialog.cpp
msgid "Error loading script from %s"
@@ -6980,7 +6982,7 @@ msgstr "La extensión no es correcta"
#: editor/script_create_dialog.cpp
msgid "Wrong extension chosen"
-msgstr "Extensión seleccionada errónea"
+msgstr "Se ha elegido una extensión incorrecta"
#: editor/script_create_dialog.cpp
msgid "Invalid Path"
@@ -7036,7 +7038,7 @@ msgstr "Script integrado"
#: editor/script_create_dialog.cpp
msgid "Attach Node Script"
-msgstr "Añadir script de nodo"
+msgstr "Añadir Script de Nodo"
#: editor/script_editor_debugger.cpp
msgid "Remote "
@@ -7140,7 +7142,7 @@ msgstr "Tipo"
#: editor/script_editor_debugger.cpp
msgid "Format"
-msgstr "Format"
+msgstr "Formato"
#: editor/script_editor_debugger.cpp
msgid "Usage"
@@ -7152,11 +7154,11 @@ msgstr "Otros"
#: editor/script_editor_debugger.cpp
msgid "Clicked Control:"
-msgstr "Controles seleccionados:"
+msgstr "Controles Seleccionados:"
#: editor/script_editor_debugger.cpp
msgid "Clicked Control Type:"
-msgstr "Tipo de controles seleccionados:"
+msgstr "Tipo de Controles Seleccionados:"
#: editor/script_editor_debugger.cpp
msgid "Live Edit Root:"
@@ -7236,7 +7238,7 @@ msgstr "Borrar entrada actual"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Double click to create a new entry"
-msgstr "Doble click para crear una nueva entrada"
+msgstr "Haz doble clic para crear una nueva entrada"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Platform:"
@@ -7248,15 +7250,15 @@ msgstr "Plataforma"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Dynamic Library"
-msgstr "Librería dinámica"
+msgstr "Librería Dinámica"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Add an architecture entry"
-msgstr "Añadir entrada de arquitectura"
+msgstr "Añadir una entrada de arquitectura"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "GDNativeLibrary"
-msgstr "\"GDNativeLibrary\""
+msgstr "GDNativeLibrary"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
msgid "Library"
@@ -7290,7 +7292,7 @@ msgstr ""
#: modules/gdscript/gdscript_functions.cpp
msgid "step argument is zero!"
-msgstr "¡El argumento «step» es cero!"
+msgstr "¡el argumento del paso es cero!"
#: modules/gdscript/gdscript_functions.cpp
msgid "Not a script with an instance"
@@ -7330,7 +7332,7 @@ msgstr "El objeto no puede proporcionar una longitud."
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Next Plane"
-msgstr "Plano siguiente"
+msgstr "Siguiente Plano"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Previous Plane"
@@ -7342,7 +7344,7 @@ msgstr "Plano:"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Next Floor"
-msgstr "Suelo Posterior"
+msgstr "Siguiente Piso"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Previous Floor"
@@ -7362,7 +7364,7 @@ msgstr "GridMap Duplicar selección"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Grid Map"
-msgstr "Rejilla"
+msgstr "Mapa de Cuadrícula"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Snap View"
@@ -7446,7 +7448,7 @@ msgstr "Seleccionar distancia:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "El nombre de la clase no puede ser una palabra reservada"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -7490,7 +7492,7 @@ msgstr "Compilaciones"
#: modules/mono/editor/mono_bottom_panel.cpp
msgid "Build Project"
-msgstr "Compilar proyecto"
+msgstr "Compilar Proyecto"
#: modules/mono/editor/mono_bottom_panel.cpp
msgid "Warnings"
@@ -7505,7 +7507,7 @@ msgid ""
"A node yielded without working memory, please read the docs on how to yield "
"properly!"
msgstr ""
-"¡Un nodo ejecutó un «yield» sin memoria de trabajo. Prueba leyendo la "
+"¡Un nodo ejecutó un yield sin memoria de trabajo. Prueba leyendo la "
"documentación sobre cómo utilizar yield!"
#: modules/visual_script/visual_script.cpp
@@ -7513,8 +7515,8 @@ msgid ""
"Node yielded, but did not return a function state in the first working "
"memory."
msgstr ""
-"Un nodo ejecutó un «yield» pero no devolvió un estado de función en la "
-"memoria de trabajo original."
+"Un nodo ejecutó un yield pero no devolvió un estado de función en la memoria "
+"de trabajo original."
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7540,7 +7542,7 @@ msgstr "Desbordamiento de pila en el nivel: "
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Signal Arguments"
-msgstr "Cambiar argumentos de la señal"
+msgstr "Cambiar Argumentos de la Señal"
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Argument Type"
@@ -7576,35 +7578,35 @@ msgstr "Otra función/variable/señal ya utiliza este nombre:"
#: modules/visual_script/visual_script_editor.cpp
msgid "Rename Function"
-msgstr "Renombrar función"
+msgstr "Renombrar Función"
#: modules/visual_script/visual_script_editor.cpp
msgid "Rename Variable"
-msgstr "Renombrar variable"
+msgstr "Renombrar Variable"
#: modules/visual_script/visual_script_editor.cpp
msgid "Rename Signal"
-msgstr "Renombrar señal"
+msgstr "Renombrar Señal"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Function"
-msgstr "Añadir función"
+msgstr "Añadir Función"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Variable"
-msgstr "Añadir variable"
+msgstr "Añadir Variable"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Signal"
-msgstr "Añadir señal"
+msgstr "Añadir Señal"
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Expression"
-msgstr "Cambiar expresión"
+msgstr "Cambiar Expresión"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Node"
-msgstr "Añadir nodo"
+msgstr "Añadir Nodo"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove VisualScript Nodes"
@@ -7612,7 +7614,7 @@ msgstr "Quitar nodos de VisualScript"
#: modules/visual_script/visual_script_editor.cpp
msgid "Duplicate VisualScript Nodes"
-msgstr "Duplicar nodos de VisualScript"
+msgstr "Duplicar Nodos de VisualScript"
#: modules/visual_script/visual_script_editor.cpp
msgid "Hold %s to drop a Getter. Hold Shift to drop a generic signature."
@@ -7632,7 +7634,7 @@ msgstr "Mantén pulsado %s para quitar una referencia simple del nodo."
#: modules/visual_script/visual_script_editor.cpp
msgid "Hold Ctrl to drop a simple reference to the node."
-msgstr "Mantén pulsado Ctrl para soltar una referencia al nodo."
+msgstr "Mantén pulsado Ctrl para soltar una referencia simple al nodo."
#: modules/visual_script/visual_script_editor.cpp
msgid "Hold %s to drop a Variable Setter."
@@ -7644,19 +7646,19 @@ msgstr "Mantén pulsado Ctrl para soltar un «Setter» de variable."
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Preload Node"
-msgstr "Añadir nodo «Preload»"
+msgstr "Añadir Nodo Preload"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Node(s) From Tree"
-msgstr "Añadir nodo/s desde árbol"
+msgstr "Añadir Nodo(s) desde Ãrbol"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Getter Property"
-msgstr "Añadir propiedad «Getter»"
+msgstr "Añadir propiedad Getter"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Setter Property"
-msgstr "Añadir propiedad «Setter»"
+msgstr "Añadir propiedad Setter"
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Base Type"
@@ -7664,15 +7666,15 @@ msgstr "Cambiar tipo base"
#: modules/visual_script/visual_script_editor.cpp
msgid "Move Node(s)"
-msgstr "Mover nodo/s"
+msgstr "Mover Nodo(s)"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove VisualScript Node"
-msgstr "Quitar nodo de VisualScript"
+msgstr "Quitar Nodo de VisualScript"
#: modules/visual_script/visual_script_editor.cpp
msgid "Connect Nodes"
-msgstr "Conectar nodos"
+msgstr "Conectar Nodos"
#: modules/visual_script/visual_script_editor.cpp
msgid "Condition"
@@ -7700,7 +7702,7 @@ msgstr "Devuelve (\"Return\")"
#: modules/visual_script/visual_script_editor.cpp
msgid "Call"
-msgstr "Call"
+msgstr "Llamada (\"Call\")"
#: modules/visual_script/visual_script_editor.cpp
msgid "Get"
@@ -7712,7 +7714,7 @@ msgstr "El script ya contiene la función '%s'"
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Input Value"
-msgstr "Cambiar valor de entrada"
+msgstr "Cambiar Valor de Entrada"
#: modules/visual_script/visual_script_editor.cpp
msgid "Can't copy the function node."
@@ -7728,35 +7730,35 @@ msgstr "Pegar nodos de VisualScript"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove Function"
-msgstr "Quitar función"
+msgstr "Quitar Función"
#: modules/visual_script/visual_script_editor.cpp
msgid "Edit Variable"
-msgstr "Editar variable"
+msgstr "Editar Variable"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove Variable"
-msgstr "Quitar variable"
+msgstr "Quitar Variable"
#: modules/visual_script/visual_script_editor.cpp
msgid "Edit Signal"
-msgstr "Editar señal"
+msgstr "Editar Señal"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove Signal"
-msgstr "Quitar señal"
+msgstr "Quitar Señal"
#: modules/visual_script/visual_script_editor.cpp
msgid "Editing Variable:"
-msgstr "Editando variable:"
+msgstr "Editando Variable:"
#: modules/visual_script/visual_script_editor.cpp
msgid "Editing Signal:"
-msgstr "Editando señal:"
+msgstr "Editando Señal:"
#: modules/visual_script/visual_script_editor.cpp
msgid "Base Type:"
-msgstr "Tipo base:"
+msgstr "Tipo Base:"
#: modules/visual_script/visual_script_editor.cpp
msgid "Available Nodes:"
@@ -7772,7 +7774,7 @@ msgstr "Editar argumentos de la señal:"
#: modules/visual_script/visual_script_editor.cpp
msgid "Edit Variable:"
-msgstr "Editar variable:"
+msgstr "Editar Variable:"
#: modules/visual_script/visual_script_editor.cpp
msgid "Delete Selected"
@@ -7780,19 +7782,19 @@ msgstr "Quitar seleccionados"
#: modules/visual_script/visual_script_editor.cpp
msgid "Find Node Type"
-msgstr "Buscar tipo de nodo"
+msgstr "Buscar Tipo de Nodo"
#: modules/visual_script/visual_script_editor.cpp
msgid "Copy Nodes"
-msgstr "Copiar nodos"
+msgstr "Copiar Nodos"
#: modules/visual_script/visual_script_editor.cpp
msgid "Cut Nodes"
-msgstr "Cortar nodos"
+msgstr "Cortar Nodos"
#: modules/visual_script/visual_script_editor.cpp
msgid "Paste Nodes"
-msgstr "Pegar nodos"
+msgstr "Pegar Nodos"
#: modules/visual_script/visual_script_flow_control.cpp
msgid "Input type not iterable: "
@@ -7812,11 +7814,11 @@ msgstr "Ãndice del nombre de la propiedad inválido."
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Base object is not a Node!"
-msgstr "¡El objeto base no es un nodo!"
+msgstr "¡El objeto base no es un Nodo!"
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Path does not lead Node!"
-msgstr "¡La ruta no apunta a un nodo!"
+msgstr "¡La ruta no apunta a un Nodo!"
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Invalid index property name '%s' in node %s."
@@ -7858,7 +7860,7 @@ msgstr "Ejecutar en navegador"
#: platform/javascript/export/export.cpp
msgid "Run exported HTML in the system's default browser."
-msgstr "Ejecutar HTML exportado en el navegador por defecto del sistema."
+msgstr "Ejecutar HTML exportado en el navegador predeterminado del sistema."
#: platform/javascript/export/export.cpp
msgid "Could not write file:"
@@ -7889,8 +7891,8 @@ msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
"order for AnimatedSprite to display frames."
msgstr ""
-"Se debe crear un recurso \"SpriteFrames\" o asignar uno en la propiedad "
-"'Frames' para que AnimatedSprite pueda mostrar fotogramas."
+"Se debe crear un recurso SpriteFrames o asignar uno en la propiedad 'Frames' "
+"para que AnimatedSprite pueda mostrar fotogramas."
#: scene/2d/canvas_modulate.cpp
msgid ""
@@ -7934,9 +7936,8 @@ msgid ""
"StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."
msgstr ""
"CollisionShape2D solo sirve para proveer de una forma de colisión a un nodo "
-"derivado de \"CollisionObject2D\". Por favor, úsalo solo como hijo de "
-"Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc... para dotarlos de "
-"forma."
+"derivado de CollisionObject2D. Por favor, úsalo solo como hijo de Area2D, "
+"StaticBody2D, RigidBody2D, KinematicBody2D, etc. para dotarlos de forma."
#: scene/2d/collision_shape_2d.cpp
msgid ""
@@ -7979,15 +7980,15 @@ msgid ""
"NavigationPolygonInstance must be a child or grandchild to a Navigation2D "
"node. It only provides navigation data."
msgstr ""
-"NavigationPolygonInstance debe ser hijo o nieto de un nodo \"Navigation2D\". "
+"NavigationPolygonInstance debe ser hijo o nieto de un nodo Navigation2D. "
"Solo provee datos de navegación."
#: scene/2d/parallax_layer.cpp
msgid ""
"ParallaxLayer node only works when set as child of a ParallaxBackground node."
msgstr ""
-"En nodo \"ParallaxLayer\" solo funciona cuando esta posicionado como hijo de "
-"un nodo \"ParallaxBackground\"."
+"En nodo ParallaxLayer solo funciona cuando esta posicionado como hijo de un "
+"nodo ParallaxBackground."
#: scene/2d/particles_2d.cpp scene/3d/particles.cpp
msgid ""
@@ -8000,8 +8001,7 @@ msgstr ""
#: scene/2d/path_2d.cpp
msgid "PathFollow2D only works when set as a child of a Path2D node."
msgstr ""
-"\"PathFollow2D\" solo funciona cuando está posicionado como hijo de un nodo "
-"\"Path2D\"."
+"PathFollow2D solo funciona cuando está colocado como hijo de un nodo Path2D."
#: scene/2d/physics_body_2d.cpp
msgid ""
@@ -8090,8 +8090,8 @@ msgid ""
msgstr ""
"Este nodo no tiene formas hijas, por lo que no puede interactuar con el "
"espacio.\n"
-"Considera añadir un \"CollisionShape\" o \"CollisionPolygon\" como hijos de "
-"este nodo para dotarlo de una forma."
+"Considera añadir un CollisionShape o CollisionPolygon como hijos de este "
+"nodo para dotarlo de una forma."
#: scene/3d/collision_polygon.cpp
msgid ""
@@ -8114,8 +8114,8 @@ msgid ""
"KinematicBody, etc. to give them a shape."
msgstr ""
"CollisionShape solo sirve para proveer de una forma a un nodo derivado de un "
-"CollisionObject. Por favor, úsalo solo como hijo de \"Area\", \"StaticBody"
-"\", \"RigidBody\", \"KinematicBody\", etc. para darles dicha forma."
+"CollisionObject. Por favor, úsalo solo como hijo de Area, StaticBody, "
+"RigidBody, KinematicBody, etc. para darles dicha forma."
#: scene/3d/collision_shape.cpp
msgid ""
@@ -8166,7 +8166,7 @@ msgstr ""
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "WorldEnvironment necesita un recurso Environment."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8180,6 +8180,9 @@ 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 ""
+"Este WorldEnvironment está siendo ignorado. Agrega un nodo Camera (para "
+"escenas 3D) o configura el Background Mode de este entorno en modo Canvas "
+"(para escenas 2D)."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8203,7 +8206,7 @@ msgstr "Modo Raw"
#: scene/gui/color_picker.cpp
msgid "Add current color as a preset"
-msgstr "Añadir el color actual como predefinido"
+msgstr "Añadir el color actual como predeterminado"
#: scene/gui/dialogs.cpp
msgid "Alert!"
@@ -8211,7 +8214,7 @@ msgstr "¡Alerta!"
#: scene/gui/dialogs.cpp
msgid "Please Confirm..."
-msgstr "Confirmar decisión…"
+msgstr "Por favor, Confirma..."
#: scene/gui/file_dialog.cpp
msgid "Select this Folder"
@@ -8246,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 ""
@@ -8256,10 +8259,10 @@ msgid ""
"obtain a size. Otherwise, make it a RenderTarget and assign its internal "
"texture to some node for display."
msgstr ""
-"Este viewport no está configurado como \"render target\". Si tienes "
-"intención de que muestre su contenido directamente en la pantalla, hazlo "
-"hijo de un Control para que pueda obtener un tamaño. Alternativamente, hazlo "
-"un RenderTarget y asigna su textura interna a algún otro nodo para mostrar."
+"Este viewport no está configurado como render target. Si quieres que muestre "
+"su contenido directamente en la pantalla, hazlo hijo de un Control para que "
+"pueda obtener un tamaño. De lo contrario, conviértelo en un RenderTarget y "
+"asigna su textura interna a algún nodo para mostrarlo."
#: scene/resources/dynamic_font.cpp
msgid "Error initializing FreeType."
@@ -8277,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 «:»)."
@@ -8303,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!"
@@ -8459,8 +8466,8 @@ msgstr "Tamaño de tipografía incorrecto."
#~ msgid "Exporting for %s"
#~ msgstr "Exportando para %s"
-#~ msgid "Setting Up.."
-#~ msgstr "Configurando.."
+#~ msgid "Setting Up..."
+#~ msgstr "Configurando..."
#~ msgid "Error loading scene."
#~ msgstr "Hubo un error al cargar la escena."
@@ -8525,8 +8532,8 @@ msgstr "Tamaño de tipografía incorrecto."
#~ msgid "Info"
#~ msgstr "Info"
-#~ msgid "Re-Import.."
-#~ msgstr "Reimportar.."
+#~ msgid "Re-Import..."
+#~ msgstr "Reimportar..."
#~ msgid "No bit masks to import!"
#~ msgstr "¡Sin máscaras de bits para importar!"
@@ -8925,14 +8932,14 @@ msgstr "Tamaño de tipografía incorrecto."
#~ msgid "Zoom (%):"
#~ msgstr "Zoom (%):"
-#~ msgid "Skeleton.."
-#~ msgstr "Esqueleto.."
+#~ msgid "Skeleton..."
+#~ msgstr "Esqueleto..."
#~ msgid "Zoom Reset"
#~ msgstr "Restablecer zoom"
-#~ msgid "Zoom Set.."
-#~ msgstr "Ajustar zoom.."
+#~ msgid "Zoom Set..."
+#~ msgstr "Ajustar zoom..."
#~ msgid "Set a Value"
#~ msgstr "Establecer valor"
@@ -9443,7 +9450,7 @@ msgstr "Tamaño de tipografía incorrecto."
#~ msgid "Export Project PCK"
#~ msgstr "Exportar PCK del proyecto"
-#~ msgid "Export.."
+#~ msgid "Export..."
#~ msgstr "Exportar…"
#~ msgid "Project Export"
@@ -9557,8 +9564,8 @@ msgstr "Tamaño de tipografía incorrecto."
#~ msgid "Reload Tool Script (Soft)"
#~ msgstr "Volver a Cargar Script de Herramientas (Soft)"
-#~ msgid "Edit Connections.."
-#~ msgstr "Editar Conecciones.."
+#~ msgid "Edit Connections..."
+#~ msgstr "Editar Conecciones..."
#~ msgid "Set Params"
#~ msgstr "Setear Params"
diff --git a/editor/translations/es_AR.po b/editor/translations/es_AR.po
index 304fc7dbc5..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-03-04 06:03+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 2.20-dev\n"
+"X-Generator: Weblate 3.0\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -502,8 +500,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Desconectar '%s' de '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "Conectar.."
+msgid "Connect..."
+msgstr "Conectar..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -922,12 +920,12 @@ msgid "Move Audio Bus"
msgstr "Mover Bus de Audio"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Guardar Layout de Bus de Audio Como.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Guardar Layout de Bus de Audio Como..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Ubicación para el Nuevo Layout.."
+msgid "Location for New Layout..."
+msgstr "Ubicación para el Nuevo Layout..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1068,12 +1066,12 @@ msgid "Updating Scene"
msgstr "Actualizando Escena"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Guardando cambios locales.."
+msgid "Storing local changes..."
+msgstr "Guardando cambios locales..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Actualizando escena.."
+msgid "Updating scene..."
+msgstr "Actualizando escena..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1141,8 +1139,8 @@ msgid "Show In File Manager"
msgstr "Mostrar en Gestor de Archivos"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Nueva Carpeta.."
+msgid "New Folder..."
+msgstr "Nueva Carpeta..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1403,20 +1401,20 @@ msgstr "Limpiar Salida"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "La exportación del proyecto falló con el código de error %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Error al guardar el recurso!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Guardar Recurso Como.."
+msgid "Save Resource As..."
+msgstr "Guardar Recurso Como..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "Ya Veo.."
+msgid "I see..."
+msgstr "Ya Veo..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1649,12 +1647,12 @@ msgid "Open Base Scene"
msgstr "Abrir Escena Base"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Abrir Escena Rapido.."
+msgid "Quick Open Scene..."
+msgstr "Abrir Escena Rapido..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Abrir Script Rapido.."
+msgid "Quick Open Script..."
+msgstr "Abrir Script Rapido..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1665,8 +1663,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Guardar cambios a '%s' antes de cerrar?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Guardar Escena Como.."
+msgid "Save Scene As..."
+msgstr "Guardar Escena Como..."
#: editor/editor_node.cpp
msgid "No"
@@ -1717,8 +1715,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Esta acción no se puede deshacer. Revertir de todos modos?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Ejecutar Escena Rapido.."
+msgid "Quick Run Scene..."
+msgstr "Ejecutar Escena Rapido..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1880,8 +1878,8 @@ msgid "Previous tab"
msgstr "Pestaña anterior"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Filtrar Archivos.."
+msgid "Filter Files..."
+msgstr "Filtrar Archivos..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1892,12 +1890,12 @@ msgid "New Scene"
msgstr "Nueva Escena"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Nueva Escena Heredada.."
+msgid "New Inherited Scene..."
+msgstr "Nueva Escena Heredada..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Abrir Escena.."
+msgid "Open Scene..."
+msgstr "Abrir Escena..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1916,16 +1914,16 @@ msgid "Open Recent"
msgstr "Abrir Reciente"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Convertir A.."
+msgid "Convert To..."
+msgstr "Convertir A..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshLibrary.."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet.."
+msgid "TileSet..."
+msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2189,8 +2187,8 @@ msgid "Save the currently edited resource."
msgstr "Guardar el recurso editado actualmente."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Guardar Como.."
+msgid "Save As..."
+msgstr "Guardar Como..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2298,8 +2296,8 @@ msgid "Creating Mesh Previews"
msgstr "Creando Vistas Previas de Mesh/es"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Miniatura.."
+msgid "Thumbnail..."
+msgstr "Miniatura..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2452,8 +2450,8 @@ msgid "(Current)"
msgstr "(Actual)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "Recuperando mirrors, esperá, por favor.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "Recuperando mirrors, esperá, por favor..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2530,8 +2528,8 @@ msgid "Error requesting url: "
msgstr "Error al pedir el url: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "Conectando al Mirror.."
+msgid "Connecting to Mirror..."
+msgstr "Conectando al Mirror..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2547,8 +2545,8 @@ msgstr "No se ha podido resolver"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Conectando.."
+msgid "Connecting..."
+msgstr "Conectando..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2560,8 +2558,8 @@ msgstr "Conectado"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Solicitando.."
+msgid "Requesting..."
+msgstr "Solicitando..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2697,12 +2695,12 @@ msgid "Collapse all"
msgstr "Colapsar todos"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Renombrar.."
+msgid "Rename..."
+msgstr "Renombrar..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "Mover A.."
+msgid "Move To..."
+msgstr "Mover A..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2713,16 +2711,16 @@ msgid "Instance"
msgstr "Instancia"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Editar Dependencias.."
+msgid "Edit Dependencies..."
+msgstr "Editar Dependencias..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Ver Dueños.."
+msgid "View Owners..."
+msgstr "Ver Dueños..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Duplicar.."
+msgid "Duplicate..."
+msgstr "Duplicar..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2748,7 +2746,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Examinando Archivos,\n"
"Aguardá, por favor."
@@ -2816,8 +2814,8 @@ msgid "Import Scene"
msgstr "Importar Escena"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Importando Escena.."
+msgid "Importing Scene..."
+msgstr "Importando Escena..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2828,8 +2826,8 @@ msgid "Generating for Mesh: "
msgstr "Generando para Mesh: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "Ejecutando Script Personalizado.."
+msgid "Running Custom Script..."
+msgstr "Ejecutando Script Personalizado..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2844,8 +2842,8 @@ msgid "Error running post-import script:"
msgstr "Error ejecutando el script de post-importacion:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Guardando.."
+msgid "Saving..."
+msgstr "Guardando..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2864,8 +2862,8 @@ msgid "Import As:"
msgstr "Importar Como:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Preseteo.."
+msgid "Preset..."
+msgstr "Preseteo..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3284,16 +3282,16 @@ msgid "Transition Node"
msgstr "Nodo Transición"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Importar Animaciones.."
+msgid "Import Animations..."
+msgstr "Importar Animaciones..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "Editar Filtros de Nodo"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "Filtros.."
+msgid "Filters..."
+msgstr "Filtros..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3360,8 +3358,8 @@ msgid "Fetching:"
msgstr "Obteniendo:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "Resolviendo.."
+msgid "Resolving..."
+msgstr "Resolviendo..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3427,8 +3425,8 @@ msgid "Site:"
msgstr "Sitio:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Soporte.."
+msgid "Support..."
+msgstr "Soporte..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3625,8 +3623,9 @@ msgid "Use Rotation Snap"
msgstr "Usar Snap de Rotación"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
-msgstr "Configurar alineado.."
+msgstr "Configurar Snap..."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap Relative"
@@ -3721,14 +3720,12 @@ msgid "Show Guides"
msgstr "Mostrar guías"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "Ver Origen"
+msgstr "Mostrar Orígen"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 Viewport"
+msgstr "Mostrar Viewport"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -4021,7 +4018,7 @@ msgstr "El mesh no tiene una superficie de donde crear contornos(outlines)!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "El tipo de la malla primitiva no es PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4052,8 +4049,8 @@ msgid "Create Convex Collision Sibling"
msgstr "Crear Collision Sibling Convexo"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "Crear Outline Mesh.."
+msgid "Create Outline Mesh..."
+msgstr "Crear Outline Mesh..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4259,8 +4256,8 @@ msgid "Error loading image:"
msgstr "Error al cargar la imagen:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Sin pixeles con transparencia > 128 en imagen.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Sin pixeles con transparencia > 128 en imagen..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4620,8 +4617,8 @@ msgid "Import Theme"
msgstr "Importar Tema"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Guardar Tema Como.."
+msgid "Save Theme As..."
+msgstr "Guardar Tema Como..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4717,8 +4714,8 @@ msgstr "Act/Desact. Panel de Scripts"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Encontrar.."
+msgid "Find..."
+msgstr "Encontrar..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4927,16 +4924,16 @@ msgid "Find Previous"
msgstr "Encontrar Anterior"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Reemplazar.."
+msgid "Replace..."
+msgstr "Reemplazar..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Ir a Función.."
+msgid "Goto Function..."
+msgstr "Ir a Función..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Ir a Línea.."
+msgid "Goto Line..."
+msgstr "Ir a Línea..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5389,12 +5386,8 @@ msgid "Transform"
msgstr "Transformar"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Configurar Snap.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "Dialogo de Transformación.."
+msgid "Transform Dialog..."
+msgstr "Dialogo de Transformación..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5646,8 +5639,8 @@ msgid "Remove All"
msgstr "Quitar Todos"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "Editar tema.."
+msgid "Edit theme..."
+msgstr "Editar tema..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5694,14 +5687,12 @@ msgid "Checked Item"
msgstr "Item Tildado"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "Agregar Item"
+msgstr "Radio Item"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "Item Tildado"
+msgstr "Radio Item Tildado"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5716,8 +5707,8 @@ msgid "Options"
msgstr "Opciones"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr "Tienes, Muchas, Variadas, Opciones!"
+msgid "Has,Many,Options"
+msgstr "Tiene,Muchas,Opciones"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5910,8 +5901,8 @@ msgid "Presets"
msgstr "Presets"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "Agregar.."
+msgid "Add..."
+msgstr "Agregar..."
#: editor/project_export.cpp
msgid "Resources"
@@ -6006,6 +5997,10 @@ msgid "Imported Project"
msgstr "Proyecto Importado"
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "Nombre de Proyecto Inválido."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "No se pudo crear la carpeta."
@@ -6209,9 +6204,11 @@ msgstr "Botón de Mouse"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
+"Nombre de acción inválido. No puede estar vacío o contener '/', ':', '=', "
+"'\\' o '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6238,8 +6235,8 @@ msgid "Control+"
msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "Presionar una Tecla.."
+msgid "Press a Key..."
+msgstr "Presionar una Tecla..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6422,8 +6419,8 @@ msgid "Property:"
msgstr "Propiedad:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
-msgstr "Sobreescribir Para.."
+msgid "Override For..."
+msgstr "Sobreescribir Para..."
#: editor/project_settings_editor.cpp
msgid "Input Map"
@@ -6518,12 +6515,12 @@ msgid "Easing Out-In"
msgstr "Easing Out-In"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "Archivo.."
+msgid "File..."
+msgstr "Archivo..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "Dir.."
+msgid "Dir..."
+msgstr "Dir..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6696,8 +6693,8 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Esta operación no puede ser realizada en escenas instanciadas."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "Guardar Nueva Escena Como.."
+msgid "Save New Scene As..."
+msgstr "Guardar Nueva Escena Como..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
@@ -7417,7 +7414,7 @@ msgstr "Elegir Instancia:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "El nombre de la clase no puede ser una palabra reservada"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -8128,7 +8125,7 @@ msgstr ""
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "WorldEnvironment necesita un recurso Environment."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8142,6 +8139,9 @@ 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 ""
+"Este WorldEnvironment esta siendo ignorado. Agregá un nodo Camera (para "
+"escenas 3D) o configurá el Background Mode de este entorno en modo Canvas "
+"(para escenas 2D)."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8239,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 ':')."
@@ -8265,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ó!"
@@ -8413,8 +8417,8 @@ msgstr "Tamaño de tipografía inválido."
#~ msgid "Exporting for %s"
#~ msgstr "Exportando para %s"
-#~ msgid "Setting Up.."
-#~ msgstr "Configurando.."
+#~ msgid "Setting Up..."
+#~ msgstr "Configurando..."
#~ msgid "Error loading scene."
#~ msgstr "Error al cargar la escena."
@@ -8476,8 +8480,8 @@ msgstr "Tamaño de tipografía inválido."
#~ msgid "Info"
#~ msgstr "Info"
-#~ msgid "Re-Import.."
-#~ msgstr "Reimportando.."
+#~ msgid "Re-Import..."
+#~ msgstr "Reimportando..."
#~ msgid "No bit masks to import!"
#~ msgstr "Sin máscaras de bits para importar!"
@@ -8873,14 +8877,14 @@ msgstr "Tamaño de tipografía inválido."
#~ msgid "Zoom (%):"
#~ msgstr "Zoom (%):"
-#~ msgid "Skeleton.."
-#~ msgstr "Esqueleto.."
+#~ msgid "Skeleton..."
+#~ msgstr "Esqueleto..."
#~ msgid "Zoom Reset"
#~ msgstr "Resetear Zoom"
-#~ msgid "Zoom Set.."
-#~ msgstr "Setear Zoom.."
+#~ msgid "Zoom Set..."
+#~ msgstr "Setear Zoom..."
#~ msgid "Set a Value"
#~ msgstr "Setear un Valor"
@@ -9362,8 +9366,8 @@ msgstr "Tamaño de tipografía inválido."
#~ msgid "Export Project PCK"
#~ msgstr "Exportar PCK de Proyecto"
-#~ msgid "Export.."
-#~ msgstr "Exportar.."
+#~ msgid "Export..."
+#~ msgstr "Exportar..."
#~ msgid "Project Export"
#~ msgstr "Exportar Proyecto"
@@ -9483,8 +9487,8 @@ msgstr "Tamaño de tipografía inválido."
#~ msgid "Reload Tool Script (Soft)"
#~ msgstr "Volver a Cargar Script de Herramientas (Soft)"
-#~ msgid "Edit Connections.."
-#~ msgstr "Editar Conecciones.."
+#~ msgid "Edit Connections..."
+#~ msgstr "Editar Conecciones..."
#~ msgid "Set Params"
#~ msgstr "Setear Params"
diff --git a/editor/translations/fa.po b/editor/translations/fa.po
index 8b7fdcbb79..f674ef99cc 100644
--- a/editor/translations/fa.po
+++ b/editor/translations/fa.po
@@ -502,7 +502,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "'s%' را از 's%' جدا کن"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "در حال اتصال..."
#: editor/connections_dialog.cpp
@@ -928,11 +928,11 @@ msgid "Move Audio Bus"
msgstr "کلید Add را جابجا کن"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1068,11 +1068,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1143,8 +1143,8 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "ساختن پوشه.."
+msgid "New Folder..."
+msgstr "ساختن پوشه..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1406,12 +1406,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "ذخیره منبع از ..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr "من میبینم ..."
#: editor/editor_node.cpp
@@ -1619,11 +1619,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1635,7 +1635,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr "ذخیره صحنه در ..."
#: editor/editor_node.cpp
@@ -1687,7 +1687,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1834,7 +1834,7 @@ msgid "Previous tab"
msgstr "زبانه قبلی"
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1846,11 +1846,11 @@ msgid "New Scene"
msgstr "صحنه جدید"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1870,15 +1870,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2124,7 +2124,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr "ذخیره در..."
#: editor/editor_node.cpp
@@ -2233,7 +2233,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2386,7 +2386,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2443,7 +2443,7 @@ msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
msgid "Request Failed."
-msgstr "در حال درخواست.."
+msgstr "در حال درخواست..."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -2465,7 +2465,7 @@ msgstr "خطای آدرس درخواستی: "
#: editor/export_template_manager.cpp
#, fuzzy
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr "در حال اتصال..."
#: editor/export_template_manager.cpp
@@ -2482,8 +2482,8 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "در حال اتصال.."
+msgid "Connecting..."
+msgstr "در حال اتصال..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2496,8 +2496,8 @@ msgstr "وصل شده"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "در حال درخواست.."
+msgid "Requesting..."
+msgstr "در حال درخواست..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2634,11 +2634,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "تغییر نام.."
+msgid "Rename..."
+msgstr "تغییر نام..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2651,16 +2651,16 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "انتخاب شده را به دو تا تکثیر کن"
#: editor/filesystem_dock.cpp
@@ -2686,7 +2686,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2752,7 +2752,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2764,7 +2764,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2780,7 +2780,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2800,7 +2800,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3217,7 +3217,7 @@ msgid "Transition Node"
msgstr "گره جابجای"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3225,7 +3225,7 @@ msgid "Edit Node Filters"
msgstr "ویرایش صاÙÛŒ های گره"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3295,7 +3295,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3363,8 +3363,8 @@ msgid "Site:"
msgstr "تارنما:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "پشتیبانی.."
+msgid "Support..."
+msgstr "پشتیبانی..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3553,6 +3553,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3977,7 +3978,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4184,7 +4185,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4550,7 +4551,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4651,7 +4652,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4862,15 +4863,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5330,11 +5331,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5590,7 +5587,7 @@ msgid "Remove All"
msgstr "برداشتن"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5659,7 +5656,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5853,7 +5850,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5947,6 +5944,11 @@ msgstr "پروژه واردشده"
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "نام پروژه:"
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "ناتوان در ساختن پوشه."
@@ -6138,8 +6140,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6167,7 +6169,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6352,7 +6354,7 @@ msgid "Property:"
msgstr "ویژگی:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6448,11 +6450,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6628,7 +6630,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -8183,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 139983464e..f80efffd42 100644
--- a/editor/translations/fi.po
+++ b/editor/translations/fi.po
@@ -2,25 +2,25 @@
# 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-01-31 04:36+0000\n"
-"Last-Translator: Sami Lehtilä <sami.lehtila@gmail.com>\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"
"Language: fi\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.19-dev\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -31,26 +31,24 @@ msgid "All Selection"
msgstr "Koko valinta"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Keyframe Time"
-msgstr "Animaatio: muuta arvoa"
+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
-#, fuzzy
msgid "Anim Change Keyframe Value"
-msgstr "Animaatio: muuta arvoa"
+msgstr "Animaatio: muuta avainruudun arvoa"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
-msgstr "Anmaatio: 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"
@@ -245,7 +242,7 @@ msgstr "Animaatio: Lisää kutsuraita"
#: editor/animation_editor.cpp
msgid "Animation zoom."
-msgstr "Animaation zoom."
+msgstr "Animaation lähennystaso."
#: editor/animation_editor.cpp
msgid "Length (s):"
@@ -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"
@@ -409,7 +406,7 @@ msgstr "Loitonna"
#: editor/code_editor.cpp
msgid "Reset Zoom"
-msgstr "Nollaa lähennys"
+msgstr "Palauta oletuslähennystaso"
#: editor/code_editor.cpp editor/script_editor_debugger.cpp
msgid "Line:"
@@ -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,10 +497,10 @@ 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.."
+msgid "Connect..."
msgstr "Yhdistä..."
#: editor/connections_dialog.cpp
@@ -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
@@ -619,9 +616,8 @@ msgid "Open"
msgstr "Avaa"
#: editor/dependency_editor.cpp
-#, fuzzy
msgid "Owners Of:"
-msgstr "Omistajat:"
+msgstr "Omistajat kohteelle:"
#: editor/dependency_editor.cpp
msgid "Remove selected files from the project? (no undo)"
@@ -638,7 +634,6 @@ msgstr ""
"Poistetaanko silti? (ei mahdollisuutta kumota)"
#: editor/dependency_editor.cpp
-#, fuzzy
msgid "Cannot remove:"
msgstr "Ei voida poistaa:"
@@ -648,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"
@@ -667,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"
@@ -680,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?"
@@ -697,14 +690,12 @@ msgid "Delete"
msgstr "Poista"
#: editor/dictionary_property_edit.cpp
-#, fuzzy
msgid "Change Dictionary Key"
-msgstr "Vaihda taulukon avainta"
+msgstr "Vaihda hakurakenteen avainta"
#: editor/dictionary_property_edit.cpp
-#, fuzzy
msgid "Change Dictionary Value"
-msgstr "Vaihda taulukon arvoa"
+msgstr "Vaihda hakurakenteen arvoa"
#: editor/editor_about.cpp
msgid "Thanks from the Godot community!"
@@ -727,9 +718,8 @@ msgid "Lead Developer"
msgstr "Pääkehittäjä"
#: editor/editor_about.cpp
-#, fuzzy
msgid "Project Manager "
-msgstr "Projektinhallinta"
+msgstr "Projektipäällikkö "
#: editor/editor_about.cpp
msgid "Developers"
@@ -741,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"
@@ -784,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"
@@ -801,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"
@@ -835,12 +825,11 @@ 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
-#, fuzzy
msgid "Change Audio Bus Volume"
-msgstr "Ääniväylä sooloksi"
+msgstr "Muuta ääniväylän voimakkuutta"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Solo"
@@ -856,7 +845,7 @@ msgstr "Käytä ääniväylän efektejä"
#: editor/editor_audio_buses.cpp
msgid "Select Audio Bus Send"
-msgstr ""
+msgstr "Valitse ääniväylän lähtö"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus Effect"
@@ -905,7 +894,7 @@ msgstr "Poista efekti"
#: editor/editor_audio_buses.cpp
msgid "Audio"
-msgstr ""
+msgstr "Äänet"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus"
@@ -925,18 +914,18 @@ 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"
msgstr "Siirrä ääniväylää"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "Tallenna ääniväylän asettelu nimellä..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr "Sijainti uudelle asettelulle..."
#: editor/editor_audio_buses.cpp
@@ -1020,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"
@@ -1039,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"
@@ -1065,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
@@ -1073,30 +1059,28 @@ msgid "Name"
msgstr "Nimi"
#: editor/editor_autoload_settings.cpp
-#, fuzzy
msgid "Singleton"
-msgstr "Ainokainen"
+msgstr "Singleton"
#: editor/editor_data.cpp
msgid "Updating Scene"
msgstr "Päivitetään skeneä"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr "Varastoidaan paikalliset muutokset..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr "Päivitetään skeneä..."
#: editor/editor_data.cpp
-#, fuzzy
msgid "[empty]"
-msgstr "(tyhjä)"
+msgstr "[tyhjä]"
#: editor/editor_data.cpp
msgid "[unsaved]"
-msgstr "[ei tallennettu]"
+msgstr "[tallentamaton]"
#: editor/editor_dir_dialog.cpp
msgid "Please select a base directory first"
@@ -1136,18 +1120,16 @@ msgid "Packing"
msgstr "Pakataan"
#: editor/editor_export.cpp platform/javascript/export/export.cpp
-#, fuzzy
msgid "Template file not found:"
-msgstr "Mallitiedostoa ei löytynyt:\n"
+msgstr "Mallitiedostoa ei löytynyt:"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "File Exists, Overwrite?"
msgstr "Tiedosto on jo olemassa, korvaa?"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
-#, fuzzy
msgid "Select Current Folder"
-msgstr "Luo kansio"
+msgstr "Valitse nykyinen kansio"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
msgid "Copy Path"
@@ -1158,8 +1140,8 @@ msgid "Show In File Manager"
msgstr "Näytä tiedostonhallinnassa"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Luo kansio..."
+msgid "New Folder..."
+msgstr "Uusi kansio..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1216,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"
@@ -1239,13 +1218,12 @@ msgid "Move Favorite Down"
msgstr "Siirrä suosikkia alas"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
-#, fuzzy
msgid "Go to parent folder"
-msgstr "Kansiota ei voitu luoda."
+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:"
@@ -1257,23 +1235,21 @@ msgid "File:"
msgstr "Tiedosto:"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
-#, fuzzy
msgid "Must use a valid extension."
-msgstr "Käytä sopivaa laajennusta"
+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
-#, fuzzy
msgid "Search Help"
-msgstr "Hae oppaasta"
+msgstr "Etsi ohjeesta"
#: editor/editor_help.cpp
msgid "Class List:"
@@ -1285,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:"
@@ -1296,27 +1272,24 @@ msgid "Inherits:"
msgstr "Perii:"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Inherited by:"
-msgstr "Peritty:"
+msgstr "Perivät:"
#: editor/editor_help.cpp
msgid "Brief Description:"
msgstr "Lyhyt kuvaus:"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Members"
-msgstr "Jäsenet:"
+msgstr "Jäsenet"
#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
msgid "Members:"
msgstr "Jäsenet:"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Public Methods"
-msgstr "Julkiset metodit:"
+msgstr "Julkiset metodit"
#: editor/editor_help.cpp
msgid "Public Methods:"
@@ -1324,66 +1297,59 @@ msgstr "Julkiset metodit:"
#: editor/editor_help.cpp
msgid "GUI Theme Items"
-msgstr "GUI teeman osat"
+msgstr "Käyttöliittymäteeman osat"
#: editor/editor_help.cpp
msgid "GUI Theme Items:"
-msgstr "GUI teeman osat:"
+msgstr "Käyttöliittymäteeman osat:"
#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
msgid "Signals:"
msgstr "Signaalit:"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Enumerations"
-msgstr "Animaatiot"
+msgstr "Enumeraatiot"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Enumerations:"
-msgstr "Animaatiot"
+msgstr "Enumeraatiot:"
#: editor/editor_help.cpp
msgid "enum "
-msgstr "enum"
+msgstr "enum "
#: editor/editor_help.cpp
-#, fuzzy
msgid "Constants"
-msgstr "Vakiot:"
+msgstr "Vakiot"
#: editor/editor_help.cpp
msgid "Constants:"
msgstr "Vakiot:"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Description"
-msgstr "Kuvaus:"
+msgstr "Kuvaus"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Online Tutorials:"
-msgstr "Oppaat"
+msgstr "Online-oppaat:"
#: 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 ""
-"Tälle metodille ei vielä löydy kuvailua. Voit auttaa meitä [color=$color]"
-"[url=$url]kirjoittamalla sellaisen[/url][/color]!"
+"Tälle luokalle ei vielä löydy kuvausta. Voit [color=$color][url=$url]auttaa "
+"luomalla sellaisen[/url][/color] tai [color=$color][url=$url2]pyytää "
+"sellaisen[/url][/color]."
#: editor/editor_help.cpp
-#, fuzzy
msgid "Properties"
-msgstr "Ominaisuudet:"
+msgstr "Ominaisuudet"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Property Description:"
msgstr "Ominaisuuden kuvaus:"
@@ -1392,13 +1358,12 @@ 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
-#, fuzzy
msgid "Methods"
-msgstr "Metodilista:"
+msgstr "Metodit"
#: editor/editor_help.cpp
msgid "Method Description:"
@@ -1409,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
@@ -1421,9 +1386,8 @@ msgid "Find"
msgstr "Etsi"
#: editor/editor_log.cpp
-#, fuzzy
msgid "Output:"
-msgstr " Tuloste:"
+msgstr "Tuloste:"
#: editor/editor_log.cpp editor/plugins/animation_tree_editor_plugin.cpp
#: editor/property_editor.cpp editor/script_editor_debugger.cpp
@@ -1433,25 +1397,24 @@ msgid "Clear"
msgstr "Tyhjennä"
#: editor/editor_log.cpp
-#, fuzzy
msgid "Clear Output"
-msgstr "Tuloste"
+msgstr "Tyhjennä tuloste"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Projektin vienti epäonnistui virhekoodilla %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Virhe tallennettaessa resurssia!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "Tallenna resurssi nimellä..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr "Ymmärrän..."
#: editor/editor_node.cpp
@@ -1467,32 +1430,28 @@ msgid "Error while saving."
msgstr "Virhe tallennettaessa."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Can't open '%s'."
-msgstr "Yhdistä..."
+msgstr "Ei voida avata tiedostoa '%s'."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Error while parsing '%s'."
-msgstr "Virhe tallennettaessa."
+msgstr "Virhe jäsennettäessä tiedostoa '%s'."
#: editor/editor_node.cpp
msgid "Unexpected end of file '%s'."
msgstr "Odottamaton loppu tiedostossa '%s'."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Missing '%s' or its dependencies."
-msgstr "Scenellä '%s' on rikkinäisiä riippuvuuksia:"
+msgstr "Tiedosto '%s' tai jokin sen riippuvuuksista puuttuu."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Error while loading '%s'."
-msgstr "Virhe tallennettaessa."
+msgstr "Virhe ladattaessa tiedostoa '%s'."
#: editor/editor_node.cpp
msgid "Saving Scene"
-msgstr "Tallennetaan sceneä"
+msgstr "Tallennetaan skeneä"
#: editor/editor_node.cpp
msgid "Analyzing"
@@ -1503,16 +1462,16 @@ msgid "Creating Thumbnail"
msgstr "Luodaan pienoiskuvaa"
#: editor/editor_node.cpp
-#, fuzzy
msgid "This operation can't be done without a tree root."
-msgstr "Tätä toimintoa ei voi tehdä ilman Sceneä."
+msgstr "Tätä toimintoa ei voi tehdä ilman että puun juuri on olemassa."
#: editor/editor_node.cpp
-#, fuzzy
msgid ""
"Couldn't save scene. Likely dependencies (instances or inheritance) couldn't "
"be satisfied."
-msgstr "Sceneä ei voitu tallentaa. Riippuvuuksia ei voitu tyydyttää."
+msgstr ""
+"Skeneä ei voitu tallentaa. Mahdollisia riippuvuuksia (ilmentymiä tai "
+"perintää) ei voida toteuttaa."
#: editor/editor_node.cpp
msgid "Failed to load resource."
@@ -1520,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!"
@@ -1540,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 ""
@@ -1558,16 +1516,16 @@ 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 ""
"This resource belongs to a scene that was instanced or inherited.\n"
"Changes to it will not be kept when saving the current scene."
msgstr ""
-"Tämä resurssi kuuluu skeneen josta on luotu instanssi, tai joka on "
+"Tämä resurssi kuuluu skeneen, josta on luotu ilmentymä, tai joka on "
"periytyvä.\n"
-"Muutokset tähän eivät ole pysyviä, kun tallennat nykyisen skenen."
+"Muutokset siihen eivät ole pysyviä, kun tallennat nykyisen skenen."
#: editor/editor_node.cpp
msgid ""
@@ -1585,9 +1543,10 @@ msgid ""
"understand this workflow."
msgstr ""
"Tämä skene on tuotu, joten siihen tehtyjä muutoksia ei säilytetä.\n"
-"Instanssin, tai periytyvän skenen luominen mahdollistaa tämän.\n"
-"Ole hyvä ja lue tarkemmat ohjeet skenejen tuomisesta jotta ymmärrät paremmin "
-"tämän työnkulun."
+"Ilmentymän tai periytyvän skenen luominen siitä mahdollistaa muutoksien "
+"tekemisen siihen.\n"
+"Ole hyvä ja lue dokumentaatiosta tarkemmat ohjeet skenejen tuomisesta, jotta "
+"ymmärrät paremmin tämän työnkulun."
#: editor/editor_node.cpp
msgid ""
@@ -1599,14 +1558,12 @@ msgstr ""
"Ole hyvä ja lue ohjeet testaamisesta ymmärtääksesi paremmin tämän työnkulun."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Expand all properties"
-msgstr "Laajenna kaikki"
+msgstr "Laajenna kaikki ominaisuudet"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Collapse all properties"
-msgstr "Pienennä kaikki"
+msgstr "Tiivistä kaikki ominaisuudet"
#: editor/editor_node.cpp
msgid "Copy Params"
@@ -1625,14 +1582,12 @@ msgid "Copy Resource"
msgstr "Kopioi resurssi"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Make Built-In"
msgstr "Tee sisäänrakennettu"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Make Sub-Resources Unique"
-msgstr "Tee ali-resursseista yksilöllisiä."
+msgstr "Tee aliresursseista yksilöllisiä"
#: editor/editor_node.cpp
msgid "Open in Help"
@@ -1640,17 +1595,16 @@ 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
-#, fuzzy
msgid ""
"No main scene has ever been defined, select one?\n"
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
-"Pääsceneä ei ole määritetty, haluatko valita sen?\n"
-"Voit muuttaa sitä myöhemmin projektin asetuksista."
+"Pääskeneä ei ole määritetty, haluatko valita sen?\n"
+"Voit muuttaa sen myöhemmin projektin asetuksista, kohdasta 'Application'."
#: editor/editor_node.cpp
msgid ""
@@ -1658,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 ""
@@ -1667,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!"
@@ -1681,32 +1635,31 @@ 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..."
+msgid "Quick Open Scene..."
+msgstr "Skenen pika-avaus..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Nopea skriptin avaus..."
+msgid "Quick Open Script..."
+msgstr "Skriptin pika-avaus..."
#: editor/editor_node.cpp
msgid "Save & Close"
msgstr "Tallenna ja sulje"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Save changes to '%s' before closing?"
-msgstr "Tallennetaanko muutokset '%s' ennen sulkemista?"
+msgstr "Tallennetaanko muutokset tiedostoon '%s' ennen sulkemista?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Tallenna scene nimellä..."
+msgid "Save Scene As..."
+msgstr "Tallenna skene nimellä..."
#: editor/editor_node.cpp
msgid "No"
@@ -1718,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"
@@ -1757,8 +1710,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Tätä toimintoa ei voida peruttaa. Palauta joka tapauksessa?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Nopea skenen käynnistys..."
+msgid "Quick Run Scene..."
+msgstr "Skenen pikakäynnistys..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1770,7 +1723,7 @@ msgstr "Poistu editorista?"
#: editor/editor_node.cpp
msgid "Open Project Manager?"
-msgstr "Projektienhallinta"
+msgstr "Avataanko projektinhallinta?"
#: editor/editor_node.cpp
msgid "Save & Quit"
@@ -1796,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."
@@ -1804,7 +1757,7 @@ msgstr "Lisäosan '%s' aktivointi epäonnistui, virheellinen asetustiedosto."
#: editor/editor_node.cpp
msgid "Unable to find script field for addon plugin at: 'res://addons/%s'."
-msgstr ""
+msgstr "Skriptikenttää ei löytynyt lisäosan tiedostosta: 'res://addons/%s'."
#: editor/editor_node.cpp
msgid "Unable to load addon script from path: '%s'."
@@ -1825,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
@@ -1838,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"
@@ -1864,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"
@@ -1911,23 +1864,23 @@ msgid "Previous tab"
msgstr "Edellinen välilehti"
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
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"
msgstr "Uusi skene"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr "Uusi peritty skene..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr "Avaa skene..."
#: editor/editor_node.cpp
@@ -1947,16 +1900,16 @@ msgid "Open Recent"
msgstr "Avaa viimeaikainen"
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr "Muunna..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MalliKirjasto..."
+msgid "MeshLibrary..."
+msgstr "Mesh-kirjastoksi..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr ""
+msgid "TileSet..."
+msgstr "Ruutuvalikoimaksi..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -1977,9 +1930,8 @@ msgid "Miscellaneous project or scene-wide tools."
msgstr "Sekalaiset projekti- tai skenetyökalut."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Project"
-msgstr "Uusi projekti"
+msgstr "Projekti"
#: editor/editor_node.cpp
msgid "Project Settings"
@@ -2003,23 +1955,23 @@ 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"
-msgstr "Julkaise etätestauksen kasnsa"
+msgstr "Julkaise etätestauksen kanssa"
#: 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 ""
-"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"
-msgstr ""
+msgstr "Kevyt käyttöönotto verkkolevyn avulla"
#: editor/editor_node.cpp
msgid ""
@@ -2038,16 +1990,15 @@ msgstr ""
"kohdalla."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Visible Collision Shapes"
-msgstr "Näytä osuma-alueet"
+msgstr "Näytä törmäysmuodot"
#: 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 ""
-"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
@@ -2059,8 +2010,8 @@ msgid ""
"Navigation meshes and polygons will be visible on the running game if this "
"option is turned on."
msgstr ""
-"Navigaatiomuodot ja -polygonit ovat näkyvillä peliä ajettaessa tämän ollessa "
-"valittuna."
+"Navigointiverkot ja niiden polygonit ovat näkyvillä peliä ajettaessa tämän "
+"ollessa valittuna."
#: editor/editor_node.cpp
msgid "Sync Scene Changes"
@@ -2075,15 +2026,13 @@ 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"
msgstr "Synkronoi skriptin muutokset"
#: editor/editor_node.cpp
-#, fuzzy
msgid ""
"When this option is turned on, any script that is saved will be reloaded on "
"the running game.\n"
@@ -2092,13 +2041,11 @@ 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ää "
-"verkkotiedostojärjestelmää ."
+"Mikäli peliä ajetaan etälaitteella, on tehokkaampaa käyttää verkkolevyä."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Editor"
-msgstr "Muokkaa"
+msgstr "Editori"
#: editor/editor_node.cpp editor/settings_config_dialog.cpp
msgid "Editor Settings"
@@ -2114,7 +2061,7 @@ msgstr "Siirry koko näytön tilaan"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Manage Export Templates"
-msgstr "Hallitse vietäviä Templateja"
+msgstr "Hallinnoi vientimalleja"
#: editor/editor_node.cpp
msgid "Help"
@@ -2136,9 +2083,8 @@ msgid "Online Docs"
msgstr "Dokumentaatio"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Q&A"
-msgstr "Kysy&Vastaa"
+msgstr "Kysymykset ja vastaukset"
#: editor/editor_node.cpp
msgid "Issue Tracker"
@@ -2154,24 +2100,23 @@ msgstr "Tietoja"
#: editor/editor_node.cpp
msgid "Play the project."
-msgstr "Käynnistä projekti"
+msgstr "Käynnistä projekti."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Play"
-msgstr "Toista"
+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"
@@ -2179,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"
@@ -2211,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."
@@ -2226,7 +2171,7 @@ msgid "Save the currently edited resource."
msgstr "Tallenna tällä hetkellä muokattu resurssi."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr "Tallenna nimellä..."
#: editor/editor_node.cpp
@@ -2256,7 +2201,7 @@ msgstr "Tuo"
#: editor/editor_node.cpp
msgid "Node"
-msgstr "Node"
+msgstr "Solmu"
#: editor/editor_node.cpp
msgid "FileSystem"
@@ -2292,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"
@@ -2321,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"
@@ -2333,10 +2277,10 @@ 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.."
+msgid "Thumbnail..."
msgstr "Pienoiskuva..."
#: editor/editor_plugin_settings.cpp
@@ -2369,13 +2313,12 @@ msgid "Start Profiling"
msgstr "Aloita profilointi"
#: editor/editor_profiler.cpp
-#, fuzzy
msgid "Measure:"
-msgstr "Mittayksikkö:"
+msgstr "Mittaa:"
#: editor/editor_profiler.cpp
msgid "Frame Time (sec)"
-msgstr "Framen aika (sek)"
+msgstr "Kuvaruudun aika (sek)"
#: editor/editor_profiler.cpp
msgid "Average Time (sec)"
@@ -2383,12 +2326,11 @@ msgstr "Keskimääräinen aika (sek)"
#: editor/editor_profiler.cpp
msgid "Frame %"
-msgstr "Frame %"
+msgstr "Kuvaruutujen %"
#: editor/editor_profiler.cpp
-#, fuzzy
msgid "Physics Frame %"
-msgstr "Kiinteä Frame %"
+msgstr "Fysiikkaruutujen %"
#: editor/editor_profiler.cpp editor/script_editor_debugger.cpp
msgid "Time:"
@@ -2396,7 +2338,7 @@ msgstr "Aika:"
#: editor/editor_profiler.cpp
msgid "Inclusive"
-msgstr ""
+msgstr "Sisältävä"
#: editor/editor_profiler.cpp
msgid "Self"
@@ -2407,14 +2349,12 @@ msgid "Frame #:"
msgstr "Ruutu #:"
#: editor/editor_profiler.cpp
-#, fuzzy
msgid "Time"
-msgstr "Aika:"
+msgstr "Aika"
#: editor/editor_profiler.cpp
-#, fuzzy
msgid "Calls"
-msgstr "Kutsu"
+msgstr "Kutsuja"
#: editor/editor_run_native.cpp
msgid "Select device from the list"
@@ -2426,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:"
@@ -2442,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:"
@@ -2450,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)"
@@ -2458,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"
@@ -2493,7 +2433,7 @@ msgid "(Current)"
msgstr "(Nykyinen)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr "Noudetaan peilipalvelimia, hetkinen..."
#: editor/export_template_manager.cpp
@@ -2502,24 +2442,23 @@ 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
-#, fuzzy
msgid "Error creating path for templates:"
-msgstr "Virhe luotaessa polkua mallille:\n"
+msgstr "Virhe luotaessa polkua malleille:"
#: editor/export_template_manager.cpp
msgid "Extracting Export Templates"
-msgstr "Puretaan vientipohjia."
+msgstr "Puretaan vientimalleja"
#: editor/export_template_manager.cpp
msgid "Importing:"
@@ -2550,7 +2489,6 @@ msgstr "Ei vastausta."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Request Failed."
msgstr "Pyyntö epäonnistui."
@@ -2565,29 +2503,24 @@ msgid "Failed:"
msgstr "Epäonnistui:"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Download Complete."
-msgstr "Lataa"
+msgstr "Lataus valmis."
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Error requesting url: "
-msgstr "Virhe tallennettaessa atlas-kuvaa:"
+msgstr "Virhe pyydettäessä osoitetta: "
#: editor/export_template_manager.cpp
-#, fuzzy
-msgid "Connecting to Mirror.."
-msgstr "Yhdistä..."
+msgid "Connecting to Mirror..."
+msgstr "Yhdistetään peilipalvelimeen..."
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Disconnected"
-msgstr "Katkaise yhteys"
+msgstr "Yhteys katkaistu"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Resolving"
-msgstr "Tallennetaan..."
+msgstr "Selvitetään"
#: editor/export_template_manager.cpp
msgid "Can't Resolve"
@@ -2595,29 +2528,25 @@ msgstr "Yhdistäminen epäonnistui"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
-msgid "Connecting.."
-msgstr "Yhdistä..."
+msgid "Connecting..."
+msgstr "Yhdistetään..."
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Can't Connect"
msgstr "Ei voitu yhdistää"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Connected"
-msgstr "Yhdistä"
+msgstr "Yhdistetty"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "Pyydetään..."
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Downloading"
-msgstr "Lataa"
+msgstr "Ladataan"
#: editor/export_template_manager.cpp
msgid "Connection Error"
@@ -2648,14 +2577,12 @@ msgid "Select template file"
msgstr "Valitse mallin tiedosto"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Export Template Manager"
msgstr "Vientimallien hallinta"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Download Templates"
-msgstr "Poista malli"
+msgstr "Lataa mallit"
#: editor/export_template_manager.cpp
msgid "Select mirror from list: "
@@ -2665,7 +2592,7 @@ msgstr "Valitse peilipalvelin listasta: "
msgid "Can't open file_type_cache.cch for writing, not saving file type cache!"
msgstr ""
"Tiedostoa file_type_cache.cch ei voitu avata kirjoittamista varten. "
-"Välimuistia ei tallenneta. "
+"Välimuistia ei tallenneta!"
#: editor/filesystem_dock.cpp
msgid "Cannot navigate to '%s' as it has not been found in the file system!"
@@ -2682,79 +2609,65 @@ msgid "View items as a list"
msgstr "Listanäkymä"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Status: Import of file failed. Please fix file and reimport manually."
msgstr ""
-"\n"
-"Tila: Tuonti epäonnistui. Ole hyvä, korjaa tiedosto, ja tuo uudelleen."
+"Tila: Tuonti epäonnistui. Ole hyvä, korjaa tiedosto ja tuo se uudelleen."
#: editor/filesystem_dock.cpp
msgid "Cannot move/rename resources root."
msgstr "Ei voitu siirtää/nimetä uudelleen resurssien päätasoa."
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Cannot move a folder into itself."
-msgstr "Hakemistoa ei voi siirtää itsensä sisään.\n"
+msgstr "Kansiota ei voi siirtää itsensä sisään."
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Error moving:"
-msgstr "Virhe tuotaessa:"
+msgstr "Virhe siirrettäessä:"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Error duplicating:"
-msgstr "Virhe ladatessa:"
+msgstr "Virhe kahdennettaessa:"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Unable to update dependencies:"
-msgstr "Scenellä '%s' on rikkinäisiä riippuvuuksia:"
+msgstr "Ei voida päivittää riippuvuuksia:"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "No name provided"
msgstr "Nimeä ei annettu"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Provided name contains invalid characters"
-msgstr "Annettu nimi sisältää laittomia kirjaimia"
+msgstr "Annettu nimi sisältää virheellisiä kirjainmerkkejä"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "No name provided."
-msgstr "Nimeä uudelleen tai siirrä..."
+msgstr "Nimeä ei annettu."
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Name contains invalid characters."
-msgstr "Kelvolliset merkit:"
+msgstr "Nimi sisältää virheellisiä kirjainmerkkejä."
#: editor/filesystem_dock.cpp
msgid "A file or folder with this name already exists."
msgstr "Tällä nimellä löytyy jo kansio tai tiedosto."
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Renaming file:"
-msgstr "Nimeä muuttuja uudelleen"
+msgstr "Nimetään tiedosto uudelleen:"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Renaming folder:"
-msgstr "Nimeä Node uudelleen"
+msgstr "Nimetään kansio uudelleen:"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Duplicating file:"
-msgstr "Monista"
+msgstr "Kahdennetaan tiedosto:"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Duplicating folder:"
-msgstr "Nimeä Node uudelleen"
+msgstr "Kahdennetaan kansio:"
#: editor/filesystem_dock.cpp
msgid "Expand all"
@@ -2765,35 +2678,32 @@ msgid "Collapse all"
msgstr "Pienennä kaikki"
#: editor/filesystem_dock.cpp
-#, fuzzy
-msgid "Rename.."
-msgstr "Nimeä uudelleen"
+msgid "Rename..."
+msgstr "Nimeä uudelleen..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr "Siirrä..."
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Open Scene(s)"
-msgstr "Avaa scene"
+msgstr "Avaa skene tai skenejä"
#: editor/filesystem_dock.cpp
msgid "Instance"
-msgstr "Instanssi"
+msgstr "Luo ilmentymä"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr "Muokkaa riippuvuuksia..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr "Tarkastele omistajia..."
#: editor/filesystem_dock.cpp
-#, fuzzy
-msgid "Duplicate.."
-msgstr "Monista"
+msgid "Duplicate..."
+msgstr "Kahdenna..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2805,7 +2715,7 @@ msgstr "Seuraava hakemisto"
#: editor/filesystem_dock.cpp
msgid "Re-Scan Filesystem"
-msgstr ""
+msgstr "Skannaa tiedostojärjestelmä uudelleen"
#: editor/filesystem_dock.cpp
msgid "Toggle folder status as Favorite"
@@ -2813,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ä instanssi valitun noden alle."
+msgstr "Luo valituista skeneistä ilmentymä valitun solmun alle."
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Selataan tiedostoja,\n"
-"Hetkinen..."
+"Hetkinen…"
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2842,49 +2751,40 @@ msgid "Remove from Group"
msgstr "Poista ryhmästä"
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Import as Single Scene"
-msgstr "Tuodaan Scene..."
+msgstr "Tuo yhtenä skenenä"
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Import with Separate Animations"
-msgstr "Tuo animaatiot..."
+msgstr "Tuo erillisten animaatioiden kanssa"
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Import with Separate Materials"
msgstr "Tuo erillisten materiaalien kanssa"
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Import with Separate Objects"
msgstr "Tuo erillisten objektien kanssa"
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Import with Separate Objects+Materials"
msgstr "Tuo erillisten objektien ja materiaalien kanssa"
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Import with Separate Objects+Animations"
msgstr "Tuo erillisten objektien ja animaatioiden kanssa"
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Import with Separate Materials+Animations"
msgstr "Tuo erillisten materiaalien ja animaatioiden kanssa"
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Import with Separate Objects+Materials+Animations"
msgstr "Tuo erillisten objektien, materiaalien ja animaatioiden kanssa"
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Import as Multiple Scenes"
-msgstr "Tuo 3D Scene"
+msgstr "Tuo useina skeneinä"
#: editor/import/resource_importer_scene.cpp
msgid "Import as Multiple Scenes+Materials"
@@ -2893,49 +2793,48 @@ 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..."
+msgid "Importing 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.."
+msgid "Running Custom Script..."
msgstr "Suorita valitsemasi skripti..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
-msgstr "Ei voitu ladata tuonnin jälkeistä skriptiä: "
+msgstr "Ei voitu ladata tuonnin jälkeistä skriptiä:"
#: editor/import/resource_importer_scene.cpp
msgid "Invalid/broken script for post-import (check console):"
-msgstr "Viallinen tuonnin jälkeinen skripti (tarkista konsoli) : "
+msgstr ""
+"Virheellinen tai viallinen tuonnin jälkeinen skripti (tarkista konsoli):"
#: editor/import/resource_importer_scene.cpp
msgid "Error running post-import script:"
-msgstr "Virhe ajettaessa tuonnin jälkeistä skriptiä: "
+msgstr "Virhe ajettaessa tuonnin jälkeistä skriptiä:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr "Tallennetaan..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
-msgstr ""
+msgstr "Aseta oletus valinnalle '%s'"
#: editor/import_dock.cpp
msgid "Clear Default for '%s'"
-msgstr ""
+msgstr "Poista oletus valinnalta '%s'"
#: editor/import_dock.cpp
msgid " Files"
@@ -2946,7 +2845,7 @@ msgid "Import As:"
msgstr "Tuo nimellä:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr "Esiasetus..."
#: editor/import_dock.cpp
@@ -2955,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"
@@ -2963,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
@@ -3055,11 +2954,11 @@ msgstr "Lisää animaatio"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Blend Next Changed"
-msgstr "Sekoita seuraavaan vaihdettu"
+msgstr "Sulauta seuraavaan vaihdettu"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Change Blend Time"
-msgstr ""
+msgstr "Muuta sulautusaikaa"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Load Animation"
@@ -3111,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 ""
+msgstr "Skaalaa animaation toistoa globaalisti solmulle."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Create new animation in player."
@@ -3139,11 +3038,11 @@ 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"
-msgstr ""
+msgstr "Muokkaa kohteen sulautusaikoja"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation Tools"
@@ -3159,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"
@@ -3199,7 +3098,7 @@ msgstr "Pakota valkoisen modulaatio"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Include Gizmos (3D)"
-msgstr ""
+msgstr "Näytä 3D-muokkaimet"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Create New Animation"
@@ -3260,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:"
@@ -3309,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"
@@ -3329,56 +3228,55 @@ 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 ""
+msgstr "3-sulautussolmu"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend4 Node"
-msgstr ""
+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.."
+msgid "Import Animations..."
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.."
+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"
@@ -3389,9 +3287,8 @@ msgid "Contents:"
msgstr "Sisällöt:"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "View Files"
-msgstr " Tiedostot"
+msgstr "Näytä tiedostot"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Can't resolve hostname:"
@@ -3402,18 +3299,16 @@ msgid "Connection error, please try again."
msgstr "Yhteysvirhe, ole hyvä ja yritä uudelleen."
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Can't connect to host:"
-msgstr "Yhdistä Nodeen:"
+msgstr "Isäntään yhdistäminen epäonnistui:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "No response from host:"
-msgstr "Ei vastausta isännältä: "
+msgstr "Ei vastausta isännältä:"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Request failed, return code:"
-msgstr "Pyydetty tiedostomuoto tuntematon:"
+msgstr "Pyyntö epäonnistui, virhekoodi:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Request failed, too many redirects"
@@ -3444,14 +3339,12 @@ msgid "Fetching:"
msgstr "Noudetaan:"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
-msgid "Resolving.."
-msgstr "Tallennetaan..."
+msgid "Resolving..."
+msgstr "Selvitetään..."
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Error making request"
-msgstr "Virhe tallennettaessa resurssia!"
+msgstr "Virhe pyynnön luonnissa"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Idle"
@@ -3462,9 +3355,8 @@ msgid "Retry"
msgstr "Yritä uudelleen"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Download Error"
-msgstr "Lataa"
+msgstr "Latausvirhe"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Download for this asset is already in progress!"
@@ -3514,7 +3406,7 @@ msgid "Site:"
msgstr "Sivu:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr "Tuki..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3522,7 +3414,6 @@ msgid "Official"
msgstr "Virallinen"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Testing"
msgstr "Testaus"
@@ -3536,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
@@ -3585,36 +3482,31 @@ msgstr "Siirrä keskikohtaa"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Action"
-msgstr "Siirrä "
+msgstr "Siirrä"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move vertical guide"
msgstr "Siirrä pystysuuntaista apuviivaa"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Create new vertical guide"
-msgstr "Luo uusi skripti"
+msgstr "Luo uusi pystysuora apuviiva"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Remove vertical guide"
-msgstr "Poista muuttuja"
+msgstr "Poista pystysuora apuviiva"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Move horizontal guide"
-msgstr "Siirrä pistettä käyrällä"
+msgstr "Siirrä vaakasuoraa apuviivaa"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Create new horizontal guide"
-msgstr "Luo uusi skripti"
+msgstr "Luo uusi vaakasuora apuviiva"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Remove horizontal guide"
-msgstr "Poista virheelliset avaimet"
+msgstr "Poista vaakasuora apuviiva"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Create new horizontal and vertical guides"
@@ -3629,14 +3521,12 @@ msgid "Edit CanvasItem"
msgstr "Muokkaa CanvasItemiä"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Anchors only"
-msgstr "Ankkuri"
+msgstr "Vain ankkurit"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Change Anchors and Margins"
-msgstr "Muuta ankkureita"
+msgstr "Muuta ankkureita ja marginaaleja"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Change Anchors"
@@ -3666,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"
@@ -3682,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."
@@ -3689,7 +3581,7 @@ msgstr "Klikkaa vaihtaaksesi objektin kääntökeskiötä."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Pan Mode"
-msgstr ""
+msgstr "Panorointitila"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Toggles snapping"
@@ -3700,9 +3592,8 @@ msgid "Use Snap"
msgstr "Käytä tarttumista"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Snapping options"
-msgstr "Animaation asetukset"
+msgstr "Tarttumisen asetukset"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to grid"
@@ -3713,6 +3604,7 @@ msgid "Use Rotation Snap"
msgstr "Tartu käännettäessä"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "Määrittele tarttuminen..."
@@ -3729,28 +3621,24 @@ msgid "Smart snapping"
msgstr "Älykäs tarttuminen"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Snap to parent"
-msgstr "Laajenna Parentiin"
+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
-#, fuzzy
msgid "Snap to node sides"
-msgstr "Tartu noden sivustoihin"
+msgstr "Tartu solmun reunoihin"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Snap to other nodes"
-msgstr "Tartu muihin nodeihin"
+msgstr "Tartu muihin solmuihin"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Snap to guides"
-msgstr "Laajenna Parentiin"
+msgstr "Tartu apuviivoihin"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
@@ -3768,7 +3656,7 @@ msgstr "Varmistaa ettei objektin lapsia voi valita."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Restores the object's children's ability to be selected."
-msgstr ""
+msgstr "Palauttaa objektin aliobjektien mahdollisuuden tulla valituksi."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Make Bones"
@@ -3792,9 +3680,8 @@ msgstr "Tyhjennä IK ketju"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "View"
-msgstr "Näytä/Tarkastele"
+msgstr "Näytä"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/polygon_2d_editor_plugin.cpp
@@ -3802,9 +3689,8 @@ msgid "Show Grid"
msgstr "Näytä ruudukko"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Helpers"
-msgstr "Näytä luut"
+msgstr "Näytä avustimet"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Rulers"
@@ -3815,14 +3701,12 @@ msgid "Show Guides"
msgstr "Näytä apuviivat"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
msgstr "Näytä origo"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 näyttöruutu"
+msgstr "Näytä näyttöikkuna"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -3838,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"
@@ -3846,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"
@@ -3857,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"
@@ -3883,21 +3766,21 @@ msgstr "Lisätään %s..."
#: editor/plugins/canvas_item_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Ok"
-msgstr ""
+msgstr "Ok"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Cannot instantiate multiple nodes without root."
-msgstr ""
+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
msgid "Error instancing scene from %s"
-msgstr "Virhe luotaessa instanssia kohteesta %s"
+msgstr "Virhe luotaessa ilmentymää kohteesta %s"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Change default type"
@@ -3908,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"
@@ -3921,48 +3804,45 @@ msgstr "Aseta kahva"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Remove item %d?"
-msgstr ""
+msgstr "Poistetaanko kohde %d?"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
#: editor/plugins/theme_editor_plugin.cpp
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Add Item"
-msgstr "Lisää"
+msgstr "Lisää kohde"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
-#, fuzzy
msgid "Remove Selected Item"
-msgstr "Poista valitut"
+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
-#, fuzzy
msgid "Ease in"
-msgstr "Framen valinta"
+msgstr "Kiihdytä alussa"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Ease out"
-msgstr ""
+msgstr "Hidasta lopussa"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Smoothstep"
-msgstr ""
+msgstr "Pehmeä askellus"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Modify Curve Point"
@@ -4002,7 +3882,7 @@ msgstr "Poista käyrän piste"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Toggle Curve Linear Tangent"
-msgstr ""
+msgstr "Aseta käyrälle lineaarinen tangentti"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Hold Shift to edit tangents individually"
@@ -4010,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"
@@ -4023,27 +3903,27 @@ msgstr "Muokkaa väriliukumaa"
#: editor/plugins/item_list_editor_plugin.cpp
msgid "Item %d"
-msgstr ""
+msgstr "Kohde %d"
#: editor/plugins/item_list_editor_plugin.cpp
msgid "Items"
-msgstr ""
+msgstr "Sisältö"
#: editor/plugins/item_list_editor_plugin.cpp
msgid "Item List Editor"
-msgstr ""
+msgstr "Sisällön muokkaus"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid ""
"No OccluderPolygon2D resource on this node.\n"
"Create and assign one?"
msgstr ""
-"Tälle nodelle ei ole OccluderPolygon2D:tä.\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."
@@ -4059,7 +3939,7 @@ msgstr "VHP: Siirrä pistettä."
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Ctrl+LMB: Split Segment."
-msgstr "Ctrl+Vasen hiirennappi: Puolita osa"
+msgstr "Ctrl+Vasen hiirennappi: Puolita osa."
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "RMB: Erase Point."
@@ -4067,60 +3947,59 @@ msgstr "OHP: Pyyhi piste."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh is empty!"
-msgstr ""
+msgstr "Mesh on tyhjä!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Static Trimesh Body"
-msgstr ""
+msgstr "Luo konkaavi staattinen kappale"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Static Convex Body"
-msgstr ""
+msgstr "Luo konveksi staattinen kappale"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-#, fuzzy
msgid "This doesn't work on scene root!"
-msgstr "Tämä ei toimi root-Scenessä!"
+msgstr "Tämä ei toimi skenen juuressa!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Trimesh Shape"
-msgstr ""
+msgstr "Luo konkaavi muoto"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Convex Shape"
-msgstr ""
+msgstr "Luo konveksi muoto"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Navigation Mesh"
-msgstr ""
+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"
-msgstr ""
+msgstr "Mallilla ei ole UV-kanavaa tällä kerroksella"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "MeshInstance lacks a Mesh!"
-msgstr ""
+msgstr "MeshInstance solmulta puuttuu Mesh!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh has not surface to create outlines from!"
-msgstr ""
+msgstr "Meshillä ei ole pintaa, josta ääriviivat voitaisiin luoda!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "Meshin primitiivityyppi ei ole PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4132,90 +4011,89 @@ 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"
-msgstr ""
+msgstr "Luo konkaavi staattinen kappale"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Convex Static Body"
-msgstr ""
+msgstr "Luo konveksi staattinen kappale"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Trimesh Collision Sibling"
-msgstr ""
+msgstr "Luo konkaavi törmäysmuoto sisareksi"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Convex Collision Sibling"
-msgstr ""
+msgstr "Luo konveksi törmäysmuoto sisareksi"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr ""
+msgid "Create Outline Mesh..."
+msgstr "Luo reunoista Mesh..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
-#, fuzzy
msgid "View UV1"
-msgstr "Näytä/Tarkastele"
+msgstr "Näytä UV1"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-#, fuzzy
msgid "View UV2"
-msgstr "Näytä/Tarkastele"
+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."
-msgstr ""
+msgstr "Pinnan lähdettä ei ole määritelty."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Surface source is invalid (invalid path)."
-msgstr ""
+msgstr "Pinnan lähde on virheellinen (virheellinen polku)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Surface source is invalid (no geometry)."
-msgstr ""
+msgstr "Pinnan lähde on virheellinen (geometria puuttuu)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Surface source is invalid (no faces)."
-msgstr ""
+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."
@@ -4223,27 +4101,27 @@ 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:"
-msgstr ""
+msgstr "Valitse kohdepinta:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Populate Surface"
-msgstr ""
+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:"
-msgstr ""
+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"
@@ -4259,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:"
@@ -4275,19 +4153,19 @@ msgstr "Satunnainen skaalaus:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Populate"
-msgstr ""
+msgstr "Täytä"
#: editor/plugins/navigation_mesh_editor_plugin.cpp
msgid "Bake!"
-msgstr ""
+msgstr "Kehitä!"
#: editor/plugins/navigation_mesh_editor_plugin.cpp
msgid "Bake the navigation mesh."
-msgstr ""
+msgstr "Kehitä navigointiverkko."
#: editor/plugins/navigation_mesh_editor_plugin.cpp
msgid "Clear the navigation mesh."
-msgstr ""
+msgstr "Tyhjennä navigointiverkko."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Setting up Configuration..."
@@ -4299,45 +4177,43 @@ msgstr "Lasketaan ruudukon kokoa..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Creating heightfield..."
-msgstr ""
+msgstr "Luodaan korkeuskenttää..."
#: editor/plugins/navigation_mesh_generator.cpp
-#, fuzzy
msgid "Marking walkable triangles..."
-msgstr "Varastoidaan paikalliset muutokset..."
+msgstr "Merkitään kuljettavat kolmiot..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Constructing compact heightfield..."
-msgstr ""
+msgstr "Rakennetaan tiivistä korkeuskenttää..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Eroding walkable area..."
-msgstr ""
+msgstr "Syövytetään kuljettavaa aluetta..."
#: editor/plugins/navigation_mesh_generator.cpp
-#, fuzzy
msgid "Partitioning..."
-msgstr "Varoitus"
+msgstr "Ositetaan..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Creating contours..."
-msgstr ""
+msgstr "Luodaan korkeuskäyriä..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Creating polymesh..."
-msgstr ""
+msgstr "Luodaan polymesh..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Converting to native navigation mesh..."
-msgstr ""
+msgstr "Muunnetaan alkuperäiseksi navigointiverkoksi..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Navigation Mesh Generator Setup:"
-msgstr ""
+msgstr "Navigointiverkon generaattorin asetukset:"
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Parsing Geometry..."
-msgstr ""
+msgstr "Jäsentää geometriaa…"
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Done!"
@@ -4345,29 +4221,29 @@ msgstr "Valmis!"
#: editor/plugins/navigation_polygon_editor_plugin.cpp
msgid "Create Navigation Polygon"
-msgstr ""
+msgstr "Luo navigointipolygoni"
#: editor/plugins/particles_2d_editor_plugin.cpp
#: editor/plugins/particles_editor_plugin.cpp
-#, fuzzy
msgid "Generating AABB"
-msgstr "Luo AABB"
+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:"
msgstr "Virhe ladattaessa kuvaa:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr ""
+msgid "No pixels with transparency > 128 in image..."
+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"
-msgstr ""
+msgstr "Kartoita näkyvä alue"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Load Emission Mask"
@@ -4375,7 +4251,7 @@ msgstr "Lataa emissiomaski"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Clear Emission Mask"
-msgstr ""
+msgstr "Tyhjennä emissiomaski"
#: editor/plugins/particles_2d_editor_plugin.cpp
#: editor/plugins/particles_editor_plugin.cpp
@@ -4388,18 +4264,16 @@ msgstr "Luotujen pisteiden määrä:"
#: editor/plugins/particles_2d_editor_plugin.cpp
#: editor/plugins/particles_editor_plugin.cpp
-#, fuzzy
msgid "Generation Time (sec):"
-msgstr "Keskimääräinen aika (sek)"
+msgstr "Luontiaika (s):"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Emission Mask"
msgstr "Emission maski"
#: editor/plugins/particles_2d_editor_plugin.cpp
-#, fuzzy
msgid "Capture from Pixel"
-msgstr "Luo Scenestä"
+msgstr "Nappaa pikselistä"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Emission Colors"
@@ -4407,15 +4281,15 @@ 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 ""
+msgstr "Solmulta puuttuu geometria (tahkot)."
#: editor/plugins/particles_editor_plugin.cpp
msgid "A processor material of type 'ParticlesMaterial' is required."
-msgstr ""
+msgstr "Tarvitaan 'ParticlesMaterial' tyyppinen prosessorimateriaali."
#: editor/plugins/particles_editor_plugin.cpp
msgid "Faces contain no area!"
@@ -4431,14 +4305,13 @@ msgstr "Luo AABB"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Mesh"
-msgstr ""
+msgstr "Luo säteilypisteet meshistä"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Node"
-msgstr ""
+msgstr "Luo säteilypisteet solmusta"
#: editor/plugins/particles_editor_plugin.cpp
-#, fuzzy
msgid "Create Emitter"
msgstr "Luo säteilijä/lähetin"
@@ -4460,26 +4333,23 @@ msgstr "Äänenvoimakkuus"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Emission Source: "
-msgstr "Emission lähde:"
+msgstr "Emission lähde: "
#: editor/plugins/particles_editor_plugin.cpp
-#, fuzzy
msgid "Generate Visibility AABB"
-msgstr "Luo AABB"
+msgstr "Kartoita näkyvä alue"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Remove Point from Curve"
msgstr "Poista pisteet käyrästä"
#: editor/plugins/path_2d_editor_plugin.cpp
-#, fuzzy
msgid "Remove Out-Control from Curve"
-msgstr "Poista pisteet käyrästä"
+msgstr "Poista lähtöohjain käyrästä"
#: editor/plugins/path_2d_editor_plugin.cpp
-#, fuzzy
msgid "Remove In-Control from Curve"
-msgstr "Poista pisteet käyrästä"
+msgstr "Poista tulo-ohjain käyrästä"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
@@ -4492,11 +4362,11 @@ msgstr "Siirrä pistettä käyrällä"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Move In-Control in Curve"
-msgstr ""
+msgstr "Siirrä tulo-ohjainta käyrällä"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Move Out-Control in Curve"
-msgstr ""
+msgstr "Siirrä lähtöohjainta käyrällä"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
@@ -4547,19 +4417,16 @@ msgid "Curve Point #"
msgstr "Käyrän piste #"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Set Curve Point Position"
-msgstr "Siirrä pistettä"
+msgstr "Aseta käyräpisteen sijainti"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Set Curve In Position"
-msgstr "Siirrä pistettä"
+msgstr "Aseta käyrän aloitussijainti"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Set Curve Out Position"
-msgstr "Siirrä pistettä"
+msgstr "Aseta käyrän lopetussijainti"
#: editor/plugins/path_editor_plugin.cpp
msgid "Split Path"
@@ -4570,14 +4437,12 @@ msgid "Remove Path Point"
msgstr "Poista polun piste"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Remove Out-Control Point"
-msgstr "Poista polygoni ja piste"
+msgstr "Poista lähtöohjaimen piste"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Remove In-Control Point"
-msgstr "Poista polygoni ja piste"
+msgstr "Poista tulo-ohjaimen piste"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Create UV Map"
@@ -4589,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"
@@ -4614,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"
@@ -4630,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"
@@ -4682,7 +4546,7 @@ msgstr "Avaa editorissa"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/scene_tree_editor.cpp
msgid "Instance:"
-msgstr ""
+msgstr "Ilmentymä:"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp
@@ -4703,21 +4567,16 @@ msgid "Paste"
msgstr "Liitä"
#: editor/plugins/resource_preloader_editor_plugin.cpp
-#, fuzzy
msgid "ResourcePreloader"
-msgstr "Resurssi"
+msgstr "Resurssien esilataaja"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Clear Recent Files"
-msgstr "Tyhjennä luut"
+msgstr "Tyhjennä viimeisimpien tiedostojen luettelo"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Close and save changes?"
-msgstr ""
-"Sulje ja tallenna muutokset?\n"
-"\""
+msgstr "Sulje ja tallenna muutokset?"
#: editor/plugins/script_editor_plugin.cpp
msgid "Error while saving theme"
@@ -4740,7 +4599,7 @@ msgid "Import Theme"
msgstr "Tuo teema"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr "Tallenna teema nimellä..."
#: editor/plugins/script_editor_plugin.cpp
@@ -4748,9 +4607,8 @@ msgid " Class Reference"
msgstr " Luokan referenssi"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Sort"
-msgstr "Lajittele:"
+msgstr "Lajittele"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp editor/scene_tree_dock.cpp
@@ -4786,15 +4644,13 @@ msgstr "Tallenna kaikki"
#: editor/plugins/script_editor_plugin.cpp
msgid "Soft Reload Script"
-msgstr ""
+msgstr "Lataa skripti uudelleen kevyesti"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Copy Script Path"
-msgstr "Kopioi polku"
+msgstr "Kopioi skriptin polku"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Show In File System"
msgstr "Näytä tiedostojärjestelmässä"
@@ -4840,7 +4696,7 @@ msgstr "Näytä/piilota skriptipaneeli"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr "Etsi..."
#: editor/plugins/script_editor_plugin.cpp
@@ -4849,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"
@@ -4880,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."
@@ -4926,21 +4781,20 @@ 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
msgid "Only resources from filesystem can be dropped."
-msgstr ""
+msgstr "Vain tiedostojärjestelmän resursseja voi raahata ja pudottaa."
#: editor/plugins/script_text_editor.cpp
msgid "Pick Color"
msgstr "Poimi väri"
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Convert Case"
-msgstr "Muunnetaan kuvia"
+msgstr "Muunna aakkoslaji"
#: editor/plugins/script_text_editor.cpp
msgid "Uppercase"
@@ -4952,7 +4806,7 @@ msgstr "Pienet kirjaimet"
#: editor/plugins/script_text_editor.cpp
msgid "Capitalize"
-msgstr ""
+msgstr "Isot alkukirjaimet"
#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp
#: scene/gui/text_edit.cpp
@@ -4971,9 +4825,8 @@ msgid "Select All"
msgstr "Valitse kaikki"
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Delete Line"
-msgstr "Poista piste"
+msgstr "Poista rivi"
#: editor/plugins/script_text_editor.cpp
msgid "Indent Left"
@@ -4988,9 +4841,8 @@ msgid "Toggle Comment"
msgstr "Näytä/Piilota kommentit"
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Fold/Unfold Line"
-msgstr "Avaa rivi"
+msgstr "Laskosta tai avaa rivi"
#: editor/plugins/script_text_editor.cpp
msgid "Fold All Lines"
@@ -5010,7 +4862,7 @@ msgstr "Täydennä symbooli"
#: editor/plugins/script_text_editor.cpp
msgid "Trim Trailing Whitespace"
-msgstr ""
+msgstr "Poista välilyönnit lopusta"
#: editor/plugins/script_text_editor.cpp
msgid "Convert Indent To Spaces"
@@ -5027,7 +4879,7 @@ msgstr "Automaattinen sisennys"
#: editor/plugins/script_text_editor.cpp
#: modules/visual_script/visual_script_editor.cpp
msgid "Toggle Breakpoint"
-msgstr ""
+msgstr "Aseta tai poista breakpoint"
#: editor/plugins/script_text_editor.cpp
msgid "Remove All Breakpoints"
@@ -5042,106 +4894,104 @@ msgid "Goto Previous Breakpoint"
msgstr "Mene edelliseen breakpointiin"
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Convert To Uppercase"
-msgstr "Muunna..."
+msgstr "Muunna isoiksi kirjaimiksi"
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Convert To Lowercase"
-msgstr "Muunna..."
+msgstr "Muunna pieniksi kirjaimiksi"
#: editor/plugins/script_text_editor.cpp
msgid "Find Previous"
msgstr "Etsi edellinen"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr "Korvaa..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr "Mene funktioon..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr "Mene riville..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
-msgstr ""
+msgstr "Asiayhteydellinen ohje"
#: editor/plugins/shader_editor_plugin.cpp
msgid "Shader"
-msgstr ""
+msgstr "Sävytin"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Constant"
-msgstr ""
+msgstr "Muuta skalaarivakiota"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Constant"
-msgstr ""
+msgstr "Muuta vektorivakiota"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change RGB Constant"
-msgstr ""
+msgstr "Muuta RGB-värivakiota"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Operator"
-msgstr ""
+msgstr "Muuta skalaarioperaattoria"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Operator"
-msgstr ""
+msgstr "Muuta vektorioperaattoria"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Scalar Operator"
-msgstr ""
+msgstr "Muuta vektori- ja skalaarioperaattoria"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change RGB Operator"
-msgstr ""
+msgstr "Muuta RGB-värioperaattoria"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Toggle Rot Only"
-msgstr ""
+msgstr "Vain kierto"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Function"
-msgstr ""
+msgstr "Muuta skalaarifunktiota"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Function"
-msgstr ""
+msgstr "Muuta vektorifunktiota"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Uniform"
-msgstr ""
+msgstr "Muuta skalaariuniformia"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Uniform"
-msgstr ""
+msgstr "Muuta vektoriuniformia"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change RGB Uniform"
-msgstr ""
+msgstr "Muuta RGB-uniformia"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Default Value"
-msgstr ""
+msgstr "Muuta oletusarvoa"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change XForm Uniform"
-msgstr ""
+msgstr "Muuta XForm-uniformia"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Texture Uniform"
-msgstr ""
+msgstr "Muuta tekstuuriuniformia"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Cubemap Uniform"
-msgstr ""
+msgstr "Muuta Cubemap-uniformia"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Comment"
@@ -5149,15 +4999,15 @@ msgstr "Vaihda kommenttia"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Add/Remove to Color Ramp"
-msgstr ""
+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"
@@ -5165,39 +5015,39 @@ msgstr "Vaihda syötteen nimi"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Connect Graph Nodes"
-msgstr ""
+msgstr "Yhdistä graafin solmut"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Disconnect Graph Nodes"
-msgstr ""
+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 ""
+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"
-msgstr ""
+msgstr "Virhe: syklinen kytkentä"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Error: Missing Input Connections"
-msgstr ""
+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"
@@ -5213,29 +5063,27 @@ msgstr "Muunnos keskeytetty."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "X-Axis Transform."
-msgstr ""
+msgstr "X-akselin muunnos."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Y-Axis Transform."
-msgstr ""
+msgstr "Y-akselin muunnos."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Z-Axis Transform."
-msgstr ""
+msgstr "Z-akselin muunnos."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Plane Transform."
-msgstr ""
+msgstr "Näkymätason muunnos."
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Scaling: "
-msgstr "Skaalaus:"
+msgstr "Skaalataan: "
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Translating: "
-msgstr "Siirtymä"
+msgstr "Siirretään: "
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotating %s degrees."
@@ -5243,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."
@@ -5251,35 +5099,31 @@ msgstr "Animaatioavain lisätty."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Objects Drawn"
-msgstr ""
+msgstr "Objekteja piirretty"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Material Changes"
-msgstr "Päivitä muutokset"
+msgstr "Materiaalimuutokset"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Shader Changes"
-msgstr "Päivitä muutokset"
+msgstr "Sävytinmuutokset"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Surface Changes"
-msgstr "Päivitä muutokset"
+msgstr "Pintamuutokset"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Draw Calls"
-msgstr ""
+msgstr "Piirtokutsuja"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Vertices"
-msgstr "Ominaisuudet:"
+msgstr "Kärkipisteet"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "FPS"
-msgstr ""
+msgstr "FPS"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Top View."
@@ -5322,9 +5166,8 @@ msgid "Rear View."
msgstr "Takanäkymä."
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Rear"
-msgstr "Taka/perä"
+msgstr "Taka"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Align with view"
@@ -5336,11 +5179,11 @@ msgstr "Asia kunnossa :("
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "No parent to instance a child at."
-msgstr ""
+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"
@@ -5352,85 +5195,75 @@ msgstr "Näytä rautalankamalli"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Display Overdraw"
-msgstr ""
+msgstr "Näytä ylipiirto"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Display Unshaded"
-msgstr "Näytä varjoton"
+msgstr "Näytä sävyttämätön"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "View Environment"
-msgstr "Ympäristö"
+msgstr "Näytä ympäristö"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "View Gizmos"
-msgstr "Näytä ruudukko"
+msgstr "Näytä muokkaimet"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Information"
msgstr "Näytä tiedot"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "View FPS"
-msgstr " Tiedostot"
+msgstr "Näytä FPS"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Half Resolution"
-msgstr "Skaalaa valintaa"
+msgstr "Puolikas näyttötarkkuus"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Audio Listener"
-msgstr ""
+msgstr "Äänikuuntelija"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Doppler Enable"
-msgstr "Ota käyttöön"
+msgstr "Doppler käytössä"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Left"
-msgstr ""
+msgstr "Liiku vasemmalle"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Right"
-msgstr ""
+msgstr "Liiku oikealle"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Freelook Forward"
-msgstr "Mene eteenpäin"
+msgstr "Liiku eteenpäin"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Freelook Backwards"
-msgstr "Taaksepäin"
+msgstr "Liiku taaksepäin"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Up"
-msgstr ""
+msgstr "Liiku ylös"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Freelook Down"
-msgstr "Rulla alas."
+msgstr "Liiku alas"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Speed Modifier"
-msgstr ""
+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 ""
@@ -5438,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)"
@@ -5456,14 +5292,12 @@ 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
-#, fuzzy
msgid "Snap Mode (%s)"
-msgstr "Tarttumisen tila:"
+msgstr "Tarttumisen tila (%s)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Bottom View"
@@ -5510,41 +5344,32 @@ msgid "Align Selection With View"
msgstr "Kohdista valinta näkymään"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Tool Select"
-msgstr "Valitse"
+msgstr "Valintatyökalu"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Tool Move"
-msgstr "Siirrä"
+msgstr "Siirtotyökalu"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Tool Rotate"
-msgstr "Ctrl: Pyöritä/kierrä"
+msgstr "Kiertotyökalu"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Tool Scale"
-msgstr "Skaalaus:"
+msgstr "Skaalaustyökalu"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Toggle Freelook"
-msgstr "Siirry koko näytön tilaan"
+msgstr "Kytke liikkuminen päälle/pois"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform"
msgstr "Muunna"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Määrittele tarttuminen..."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr ""
+msgid "Transform Dialog..."
+msgstr "Muunnosikkuna..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5585,7 +5410,7 @@ msgstr "Asetukset"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Skeleton Gizmo visibility"
-msgstr ""
+msgstr "Luurankomuokkaimen näkyvyys"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Snap Settings"
@@ -5613,19 +5438,19 @@ msgstr "Näkökentän perspektiivi (ast.):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Z-Near:"
-msgstr "Minimi etäisyys:"
+msgstr "Pienin etäisyys:"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Z-Far:"
-msgstr "Maksimi etäisyys:"
+msgstr "Suurin etäisyys:"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Change"
-msgstr ""
+msgstr "Muunnoksen muutos"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Translate:"
-msgstr "Käännä:"
+msgstr "Siirrä:"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotate (deg.):"
@@ -5637,7 +5462,7 @@ msgstr "Skaalaa (kuvasuhde):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Type"
-msgstr ""
+msgstr "Muunnoksen tyyppi"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Pre"
@@ -5657,7 +5482,7 @@ msgstr "Lisää frame"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Resource clipboard is empty or not a texture!"
-msgstr ""
+msgstr "Resurssileikepöytä on tyhjä tai ei sisällä tekstuuria!"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Paste Frame"
@@ -5688,9 +5513,8 @@ msgid "Speed (FPS):"
msgstr "Nopeus (FPS):"
#: editor/plugins/sprite_frames_editor_plugin.cpp
-#, fuzzy
msgid "Loop"
-msgstr "Toisto"
+msgstr "Toista"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Animation Frames"
@@ -5705,33 +5529,28 @@ msgid "Insert Empty (After)"
msgstr "Syötä tyhjä (jälkeen)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
-#, fuzzy
msgid "Move (Before)"
-msgstr "Poista Node(t)"
+msgstr "Siirrä (ennen)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
-#, fuzzy
msgid "Move (After)"
-msgstr "Siirry vasemmalle"
+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:"
@@ -5781,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"
@@ -5795,29 +5613,28 @@ 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"
msgstr "Poista kaikki"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
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"
-msgstr ""
+msgstr "Lisää luokka"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Remove Class Items"
-msgstr ""
+msgstr "Poista luokka"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Create Empty Template"
@@ -5833,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"
@@ -5852,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 "Have,Many,Several,Options!"
-msgstr "On,Monia,Useita,Asetuksia"
+msgid "Has,Many,Options"
+msgstr "On,Useita,Asetuksia"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5916,13 +5729,12 @@ 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"
-msgstr ""
+msgstr "Täytä ruudukko"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Line Draw"
@@ -5930,7 +5742,7 @@ msgstr "Viivan piirto"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rectangle Paint"
-msgstr ""
+msgstr "Suorakaidetäyttö"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Bucket Fill"
@@ -5938,19 +5750,19 @@ msgstr "Täyttö"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Erase TileMap"
-msgstr ""
+msgstr "Tyhjennä ruudukko"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Erase selection"
-msgstr ""
+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"
-msgstr ""
+msgstr "Transponoi"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Mirror X"
@@ -5961,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"
@@ -5987,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:"
@@ -6002,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"
@@ -6012,37 +5822,39 @@ msgstr "Luo skenestä"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Merge from Scene"
-msgstr ""
+msgstr "Yhdistä skenestä"
#: editor/plugins/tile_set_editor_plugin.cpp editor/script_editor_debugger.cpp
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 ""
"LMB: set bit on.\n"
"RMB: set bit off."
msgstr ""
+"Hiiren vasen: aseta bitti päälle.\n"
+"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"
@@ -6054,7 +5866,7 @@ msgstr "Suoritettava"
#: editor/project_export.cpp
msgid "Delete patch '%s' from list?"
-msgstr ""
+msgstr "Poista päivitys '%s' listasta?"
#: editor/project_export.cpp
msgid "Delete preset '%s'?"
@@ -6062,14 +5874,14 @@ msgstr "Poista esiasetus '%s'?"
#: editor/project_export.cpp
msgid "Export templates for this platform are missing/corrupted: "
-msgstr ""
+msgstr "Vientimallit tälle alustalle puuttuvat tai ovat viallisia: "
#: editor/project_export.cpp
msgid "Presets"
msgstr "Esiasetukset"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr "Lisää..."
#: editor/project_export.cpp
@@ -6082,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)"
@@ -6112,25 +5924,23 @@ msgstr ""
#: editor/project_export.cpp
msgid "Patches"
-msgstr ""
+msgstr "Päivitykset"
#: editor/project_export.cpp
msgid "Make Patch"
-msgstr "Tee patchi"
+msgstr "Luo päivitys"
#: editor/project_export.cpp
-#, fuzzy
msgid "Features"
-msgstr "Tekstuuri"
+msgstr "Ominaisuudet"
#: editor/project_export.cpp
msgid "Custom (comma-separated):"
-msgstr ""
+msgstr "Mukautettu (pilkulla erotettu):"
#: editor/project_export.cpp
-#, fuzzy
msgid "Feature List:"
-msgstr "Metodilista:"
+msgstr "Ominaisuuslista:"
#: editor/project_export.cpp
msgid "Export PCK/Zip"
@@ -6142,7 +5952,7 @@ msgstr "Tälle alustalle ei löytynyt vientipohjia:"
#: editor/project_export.cpp
msgid "Export templates for this platform are missing/corrupted:"
-msgstr ""
+msgstr "Vientimallit tälle alustalle puuttuvat tai ovat viallisia:"
#: editor/project_export.cpp
msgid "Export With Debug"
@@ -6157,22 +5967,24 @@ msgid "Please choose a 'project.godot' file."
msgstr "Ole hyvä ja valitse 'project.godot' tiedosto."
#: editor/project_manager.cpp
-#, fuzzy
msgid "Please choose an empty folder."
-msgstr "Ole hyvä ja valitse 'project.godot' tiedosto."
+msgstr "Ole hyvä ja valitse tyhjä kansio."
#: editor/project_manager.cpp
msgid "Imported Project"
msgstr "Tuotu projekti"
#: editor/project_manager.cpp
-#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Virheellinen projektin nimi."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "Kansiota ei voitu luoda."
#: editor/project_manager.cpp
msgid "There is already a folder in this path with the specified name."
-msgstr ""
+msgstr "Polusta löytyy jo kansio annetulla nimellä."
#: editor/project_manager.cpp
msgid "It would be a good idea to name your project."
@@ -6183,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:"
@@ -6216,34 +6027,30 @@ msgid "Import Existing Project"
msgstr "Tuo olemassaoleva projekti"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Import & Edit"
-msgstr "Tuo & Avaa"
+msgstr "Tuo ja muokkaa"
#: editor/project_manager.cpp
msgid "Create New Project"
msgstr "Luo uusi projekti"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Create & Edit"
-msgstr "Luo säteilijä/lähetin"
+msgstr "Luo ja muokkaa"
#: editor/project_manager.cpp
msgid "Install Project:"
msgstr "Asenna projekti:"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Install & Edit"
-msgstr "Asenna"
+msgstr "Asenna ja muokkaa"
#: editor/project_manager.cpp
msgid "Project Name:"
msgstr "Projektin nimi:"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Create folder"
msgstr "Luo kansio"
@@ -6273,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 ""
@@ -6337,14 +6144,12 @@ msgid "Exit"
msgstr "Poistu"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Restart Now"
-msgstr "Käynnistä uudelleen (s):"
+msgstr "Käynnistä uudelleen nyt"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Can't run project"
-msgstr "Yhdistä..."
+msgstr "Projektia ei voida käynnistää"
#: editor/project_manager.cpp
msgid ""
@@ -6356,7 +6161,7 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Key "
-msgstr "Näppäin... "
+msgstr "Näppäin "
#: editor/project_settings_editor.cpp
msgid "Joy Button"
@@ -6372,9 +6177,11 @@ msgstr "Hiiren painike"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"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 '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6382,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+"
@@ -6401,7 +6208,7 @@ msgid "Control+"
msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr "Paina näppäintä..."
#: editor/project_settings_editor.cpp
@@ -6457,18 +6264,16 @@ 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
-#, fuzzy
msgid "Add Event"
-msgstr "Lisää tyhjä"
+msgstr "Lisää tapahtuma"
#: editor/project_settings_editor.cpp
msgid "Device"
@@ -6504,7 +6309,7 @@ msgstr "Lisää yleinen ominaisuus"
#: editor/project_settings_editor.cpp
msgid "Select a setting item first!"
-msgstr ""
+msgstr "Valitse asetus ensin!"
#: editor/project_settings_editor.cpp
msgid "No property '%s' exists."
@@ -6512,12 +6317,11 @@ msgstr "Ominaisuutta '%s' ei löytynyt."
#: editor/project_settings_editor.cpp
msgid "Setting '%s' is internal, and it can't be deleted."
-msgstr ""
+msgstr "Asetus '%s' on sisäinen, eikä sitä voi poistaa."
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Delete Item"
-msgstr "Poista syöte"
+msgstr "Poista kohde"
#: editor/project_settings_editor.cpp
msgid "Already existing"
@@ -6525,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."
@@ -6537,7 +6341,7 @@ msgstr "Asetukset tallennettu onnistuneesti."
#: editor/project_settings_editor.cpp
msgid "Override for Feature"
-msgstr ""
+msgstr "Ominaisuuden ohitus"
#: editor/project_settings_editor.cpp
msgid "Add Translation"
@@ -6549,53 +6353,51 @@ msgstr "Poista käännös"
#: editor/project_settings_editor.cpp
msgid "Add Remapped Path"
-msgstr ""
+msgstr "Lisää korvaavuuspolku"
#: editor/project_settings_editor.cpp
msgid "Resource Remap Add Remap"
-msgstr ""
+msgstr "Lisää resurssin korvaavuus"
#: editor/project_settings_editor.cpp
msgid "Change Resource Remap Language"
-msgstr ""
+msgstr "Vaihda resurssin korvaavuuskieli"
#: editor/project_settings_editor.cpp
msgid "Remove Resource Remap"
-msgstr ""
+msgstr "Poista resurssin korvaavuus"
#: editor/project_settings_editor.cpp
msgid "Remove Resource Remap Option"
-msgstr ""
+msgstr "Poista resurssin korvaavuusvalinta"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Changed Locale Filter"
-msgstr "Muuta kameran kokoa"
+msgstr "Vaihdettu kielisuodatin"
#: editor/project_settings_editor.cpp
msgid "Changed Locale Filter Mode"
-msgstr ""
+msgstr "Vaihdettu kielisuodattimen tila"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Project Settings (project.godot)"
-msgstr "Projektin asetukset"
+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:"
msgstr "Ominaisuus:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
-msgstr ""
+msgid "Override For..."
+msgstr "Ohita alustalle..."
#: editor/project_settings_editor.cpp
msgid "Input Map"
-msgstr ""
+msgstr "Syötekartta"
#: editor/project_settings_editor.cpp
msgid "Action:"
@@ -6623,7 +6425,7 @@ msgstr "Käännökset:"
#: editor/project_settings_editor.cpp
msgid "Remaps"
-msgstr ""
+msgstr "Korvaavuudet"
#: editor/project_settings_editor.cpp
msgid "Resources:"
@@ -6631,11 +6433,11 @@ msgstr "Resurssit:"
#: editor/project_settings_editor.cpp
msgid "Remaps by Locale:"
-msgstr ""
+msgstr "Korvaavuudet kielikohtaisesti:"
#: editor/project_settings_editor.cpp
msgid "Locale"
-msgstr "Kieli"
+msgstr "Kielialue"
#: editor/project_settings_editor.cpp
msgid "Locales Filter"
@@ -6659,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"
@@ -6667,40 +6469,39 @@ msgstr "Valitse näyttöruutu"
#: editor/property_editor.cpp
msgid "Ease In"
-msgstr ""
+msgstr "Kiihdytä alussa"
#: editor/property_editor.cpp
msgid "Ease Out"
-msgstr ""
+msgstr "Hidasta lopussa"
#: editor/property_editor.cpp
msgid "Zero"
-msgstr ""
+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.."
+msgid "File..."
msgstr "Tiedosto..."
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr "Hakemisto..."
#: editor/property_editor.cpp
msgid "Assign"
-msgstr ""
+msgstr "Aseta"
#: editor/property_editor.cpp
-#, fuzzy
msgid "Select Node"
-msgstr "Valitse Node"
+msgstr "Valitse solmu"
#: editor/property_editor.cpp
msgid "New Script"
@@ -6711,9 +6512,8 @@ msgid "New %s"
msgstr "Uusi %s"
#: editor/property_editor.cpp
-#, fuzzy
msgid "Make Unique"
-msgstr "Tee luut"
+msgstr "Tee yksilölliseksi"
#: editor/property_editor.cpp
msgid "Show in File System"
@@ -6728,26 +6528,24 @@ 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."
-msgstr ""
+msgstr "Bitti %d, arvo %d."
#: editor/property_editor.cpp
msgid "On"
msgstr "Päällä"
#: editor/property_editor.cpp
-#, fuzzy
msgid "[Empty]"
-msgstr "Lisää tyhjä"
+msgstr "[Tyhjä]"
#: editor/property_editor.cpp modules/visual_script/visual_script_editor.cpp
msgid "Set"
@@ -6771,15 +6569,15 @@ msgstr "Valitse metodi"
#: editor/pvrtc_compress.cpp
msgid "Could not execute PVRTC tool:"
-msgstr ""
+msgstr "PVRTC-työkalun suoritus ei onnistunut:"
#: editor/pvrtc_compress.cpp
msgid "Can't load back converted image using PVRTC tool:"
-msgstr ""
+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):"
@@ -6787,7 +6585,7 @@ msgstr "Valitse uusi isäntä:"
#: editor/reparent_dialog.cpp
msgid "Keep Global Transform"
-msgstr ""
+msgstr "Pidä globaali muunnos"
#: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp
msgid "Reparent"
@@ -6795,7 +6593,7 @@ msgstr "Uusi isäntä"
#: editor/run_settings_dialog.cpp
msgid "Run Mode:"
-msgstr ""
+msgstr "Käynnistystila:"
#: editor/run_settings_dialog.cpp
msgid "Current Scene"
@@ -6811,74 +6609,76 @@ 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
msgid "OK"
-msgstr ""
+msgstr "OK"
#: editor/scene_tree_dock.cpp
msgid "No parent to instance the scenes at."
-msgstr ""
+msgstr "Solmua, jonka alle skenen ilmentymä luodaan, ei ole valittu."
#: editor/scene_tree_dock.cpp
msgid "Error loading scene from %s"
-msgstr ""
+msgstr "Virhe ladattaessa skeneä paikasta %s"
#: editor/scene_tree_dock.cpp
msgid ""
"Cannot instance the scene '%s' because the current scene exists within one "
"of its nodes."
msgstr ""
+"Skenestä '%s' ei voida luoda ilmentymää, koska nykyinen skene on olemassa "
+"jossakin sen solmuista."
#: editor/scene_tree_dock.cpp
msgid "Instance Scene(s)"
-msgstr ""
+msgstr "Luo ilmentymä skenestä tai skeneistä"
#: editor/scene_tree_dock.cpp
msgid "This operation can't be done on the tree root."
-msgstr ""
+msgstr "Tätä toimenpidettä ei voi tehdä puun juurelle."
#: editor/scene_tree_dock.cpp
msgid "Move Node In Parent"
-msgstr ""
+msgstr "Siirrä solmu isännän alle"
#: editor/scene_tree_dock.cpp
msgid "Move Nodes In Parent"
-msgstr ""
+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 ""
+msgstr "Ei voi tehdä juurisolmulle."
#: editor/scene_tree_dock.cpp
msgid "This operation can't be done on instanced scenes."
-msgstr ""
+msgstr "Tätä toimintoa ei voi tehdä skenejen ilmentymille."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "Tallenna uusi scene nimellä..."
+msgid "Save New Scene As..."
+msgstr "Tallenna uusi skene nimellä..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
-msgstr ""
+msgstr "Muokattavat alisolmut"
#: editor/scene_tree_dock.cpp
msgid "Load As Placeholder"
-msgstr ""
+msgstr "Lataa paikanpitäjäksi"
#: editor/scene_tree_dock.cpp
msgid "Discard Instancing"
-msgstr ""
+msgstr "Hylkää ilmentymä"
#: editor/scene_tree_dock.cpp
msgid "Makes Sense!"
@@ -6886,50 +6686,51 @@ 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 ""
"Couldn't save new scene. Likely dependencies (instances) couldn't be "
"satisfied."
msgstr ""
+"Skeneä ei voitu tallentaa. Mahdollisia riippuvuuksia (ilmentymiä) ei voida "
+"toteuttaa."
#: 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."
-msgstr ""
+msgstr "Virhe kahdennettaessa skeneä sen tallentamiseksi."
#: editor/scene_tree_dock.cpp
-#, fuzzy
msgid "Sub-Resources"
-msgstr "Resurssit"
+msgstr "Aliresurssit"
#: editor/scene_tree_dock.cpp
msgid "Clear Inheritance"
-msgstr ""
+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"
-msgstr ""
+msgstr "Luo aliskenen ilmentymä"
#: editor/scene_tree_dock.cpp
msgid "Change Type"
@@ -6945,15 +6746,15 @@ 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"
-msgstr ""
+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)"
@@ -6961,40 +6762,39 @@ 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 solmuksi. Luo periytetyn skenen jos "
+"juurisolmua ei ole olemassa."
#: editor/scene_tree_dock.cpp
-#, fuzzy
msgid "Filter nodes"
-msgstr "Suodattimet"
+msgstr "Suodata solmuja"
#: editor/scene_tree_dock.cpp
msgid "Attach a new or existing script for the selected node."
-msgstr ""
+msgstr "Liitä uusi tai olemassa oleva skripti valitulle solmulle."
#: editor/scene_tree_dock.cpp
msgid "Clear a script for the selected node."
-msgstr ""
+msgstr "Poista skripti valitulta solmulta."
#: editor/scene_tree_dock.cpp
-#, fuzzy
msgid "Remote"
-msgstr "Poista"
+msgstr "Etäinen"
#: editor/scene_tree_dock.cpp
-#, fuzzy
msgid "Local"
-msgstr "Skaalaus:"
+msgstr "Paikallinen"
#: editor/scene_tree_dock.cpp
msgid "Clear Inheritance? (No Undo!)"
-msgstr ""
+msgstr "Poistetaanko perintä? (Ei voi perua!)"
#: editor/scene_tree_dock.cpp
msgid "Clear!"
@@ -7002,84 +6802,91 @@ 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 ""
+msgstr "Solmun konfiguroinnin varoitus:"
#: editor/scene_tree_editor.cpp
msgid ""
"Node has connection(s) and group(s)\n"
"Click to show signals dock."
msgstr ""
+"Solmulla on liitäntöjä ja ryhmiä\n"
+"Napsauta näyttääksesi signaalitelakan."
#: editor/scene_tree_editor.cpp
msgid ""
"Node has connections.\n"
"Click to show signals dock."
msgstr ""
+"Solmulla on liitäntöjä.\n"
+"Napsauta näyttääksesi signaalitelakan."
#: editor/scene_tree_editor.cpp
msgid ""
"Node is in group(s).\n"
"Click to show groups dock."
msgstr ""
+"Solmu kuuluu ryhmään.\n"
+"Napsauta näyttääksesi ryhmätelakan."
#: editor/scene_tree_editor.cpp
-#, fuzzy
msgid "Open script"
-msgstr "Seuraava skripti"
+msgstr "Avaa skripti"
#: editor/scene_tree_editor.cpp
msgid ""
"Node is locked.\n"
"Click to unlock"
msgstr ""
+"Solmu on lukittu.\n"
+"Napsauta lukituksen avaamiseksi"
#: editor/scene_tree_editor.cpp
msgid ""
"Children are not selectable.\n"
"Click to make selectable"
msgstr ""
+"Alisolmut eivät ole valittavissa.\n"
+"Napsauta niiden tekemiseksi valittavaksi"
#: editor/scene_tree_editor.cpp
msgid "Toggle Visibility"
-msgstr ""
+msgstr "Aseta näkyvyys"
#: editor/scene_tree_editor.cpp
msgid "Invalid node name, the following characters are not allowed:"
-msgstr ""
+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 ""
+msgstr "Skenepuu (solmut):"
#: editor/scene_tree_editor.cpp
msgid "Node Configuration Warning!"
-msgstr ""
+msgstr "Solmun konfigurointivaroitus!"
#: editor/scene_tree_editor.cpp
msgid "Select a Node"
-msgstr "Valitse Node"
+msgstr "Valitse solmu"
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Error loading template '%s'"
-msgstr "Virhe ladattaessa kuvaa:"
+msgstr "Virhe ladattaessa mallia '%s'"
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Error - Could not create script in filesystem."
-msgstr "Ei voitu luoda skriptiä tiedostojärjestelmään."
+msgstr "Virhe - Ei voitu luoda skriptiä tiedostojärjestelmään."
#: editor/script_create_dialog.cpp
msgid "Error loading script from %s"
@@ -7087,7 +6894,7 @@ msgstr "Virhe ladattaessa skripti %s:stä"
#: editor/script_create_dialog.cpp
msgid "N/A"
-msgstr ""
+msgstr "Ei mitään"
#: editor/script_create_dialog.cpp
msgid "Path is empty"
@@ -7099,16 +6906,15 @@ msgstr "Polku ei ole paikallinen"
#: editor/script_create_dialog.cpp
msgid "Invalid base path"
-msgstr ""
+msgstr "Virheellinen kantapolku"
#: editor/script_create_dialog.cpp
msgid "Directory of the same name exists"
msgstr "Samanniminen hakemisto on jo olemassa"
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "File exists, will be reused"
-msgstr "Tiedosto on jo olemassa, korvaa?"
+msgstr "Tiedosto on jo olemassa, käytetään uudelleen"
#: editor/script_create_dialog.cpp
msgid "Invalid extension"
@@ -7116,12 +6922,11 @@ msgstr "Virheellinen laajennus"
#: editor/script_create_dialog.cpp
msgid "Wrong extension chosen"
-msgstr ""
+msgstr "Valittu väärä tiedostopääte"
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Invalid Path"
-msgstr "Virheellinen polku."
+msgstr "Virheellinen polku"
#: editor/script_create_dialog.cpp
msgid "Invalid class name"
@@ -7129,62 +6934,55 @@ msgstr "Virheellinen luokan nimi"
#: editor/script_create_dialog.cpp
msgid "Invalid inherited parent name or path"
-msgstr ""
+msgstr "Virheellinen peritty isännän nimi tai polku"
#: editor/script_create_dialog.cpp
msgid "Script valid"
-msgstr ""
+msgstr "Skripti kelpaa"
#: editor/script_create_dialog.cpp
msgid "Allowed: a-z, A-Z, 0-9 and _"
-msgstr ""
+msgstr "Sallittu: a-z, A-Z, 0-9 ja _"
#: editor/script_create_dialog.cpp
msgid "Built-in script (into scene file)"
-msgstr ""
+msgstr "Sisäänrakennettu skripti (skenetiedostoon)"
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Create new script file"
-msgstr "Luo uusi skripti"
+msgstr "Luo uusi skriptitiedosto"
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Load existing script file"
-msgstr "Lataa olemassaoleva skripti"
+msgstr "Lataa olemassaoleva skriptitiedosto"
#: editor/script_create_dialog.cpp
msgid "Language"
msgstr "Kieli"
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Inherits"
-msgstr "Perii:"
+msgstr "Perii"
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Class Name"
-msgstr "Luokan nimi:"
+msgstr "Luokan nimi"
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Template"
-msgstr "Poista malli"
+msgstr "Malli"
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Built-in Script"
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
-#, fuzzy
msgid "Remote "
-msgstr "Poista"
+msgstr "Etäinen "
#: editor/script_editor_debugger.cpp
msgid "Bytes:"
@@ -7208,7 +7006,7 @@ msgstr "Funktio:"
#: editor/script_editor_debugger.cpp
msgid "Pick one or more items from the list to display the graph."
-msgstr ""
+msgstr "Valitse yksi tai useampi kohde listasta näyttääksesi graafin."
#: editor/script_editor_debugger.cpp modules/mono/editor/mono_bottom_panel.cpp
msgid "Errors"
@@ -7216,20 +7014,19 @@ msgstr "Virheet"
#: editor/script_editor_debugger.cpp
msgid "Child Process Connected"
-msgstr "Lapsiprosessi yhdistetty"
+msgstr "Aliprosessi yhdistetty"
#: editor/script_editor_debugger.cpp
-#, fuzzy
msgid "Copy Error"
-msgstr "Lataa virheet"
+msgstr "Kopiointivirhe"
#: editor/script_editor_debugger.cpp
msgid "Inspect Previous Instance"
-msgstr "Tarkastele edellistä instanssia"
+msgstr "Tarkastele edellistä ilmentymää"
#: editor/script_editor_debugger.cpp
msgid "Inspect Next Instance"
-msgstr "Tarkastele seuraavaa instanssia"
+msgstr "Tarkastele seuraavaa ilmentymää"
#: editor/script_editor_debugger.cpp
msgid "Stack Frames"
@@ -7245,15 +7042,15 @@ msgstr "Virheet:"
#: editor/script_editor_debugger.cpp
msgid "Stack Trace (if applicable):"
-msgstr ""
+msgstr "Metodipino (jos soveltuva):"
#: editor/script_editor_debugger.cpp
msgid "Profiler"
-msgstr ""
+msgstr "Profiloija"
#: editor/script_editor_debugger.cpp
msgid "Monitor"
-msgstr ""
+msgstr "Monitoroija"
#: editor/script_editor_debugger.cpp
msgid "Value"
@@ -7261,11 +7058,11 @@ msgstr "Arvo"
#: editor/script_editor_debugger.cpp
msgid "Monitors"
-msgstr ""
+msgstr "Monitoroijat"
#: editor/script_editor_debugger.cpp
msgid "List of Video Memory Usage by Resource:"
-msgstr ""
+msgstr "Lista näyttömuistin käytöstä resurssikohtaisesti:"
#: editor/script_editor_debugger.cpp
msgid "Total:"
@@ -7273,11 +7070,11 @@ msgstr "Yhteensä:"
#: editor/script_editor_debugger.cpp
msgid "Video Mem"
-msgstr ""
+msgstr "Näyttömuisti"
#: editor/script_editor_debugger.cpp
msgid "Resource Path"
-msgstr ""
+msgstr "Resurssipolku"
#: editor/script_editor_debugger.cpp
msgid "Type"
@@ -7293,23 +7090,23 @@ msgstr "Käyttö"
#: editor/script_editor_debugger.cpp
msgid "Misc"
-msgstr ""
+msgstr "Sekalaista"
#: editor/script_editor_debugger.cpp
msgid "Clicked Control:"
-msgstr ""
+msgstr "Napsautettu kontrolli:"
#: editor/script_editor_debugger.cpp
msgid "Clicked Control Type:"
-msgstr ""
+msgstr "Napsautetun kontrollin tyyppi:"
#: editor/script_editor_debugger.cpp
msgid "Live Edit Root:"
-msgstr ""
+msgstr "Juuren suora muokkaus:"
#: editor/script_editor_debugger.cpp
msgid "Set From Tree"
-msgstr ""
+msgstr "Aseta puusta"
#: editor/settings_config_dialog.cpp
msgid "Shortcuts"
@@ -7317,7 +7114,7 @@ msgstr "Pikakuvakkeet"
#: editor/settings_config_dialog.cpp
msgid "Binding"
-msgstr ""
+msgstr "Sidonta"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Light Radius"
@@ -7325,7 +7122,7 @@ msgstr "Muuta valon sädettä"
#: editor/spatial_editor_gizmos.cpp
msgid "Change AudioStreamPlayer3D Emission Angle"
-msgstr ""
+msgstr "Muuta AudioStreamPlayer3D solmun suuntausta"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Camera FOV"
@@ -7337,19 +7134,19 @@ msgstr "Muuta kameran kokoa"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Sphere Shape Radius"
-msgstr "Muuta pallon sädettä"
+msgstr "Muuta pallomuodon sädettä"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Box Shape Extents"
-msgstr ""
+msgstr "Muuta laatikkomuodon ulottuvuuksia"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Capsule Shape Radius"
-msgstr ""
+msgstr "Muuta kapselimuodon sädettä"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Capsule Shape Height"
-msgstr ""
+msgstr "Muuta kapselimuodon korkeutta"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Ray Shape Length"
@@ -7357,32 +7154,31 @@ msgstr "Vaihda säteen muodon pituutta"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Notifier Extents"
-msgstr ""
+msgstr "Muuta ilmoittajan kattavuutta"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Particles AABB"
-msgstr ""
+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"
-msgstr ""
+msgstr "Valitse dynaaminen kirjasto tälle kohteelle"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Select dependencies of the library for this entry"
-msgstr ""
+msgstr "Valitse kirjaston riippuvuudet tälle kohteelle"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
-#, fuzzy
msgid "Remove current entry"
-msgstr "Poista käyrän piste"
+msgstr "Poista nykyinen kohde"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Double click to create a new entry"
-msgstr ""
+msgstr "Kaksoisnapsauta luodaksesi uuden kohteen"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Platform:"
@@ -7393,28 +7189,24 @@ msgid "Platform"
msgstr "Alusta"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
-#, fuzzy
msgid "Dynamic Library"
-msgstr "Vie kirjasto"
+msgstr "Dynaaminen kirjasto"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Add an architecture entry"
-msgstr ""
+msgstr "Lisää arkkitehtuurikohde"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
-#, fuzzy
msgid "GDNativeLibrary"
-msgstr "Vie kirjasto"
+msgstr "GDNativeLibrary"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
-#, fuzzy
msgid "Library"
-msgstr "Vie kirjasto"
+msgstr "Kirjasto"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
-#, fuzzy
msgid "Status"
-msgstr "Tila:"
+msgstr "Tila"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
msgid "Libraries: "
@@ -7422,113 +7214,110 @@ msgstr "Kirjastot: "
#: modules/gdnative/register_types.cpp
msgid "GDNative"
-msgstr ""
+msgstr "GDNative"
#: modules/gdscript/gdscript_functions.cpp
#: modules/visual_script/visual_script_builtin_funcs.cpp
msgid "Invalid type argument to convert(), use TYPE_* constants."
msgstr ""
+"Virheellinen tyyppiargumentti convert() metodille, käytä TYPE_* vakioita."
#: modules/gdscript/gdscript_functions.cpp modules/mono/glue/glue_header.h
#: modules/visual_script/visual_script_builtin_funcs.cpp
msgid "Not enough bytes for decoding bytes, or invalid format."
-msgstr ""
+msgstr "Ei tarpeeksi tavuja tavujen purkamiseksi tai virheellinen formaatti."
#: modules/gdscript/gdscript_functions.cpp
msgid "step argument is zero!"
-msgstr ""
+msgstr "askeleen argumentti on nolla!"
#: modules/gdscript/gdscript_functions.cpp
msgid "Not a script with an instance"
-msgstr ""
+msgstr "Ei ole skripti, jolla on ilmentymä"
#: modules/gdscript/gdscript_functions.cpp
msgid "Not based on a script"
-msgstr ""
+msgstr "Ei pohjaudu skriptiin"
#: modules/gdscript/gdscript_functions.cpp
msgid "Not based on a resource file"
-msgstr ""
+msgstr "Ei pohjaudu resurssitiedostoon"
#: modules/gdscript/gdscript_functions.cpp
msgid "Invalid instance dictionary format (missing @path)"
-msgstr ""
+msgstr "Virheellinen ilmentymän sanakirjaformaatti (puuttuu @path)"
#: modules/gdscript/gdscript_functions.cpp
msgid "Invalid instance dictionary format (can't load script at @path)"
msgstr ""
+"Virheellinen ilmentymän sanakirjaformaatti (ei voida ladata skriptiä polusta "
+"@path)"
#: modules/gdscript/gdscript_functions.cpp
msgid "Invalid instance dictionary format (invalid script at @path)"
msgstr ""
+"Virheellinen ilmentymän sanakirjaformaatti (virheellinen skripti kohdassa "
+"@path)"
#: modules/gdscript/gdscript_functions.cpp
msgid "Invalid instance dictionary (invalid subclasses)"
-msgstr ""
+msgstr "Virheellinen ilmentymän sanakirja (virheelliset aliluokat)"
#: modules/gdscript/gdscript_functions.cpp
msgid "Object can't provide a length."
-msgstr ""
+msgstr "Objektille ei voida määrittää pituutta."
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Next Plane"
-msgstr "Seuraava välilehti"
+msgstr "Seuraava taso"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Previous Plane"
-msgstr "Edellinen välilehti"
+msgstr "Edellinen taso"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Plane:"
-msgstr ""
+msgstr "Taso:"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Next Floor"
-msgstr ""
+msgstr "Seuraava kerros"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Previous Floor"
-msgstr "Edellinen välilehti"
+msgstr "Edellinen kerros"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Floor:"
-msgstr ""
+msgstr "Kerros:"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "GridMap Delete Selection"
-msgstr "Poista valitut"
+msgstr "Poista valinta"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "GridMap Duplicate Selection"
-msgstr "Monista valinta"
+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
-#, fuzzy
msgid "Clip Disabled"
-msgstr "Poistettu käytöstä"
+msgstr "Leikkaus pois käytöstä"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Clip Above"
-msgstr ""
+msgstr "Leikkaa yläpuolelta"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Clip Below"
-msgstr ""
+msgstr "Leikkaa alapuolelta"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Edit X Axis"
@@ -7543,166 +7332,156 @@ 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
-#, fuzzy
msgid "Create Area"
-msgstr "Luo uusi"
+msgstr "Luo alue"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Create Exterior Connector"
-msgstr "Luo uusi projekti"
+msgstr "Luo ulkoliitin"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Erase Area"
-msgstr ""
+msgstr "Tyhjennä alue"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Clear Selection"
-msgstr "Valinta keskikohtaan"
+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
-#, fuzzy
msgid "Pick Distance:"
-msgstr "Poimi tile"
+msgstr "Poimintaetäisyys:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "Luokan nimi ei voi olla varattu avainsana"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
-msgstr ""
+msgstr "Luodaan ratkaisua..."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating C# project..."
msgstr "Luodaan C# projekti..."
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Failed to create solution."
-msgstr "Ääriviivoja ei voitu luoda!"
+msgstr "Ratkaisun luonti epäonnistui."
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Failed to save solution."
-msgstr "Resurssin lataaminen epäonnistui."
+msgstr "Ratkaisun tallennus epäonnistui."
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Done"
-msgstr "Valmis!"
+msgstr "Valmis"
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Failed to create C# project."
-msgstr "Resurssin lataaminen epäonnistui."
+msgstr "C# projektin luonti epäonnistui."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Mono"
-msgstr ""
+msgstr "Mono"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "About C# support"
-msgstr ""
+msgstr "Lisätietoja C# tuesta"
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Create C# solution"
-msgstr "Luo ääriviivat"
+msgstr "Luo C# ratkaisu"
#: modules/mono/editor/mono_bottom_panel.cpp
msgid "Builds"
-msgstr ""
+msgstr "Käännökset"
#: modules/mono/editor/mono_bottom_panel.cpp
-#, fuzzy
msgid "Build Project"
-msgstr "Uusi projekti"
+msgstr "Käännä projekti"
#: modules/mono/editor/mono_bottom_panel.cpp
-#, fuzzy
msgid "Warnings"
-msgstr "Varoitus"
+msgstr "Varoitukset"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "Sisemmän poikkeuksen kutsupinon loppu"
#: modules/visual_script/visual_script.cpp
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 ""
"Return value must be assigned to first element of node working memory! Fix "
"your node please."
msgstr ""
+"Paluuarvo täytyy sijoittaa työmuistin ensimmäiselle elementille! Ole hyvä ja "
+"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: "
-msgstr ""
+msgstr "Pinon ylivuoto pinosyvyydellä: "
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Signal Arguments"
-msgstr ""
+msgstr "Muuta signaalin argumentit"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Change Argument Type"
-msgstr "Vaihda taulukon arvon tyyppiä"
+msgstr "Vaihda argumentin tyyppiä"
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Argument name"
@@ -7710,12 +7489,11 @@ msgstr "Vaihda argumentin nimi"
#: modules/visual_script/visual_script_editor.cpp
msgid "Set Variable Default Value"
-msgstr ""
+msgstr "Aseta muuttujan oletusarvo"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Set Variable Type"
-msgstr "Muokkaa muuttujaa:"
+msgstr "Aseta muuttujan tyyppi"
#: modules/visual_script/visual_script_editor.cpp
msgid "Functions:"
@@ -7763,76 +7541,76 @@ 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
-#, fuzzy
msgid "Remove VisualScript Nodes"
-msgstr "Poista virheelliset avaimet"
+msgstr "Poista VisualScript solmut"
#: modules/visual_script/visual_script_editor.cpp
msgid "Duplicate VisualScript Nodes"
-msgstr ""
+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 ""
+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"
-msgstr ""
+msgstr "Lisää palauttajaominaisuus"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Setter Property"
-msgstr ""
+msgstr "Lisää asettajaominaisuus"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Change Base Type"
-msgstr "Muuta tyyppiä"
+msgstr "Muuta kantatyyppiä"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Move Node(s)"
-msgstr "Poista Node(t)"
+msgstr "Siirrä solmu(t)"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Remove VisualScript Node"
-msgstr "Poista muuttuja"
+msgstr "Poista VisualScript solmu"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Connect Nodes"
-msgstr "Yhdistä Nodeen:"
+msgstr "Kytke solmut"
#: modules/visual_script/visual_script_editor.cpp
msgid "Condition"
@@ -7840,19 +7618,19 @@ msgstr "Ehtolause"
#: modules/visual_script/visual_script_editor.cpp
msgid "Sequence"
-msgstr ""
+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"
@@ -7864,48 +7642,43 @@ 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'"
-msgstr ""
+msgstr "Skriptillä on jo funktio '%s'"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Change Input Value"
-msgstr "Vaihda syötteen nimi"
+msgstr "Vaihda syötteen arvo"
#: modules/visual_script/visual_script_editor.cpp
msgid "Can't copy the function node."
-msgstr ""
+msgstr "Ei voida kopioida funktiosolmua."
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Clipboard is empty!"
-msgstr "Resurssien leikepöytä on tyhjä!"
+msgstr "Leikepöytä on tyhjä!"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Paste VisualScript Nodes"
-msgstr "Liitä Nodet"
+msgstr "Liitä VisualScript solmut"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove Function"
msgstr "Poista funktio"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Edit Variable"
-msgstr "Muokkaa muuttujaa:"
+msgstr "Muokkaa muuttujaa"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove Variable"
msgstr "Poista muuttuja"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Edit Signal"
-msgstr "Muokataan signaalia:"
+msgstr "Muokkaa signaalia"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove Signal"
@@ -7921,19 +7694,19 @@ msgstr "Muokataan signaalia:"
#: modules/visual_script/visual_script_editor.cpp
msgid "Base Type:"
-msgstr ""
+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"
-msgstr ""
+msgstr "Valitse tai luo funktio graafin muokkaamiseksi"
#: modules/visual_script/visual_script_editor.cpp
msgid "Edit Signal Arguments:"
-msgstr ""
+msgstr "Muokkaa signaalin argumentteja:"
#: modules/visual_script/visual_script_editor.cpp
msgid "Edit Variable:"
@@ -7945,51 +7718,51 @@ 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: "
-msgstr ""
+msgstr "Syötetyyppi ei ole iteroitavissa: "
#: modules/visual_script/visual_script_flow_control.cpp
msgid "Iterator became invalid"
-msgstr ""
+msgstr "Iteraattori muuttui epäkelvoksi"
#: modules/visual_script/visual_script_flow_control.cpp
msgid "Iterator became invalid: "
-msgstr ""
+msgstr "Iteraattori muuttui epäkelvoksi: "
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Invalid index property name."
-msgstr ""
+msgstr "Virheellinen osoitinominaisuuden nimi."
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Base object is not a Node!"
-msgstr ""
+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 ""
+msgstr "Virheellinen osoitinominaisuuden nimi '%s' solmussa %s."
#: modules/visual_script/visual_script_nodes.cpp
msgid ": Invalid argument of type: "
-msgstr ""
+msgstr ": Virheellinen argumentti tyyppiä: "
#: modules/visual_script/visual_script_nodes.cpp
msgid ": Invalid arguments: "
@@ -7997,21 +7770,24 @@ msgstr ": Virheelliset argumentit: "
#: modules/visual_script/visual_script_nodes.cpp
msgid "VariableGet not found in script: "
-msgstr ""
+msgstr "VariableGet ei löytynyt skriptistä: "
#: modules/visual_script/visual_script_nodes.cpp
msgid "VariableSet not found in script: "
-msgstr ""
+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 solmulla ei ole _step() metodia, graafia ei voida käsitellä."
#: modules/visual_script/visual_script_nodes.cpp
msgid ""
"Invalid return value from _step(), must be integer (seq out), or string "
"(error)."
msgstr ""
+"Virheellinen paluuarvo _step() metodilta, täytyy olla kokonaisluku (seq out) "
+"tai merkkijono (virhe)."
#: platform/javascript/export/export.cpp
msgid "Run in Browser"
@@ -8022,46 +7798,44 @@ msgid "Run exported HTML in the system's default browser."
msgstr "Suorita viety HTML järjestelmän oletusselaimessa."
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Could not write file:"
-msgstr "Ei voitu kirjoittaa tiedostoa:\n"
+msgstr "Ei voitu kirjoittaa tiedostoa:"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Could not open template for export:"
-msgstr "Kansiota ei voitu luoda."
+msgstr "Mallin avaus vientiin epäonnistui:"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Invalid export template:"
-msgstr "Hallitse vietäviä Templateja"
+msgstr "Virheellinen vientimalli:"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Could not read custom HTML shell:"
-msgstr "Ei voitu lukea tiedostoa:\n"
+msgstr "Ei voitu lukea mukautettua HTML tulkkia:"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Could not read boot splash image file:"
-msgstr "Ei voitu lukea tiedostoa:\n"
+msgstr "Ei voitu lukea käynnistyskuvan tiedostoa:"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Using default boot splash image."
-msgstr "Ei voitu lukea tiedostoa:\n"
+msgstr "Käytetään oletuskäynnistyskuvaa."
#: scene/2d/animated_sprite.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
"order for AnimatedSprite to display frames."
msgstr ""
+"SpriteFrames resurssi on luotava tai asetettava 'Frames' ominaisuudelle, "
+"jotta AnimatedSprite voi näyttää ruutuja."
#: scene/2d/canvas_modulate.cpp
msgid ""
"Only one visible CanvasModulate is allowed per scene (or set of instanced "
"scenes). The first created one will work, while the rest will be ignored."
msgstr ""
+"Vain yksi CanvasModulate on sallittu per skene (tai per skeneilmentymien "
+"joukko). Ensimmäisenä luotu toimii ja loput jätetään huomioimatta."
#: scene/2d/collision_object_2d.cpp
msgid ""
@@ -8069,6 +7843,10 @@ msgid ""
"Consider adding CollisionShape2D or CollisionPolygon2D children nodes to "
"define its shape."
msgstr ""
+"Tämän solmun alaisuudessa ei ole muotoja, joten se ei voi olla "
+"vuorovaikutuksessa avaruuden kanssa.\n"
+"Harkitse CollisionShape2D tai CollisionPolygon2D solmun lisäämistä "
+"alisolmuksi muodon määrittämiseksi."
#: scene/2d/collision_polygon_2d.cpp
msgid ""
@@ -8076,10 +7854,13 @@ msgid ""
"CollisionObject2D derived node. Please only use it as a child of Area2D, "
"StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."
msgstr ""
+"CollisionPolygon2D 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_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 ""
@@ -8087,56 +7868,73 @@ 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 solmusta "
+"perityille solmuille. Käytä sitä ainoastaan Area2D, StaticBody2D, "
+"RigidBody2D, KinematicBody2D, jne. alla antaaksesi niille muodon."
#: scene/2d/collision_shape_2d.cpp
msgid ""
"A shape must be provided for CollisionShape2D to function. Please create a "
"shape resource for it!"
msgstr ""
+"CollisionShape2D solmulla täytyy olla muoto, jotta se toimisi. Ole hyvä ja "
+"luo sille muotoresurssi!"
#: scene/2d/light_2d.cpp
msgid ""
"A texture with the shape of the light must be supplied to the 'texture' "
"property."
msgstr ""
+"Tekstuuri, jolta löytyy valon muoto, täytyy antaa 'texture' ominaisuudella."
#: scene/2d/light_occluder_2d.cpp
msgid ""
"An occluder polygon must be set (or drawn) for this occluder to take effect."
msgstr ""
+"Toimimiseksi tälle peittäjälle on asetettava (tai piirrettävä) peittävä "
+"monikulmio."
#: scene/2d/light_occluder_2d.cpp
msgid "The occluder polygon for this occluder is empty. Please draw a polygon!"
msgstr ""
+"Peittävä monikulmio tälle peittäjälle on tyhjä. Ole hyvä ja piirrä "
+"monikulmio!"
#: scene/2d/navigation_polygon.cpp
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 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 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 solmu toimii ainoastaan, jos se on ParallaxBackground solmun "
+"alla."
#: scene/2d/particles_2d.cpp scene/3d/particles.cpp
msgid ""
"A material to process the particles is not assigned, so no behavior is "
"imprinted."
msgstr ""
+"Materiaalia partikkeleiden käsittelemiseksi ei ole määritetty, joten mitään "
+"ei tapahdu."
#: 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 ""
@@ -8144,68 +7942,78 @@ 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 ""
"VisibilityEnable2D works best when used with the edited scene root directly "
"as parent."
msgstr ""
+"VisibilityEnable2D toimii parhaiten, kun sitä käytetään suoraan muokatun "
+"skenen juuren isäntänä."
#: scene/3d/arvr_nodes.cpp
msgid "ARVRCamera must have an ARVROrigin node as its parent"
-msgstr ""
+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 ""
+msgstr "ARVRController solmun isännän täytyy olla ARVROrigin solmu"
#: scene/3d/arvr_nodes.cpp
msgid ""
"The controller id must not be 0 or this controller will not be bound to an "
"actual controller"
msgstr ""
+"Ohjaimen tunnus ei saa olla 0, tai tämä ohjain ei ole sidottu oikeaan "
+"ohjaimeen"
#: scene/3d/arvr_nodes.cpp
msgid "ARVRAnchor must have an ARVROrigin node as its parent"
-msgstr ""
+msgstr "ARVRAnchor solmun isännän täytyy olla ARVROrigin solmu"
#: scene/3d/arvr_nodes.cpp
msgid ""
"The anchor id must not be 0 or this anchor will not be bound to an actual "
"anchor"
msgstr ""
+"Ankkurin tunnus ei saa olla 0, tai tämä ankkuri ei ole sidottu oikeaan "
+"ankkuriin"
#: scene/3d/arvr_nodes.cpp
msgid "ARVROrigin requires an ARVRCamera child node"
-msgstr ""
+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)"
-msgstr ""
+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"
-msgstr ""
+msgstr "Viimeistellään piirto"
#: scene/3d/baked_lightmap.cpp
msgid "Lighting Meshes: "
-msgstr ""
+msgstr "Valaistaan meshejä: "
#: scene/3d/collision_object.cpp
msgid ""
@@ -8213,6 +8021,10 @@ msgid ""
"Consider adding CollisionShape or CollisionPolygon children nodes to define "
"its shape."
msgstr ""
+"Tällä solmulla ei ole alimuotoja, joten se ei voi olla vuorovaikutuksessa "
+"avaruuden kanssa.\n"
+"Harkitse CollisionShape tai CollisionPolygon solmun lisäämistä sen "
+"alisolmuksi määritelläksesi sen muodon."
#: scene/3d/collision_polygon.cpp
msgid ""
@@ -8220,10 +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 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 ""
@@ -8231,31 +8046,42 @@ msgid ""
"derived node. Please only use it as a child of Area, StaticBody, RigidBody, "
"KinematicBody, etc. to give them a shape."
msgstr ""
+"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 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 solmulle täytyy asettaa tai luoda NavigationMesh resurssi, jotta se "
+"toimisi."
#: scene/3d/navigation_mesh.cpp
msgid ""
"NavigationMeshInstance must be a child or grandchild to a Navigation node. "
"It only provides navigation data."
msgstr ""
+"NavigationMeshInstance solmun täytyy olla Navigation solmun alaisuudessa. Se "
+"tarjoaa vain navigointidataa."
#: scene/3d/particles.cpp
msgid ""
"Nothing is visible because meshes have not been assigned to draw passes."
msgstr ""
+"Mitään ei näy, koska mesheille ei ole asetettu piirtopyyhkäisyjä (draw "
+"passes)."
#: scene/3d/physics_body.cpp
msgid ""
@@ -8263,42 +8089,53 @@ 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 ""
+msgstr "Polkuominaisuuden täytyy osoittaa Spatial solmuun toimiakseen."
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "WorldEnvironment tarvitsee Environment resurssin."
#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+"Vain yksi WorldEnvironment on sallittu per skene (tai per skeneilmentymien "
+"joukko)."
#: scene/3d/scenario_fx.cpp
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 ""
+"Tämä WorldEnvironment jätetään huomioimatta. Lisää joko Camera (3D-"
+"skeneille) tai aseta tälle ympäristölle Background Mode asetukseksi Canvas "
+"(2D-skeneille)."
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
"order for AnimatedSprite3D to display frames."
msgstr ""
+"AnimatedSprite3D solmulle täytyy luoda tai asettaa 'Frames' ominaisuudeksi "
+"SpriteFrames resurssi ruutujen näyttämiseksi."
#: scene/3d/vehicle_body.cpp
msgid ""
"VehicleWheel serves to provide a wheel system to a VehicleBody. Please use "
"it as a child of a VehicleBody."
msgstr ""
+"VehicleWheel solmu tarjoaa rengasjärjestelmän VehicleBody solmulle. Ole hyvä "
+"ja käytä sitä VehicleBody solmun alla."
#: scene/gui/color_picker.cpp
-#, fuzzy
msgid "Raw Mode"
-msgstr "Kääntötila"
+msgstr "Raakatila"
#: scene/gui/color_picker.cpp
msgid "Add current color as a preset"
@@ -8313,9 +8150,8 @@ msgid "Please Confirm..."
msgstr "Ole hyvä ja vahvista..."
#: scene/gui/file_dialog.cpp
-#, fuzzy
msgid "Select this Folder"
-msgstr "Valitse metodi"
+msgstr "Valitse tämä kansio"
#: scene/gui/popup.cpp
msgid ""
@@ -8342,13 +8178,12 @@ msgid "(Other)"
msgstr "(Muu)"
#: scene/main/scene_tree.cpp
-#, fuzzy
msgid ""
"Default Environment as specified in Project Settings (Rendering -> "
"Environment -> Default Environment) could not be loaded."
msgstr ""
-"Projektin asetuksissa määriteltyä oletusympäristöä (Renderöinti -> Näkymä -"
-"> Oletusympäristö) ei voitu ladata."
+"Projektin asetuksissa määriteltyä oletusympäristöä (Rendering -> "
+"Environment -> Default Environment) ei voitu ladata."
#: scene/main/viewport.cpp
msgid ""
@@ -8357,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."
@@ -8378,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 ':')."
@@ -8408,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!"
@@ -8564,7 +8403,7 @@ msgstr "Virheellinen fonttikoko."
#~ msgid "Info"
#~ msgstr "Tietoja"
-#~ msgid "Re-Import.."
+#~ msgid "Re-Import..."
#~ msgstr "Tuo uudelleen..."
#~ msgid "Target path is empty."
@@ -8816,13 +8655,13 @@ msgstr "Virheellinen fonttikoko."
#~ msgid "Zoom (%):"
#~ msgstr "Lähennä (%):"
-#~ msgid "Skeleton.."
+#~ msgid "Skeleton..."
#~ msgstr "Luuranko..."
#~ msgid "Zoom Reset"
#~ msgstr "Palauta lähennys"
-#~ msgid "Zoom Set.."
+#~ msgid "Zoom Set..."
#~ msgstr "Aseta Zoomaus..."
#~ msgid "Set a Value"
diff --git a/editor/translations/fr.po b/editor/translations/fr.po
index 56969fe974..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.
@@ -13,18 +12,23 @@
# finkiki <specialpopol@gmx.fr>, 2016.
# Gilles Roudiere <gilles.roudiere@gmail.com>, 2017-2018.
# Hugo Locurcio <hugo.l@openmailbox.org>, 2016-2018.
+# Javier Ocampos <xavier.ocampos@gmail.com>, 2018.
# John Bernier <john.bp@unknit.net>, 2018.
-# Kanabenki <lucien.menassol@gmail.com>, 2017.
+# Kanabenki <lucien.menassol@gmail.com>, 2017, 2018.
# keltwookie <keltwookie@protonmail.com>, 2017-2018.
# 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.
# Nicolas Lehuen <nicolas@lehuen.com>, 2016.
# Nobelix <noe.le.cam@laposte.net>, 2017.
-# Omicron <tritonic.dev@gmail.com>, 2016, 2018.
+# 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.
@@ -36,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-05-02 21:48+0000\n"
-"Last-Translator: Omicron <omicron666.dev@gmail.com>\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"
@@ -50,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-dev\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -532,7 +535,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Déconnecter « %s » de « %s »"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "Connecter…"
#: editor/connections_dialog.cpp
@@ -858,19 +861,19 @@ msgstr "Ajouter effet"
#: editor/editor_audio_buses.cpp
msgid "Rename Audio Bus"
-msgstr "Renommer bus audio"
+msgstr "Renommer le bus audio"
#: editor/editor_audio_buses.cpp
msgid "Change Audio Bus Volume"
-msgstr "Modifier le volume audio du bus"
+msgstr "Modifier le volume du bus audio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Solo"
-msgstr "Activer/désactiver le mode solo pour le bus audio"
+msgstr "Activer/désactiver le mode solo du bus audio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Mute"
-msgstr "Activer/désactiver le mode muet pour le bus audio"
+msgstr "Activer/désactiver le mode muet du bus audio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Bypass Effects"
@@ -878,11 +881,11 @@ msgstr "Activer/désactiver le contournement du bus audio"
#: editor/editor_audio_buses.cpp
msgid "Select Audio Bus Send"
-msgstr "Sélectionner l'envoi de tranport audio"
+msgstr "Sélectionner l'envoi du bus audio"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus Effect"
-msgstr "Ajouter effet de tranport audio"
+msgstr "Ajouter un effet bus audio"
#: editor/editor_audio_buses.cpp
msgid "Move Bus Effect"
@@ -894,7 +897,7 @@ msgstr "Supprimer l'effet de transport"
#: editor/editor_audio_buses.cpp
msgid "Audio Bus, Drag and Drop to rearrange."
-msgstr "Transport audio, glisser-déposer pour réorganiser."
+msgstr "Bus audio, glisser-déposer pour réorganiser."
#: editor/editor_audio_buses.cpp
msgid "Solo"
@@ -931,19 +934,19 @@ msgstr "Audio"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus"
-msgstr "Ajouter un transport audio"
+msgstr "Ajouter un bus audio"
#: editor/editor_audio_buses.cpp
msgid "Master bus can't be deleted!"
-msgstr "Le transport maître ne peut pas être supprimé !"
+msgstr "Le bus maître ne peut pas être supprimé !"
#: editor/editor_audio_buses.cpp
msgid "Delete Audio Bus"
-msgstr "Supprimer le transport audio"
+msgstr "Supprimer le bus audio"
#: editor/editor_audio_buses.cpp
msgid "Duplicate Audio Bus"
-msgstr "Dupliquer le transport audio"
+msgstr "Dupliquer le bus audio"
#: editor/editor_audio_buses.cpp
msgid "Reset Bus Volume"
@@ -951,19 +954,19 @@ msgstr "Réinitialiser le volume de bus"
#: editor/editor_audio_buses.cpp
msgid "Move Audio Bus"
-msgstr "Déplacer le transport audio"
+msgstr "Déplacer le bus audio"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Enregistrer l'agencement du transport audio sous.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Enregistrer la disposition des bus audio sous…"
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Emplacement du nouvel agencement.."
+msgid "Location for New Layout..."
+msgstr "Emplacement du nouvel agencement..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
-msgstr "Ouvrir agencement de transport audio"
+msgstr "Ouvrir une disposition de bus audio"
#: editor/editor_audio_buses.cpp
msgid "There is no 'res://default_bus_layout.tres' file."
@@ -971,11 +974,11 @@ msgstr "Il n'existe aucun 'res://default_bus_layout.tres'."
#: editor/editor_audio_buses.cpp
msgid "Invalid file, not an audio bus layout."
-msgstr "Fichier invalide, pas un agencement de transport audio."
+msgstr "Fichier invalide, pas une disposition de bus audio."
#: editor/editor_audio_buses.cpp
msgid "Add Bus"
-msgstr "Ajouter un transport"
+msgstr "Ajouter un bus"
#: editor/editor_audio_buses.cpp
msgid "Create a new Bus Layout."
@@ -1100,11 +1103,11 @@ msgid "Updating Scene"
msgstr "Mise à jour de la scène"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr "Stockage des modifications locales…"
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr "Mise à jour de la scène…"
#: editor/editor_data.cpp
@@ -1173,8 +1176,8 @@ msgid "Show In File Manager"
msgstr "Montrer dans le gestionnaire de fichiers"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Nouveau dossier.."
+msgid "New Folder..."
+msgstr "Nouveau dossier..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1435,19 +1438,19 @@ msgstr "Effacer la sortie"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "L'export du projet a échoué avec le code erreur %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Erreur d'enregistrement de la ressource !"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "Enregistrer la ressource sous…"
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr "Je vois…"
#: editor/editor_node.cpp
@@ -1520,11 +1523,11 @@ msgstr "Erreur d'enregistrement de la MeshLibrary !"
#: editor/editor_node.cpp
msgid "Can't load TileSet for merging!"
-msgstr "Impossible de charger la TileSet pour fusion !"
+msgstr "Impossible de charger le TileSet pour fusion !"
#: editor/editor_node.cpp
msgid "Error saving TileSet!"
-msgstr "Erreur d'enregistrement de la TileSet !"
+msgstr "Erreur d'enregistrement du TileSet !"
#: editor/editor_node.cpp
msgid "Error trying to save layout!"
@@ -1682,11 +1685,11 @@ msgid "Open Base Scene"
msgstr "Ouvrir scène de base"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr "Ouvrir une scène rapidement…"
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr "Ouvrir un script rapidement…"
#: editor/editor_node.cpp
@@ -1698,7 +1701,7 @@ msgid "Save changes to '%s' before closing?"
msgstr "Sauvegarder modifications de '%s' avant de quitter ?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr "Enregistrer la scène sous…"
#: editor/editor_node.cpp
@@ -1724,11 +1727,11 @@ 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"
-msgstr "Exporter un ensemble de tuiles"
+msgstr "Exporter le TileSet"
#: editor/editor_node.cpp
msgid "This operation can't be done without a selected node."
@@ -1751,7 +1754,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Cette action ne peut être annulée. Réinitialiser quand même ?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr "Lancer une scène rapidement…"
#: editor/editor_node.cpp
@@ -1918,8 +1921,8 @@ msgid "Previous tab"
msgstr "Onglet precedent"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Filtrer Fichiers.."
+msgid "Filter Files..."
+msgstr "Filtrer Fichiers..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1930,11 +1933,11 @@ msgid "New Scene"
msgstr "Nouvelle scène"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr "Nouvelle scène héritée…"
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr "Ouvrir une scène…"
#: editor/editor_node.cpp
@@ -1954,15 +1957,15 @@ msgid "Open Recent"
msgstr "Fichiers récents"
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr "Convertir vers…"
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr "MeshLibrary…"
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr "TileSet…"
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2025,7 +2028,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Small Deploy with Network FS"
-msgstr "Petit déploiement avec le réseau"
+msgstr "Déploiement minime avec système de fichier réseau"
#: editor/editor_node.cpp
msgid ""
@@ -2228,7 +2231,7 @@ msgid "Save the currently edited resource."
msgstr "Enregistrer la ressource actuellement modifiée."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr "Enregistrer sous…"
#: editor/editor_node.cpp
@@ -2337,7 +2340,7 @@ msgid "Creating Mesh Previews"
msgstr "Création des prévisualisations des maillages"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr "Aperçu…"
#: editor/editor_plugin_settings.cpp
@@ -2490,8 +2493,8 @@ msgid "(Current)"
msgstr "(Actuel)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "Récupération des miroirs, veuillez patienter.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "Récupération des miroirs, veuillez patienter..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2568,8 +2571,8 @@ msgid "Error requesting url: "
msgstr "Erreur lors de la requête de l’URL : "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "Connexion au miroir"
+msgid "Connecting to Mirror..."
+msgstr "Connexion au Miroir..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2585,8 +2588,8 @@ msgstr "Impossible à résoudre"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Connexion en cours.."
+msgid "Connecting..."
+msgstr "Connexion en cours..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2598,8 +2601,8 @@ msgstr "Connecté"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Envoi d'une requête.."
+msgid "Requesting..."
+msgstr "Envoi d'une requête..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2736,11 +2739,11 @@ msgid "Collapse all"
msgstr "Réduire tout"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Renommer.."
+msgid "Rename..."
+msgstr "Renommer..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr "Déplacer vers…"
#: editor/filesystem_dock.cpp
@@ -2752,15 +2755,15 @@ msgid "Instance"
msgstr "Instance"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr "Modifier les dépendances…"
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr "Voir les propriétaires…"
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Dupliquer…"
#: editor/filesystem_dock.cpp
@@ -2788,7 +2791,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Analyse des fichiers en cours,\n"
"Veuillez patienter..."
@@ -2856,7 +2859,7 @@ msgid "Import Scene"
msgstr "Importer une scène"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr "Importation de la scène…"
#: editor/import/resource_importer_scene.cpp
@@ -2865,10 +2868,10 @@ msgstr "Génération des lightmaps :"
#: editor/import/resource_importer_scene.cpp
msgid "Generating for Mesh: "
-msgstr "Généreration pour le Mesh : "
+msgstr "Génération pour le Mesh : "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr "Lancement du script personnalisé…"
#: editor/import/resource_importer_scene.cpp
@@ -2885,7 +2888,7 @@ msgid "Error running post-import script:"
msgstr "Erreur d'exécution du script de post-importation :"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr "Enregistrement…"
#: editor/import_dock.cpp
@@ -2905,7 +2908,7 @@ msgid "Import As:"
msgstr "Importer comme :"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr "Pré-réglage…"
#: editor/import_dock.cpp
@@ -3324,7 +3327,7 @@ msgid "Transition Node"
msgstr "NÅ“ud Transition"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr "Importer des animations…"
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3332,7 +3335,7 @@ msgid "Edit Node Filters"
msgstr "Modifier les filtres de nœud"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr "Filtres…"
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3400,8 +3403,8 @@ msgid "Fetching:"
msgstr "Récupération:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "Résolution.."
+msgid "Resolving..."
+msgstr "Résolution..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3467,7 +3470,7 @@ msgid "Site:"
msgstr "Site :"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr "Support…"
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3666,6 +3669,7 @@ msgid "Use Rotation Snap"
msgstr "Rotation alignée"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "Configurer le magnétisme…"
@@ -3759,17 +3763,15 @@ 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
-#, fuzzy
msgid "Show Origin"
msgstr "Afficher l'origine"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 vue"
+msgstr "Afficher la Viewport"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -4062,7 +4064,7 @@ msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "Le type de maillage primitif n'est pas PRIMITIVE_TRIANGLES !"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4093,7 +4095,7 @@ msgid "Create Convex Collision Sibling"
msgstr "Créer une collision convexe"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr "Créer un maillage de contour…"
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4263,7 +4265,7 @@ msgstr "Partitionnement..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Creating contours..."
-msgstr "Création des coutours..."
+msgstr "Création des contours..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Creating polymesh..."
@@ -4304,8 +4306,8 @@ msgid "Error loading image:"
msgstr "Erreur de chargement d'image :"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Pas de pixels avec transparence > 128 dans l'image.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Pas de pixels avec transparence > 128 dans l'image..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4556,7 +4558,7 @@ msgstr "Mettre à l'échelle le polygone"
#: editor/project_settings_editor.cpp editor/property_editor.cpp
#: modules/visual_script/visual_script_editor.cpp
msgid "Edit"
-msgstr "Modifier"
+msgstr "Édition"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Polygon->UV"
@@ -4665,7 +4667,7 @@ msgid "Import Theme"
msgstr "Importer un thème"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr "Enregistrer le thème sous…"
#: editor/plugins/script_editor_plugin.cpp
@@ -4758,11 +4760,11 @@ msgstr "Lancer"
#: editor/plugins/script_editor_plugin.cpp
msgid "Toggle Scripts Panel"
-msgstr "Afficher/Cacher la panneau des scripts"
+msgstr "Afficher/Cacher le panneau des scripts"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr "Trouver…"
#: editor/plugins/script_editor_plugin.cpp
@@ -4860,7 +4862,7 @@ msgstr "Prélever une couleur"
#: editor/plugins/script_text_editor.cpp
msgid "Convert Case"
-msgstr "Cas de conversion"
+msgstr "Modifier la casse"
#: editor/plugins/script_text_editor.cpp
msgid "Uppercase"
@@ -4969,18 +4971,18 @@ msgstr "Convertir en minuscule"
#: editor/plugins/script_text_editor.cpp
msgid "Find Previous"
-msgstr "trouver précédente"
+msgstr "Trouver le précédent"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr "Remplacer…"
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr "Aller à la fonction…"
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr "Aller à la ligne…"
#: editor/plugins/script_text_editor.cpp
@@ -5436,11 +5438,7 @@ msgid "Transform"
msgstr "Transformation"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Configurer la grille…"
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr "Dialogue de transformation…"
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5693,7 +5691,7 @@ msgid "Remove All"
msgstr "Supprimer tout"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr "Éditer le thème..."
#: editor/plugins/theme_editor_plugin.cpp
@@ -5741,14 +5739,12 @@ msgid "Checked Item"
msgstr "Item coché"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "Ajouter un item"
+msgstr "Item radio"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "Item coché"
+msgstr "Item radio coché"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5763,8 +5759,8 @@ msgid "Options"
msgstr "Options"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr "Ont,Plusieurs,Possibilités,D'options !"
+msgid "Has,Many,Options"
+msgstr "Possède,Plusieurs,Options"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5832,7 +5828,7 @@ msgstr "Supprimer la sélection"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Find tile"
-msgstr "Chercher une case"
+msgstr "Trouver une tuile"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Transpose"
@@ -5888,7 +5884,7 @@ msgstr "Fusionner depuis la scène ?"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Tile Set"
-msgstr "Ensemble de cases"
+msgstr "Jeu de tuiles"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -5904,31 +5900,31 @@ msgstr "Erreur"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Autotiles"
-msgstr "Coupe automatique"
+msgstr "Autotiles"
#: 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 ""
-"Sélectionne une ressource à utiliser comme icône, celle-ci sera aussi "
-"utilisée sur les liaisons autotile invalides."
+"Sélectionner une sous-tuile à utiliser comme icône, celle-ci sera aussi "
+"utilisée pour les liaisons de tuiles automatiques invalides."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid ""
"LMB: set bit on.\n"
"RMB: set bit off."
msgstr ""
-"Clic gauche : Activer\n"
-"Clic droit : Désactiver"
+"Clic-gauche : Activer\n"
+"Clic-droit : Désactiver"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Select current edited sub-tile."
-msgstr "Enregistrer la ressource en cours de modification."
+msgstr "Sélectionner la sous-tuile en cours d'édition."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Select sub-tile to change its priority."
-msgstr "Sélectionner une sous-case pour changer sa priorité."
+msgstr "Sélectionner une sous-tuile pour changer sa priorité."
#: editor/progress_dialog.cpp scene/gui/dialogs.cpp
msgid "Cancel"
@@ -5955,7 +5951,7 @@ msgid "Presets"
msgstr "Pré-réglages"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr "Ajouter…"
#: editor/project_export.cpp
@@ -6049,6 +6045,10 @@ msgid "Imported Project"
msgstr "Projet importé"
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "Nom du Projet Invalide."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "Impossible de créer le dossier."
@@ -6252,9 +6252,11 @@ msgstr "Bouton de souris"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
+"Nom d'action invalide. Il ne peux être vide ou contenir '/', ':', '=', '\\' "
+"ou '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6281,7 +6283,7 @@ msgid "Control+"
msgstr "Contrôle+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr "Appuyez sur une touche…"
#: editor/project_settings_editor.cpp
@@ -6414,7 +6416,7 @@ msgstr "Paramètres enregistrés avec succès."
#: editor/project_settings_editor.cpp
msgid "Override for Feature"
-msgstr "Remplacement de fonctionnalité"
+msgstr "Écrasement d'un paramètre, dédié à un tag de fonctionnalité"
#: editor/project_settings_editor.cpp
msgid "Add Translation"
@@ -6465,8 +6467,8 @@ msgid "Property:"
msgstr "Propriété :"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
-msgstr "Remplacement pour.."
+msgid "Override For..."
+msgstr "Écraser pour…"
#: editor/project_settings_editor.cpp
msgid "Input Map"
@@ -6561,11 +6563,11 @@ msgid "Easing Out-In"
msgstr "Ease out-in"
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr "Fichier…"
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr "Répertoire…"
#: editor/property_editor.cpp
@@ -6739,7 +6741,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Cette opération ne peut être réalisée sur des scènes instanciées."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr "Enregistrer la nouvelle scène sous…"
#: editor/scene_tree_dock.cpp
@@ -7170,11 +7172,11 @@ msgstr "Divers"
#: editor/script_editor_debugger.cpp
msgid "Clicked Control:"
-msgstr "Control cliqué :"
+msgstr "Contrôle cliqué :"
#: editor/script_editor_debugger.cpp
msgid "Clicked Control Type:"
-msgstr "Type de Control cliqué :"
+msgstr "Type de contrôle cliqué :"
#: editor/script_editor_debugger.cpp
msgid "Live Edit Root:"
@@ -7461,7 +7463,7 @@ msgstr "Choisissez distance :"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "Le nom de la classe ne peut pas être un mot clé réservé"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -7469,7 +7471,7 @@ msgstr "Génération de la solution en cours..."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating C# project..."
-msgstr "Création du projet C# ..."
+msgstr "Création du projet C#..."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Failed to create solution."
@@ -8178,7 +8180,7 @@ msgstr ""
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "L'environnement mondial a besoin d'une ressource environnementale."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8192,6 +8194,9 @@ 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 ""
+"Cet WorldEnvironment est ignoré. Ajoutez une caméra (pour les scènes 3D) ou "
+"définissez le mode Background Mode de cet environnement sur Canvas (pour les "
+"scènes 2D)."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8290,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 « : »)."
@@ -8319,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é !"
@@ -8469,7 +8478,7 @@ msgstr "Taille de police invalide."
#~ msgid "Exporting for %s"
#~ msgstr "Exportation pour %s"
-#~ msgid "Setting Up.."
+#~ msgid "Setting Up..."
#~ msgstr "Configuration…"
#~ msgid "Error loading scene."
@@ -8532,7 +8541,7 @@ msgstr "Taille de police invalide."
#~ msgid "Info"
#~ msgstr "Information"
-#~ msgid "Re-Import.."
+#~ msgid "Re-Import..."
#~ msgstr "Ré-importer…"
#~ msgid "No bit masks to import!"
@@ -8929,13 +8938,13 @@ msgstr "Taille de police invalide."
#~ msgid "Zoom (%):"
#~ msgstr "Zoom (%) :"
-#~ msgid "Skeleton.."
+#~ msgid "Skeleton..."
#~ msgstr "Squelette…"
#~ msgid "Zoom Reset"
#~ msgstr "Réinitialiser le zoom"
-#~ msgid "Zoom Set.."
+#~ msgid "Zoom Set..."
#~ msgstr "Définir le zoom…"
#~ msgid "Set a Value"
@@ -9413,7 +9422,7 @@ msgstr "Taille de police invalide."
#~ msgid "Export Project PCK"
#~ msgstr "Exporter le PCK du projet"
-#~ msgid "Export.."
+#~ msgid "Export..."
#~ msgstr "Exporter…"
#~ msgid "Project Export"
@@ -9508,7 +9517,7 @@ msgstr "Taille de police invalide."
#~ msgid "Method In Node:"
#~ msgstr "Méthode dans le nœud :"
-#~ msgid "Edit Connections.."
+#~ msgid "Edit Connections..."
#~ msgstr "Modifier les connexions..."
#~ msgid "Set Params"
@@ -9563,5 +9572,5 @@ msgstr "Taille de police invalide."
#~ "modifiez les options d'exportation par la suite. Vous pouvez également "
#~ "générer des atlas à l'exportation."
-#~ msgid "Merging.."
+#~ msgid "Merging..."
#~ msgstr "Fusion..."
diff --git a/editor/translations/he.po b/editor/translations/he.po
index ee0a66cec1..0f1881211f 100644
--- a/editor/translations/he.po
+++ b/editor/translations/he.po
@@ -498,7 +498,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr ""
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -908,11 +908,11 @@ msgid "Move Audio Bus"
msgstr "הזזת ×פיק שמע"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "שמירת פריסת ×פיקי השמע בתור…"
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr "×ž×™×§×•× ×œ×¤×¨×™×¡×” החדשה…"
#: editor/editor_audio_buses.cpp
@@ -1048,11 +1048,11 @@ msgid "Updating Scene"
msgstr "הסצנה מתעדכנת"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr "×”×©×™× ×•×™×™× ×”×ž×§×•×ž×™×™× ×ž×וחסני×…"
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr "הסצנה מתעדכנת…"
#: editor/editor_data.cpp
@@ -1121,7 +1121,7 @@ msgid "Show In File Manager"
msgstr "הצגה במנהל הקבצי×"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr "תיקייה חדשה…"
#: editor/editor_file_dialog.cpp
@@ -1383,12 +1383,12 @@ msgid "Error saving resource!"
msgstr "שגי××” בשמירת המש×ב!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "שמירת המש×ב בתור…"
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr "×× ×™ רו×ה…"
#: editor/editor_node.cpp
@@ -1597,11 +1597,11 @@ msgid "Open Base Scene"
msgstr "פתיחת סצנת בסיס"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr "פתיחת סצנה מהירה…"
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr "פתיחת סקריפט מהירה…"
#: editor/editor_node.cpp
@@ -1613,7 +1613,7 @@ msgid "Save changes to '%s' before closing?"
msgstr "לשמור ×ת ×”×©×™× ×•×™×™× ×œÖ¾â€š%s’ לפני הסגירה?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr "שמירת סצנה בש×…"
#: editor/editor_node.cpp
@@ -1665,7 +1665,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "×œ× × ×™×ª×Ÿ לבטל פעולה זו. לשחזר בכל ×–×ת?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1812,7 +1812,7 @@ msgid "Previous tab"
msgstr "הלשונית הקודמת"
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1824,11 +1824,11 @@ msgid "New Scene"
msgstr "סצנה חדשה"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr "סצנה חדשה בירושה…"
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr "פתיחת סצנה…"
#: editor/editor_node.cpp
@@ -1848,15 +1848,15 @@ msgid "Open Recent"
msgstr "פתיחה מה×חרוני×"
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr "המרה ×ל…"
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2103,7 +2103,7 @@ msgid "Save the currently edited resource."
msgstr "שמירת המש×ב שנערך כרגע."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr "שמירה בש×…"
#: editor/editor_node.cpp
@@ -2212,7 +2212,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr "תמונה ממוזערת…"
#: editor/editor_plugin_settings.cpp
@@ -2363,7 +2363,7 @@ msgid "(Current)"
msgstr "(נוכחי)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2439,7 +2439,7 @@ msgid "Error requesting url: "
msgstr "שגי××” בבקשת כתובת: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2456,7 +2456,7 @@ msgstr "×œ× × ×™×ª×Ÿ לפתור"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr "מתבצעת התחברות…"
#: editor/export_template_manager.cpp
@@ -2469,7 +2469,7 @@ msgstr "מחובר"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "מוגשת בקשה…"
#: editor/export_template_manager.cpp
@@ -2602,11 +2602,11 @@ msgid "Collapse all"
msgstr "×œ×¦×ž×¦× ×”×›×•×œ"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr "שינוי ש×…"
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr "העברה ×ל…"
#: editor/filesystem_dock.cpp
@@ -2618,15 +2618,15 @@ msgid "Instance"
msgstr "עותק"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr "עריכת תלויות…"
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr "צפייה בבעלי×…"
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "שכפול…"
#: editor/filesystem_dock.cpp
@@ -2652,7 +2652,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"×”×§×‘×¦×™× × ×¡×¨×§×™×,\n"
"× × ×œ×”×ž×ª×™×Ÿâ€¦"
@@ -2720,7 +2720,7 @@ msgid "Import Scene"
msgstr "×™×™×‘×•× ×¡×¦× ×”"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr "סצנה מיוב×ת…"
#: editor/import/resource_importer_scene.cpp
@@ -2732,7 +2732,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr "מופעל סקריפט מות×× ×ישית…"
#: editor/import/resource_importer_scene.cpp
@@ -2748,7 +2748,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr "שמירה…"
#: editor/import_dock.cpp
@@ -2768,7 +2768,7 @@ msgid "Import As:"
msgstr "×™×™×‘×•× ×‘×ª×•×¨:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr "ערכה מוגדרת…"
#: editor/import_dock.cpp
@@ -3182,7 +3182,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3190,7 +3190,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3258,7 +3258,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3325,7 +3325,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3512,8 +3512,9 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
-msgstr ""
+msgstr "הגדרת הצמדה…"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap Relative"
@@ -3933,7 +3934,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4138,7 +4139,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4499,7 +4500,7 @@ msgid "Import Theme"
msgstr "×™×™×‘×•× ×¢×¨×›×ª עיצוב"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr "שמירת ערכת עיצוב בש×…"
#: editor/plugins/script_editor_plugin.cpp
@@ -4596,7 +4597,7 @@ msgstr "החלפת תצוגת חלונית סקריפטי×"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr "×יתור…"
#: editor/plugins/script_editor_plugin.cpp
@@ -4804,15 +4805,15 @@ msgid "Find Previous"
msgstr "×יתור הקוד×"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr "החלפה…"
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr "מעבר לפונקציה…"
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr "מעבר לשורה…"
#: editor/plugins/script_text_editor.cpp
@@ -5266,11 +5267,7 @@ msgid "Transform"
msgstr "התמרה"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "הגדרת הצמדה…"
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5523,7 +5520,7 @@ msgid "Remove All"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5591,7 +5588,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5779,7 +5776,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5869,6 +5866,11 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+#, fuzzy
+msgid "Invalid Project Name."
+msgstr "×©× ×©×’×•×™."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -6057,8 +6059,8 @@ msgstr "כפתור עכבר"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6086,7 +6088,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr "× × ×œ×œ×—×•×¥ על מקש…"
#: editor/project_settings_editor.cpp
@@ -6270,7 +6272,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6366,11 +6368,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6541,7 +6543,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -7975,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/hi.po b/editor/translations/hi.po
index e017935860..3340f13471 100644
--- a/editor/translations/hi.po
+++ b/editor/translations/hi.po
@@ -511,8 +511,8 @@ msgstr "जà¥à¤¡à¤¿à¤¯à¥‡ '%s' to '%s'"
#: editor/connections_dialog.cpp
#, fuzzy
-msgid "Connect.."
-msgstr "जà¥à¤¡à¤¿à¤¯à¥‡.."
+msgid "Connect..."
+msgstr "जà¥à¤¡à¤¿à¤¯à¥‡..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -946,11 +946,11 @@ msgid "Move Audio Bus"
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1086,11 +1086,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1159,7 +1159,7 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr ""
#: editor/editor_file_dialog.cpp
@@ -1421,12 +1421,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1631,11 +1631,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1647,7 +1647,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr ""
#: editor/editor_node.cpp
@@ -1699,7 +1699,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1844,7 +1844,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1856,11 +1856,11 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1880,15 +1880,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2133,7 +2133,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2242,7 +2242,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2393,7 +2393,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2469,7 +2469,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2486,7 +2486,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2500,7 +2500,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2637,11 +2637,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2653,16 +2653,16 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿"
#: editor/filesystem_dock.cpp
@@ -2688,7 +2688,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2754,7 +2754,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2766,7 +2766,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2782,7 +2782,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2802,7 +2802,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3217,7 +3217,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3225,7 +3225,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3293,7 +3293,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3360,7 +3360,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3547,6 +3547,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3968,7 +3969,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4173,7 +4174,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4535,7 +4536,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4632,7 +4633,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4838,15 +4839,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5297,11 +5298,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5554,7 +5551,7 @@ msgid "Remove All"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5622,7 +5619,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5810,7 +5807,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5900,6 +5897,11 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+#, fuzzy
+msgid "Invalid Project Name."
+msgstr "गलत फॉणà¥à¤Ÿ का आकार |"
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -6088,8 +6090,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6117,7 +6119,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6301,7 +6303,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6397,11 +6399,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6572,7 +6574,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
diff --git a/editor/translations/hu.po b/editor/translations/hu.po
index b6151574e9..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."
@@ -500,7 +499,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "'%s' Lecsatlakoztatása '%s'-ról"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "Kapcsolás..."
#: editor/connections_dialog.cpp
@@ -920,12 +919,12 @@ msgid "Move Audio Bus"
msgstr "Hangbusz Ãthelyezése"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Hangbusz Elrendezés Mentése Másként.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Hangbusz Elrendezés Mentése Másként..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Új Elrendezés Helye.."
+msgid "Location for New Layout..."
+msgstr "Új Elrendezés Helye..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1061,12 +1060,12 @@ msgid "Updating Scene"
msgstr "Scene Frissítése"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Helyi módosítások eltárolása.."
+msgid "Storing local changes..."
+msgstr "Helyi módosítások eltárolása..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Scene frissítése.."
+msgid "Updating scene..."
+msgstr "Scene frissítése..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1134,8 +1133,8 @@ msgid "Show In File Manager"
msgstr "Mutat Fájlkezelőben"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Új Mappa.."
+msgid "New Folder..."
+msgstr "Új Mappa..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1396,20 +1395,20 @@ 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!"
msgstr "Hiba történt az erőforrás mentésekor!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Erőforrás Mentése Másként.."
+msgid "Save Resource As..."
+msgstr "Erőforrás Mentése Másként..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "Értem.."
+msgid "I see..."
+msgstr "Értem..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1645,12 +1644,12 @@ msgid "Open Base Scene"
msgstr "Alap Scene megnyitás"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Scene gyors megnyitás"
+msgid "Quick Open Scene..."
+msgstr "Jelenet gyors megnyitása..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Szkript gyors megnyitás"
+msgid "Quick Open Script..."
+msgstr "Szkript gyors megnyitás..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1661,8 +1660,8 @@ msgid "Save changes to '%s' before closing?"
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"
+msgid "Save Scene As..."
+msgstr "Scene mentés másként..."
#: editor/editor_node.cpp
msgid "No"
@@ -1713,8 +1712,8 @@ msgid "This action cannot be undone. Revert anyway?"
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"
+msgid "Quick Run Scene..."
+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."
@@ -1875,8 +1874,8 @@ msgid "Previous tab"
msgstr "Előző fül"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Fájlok Szűrése.."
+msgid "Filter Files..."
+msgstr "Fájlok Szűrése..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1887,12 +1886,12 @@ msgid "New Scene"
msgstr "Új Scene"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Új örökölt Scene"
+msgid "New Inherited Scene..."
+msgstr "Új örökölt Jelenet..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Scene megnyitása"
+msgid "Open Scene..."
+msgstr "Jelenet megnyitása..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1911,16 +1910,16 @@ msgid "Open Recent"
msgstr "Legutóbbi Megnyitása"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Ãtkonvertálás.."
+msgid "Convert To..."
+msgstr "Ãtkonvertálás..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshLibrary-ra.."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary-ra..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet-re.."
+msgid "TileSet..."
+msgstr "TileSet-re..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -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 ""
@@ -2184,8 +2183,8 @@ msgid "Save the currently edited resource."
msgstr "A jelenleg szerkesztett erőforrás elmentése."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Mentés Másként.."
+msgid "Save As..."
+msgstr "Mentés Másként..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2293,8 +2292,8 @@ msgid "Creating Mesh Previews"
msgstr "Háló Előnézetek Létrehozása"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Indexkép.."
+msgid "Thumbnail..."
+msgstr "Indexkép..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2446,8 +2445,8 @@ msgid "(Current)"
msgstr "(Jelenlegi)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "Tükrök letöltése, kérjük várjon.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "Tükrök letöltése, kérjük várjon..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2524,8 +2523,8 @@ msgid "Error requesting url: "
msgstr "Hiba történt az url lekérdezésekor: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "Csatlakozás Tükörhöz.."
+msgid "Connecting to Mirror..."
+msgstr "Csatlakozás Tükörhöz..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2541,8 +2540,8 @@ msgstr "Nem Megoldható"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Csatlakozás.."
+msgid "Connecting..."
+msgstr "Csatlakozás..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2554,8 +2553,8 @@ msgstr "Csatlakozva"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Lekérdezés.."
+msgid "Requesting..."
+msgstr "Lekérdezés..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2690,12 +2689,12 @@ msgid "Collapse all"
msgstr "Összes összecsukása"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Ãtnevezés.."
+msgid "Rename..."
+msgstr "Ãtnevezés..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "Ãthelyezés.."
+msgid "Move To..."
+msgstr "Ãthelyezés..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2706,16 +2705,16 @@ msgid "Instance"
msgstr "Példány"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Függőségek Szerkesztése.."
+msgid "Edit Dependencies..."
+msgstr "Függőségek Szerkesztése..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Tulajdonosok Megtekintése.."
+msgid "View Owners..."
+msgstr "Tulajdonosok Megtekintése..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Megkettőzés.."
+msgid "Duplicate..."
+msgstr "Megkettőzés..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2740,10 +2739,10 @@ msgstr "Kiválasztott Scene(k) példányosítása a kiválasztott Node gyermekek
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Fájlok Vizsgálata,\n"
-"Kérem Várjon.."
+"Kérem Várjon..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2808,8 +2807,8 @@ msgid "Import Scene"
msgstr "Scene importálás"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Scene importálás"
+msgid "Importing Scene..."
+msgstr "Jelenet importálása..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2820,8 +2819,8 @@ msgid "Generating for Mesh: "
msgstr "Létrehozás a Következő Hálóhoz: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "Tetszőleges Szkript Futtatása.."
+msgid "Running Custom Script..."
+msgstr "Tetszőleges Szkript Futtatása..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2837,8 +2836,8 @@ msgid "Error running post-import script:"
msgstr "Hiba történt az importálás utána szkript futtatásakor:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Mentés.."
+msgid "Saving..."
+msgstr "Mentés..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2857,8 +2856,8 @@ msgid "Import As:"
msgstr "Importálás Mint:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Beépített Beállítások.."
+msgid "Preset..."
+msgstr "Beépített Beállítások..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3276,16 +3275,16 @@ msgid "Transition Node"
msgstr "Ãtmenet Node"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Animációk Importálása.."
+msgid "Import Animations..."
+msgstr "Animációk Importálása..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "Node szűrők szerkesztés"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "Szűrők.."
+msgid "Filters..."
+msgstr "Szűrők..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3354,8 +3353,8 @@ msgid "Fetching:"
msgstr "Lekérés:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "Megoldás.."
+msgid "Resolving..."
+msgstr "Megoldás..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3421,8 +3420,8 @@ msgid "Site:"
msgstr "Oldal:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Támogatás.."
+msgid "Support..."
+msgstr "Támogatás..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -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"
@@ -3621,6 +3620,7 @@ msgid "Use Rotation Snap"
msgstr "Forgatási Illesztés Használata"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "Illesztés Beállítása..."
@@ -3717,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"
@@ -4018,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!"
@@ -4049,8 +4047,8 @@ msgid "Create Convex Collision Sibling"
msgstr "Konvex Ütközési Testvér Létrehozása"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "Körvonalháló Létrehozása.."
+msgid "Create Outline Mesh..."
+msgstr "Körvonalháló Létrehozása..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4255,8 +4253,8 @@ msgid "Error loading image:"
msgstr "Hiba a kép betöltésekor:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Nem létezik egyetlen pixel sem >128-as átlátszósággal a képben.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Nem létezik egyetlen pixel sem >128-as átlátszósággal a képben..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4616,8 +4614,8 @@ msgid "Import Theme"
msgstr "Téma Importálása"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Téma Mentése Másként.."
+msgid "Save Theme As..."
+msgstr "Téma Mentése Másként..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4713,8 +4711,8 @@ msgstr "Szkript Panel Megjelenítése"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Keresés.."
+msgid "Find..."
+msgstr "Keresés..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4923,16 +4921,16 @@ msgid "Find Previous"
msgstr "Előző Keresése"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Csere.."
+msgid "Replace..."
+msgstr "Csere..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Ugrás Funkcióra.."
+msgid "Goto Function..."
+msgstr "Ugrás Funkcióra..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Ugrás Sorra.."
+msgid "Goto Line..."
+msgstr "Ugrás Sorra..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -4964,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"
@@ -4972,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"
@@ -5092,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: "
@@ -5244,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"
@@ -5382,11 +5380,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5639,7 +5633,7 @@ msgid "Remove All"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5687,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"
@@ -5708,7 +5701,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5896,7 +5889,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5986,6 +5979,10 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "Érvénytelen projektnév."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -6172,8 +6169,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6201,7 +6198,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6378,14 +6375,14 @@ 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:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6481,11 +6478,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6656,7 +6653,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -7242,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"
@@ -7722,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."
@@ -8100,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."
@@ -8110,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 21d333009f..3956378ce7 100644
--- a/editor/translations/id.po
+++ b/editor/translations/id.po
@@ -2,31 +2,31 @@
# 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.
# Sofyan Sugianto <sofyanartem@gmail.com>, 2017-2018.
# 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-04-18 16:38+0000\n"
-"Last-Translator: Tito <ijavadroid@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"
@@ -506,8 +506,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Memutuskan '%s' dari '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "Menyambungkan.."
+msgid "Connect..."
+msgstr "Menyambungkan..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -701,9 +701,8 @@ msgid "Change Dictionary Key"
msgstr "Ubah Kunci Kamus"
#: editor/dictionary_property_edit.cpp
-#, fuzzy
msgid "Change Dictionary Value"
-msgstr "Ubah Nilai Dictionary"
+msgstr "Ubah Nilai Kamus"
#: editor/editor_about.cpp
msgid "Thanks from the Godot community!"
@@ -931,12 +930,12 @@ msgid "Move Audio Bus"
msgstr "Pindahkan Audio Bus"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Simpan Layout Suara Bus Ke.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Simpan Layout Suara Bus Ke..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Lokasi untuk Layout Baru.."
+msgid "Location for New Layout..."
+msgstr "Lokasi untuk Layout Baru..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1074,12 +1073,12 @@ msgid "Updating Scene"
msgstr "Memperbaharui Scene"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Menyimpan perubahan-perubahan lokal.."
+msgid "Storing local changes..."
+msgstr "Menyimpan perubahan-perubahan lokal..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Memperbaharui scene.."
+msgid "Updating scene..."
+msgstr "Memperbaharui scene..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1147,7 +1146,7 @@ msgid "Show In File Manager"
msgstr "Tampilkan di Manajer Berkas"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr "Buat Direktori..."
#: editor/editor_file_dialog.cpp
@@ -1411,21 +1410,21 @@ msgstr "Bersihkan Luaran"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Ekspor proyek gagal dengan kode kesalahan% d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Error menyimpan resource!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Simpan Resource Sebagai.."
+msgid "Save Resource As..."
+msgstr "Simpan Resource Sebagai..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
#, fuzzy
-msgid "I see.."
-msgstr "Aku tahu.."
+msgid "I see..."
+msgstr "Aku tahu..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1571,11 +1570,11 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Expand all properties"
-msgstr ""
+msgstr "Perluas semua properti"
#: editor/editor_node.cpp
msgid "Collapse all properties"
-msgstr ""
+msgstr "Ciutkan semua properti"
#: editor/editor_node.cpp
msgid "Copy Params"
@@ -1658,12 +1657,12 @@ msgid "Open Base Scene"
msgstr "Buka Scene Dasar"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Buka Cepat Scene.."
+msgid "Quick Open Scene..."
+msgstr "Buka Cepat Scene..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Buka Cepat Script.."
+msgid "Quick Open Script..."
+msgstr "Buka Cepat Script..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1674,8 +1673,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Simpan perubahan '%s' sebelum tutup?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Simpan Scene Sebagai.."
+msgid "Save Scene As..."
+msgstr "Simpan Scene Sebagai..."
#: editor/editor_node.cpp
msgid "No"
@@ -1726,8 +1725,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Tindakan ini tidak dapat dibatalkan. Pulihkan saja?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Jalankan Cepat Scene.."
+msgid "Quick Run Scene..."
+msgstr "Jalankan Cepat Scene..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1802,9 +1801,8 @@ msgstr ""
#: editor/editor_node.cpp editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
-#, fuzzy
msgid "Ugh"
-msgstr "Wadoo"
+msgstr "Duh"
#: editor/editor_node.cpp
msgid ""
@@ -1885,8 +1883,8 @@ msgid "Previous tab"
msgstr "Tab sebelumnya"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Saring berkas.."
+msgid "Filter Files..."
+msgstr "Saring berkas..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1897,12 +1895,12 @@ msgid "New Scene"
msgstr "Scene Baru"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Scene Turunan Baru.."
+msgid "New Inherited Scene..."
+msgstr "Scene Turunan Baru..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Buka Scene.."
+msgid "Open Scene..."
+msgstr "Buka Scene..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1921,16 +1919,16 @@ msgid "Open Recent"
msgstr "Buka baru-baru ini"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Ubah ke.."
+msgid "Convert To..."
+msgstr "Ubah ke..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr ""
+msgid "MeshLibrary..."
+msgstr "PerpustakaanMesh..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet.."
+msgid "TileSet..."
+msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -1976,7 +1974,7 @@ msgstr "Keluar ke daftar proyek"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Debug"
-msgstr ""
+msgstr "\"Debug\""
#: editor/editor_node.cpp
msgid "Deploy with Remote Debug"
@@ -2193,8 +2191,8 @@ msgid "Save the currently edited resource."
msgstr "Simpan sumber yang sedang diatur."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Simpan Sebagai.."
+msgid "Save As..."
+msgstr "Simpan Sebagai..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2223,7 +2221,7 @@ msgstr "Impor"
#: editor/editor_node.cpp
msgid "Node"
-msgstr ""
+msgstr "Node"
#: editor/editor_node.cpp
msgid "FileSystem"
@@ -2302,8 +2300,8 @@ msgid "Creating Mesh Previews"
msgstr "Buat Pratinjau Mesh"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr ""
+msgid "Thumbnail..."
+msgstr "Thumbnail..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2348,7 +2346,7 @@ msgstr "Waktu Rata-rata (sec)"
#: editor/editor_profiler.cpp
msgid "Frame %"
-msgstr ""
+msgstr "Bingkai %"
#: editor/editor_profiler.cpp
msgid "Physics Frame %"
@@ -2455,8 +2453,8 @@ msgid "(Current)"
msgstr "(Kondisi Saat Ini)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "Mendapatkan informasi cermin, silakan tunggu.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "Mendapatkan informasi cermin, silakan tunggu..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2516,8 +2514,9 @@ msgstr "Permintaan Gagal."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
+#, fuzzy
msgid "Redirect Loop."
-msgstr ""
+msgstr "Mengarahkan Loop"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -2533,8 +2532,8 @@ msgid "Error requesting url: "
msgstr "Kesalahan saat meminta url: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "Menyambungkan.."
+msgid "Connecting to Mirror..."
+msgstr "Menyambungkan..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2542,16 +2541,16 @@ msgstr "Terputus"
#: editor/export_template_manager.cpp
msgid "Resolving"
-msgstr ""
+msgstr "Menyelesaikan"
#: editor/export_template_manager.cpp
msgid "Can't Resolve"
-msgstr ""
+msgstr "Tidak Bisa Menyelesaikan"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Menyambungkan.."
+msgid "Connecting..."
+msgstr "Menyambungkan..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2563,8 +2562,8 @@ msgstr "Terhubung"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Melakukan permintaan.."
+msgid "Requesting..."
+msgstr "Melakukan permintaan..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2693,19 +2692,19 @@ msgstr "Menggandakan folder:"
#: editor/filesystem_dock.cpp
msgid "Expand all"
-msgstr ""
+msgstr "Perluas semua"
#: editor/filesystem_dock.cpp
msgid "Collapse all"
-msgstr ""
+msgstr "Ciutkan semua"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Ubah Nama.."
+msgid "Rename..."
+msgstr "Ubah Nama..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "Pindahkan ke.."
+msgid "Move To..."
+msgstr "Pindahkan ke..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2716,16 +2715,16 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Sunting Dependensi.."
+msgid "Edit Dependencies..."
+msgstr "Sunting Dependensi..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Tampilkan Pemilik Berkas.."
+msgid "View Owners..."
+msgstr "Tampilkan Pemilik Berkas..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Gandakan.."
+msgid "Duplicate..."
+msgstr "Gandakan..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2750,10 +2749,10 @@ msgstr "Instance scene terpilih sebagai anak node saat ini."
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Memindai Berkas,\n"
-"Silakan Tunggu.."
+"Silakan Tunggu..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2818,8 +2817,8 @@ msgid "Import Scene"
msgstr "Impor Scene"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Mengimpor scene.."
+msgid "Importing Scene..."
+msgstr "Mengimpor scene..."
#: editor/import/resource_importer_scene.cpp
#, fuzzy
@@ -2832,8 +2831,8 @@ msgstr ""
#: editor/import/resource_importer_scene.cpp
#, fuzzy
-msgid "Running Custom Script.."
-msgstr "Menjalankan Skrip Buatan.."
+msgid "Running Custom Script..."
+msgstr "Menjalankan Skrip Buatan..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2848,8 +2847,8 @@ msgid "Error running post-import script:"
msgstr "Kesalahan saat menjalankan skrip post-import:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Menyimpan.."
+msgid "Saving..."
+msgstr "Menyimpan..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2868,7 +2867,7 @@ msgid "Import As:"
msgstr "Impor sebagai:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -2877,7 +2876,7 @@ msgstr "Impor ulang"
#: editor/multi_node_edit.cpp
msgid "MultiNode Set"
-msgstr ""
+msgstr "Set MultiNode"
#: editor/node_dock.cpp
msgid "Groups"
@@ -2987,7 +2986,7 @@ msgstr ""
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Change Blend Time"
-msgstr ""
+msgstr "Ubah Waktu Blend"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Load Animation"
@@ -3093,8 +3092,9 @@ msgid "Copy Animation"
msgstr "Salin Animasi"
#: editor/plugins/animation_player_editor_plugin.cpp
+#, fuzzy
msgid "Onion Skinning"
-msgstr ""
+msgstr "Onion Skinning"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Enable Onion Skinning"
@@ -3306,8 +3306,8 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Impor Animasi.."
+msgid "Import Animations..."
+msgstr "Impor Animasi..."
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
@@ -3315,8 +3315,8 @@ msgid "Edit Node Filters"
msgstr "Sunting Filter Node"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "Penyaring.."
+msgid "Filters..."
+msgstr "Penyaring..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3388,7 +3388,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3455,8 +3455,8 @@ msgid "Site:"
msgstr "Situs:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Dukungan.."
+msgid "Support..."
+msgstr "Dukungan..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3654,6 +3654,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -4086,7 +4087,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4238,7 +4239,7 @@ msgstr ""
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
msgid "Marking walkable triangles..."
-msgstr "Menyimpan perubahan-perubahan lokal.."
+msgstr "Menyimpan perubahan-perubahan lokal..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Constructing compact heightfield..."
@@ -4294,7 +4295,7 @@ msgid "Error loading image:"
msgstr "Galat saat memuat gambar:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4663,7 +4664,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4764,8 +4765,8 @@ msgstr "Beralih Favorit"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Cari.."
+msgid "Find..."
+msgstr "Cari..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4976,15 +4977,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5175,7 +5176,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
#, fuzzy
msgid "Material Changes"
-msgstr "Menyimpan perubahan-perubahan lokal.."
+msgstr "Menyimpan perubahan-perubahan lokal..."
#: editor/plugins/spatial_editor_plugin.cpp
#, fuzzy
@@ -5448,11 +5449,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5709,8 +5706,8 @@ msgid "Remove All"
msgstr "Hapus"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "Sunting tema.."
+msgid "Edit theme..."
+msgstr "Sunting tema..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5779,7 +5776,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5908,7 +5905,7 @@ msgstr ""
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
msgid "Tile Set"
-msgstr "TileSet.."
+msgstr "TileSet..."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -5975,7 +5972,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -6070,6 +6067,11 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Nama Projek:"
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "Tidak dapat membuat folder."
@@ -6161,7 +6163,7 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
msgid "Can't open project"
-msgstr "Menyambungkan.."
+msgstr "Menyambungkan..."
#: editor/project_manager.cpp
#, fuzzy
@@ -6242,7 +6244,7 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
msgid "Can't run project"
-msgstr "Menyambungkan.."
+msgstr "Menyambungkan..."
#: editor/project_manager.cpp
msgid ""
@@ -6268,8 +6270,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6297,7 +6299,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6480,14 +6482,14 @@ 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:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6584,11 +6586,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "Berkas.."
+msgid "File..."
+msgstr "Berkas..."
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6769,7 +6771,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -6939,7 +6941,7 @@ msgstr ""
#: editor/scene_tree_editor.cpp
#, fuzzy
msgid "Open script"
-msgstr "Buka Cepat Script.."
+msgstr "Buka Cepat Script..."
#: editor/scene_tree_editor.cpp
msgid ""
@@ -8284,9 +8286,8 @@ msgid "Please Confirm..."
msgstr "Mohon konfirmasi..."
#: scene/gui/file_dialog.cpp
-#, fuzzy
msgid "Select this Folder"
-msgstr "Metode Publik:"
+msgstr "Pilih Folder ini"
#: scene/gui/popup.cpp
msgid ""
@@ -8307,7 +8308,7 @@ msgstr ""
#: scene/gui/tree.cpp
msgid "(Other)"
-msgstr ""
+msgstr "(Yang Lain)"
#: scene/main/scene_tree.cpp
#, fuzzy
@@ -8319,7 +8320,6 @@ msgstr ""
"> Lingkungan Baku) tidak dapat dimuat"
#: scene/main/viewport.cpp
-#, fuzzy
msgid ""
"This viewport is not set as render target. If you intend for it to display "
"its contents directly to the screen, make it a child of a Control so it can "
@@ -8328,9 +8328,9 @@ msgid ""
msgstr ""
"Viewport ini tidak diatur sebagai target render. Jika anda berniat untuk "
"menampilkan konten-kontennya secara langsung ke layar, buatlah sebuah child "
-"dari kontrol jadi hal tersebut bisa memperoleh ukuran. Jika tidak, buatlah "
-"sebuah RenderTarget dan tetapkannya tekstur internal untuk beberapa node "
-"untuk ditampilkan."
+"dari Kontrol jadi hal tersebut bisa memperoleh ukuran. Jika tidak, buatlah "
+"sebuah RenderTarget dan tetapkan tekstur internal untuk beberapa node untuk "
+"ditampilkan."
#: scene/resources/dynamic_font.cpp
msgid "Error initializing FreeType."
@@ -8349,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:"
@@ -8363,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!"
@@ -8410,7 +8414,7 @@ msgstr "Ukuran font tidak sah."
#, fuzzy
#~ msgid "Setting '"
-#~ msgstr "Mengatur.."
+#~ msgstr "Mengatur..."
#, fuzzy
#~ msgid "Selection -> Duplicate"
@@ -8459,8 +8463,8 @@ msgstr "Ukuran font tidak sah."
#~ msgid "Exporting for %s"
#~ msgstr "Mengekspor untuk %s"
-#~ msgid "Setting Up.."
-#~ msgstr "Mengatur.."
+#~ msgid "Setting Up..."
+#~ msgstr "Mengatur..."
#~ msgid "Error loading scene."
#~ msgstr "Gagal memuat scene."
@@ -8484,8 +8488,8 @@ msgstr "Ukuran font tidak sah."
#~ msgid "No files selected!"
#~ msgstr "Tidak ada berkas dipilih!"
-#~ msgid "Re-Import.."
-#~ msgstr "Impor Ulang.."
+#~ msgid "Re-Import..."
+#~ msgstr "Impor Ulang..."
#, fuzzy
#~ msgid "Root Node Name:"
diff --git a/editor/translations/is.po b/editor/translations/is.po
index eb4f29126c..98a376edca 100644
--- a/editor/translations/is.po
+++ b/editor/translations/is.po
@@ -4,138 +4,165 @@
# This file is distributed under the same license as the Godot source code.
#
# Jóhannes G. Þorsteinsson <johannesg@johannesg.com>, 2017.
+# Kaan Gül <qaantum@hotmail.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2017-12-05 21:47+0000\n"
-"Last-Translator: Jóhannes G. Þorsteinsson <johannesg@johannesg.com>\n"
+"PO-Revision-Date: 2018-06-05 05:39+0000\n"
+"Last-Translator: Kaan Gül <qaantum@hotmail.com>\n"
"Language-Team: Icelandic <https://hosted.weblate.org/projects/godot-engine/"
"godot/is/>\n"
"Language: is\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.18-dev\n"
+"X-Generator: Weblate 3.0\n"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Disabled"
msgstr "Óvirkt"
#: editor/animation_editor.cpp
msgid "All Selection"
-msgstr "Allt Val"
+msgstr "Allt úrvalið"
#: editor/animation_editor.cpp
#, fuzzy
msgid "Anim Change Keyframe Time"
-msgstr "Hreyfimynd Breyta Gildi"
+msgstr "Anim breyta lyklagrind tími"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Change Transition"
-msgstr "Hreyfimynd Breyta Stöðuskiptum"
+msgstr "Anim breyting umskipti"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Change Transform"
-msgstr "Hreyfimynd Breyta Ummyndun"
+msgstr "Breyta umbreytingu"
#: editor/animation_editor.cpp
#, fuzzy
msgid "Anim Change Keyframe Value"
-msgstr "Hreyfimynd Breyta Gildi"
+msgstr "Anim breyta lyklagrind gildi"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Change Call"
-msgstr "Hreyfimynd Breyta Kalli"
+msgstr "Útkall breyting símtal"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Add Track"
-msgstr "Hreyfimynd Bæta Við Rás"
+msgstr "Anim bæta við lag"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Duplicate Keys"
-msgstr "Hreyfimynd Tvöfalda Lykla"
+msgstr "Tvíteknir lyklar"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Move Anim Track Up"
-msgstr "Færa Hreyfimynda Rás Upp"
+msgstr "Færa Anim track upp"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Move Anim Track Down"
-msgstr "Færa Hreyfimynda Rás Niður"
+msgstr "Færa Anim track niður"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Remove Anim Track"
-msgstr "Fjarlægja Hreyfimynda Rás"
+msgstr "Fjarlægja Anim track"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Set Transitions to:"
-msgstr "Sitja Stöðuskipti á:"
+msgstr "Stillið breyting á:"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Track Rename"
-msgstr "Endurnefna Hreyfimyndarás"
+msgstr "Endurnefning Anim track"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Track Change Interpolation"
-msgstr "Breyta Brúun á Hreyfimyndarás"
+msgstr "Breytingar á Anim track"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Track Change Value Mode"
-msgstr ""
+msgstr "Breyta gildisstilling í Anim track"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Track Change Wrap Mode"
-msgstr ""
+msgstr "Anim track breyta hulum ham"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Edit Node Curve"
-msgstr ""
+msgstr "Breyta hnútnum Ferill"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Edit Selection Curve"
-msgstr ""
+msgstr "Breyta valferil"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Delete Keys"
-msgstr ""
+msgstr "Anim DELETE-lyklar"
#: editor/animation_editor.cpp editor/plugins/tile_map_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
+#, fuzzy
msgid "Duplicate Selection"
-msgstr ""
+msgstr "Afrita val"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Duplicate Transposed"
-msgstr ""
+msgstr "Tvískipt transposed"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Remove Selection"
-msgstr ""
+msgstr "Fjarlægja val"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Continuous"
-msgstr ""
+msgstr "Samfellt"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Discrete"
-msgstr ""
+msgstr "Afmarkað"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Trigger"
-msgstr ""
+msgstr "Kveikja:"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Add Key"
-msgstr ""
+msgstr "Anim bæta við lykli"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Move Keys"
-msgstr ""
+msgstr "Færa lykla af Anim"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Scale Selection"
-msgstr ""
+msgstr "Val á kvarða"
#: editor/animation_editor.cpp
msgid "Scale From Cursor"
@@ -496,7 +523,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr ""
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -906,11 +933,11 @@ msgid "Move Audio Bus"
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1046,11 +1073,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1119,7 +1146,7 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr ""
#: editor/editor_file_dialog.cpp
@@ -1381,12 +1408,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1591,11 +1618,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1607,7 +1634,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr ""
#: editor/editor_node.cpp
@@ -1659,7 +1686,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1804,7 +1831,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1816,11 +1843,11 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1840,15 +1867,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2093,7 +2120,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2202,7 +2229,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2353,7 +2380,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2429,7 +2456,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2446,7 +2473,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2459,7 +2486,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2591,11 +2618,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2607,16 +2634,16 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Hreyfimynd Tvöfalda Lykla"
#: editor/filesystem_dock.cpp
@@ -2642,7 +2669,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2708,7 +2735,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2720,7 +2747,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2736,7 +2763,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2756,7 +2783,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3170,7 +3197,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3178,7 +3205,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3246,7 +3273,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3313,7 +3340,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3500,6 +3527,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3921,7 +3949,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4126,7 +4154,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4487,7 +4515,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4584,7 +4612,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4790,15 +4818,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5249,11 +5277,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5506,7 +5530,7 @@ msgid "Remove All"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5574,7 +5598,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5762,7 +5786,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5852,6 +5876,10 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr ""
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -6038,8 +6066,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6067,7 +6095,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6251,7 +6279,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6347,11 +6375,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6522,7 +6550,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
diff --git a/editor/translations/it.po b/editor/translations/it.po
index 85f4e665a1..2d566fe163 100644
--- a/editor/translations/it.po
+++ b/editor/translations/it.po
@@ -3,6 +3,7 @@
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
#
+# Alessio Corridori <alessiocorridori@hotmail.com>, 2018.
# Dario Bonfanti <bonfi.96@hotmail.it>, 2016-2017.
# Dario D'Ambra <legione0@gmail.com>, 2017.
# dariocavada <cavada@ectrlsolutions.com>, 2017.
@@ -19,8 +20,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-05-02 10:38+0000\n"
-"Last-Translator: Samuele Zolfanelli <samdazel@gmail.com>\n"
+"PO-Revision-Date: 2018-05-18 16:39+0000\n"
+"Last-Translator: Alessio Corridori <alessiocorridori@hotmail.com>\n"
"Language-Team: Italian <https://hosted.weblate.org/projects/godot-engine/"
"godot/it/>\n"
"Language: it\n"
@@ -511,8 +512,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Disconnetti '%s' da '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "Connetti.."
+msgid "Connect..."
+msgstr "Connetti..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -934,12 +935,12 @@ msgid "Move Audio Bus"
msgstr "Sposta bus audio"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "Salva Layout Bus Audio Come..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Posizione per Nuovo Layout.."
+msgid "Location for New Layout..."
+msgstr "Posizione per Nuovo Layout..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1080,12 +1081,12 @@ msgid "Updating Scene"
msgstr "Aggiornamento Scena"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Memorizzando i cambiamenti locali.."
+msgid "Storing local changes..."
+msgstr "Memorizzando i cambiamenti locali..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Aggiornando la scena.."
+msgid "Updating scene..."
+msgstr "Aggiornando la scena..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1154,8 +1155,8 @@ msgid "Show In File Manager"
msgstr "Mostra nel File Manager"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Nuova Cartella.."
+msgid "New Folder..."
+msgstr "Nuova Cartella..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1424,13 +1425,13 @@ msgid "Error saving resource!"
msgstr "Errore salvando la Risorsa!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Salva Risorsa Come.."
+msgid "Save Resource As..."
+msgstr "Salva Risorsa Come..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "Capisco.."
+msgid "I see..."
+msgstr "Capisco..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1666,12 +1667,12 @@ msgid "Open Base Scene"
msgstr "Apri Scena Base"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Apri scena rapido.."
+msgid "Quick Open Scene..."
+msgstr "Apri scena rapido..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Apri Script Rapido.."
+msgid "Quick Open Script..."
+msgstr "Apri Script Rapido..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1682,8 +1683,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Salvare le modifiche a '%s' prima di chiudere?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Salva Scena Come.."
+msgid "Save Scene As..."
+msgstr "Salva Scena Come..."
#: editor/editor_node.cpp
msgid "No"
@@ -1734,8 +1735,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Questa azione non può essere annullata. Ripristinare comunque?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Esegui Scena Rapido.."
+msgid "Quick Run Scene..."
+msgstr "Esegui Scena Rapido..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1895,8 +1896,8 @@ msgid "Previous tab"
msgstr "Scheda precedente"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Filtra Files.."
+msgid "Filter Files..."
+msgstr "Filtra Files..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1907,12 +1908,12 @@ msgid "New Scene"
msgstr "Nuova scena"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Nuova Scena Ereditata.."
+msgid "New Inherited Scene..."
+msgstr "Nuova Scena Ereditata..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Apri Scena.."
+msgid "Open Scene..."
+msgstr "Apri Scena..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1931,16 +1932,16 @@ msgid "Open Recent"
msgstr "Apri Recente"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Converti In.."
+msgid "Convert To..."
+msgstr "Converti In..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshLibrary.."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet.."
+msgid "TileSet..."
+msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2203,8 +2204,8 @@ msgid "Save the currently edited resource."
msgstr "Salva la risorsa in modifica."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Salva Come.."
+msgid "Save As..."
+msgstr "Salva Come..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2312,8 +2313,8 @@ msgid "Creating Mesh Previews"
msgstr "Creazione Anteprime Mesh"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Miniatura.."
+msgid "Thumbnail..."
+msgstr "Miniatura..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2468,7 +2469,7 @@ msgid "(Current)"
msgstr "(Corrente)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr "Recupero dei mirror, attendi..."
#: editor/export_template_manager.cpp
@@ -2547,8 +2548,8 @@ msgid "Error requesting url: "
msgstr "Errore di connessione all'URL: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "Connessione al mirror in corso.."
+msgid "Connecting to Mirror..."
+msgstr "Connessione al mirror in corso..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2564,8 +2565,8 @@ msgstr "Impossibile risolvere"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Connettendo.."
+msgid "Connecting..."
+msgstr "Connettendo..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2578,8 +2579,8 @@ msgstr "Connesso"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Richiedendo.."
+msgid "Requesting..."
+msgstr "Richiedendo..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2718,12 +2719,12 @@ msgid "Collapse all"
msgstr "Comprimi tutto"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Rinomina.."
+msgid "Rename..."
+msgstr "Rinomina..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "Sposta in.."
+msgid "Move To..."
+msgstr "Sposta in..."
#: editor/filesystem_dock.cpp
#, fuzzy
@@ -2735,16 +2736,16 @@ msgid "Instance"
msgstr "Istanza"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Modifica Dipendenze.."
+msgid "Edit Dependencies..."
+msgstr "Modifica Dipendenze..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Vedi Proprietari.."
+msgid "View Owners..."
+msgstr "Vedi Proprietari..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Duplica.."
+msgid "Duplicate..."
+msgstr "Duplica..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2769,10 +2770,10 @@ msgstr "Istanzia le scene selezionate come figlie del nodo selezionato."
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Scansione File,\n"
-"Si prega di attendere.."
+"Si prega di attendere..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2837,8 +2838,8 @@ msgid "Import Scene"
msgstr "Importa Scena"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Importando Scena.."
+msgid "Importing Scene..."
+msgstr "Importando Scena..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2850,8 +2851,8 @@ msgid "Generating for Mesh: "
msgstr "Generando per Mesh: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "Eseguendo Script Personalizzato.."
+msgid "Running Custom Script..."
+msgstr "Eseguendo Script Personalizzato..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2866,8 +2867,8 @@ msgid "Error running post-import script:"
msgstr "Errore di esecuzione dello script di post-import:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Salvataggio.."
+msgid "Saving..."
+msgstr "Salvataggio..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2886,8 +2887,8 @@ msgid "Import As:"
msgstr "Importa Come:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Preset.."
+msgid "Preset..."
+msgstr "Preset..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3308,16 +3309,16 @@ msgid "Transition Node"
msgstr "Nodo Transizione"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Importa animazioni.."
+msgid "Import Animations..."
+msgstr "Importa animazioni..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "Modifica Filtri Nodi"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "Filtri.."
+msgid "Filters..."
+msgstr "Filtri..."
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
@@ -3385,8 +3386,8 @@ msgid "Fetching:"
msgstr "Recupero:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "Risolvendo.."
+msgid "Resolving..."
+msgstr "Risolvendo..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3452,8 +3453,8 @@ msgid "Site:"
msgstr "Sito:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Supporta.."
+msgid "Support..."
+msgstr "Supporta..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3652,6 +3653,7 @@ msgid "Use Rotation Snap"
msgstr "Usa lo Snap di Rotazione"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "Configura Snap..."
@@ -4100,8 +4102,8 @@ msgid "Create Convex Collision Sibling"
msgstr "Crea Fratello di Collisione Convessa"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "Crea Mesh di Outline.."
+msgid "Create Outline Mesh..."
+msgstr "Crea Mesh di Outline..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
#, fuzzy
@@ -4258,7 +4260,7 @@ msgstr "Creazione Octree Luci"
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
msgid "Marking walkable triangles..."
-msgstr "Stringhe Traducibili.."
+msgstr "Stringhe Traducibili..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Constructing compact heightfield..."
@@ -4280,7 +4282,7 @@ msgstr "Creazione Octree Texture"
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
msgid "Creating polymesh..."
-msgstr "Crea Mesh di Outline.."
+msgstr "Crea Mesh di Outline..."
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
@@ -4321,8 +4323,8 @@ msgid "Error loading image:"
msgstr "Errore di caricamento immagine:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Nessun pixel con trasparenza >128 nell'immagine.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Nessun pixel con trasparenza >128 nell'immagine..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4688,8 +4690,8 @@ msgid "Import Theme"
msgstr "Importa Tema"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Salva Tema Come.."
+msgid "Save Theme As..."
+msgstr "Salva Tema Come..."
#: editor/plugins/script_editor_plugin.cpp
#, fuzzy
@@ -4791,8 +4793,8 @@ msgstr "Attiva Preferito"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Trova.."
+msgid "Find..."
+msgstr "Trova..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -5006,16 +5008,16 @@ msgid "Find Previous"
msgstr "Trova Precedente"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Rimpiazza.."
+msgid "Replace..."
+msgstr "Rimpiazza..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Vai a Funzione.."
+msgid "Goto Function..."
+msgstr "Vai a Funzione..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Vai a Linea.."
+msgid "Goto Line..."
+msgstr "Vai a Linea..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5475,12 +5477,8 @@ msgid "Transform"
msgstr "Transform"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Configura Snap..."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "Finestra di Transform.."
+msgid "Transform Dialog..."
+msgstr "Finestra di Transform..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5739,7 +5737,7 @@ msgid "Remove All"
msgstr "Rimuovi"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr "Modifica Tema…"
#: editor/plugins/theme_editor_plugin.cpp
@@ -5810,7 +5808,8 @@ msgid "Options"
msgstr "Opzioni"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+#, fuzzy
+msgid "Has,Many,Options"
msgstr "Ha, Molte, Diverse, Opzioni!"
#: editor/plugins/theme_editor_plugin.cpp
@@ -5940,7 +5939,7 @@ msgstr "Unisci da scena?"
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
msgid "Tile Set"
-msgstr "TileSet.."
+msgstr "TileSet..."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -6007,7 +6006,7 @@ msgid "Presets"
msgstr "Presets"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr "Aggiungi..."
#: editor/project_export.cpp
@@ -6108,6 +6107,11 @@ msgstr "Progetto Importato"
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Nome Progetto:"
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "Impossibile creare cartella."
@@ -6318,8 +6322,8 @@ msgstr "Pulsante Mouse"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6347,8 +6351,8 @@ msgid "Control+"
msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "Premi un tasto.."
+msgid "Press a Key..."
+msgstr "Premi un tasto..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6530,15 +6534,15 @@ msgstr "Impostazioni Progetto (project.godot)"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "General"
-msgstr "Generali"
+msgstr "Informazioni Generali"
#: editor/project_settings_editor.cpp editor/property_editor.cpp
msgid "Property:"
msgstr "Proprietà:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
-msgstr "Sovrascrivi Per.."
+msgid "Override For..."
+msgstr "Sovrascrivi Per..."
#: editor/project_settings_editor.cpp
msgid "Input Map"
@@ -6637,12 +6641,12 @@ msgid "Easing Out-In"
msgstr "Easing Out-In"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "File.."
+msgid "File..."
+msgstr "File..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "Dir.."
+msgid "Dir..."
+msgstr "Dir..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6673,7 +6677,7 @@ msgstr "Mostra nel File System"
#: editor/property_editor.cpp
#, fuzzy
msgid "Convert To %s"
-msgstr "Converti In.."
+msgstr "Converti In..."
#: editor/property_editor.cpp
msgid "Error loading file: Not a resource!"
@@ -6820,8 +6824,8 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Questa operazione no può essere eseguita su scene istanziate."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "Salva Nuova Scena Come.."
+msgid "Save New Scene As..."
+msgstr "Salva Nuova Scena Come..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
@@ -7353,12 +7357,12 @@ msgstr ""
#: modules/gdnative/gdnative_library_editor_plugin.cpp
#, fuzzy
msgid "Platform"
-msgstr "Copia A Piattaforma.."
+msgstr "Copia A Piattaforma..."
#: modules/gdnative/gdnative_library_editor_plugin.cpp
#, fuzzy
msgid "Dynamic Library"
-msgstr "MeshLibrary.."
+msgstr "MeshLibrary..."
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Add an architecture entry"
@@ -7372,7 +7376,7 @@ msgstr "GDNative"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
#, fuzzy
msgid "Library"
-msgstr "MeshLibrary.."
+msgstr "MeshLibrary..."
#: modules/gdnative/gdnative_library_singleton_editor.cpp
#, fuzzy
@@ -8445,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 ':')."
@@ -8475,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!"
@@ -8623,8 +8631,8 @@ msgstr "Dimensione font Invalida."
#~ msgid "Exporting for %s"
#~ msgstr "Esportando per %s"
-#~ msgid "Setting Up.."
-#~ msgstr "Impostando.."
+#~ msgid "Setting Up..."
+#~ msgstr "Impostando..."
#~ msgid "Error loading scene."
#~ msgstr "Errore di caricamento della scena."
@@ -8688,8 +8696,8 @@ msgstr "Dimensione font Invalida."
#~ msgid "Info"
#~ msgstr "Info"
-#~ msgid "Re-Import.."
-#~ msgstr "Re-Importa.."
+#~ msgid "Re-Import..."
+#~ msgstr "Re-Importa..."
#~ msgid "No bit masks to import!"
#~ msgstr "Nessuna bit mask da importare!"
@@ -9084,14 +9092,14 @@ msgstr "Dimensione font Invalida."
#~ msgid "Zoom (%):"
#~ msgstr "Zoom(%):"
-#~ msgid "Skeleton.."
-#~ msgstr "Scheletro.."
+#~ msgid "Skeleton..."
+#~ msgstr "Scheletro..."
#~ msgid "Zoom Reset"
#~ msgstr "Zoom Reset"
-#~ msgid "Zoom Set.."
-#~ msgstr "Imposta Zoom.."
+#~ msgid "Zoom Set..."
+#~ msgstr "Imposta Zoom..."
#~ msgid "Set a Value"
#~ msgstr "Imposta un Valore"
@@ -9566,8 +9574,8 @@ msgstr "Dimensione font Invalida."
#~ msgid "Export Project PCK"
#~ msgstr "Esporta Progetto PCK"
-#~ msgid "Export.."
-#~ msgstr "Esporta.."
+#~ msgid "Export..."
+#~ msgstr "Esporta..."
#~ msgid "Project Export"
#~ msgstr "Esportazione Progetto"
@@ -9687,8 +9695,8 @@ msgstr "Dimensione font Invalida."
#~ msgid "Reload Tool Script (Soft)"
#~ msgstr "Ricarica Tool Script (Soft)"
-#~ msgid "Edit Connections.."
-#~ msgstr "Modifica Connessioni.."
+#~ msgid "Edit Connections..."
+#~ msgstr "Modifica Connessioni..."
#~ msgid "Set Params"
#~ msgstr "Imposta parametri"
@@ -9742,5 +9750,5 @@ msgstr "Dimensione font Invalida."
#~ msgid "Next Time:"
#~ msgstr "Prossima Volta:"
-#~ msgid "Merging.."
-#~ msgstr "Unione.."
+#~ msgid "Merging..."
+#~ msgstr "Unione..."
diff --git a/editor/translations/ja.po b/editor/translations/ja.po
index e05530b258..5ce73d0442 100644
--- a/editor/translations/ja.po
+++ b/editor/translations/ja.po
@@ -2,30 +2,31 @@
# 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.
-# Daisuke Saito <d.saito@coriginate.com>, 2017.
+# D_first <dntk.daisei@gmail.com>, 2017, 2018.
+# Daisuke Saito <d.saito@coriginate.com>, 2017, 2018.
# h416 <shinichiro.hirama@gmail.com>, 2017.
-# hopping tappy (ãŸã£ã´ã•ã‚“) <hopping.tappy@gmail.com>, 2016-2017.
-# Jun Shiozawa <haresecret@gmail.com>, 2017.
+# hopping tappy (ãŸã£ã´ã•ã‚“) <hopping.tappy@gmail.com>, 2016-2017, 2018.
+# Jun Shiozawa <haresecret@gmail.com>, 2017, 2018.
# Lexi Grafen <shfeedly@gmail.com>, 2017.
# NoahDigital <taku_58@hotmail.com>, 2017.
+# 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-04-02 08:38+0000\n"
-"Last-Translator: Tohru Ike (rokujyouhitoma) <rokujyouhitomajp@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 2.20-dev\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -41,14 +42,12 @@ msgid "Anim Change Keyframe Time"
msgstr "Anim 値を変更"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Transition"
-msgstr "Anim é·ç§»ï¼ˆãƒˆãƒ©ãƒ³ã‚¸ã‚·ãƒ§ãƒ³ï¼‰"
+msgstr "アニメーション 変化ã¨ãã®ç§»ã‚Šå¤‰ã‚り(トランジション)"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Transform"
-msgstr "Anim 変形(トランスフォーム)"
+msgstr "アニメーションã®ãƒˆãƒ©ãƒ³ã‚¹ãƒ•ã‚©ãƒ¼ãƒ ï¼ˆå¤‰å½¢ï¼‰"
#: editor/animation_editor.cpp
#, fuzzy
@@ -66,9 +65,8 @@ msgid "Anim Add Track"
msgstr "Anim トラックを追加"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Duplicate Keys"
-msgstr "Anim キーを複製"
+msgstr "アニメーションã®ã‚­ãƒ¼ãƒ•ãƒ¬ãƒ¼ãƒ ã‚’複製"
#: editor/animation_editor.cpp
msgid "Move Anim Track Up"
@@ -84,7 +82,7 @@ msgstr "Anim トラックを削除"
#: editor/animation_editor.cpp
msgid "Set Transitions to:"
-msgstr "ã“ã‚Œã«ãƒˆãƒ©ãƒ³ã‚¸ã‚·ãƒ§ãƒ³ã‚’設定:"
+msgstr "トランジションを設定:"
#: editor/animation_editor.cpp
msgid "Anim Track Rename"
@@ -114,7 +112,7 @@ msgstr "é¸æŠžæ›²ç·šã‚’編集"
#: editor/animation_editor.cpp
msgid "Anim Delete Keys"
-msgstr "Anim キー削除"
+msgstr "アニメーションã®ã‚­ãƒ¼ãƒ•ãƒ¬ãƒ¼ãƒ ã‚’削除"
#: editor/animation_editor.cpp editor/plugins/tile_map_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
@@ -536,8 +534,8 @@ msgstr "'%s' ã‚’ '%s' ã«æŽ¥ç¶š"
#: editor/connections_dialog.cpp
#, fuzzy
-msgid "Connect.."
-msgstr "接続.."
+msgid "Connect..."
+msgstr "接続..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -580,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 "検索:"
@@ -639,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 "パス"
@@ -855,14 +851,12 @@ msgstr ""
"ãƒãƒ¼ãƒãƒ³ãƒˆã®è‘—作権ãŠã‚ˆã³ãƒ©ã‚¤ã‚»ãƒ³ã‚¹æ¡é …ã®å®Œå…¨ãªãƒªã‚¹ãƒˆã§ã™ã€‚"
#: editor/editor_about.cpp
-#, fuzzy
msgid "All Components"
-msgstr "コンテンツ:"
+msgstr "ã™ã¹ã¦ã®ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆ(構æˆéƒ¨åˆ†)"
#: editor/editor_about.cpp
-#, fuzzy
msgid "Components"
-msgstr "コンテンツ:"
+msgstr "コンãƒãƒ¼ãƒãƒ³ãƒˆ(構æˆéƒ¨åˆ†)"
#: editor/editor_about.cpp
#, fuzzy
@@ -870,9 +864,8 @@ msgid "Licenses"
msgstr "ライセンス"
#: editor/editor_asset_installer.cpp editor/project_manager.cpp
-#, fuzzy
msgid "Error opening package file, not in zip format."
-msgstr "zipå½¢å¼ã§ãªã„ãŸã‚パッケージをファイルを開ãéš›ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgstr "パッケージファイルを開ã‘ã¾ã›ã‚“ã§ã—ãŸã€‚ zip å½¢å¼ã§ã¯ã‚ã‚Šã¾ã›ã‚“。"
#: editor/editor_asset_installer.cpp
#, fuzzy
@@ -896,9 +889,8 @@ msgid "Install"
msgstr "インストール"
#: editor/editor_asset_installer.cpp
-#, fuzzy
msgid "Package Installer"
-msgstr "パッケージインストールæˆåŠŸ!"
+msgstr "パッケージインストーラー"
#: editor/editor_audio_buses.cpp
msgid "Speakers"
@@ -922,24 +914,23 @@ msgstr "オーディオãƒã‚¹ã‚’ソロã«åˆ‡ã‚Šæ›¿ãˆ"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Mute"
-msgstr "オーディオãƒã‚¹ã‚’ミュートã«åˆ‡ã‚Šæ›¿ãˆ"
+msgstr "オーディオãƒã‚¹ã‚’ミュート(無音)ã«åˆ‡ã‚Šæ›¿ãˆ"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Bypass Effects"
-msgstr "オーディオãƒã‚¹ã®ãƒã‚¤ãƒ‘スエフェクト切り替ãˆ"
+msgstr "オーディオãƒã‚¹ã®ãƒã‚¤ãƒ‘スエフェクトã®åˆ‡ã‚Šæ›¿ãˆ"
#: editor/editor_audio_buses.cpp
msgid "Select Audio Bus Send"
-msgstr "オーディオãƒã‚¹ã®é€ä¿¡å…ˆã‚’é¸æŠž"
+msgstr "オーディオãƒã‚¹ã®å‡ºåŠ›å…ˆã®é¸æŠž"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus Effect"
msgstr "オーディオãƒã‚¹ã‚¨ãƒ•ã‚§ã‚¯ãƒˆã‚’追加"
#: editor/editor_audio_buses.cpp
-#, fuzzy
msgid "Move Bus Effect"
-msgstr "ãƒã‚¹ã‚¨ãƒ•ã‚§ã‚¯ãƒˆã®ç§»å‹•"
+msgstr "ãƒã‚¹ã‚¨ãƒ•ã‚§ã‚¯ãƒˆã‚’移動"
#: editor/editor_audio_buses.cpp
msgid "Delete Bus Effect"
@@ -951,11 +942,11 @@ msgstr "オーディオãƒã‚¹ã‚’ドラッグ・アンド・ドロップã§(å†)æ
#: editor/editor_audio_buses.cpp
msgid "Solo"
-msgstr "ソロ"
+msgstr "ソロ(独立)"
#: editor/editor_audio_buses.cpp
msgid "Mute"
-msgstr "ミュート"
+msgstr "ミュート(無音)"
#: editor/editor_audio_buses.cpp
msgid "Bypass"
@@ -1009,12 +1000,12 @@ msgstr "オーディオãƒã‚¹ã‚’移動"
#: editor/editor_audio_buses.cpp
#, fuzzy
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "オーディオãƒã‚¹ã®ãƒ¬ã‚¤ã‚¢ã‚¦ãƒˆã‚’別åã§ä¿å­˜"
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "æ–°ã—ã„レイアウトã®å ´æ‰€.."
+msgid "Location for New Layout..."
+msgstr "æ–°ã—ã„レイアウトã®å ´æ‰€..."
#: editor/editor_audio_buses.cpp
#, fuzzy
@@ -1023,7 +1014,8 @@ msgstr "オーディオãƒã‚¹ã®ãƒ¬ã‚¤ã‚¢ã‚¦ãƒˆã‚’é–‹ã"
#: editor/editor_audio_buses.cpp
msgid "There is no 'res://default_bus_layout.tres' file."
-msgstr "'res://default_bus_layout.tres' ファイルãŒã‚ã‚Šã¾ã›ã‚“."
+msgstr ""
+"リソースディレクトリã«ã€Œres://default_bus_layout.tresã€ãƒ•ã‚¡ã‚¤ãƒ«ãŒã‚ã‚Šã¾ã›ã‚“!"
#: editor/editor_audio_buses.cpp
msgid "Invalid file, not an audio bus layout."
@@ -1149,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
@@ -1158,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 "åå‰"
@@ -1173,12 +1164,12 @@ msgstr "シーンを更新"
#: editor/editor_data.cpp
#, fuzzy
-msgid "Storing local changes.."
-msgstr "ローカル環境ã®å¤‰æ›´ã‚’ä¿å­˜ã™ã‚‹.."
+msgid "Storing local changes..."
+msgstr "ローカル環境ã®å¤‰æ›´ã‚’ä¿å­˜ã™ã‚‹..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "シーンを更新ã—ã¦ã„ã¾ã™.."
+msgid "Updating scene..."
+msgstr "シーンを更新ã—ã¦ã„ã¾ã™..."
#: editor/editor_data.cpp
#, fuzzy
@@ -1187,7 +1178,7 @@ msgstr "(空)"
#: editor/editor_data.cpp
msgid "[unsaved]"
-msgstr ""
+msgstr "(未ä¿å­˜)"
#: editor/editor_dir_dialog.cpp
#, fuzzy
@@ -1202,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
@@ -1241,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"
@@ -1254,8 +1244,8 @@ msgid "Show In File Manager"
msgstr "ファイルマãƒãƒ¼ã‚¸ãƒ£ãƒ¼ã§è¡¨ç¤º"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "フォルダを作æˆã™ã‚‹.."
+msgid "New Folder..."
+msgstr "フォルダを作æˆã™ã‚‹..."
#: editor/editor_file_dialog.cpp
#, fuzzy
@@ -1376,7 +1366,6 @@ msgstr "アセットを(å†ï¼‰ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
#: editor/editor_help.cpp editor/editor_node.cpp
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Search Help"
msgstr "ヘルプを検索"
@@ -1555,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
@@ -1564,14 +1553,14 @@ msgstr "リソースä¿å­˜ã‚¨ãƒ©ãƒ¼!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
#, fuzzy
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "~ã¨ã„ã†åå‰ã§ãƒªã‚½ãƒ¼ã‚¹ã‚’ä¿å­˜ã™ã‚‹"
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
#, fuzzy
-msgid "I see.."
-msgstr "ã‚ã‹ã£ãŸ.."
+msgid "I see..."
+msgstr "ã‚ã‹ã£ãŸ..."
#: editor/editor_node.cpp
#, fuzzy
@@ -1584,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
@@ -1594,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'."
@@ -1608,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"
@@ -1835,13 +1821,13 @@ msgid "Open Base Scene"
msgstr "基本シーンを開ã"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "シーンã®ã‚¯ã‚¤ãƒƒã‚¯ã‚ªãƒ¼ãƒ—ン.."
+msgid "Quick Open Scene..."
+msgstr "シーンã®ã‚¯ã‚¤ãƒƒã‚¯ã‚ªãƒ¼ãƒ—ン..."
#: editor/editor_node.cpp
#, fuzzy
-msgid "Quick Open Script.."
-msgstr "スクリプトã®ã‚¯ã‚¤ãƒƒã‚¯ã‚ªãƒ¼ãƒ—ン.."
+msgid "Quick Open Script..."
+msgstr "スクリプトã®ã‚¯ã‚¤ãƒƒã‚¯ã‚ªãƒ¼ãƒ—ン..."
#: editor/editor_node.cpp
#, fuzzy
@@ -1854,7 +1840,7 @@ msgstr "終了ã™ã‚‹å‰ã«ã€'%s' ã¸ã®å¤‰æ›´ã‚’ä¿å­˜ã—ã¾ã™ã‹ï¼Ÿ"
#: editor/editor_node.cpp
#, fuzzy
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr "~ã®åå‰ã§ã‚·ãƒ¼ãƒ³ã‚’ä¿å­˜ã™ã‚‹"
#: editor/editor_node.cpp
@@ -1917,7 +1903,7 @@ msgstr "ã“ã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯undoã§ãã¾ã›ã‚“. å…ƒã«æˆ»ã—ã¾ã™ã‹ï¼Ÿ"
#: editor/editor_node.cpp
#, fuzzy
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr "シーンをクイックランã™ã‚‹"
#: editor/editor_node.cpp
@@ -1929,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?"
@@ -2096,8 +2080,8 @@ msgstr "以å‰ã®ã‚¿ãƒ–"
#: editor/editor_node.cpp
#, fuzzy
-msgid "Filter Files.."
-msgstr "ファイルを絞り込む.."
+msgid "Filter Files..."
+msgstr "ファイルを絞り込む..."
#: editor/editor_node.cpp
#, fuzzy
@@ -2111,13 +2095,13 @@ msgstr "æ–°ã—ã„シーン"
#: editor/editor_node.cpp
#, fuzzy
-msgid "New Inherited Scene.."
-msgstr "æ–°ã—ã„継承ã—ãŸã‚·ãƒ¼ãƒ³.."
+msgid "New Inherited Scene..."
+msgstr "æ–°ã—ã„継承ã—ãŸã‚·ãƒ¼ãƒ³..."
#: editor/editor_node.cpp
#, fuzzy
-msgid "Open Scene.."
-msgstr "シーンを開ã.."
+msgid "Open Scene..."
+msgstr "シーンを開ã..."
#: editor/editor_node.cpp
#, fuzzy
@@ -2139,18 +2123,18 @@ msgstr "最近使ã£ãŸãƒ•ã‚¡ã‚¤ãƒ«ã‚’é–‹ã"
#: editor/editor_node.cpp
#, fuzzy
-msgid "Convert To.."
-msgstr "~ã«å¤‰æ›ã™ã‚‹.."
+msgid "Convert To..."
+msgstr "~ã«å¤‰æ›ã™ã‚‹..."
#: editor/editor_node.cpp
#, fuzzy
-msgid "MeshLibrary.."
-msgstr "メッシュライブラリ.."
+msgid "MeshLibrary..."
+msgstr "メッシュライブラリ..."
#: editor/editor_node.cpp
#, fuzzy
-msgid "TileSet.."
-msgstr "タイルセット.."
+msgid "TileSet..."
+msgstr "タイルセット..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2199,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
@@ -2339,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 "検索"
@@ -2358,7 +2341,6 @@ msgid "Issue Tracker"
msgstr "課題(ãƒã‚°ï¼‰ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ "
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Community"
msgstr "コミュニティ"
@@ -2456,8 +2438,8 @@ msgid "Save the currently edited resource."
msgstr "ç¾åœ¨ç·¨é›†ä¸­ã®ãƒªã‚½ãƒ¼ã‚¹ã‚’ä¿å­˜ã™ã‚‹"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "åå‰ã‚’付ã‘ã¦ä¿å­˜.."
+msgid "Save As..."
+msgstr "åå‰ã‚’付ã‘ã¦ä¿å­˜..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2486,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
@@ -2535,7 +2517,7 @@ msgstr "é–‹ã„ã¦ã‚¹ã‚¯ãƒªãƒ—トを実行ã™ã‚‹"
#: editor/editor_node.cpp
#, fuzzy
msgid "New Inherited"
-msgstr "æ–°ã—ã„継承ã—ãŸã‚·ãƒ¼ãƒ³.."
+msgstr "æ–°ã—ã„継承ã—ãŸã‚·ãƒ¼ãƒ³..."
#: editor/editor_node.cpp
msgid "Load Errors"
@@ -2574,8 +2556,8 @@ msgid "Creating Mesh Previews"
msgstr "メッシュライブラリを生æˆ"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "サムãƒã‚¤ãƒ«.."
+msgid "Thumbnail..."
+msgstr "サムãƒã‚¤ãƒ«..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2733,8 +2715,8 @@ msgid "(Current)"
msgstr "(ç¾åœ¨ã®ï¼‰"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "ミラーサイトをå–å¾—ã—ã¦ã„ã¾ã™ã€‚ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "ミラーサイトをå–å¾—ã—ã¦ã„ã¾ã™ã€‚ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2817,8 +2799,8 @@ msgid "Error requesting url: "
msgstr "urlã®è¦æ±‚ã«å¤±æ•—ã—ã¾ã—ãŸ: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "ミラーサイトã«æŽ¥ç¶šä¸­.."
+msgid "Connecting to Mirror..."
+msgstr "ミラーサイトã«æŽ¥ç¶šä¸­..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2835,8 +2817,8 @@ msgstr "解決ã§ãã¾ã›ã‚“"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "接続中.."
+msgid "Connecting..."
+msgstr "接続中..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2849,8 +2831,8 @@ msgstr "接続ã—ã¾ã—ãŸ"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "リクエスト中.."
+msgid "Requesting..."
+msgstr "リクエスト中..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2914,7 +2896,7 @@ msgstr "サムãƒã‚¤ãƒ«è¡¨ç¤º"
#: editor/filesystem_dock.cpp
msgid "View items as a list"
-msgstr "リスト表示"
+msgstr "リストã§ã‚¢ã‚¤ãƒ†ãƒ ã‚’見る"
#: editor/filesystem_dock.cpp
#, fuzzy
@@ -2996,12 +2978,12 @@ msgid "Collapse all"
msgstr "ã™ã¹ã¦æŠ˜ã‚ŠãŸãŸã‚€"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "åå‰ã‚’変更ã™ã‚‹.."
+msgid "Rename..."
+msgstr "åå‰ã‚’変更ã™ã‚‹..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "~ã¸ç§»å‹•ã™ã‚‹.."
+msgid "Move To..."
+msgstr "~ã¸ç§»å‹•ã™ã‚‹..."
#: editor/filesystem_dock.cpp
#, fuzzy
@@ -3013,17 +2995,17 @@ msgid "Instance"
msgstr "インスタンス"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "ä¾å­˜é–¢ä¿‚を編集.."
+msgid "Edit Dependencies..."
+msgstr "ä¾å­˜é–¢ä¿‚を編集..."
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "View Owners.."
-msgstr "オーナーを見る.."
+msgid "View Owners..."
+msgstr "オーナーを見る..."
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "複製"
#: editor/filesystem_dock.cpp
@@ -3051,7 +3033,7 @@ msgstr "é¸æŠžã—ãŸãƒŽãƒ¼ãƒ‰ã®å­ã¨ã—ã¦ã€é¸æŠžã—ãŸã‚·ãƒ¼ãƒ³ã‚’インス
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"ファイルをスキャンã—ã¦ã„ã¾ã™\n"
"ã—ã°ã‚‰ããŠå¾…ã¡ä¸‹ã•ã„..."
@@ -3064,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
@@ -3079,12 +3061,12 @@ msgstr "グループã‹ã‚‰å–り除ã"
#: editor/import/resource_importer_scene.cpp
#, fuzzy
msgid "Import as Single Scene"
-msgstr "シーンをインãƒãƒ¼ãƒˆä¸­.."
+msgstr "シーンをインãƒãƒ¼ãƒˆä¸­..."
#: editor/import/resource_importer_scene.cpp
#, fuzzy
msgid "Import with Separate Animations"
-msgstr "アニメーションをインãƒãƒ¼ãƒˆ.."
+msgstr "アニメーションをインãƒãƒ¼ãƒˆ..."
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Materials"
@@ -3127,8 +3109,8 @@ msgstr "シーンをインãƒãƒ¼ãƒˆ"
#: editor/import/resource_importer_scene.cpp
#, fuzzy
-msgid "Importing Scene.."
-msgstr "シーンをインãƒãƒ¼ãƒˆä¸­.."
+msgid "Importing Scene..."
+msgstr "シーンをインãƒãƒ¼ãƒˆä¸­..."
#: editor/import/resource_importer_scene.cpp
#, fuzzy
@@ -3142,7 +3124,7 @@ msgstr "軸平行境界ボックス(AABB)を生æˆ"
#: editor/import/resource_importer_scene.cpp
#, fuzzy
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr "カスタムスクリプトを実行中"
#: editor/import/resource_importer_scene.cpp
@@ -3163,8 +3145,8 @@ msgstr "インãƒãƒ¼ãƒˆæ¸ˆã¿ã®ã‚¹ã‚¯ãƒªãƒ—ト実行エラー"
#: editor/import/resource_importer_scene.cpp
#, fuzzy
-msgid "Saving.."
-msgstr "ä¿å­˜ä¸­.."
+msgid "Saving..."
+msgstr "ä¿å­˜ä¸­..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -3185,8 +3167,8 @@ msgid "Import As:"
msgstr "~ã¨ã—ã¦ã‚¤ãƒ³ãƒãƒ¼ãƒˆ:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "åˆæœŸè¨­å®šå€¤.."
+msgid "Preset..."
+msgstr "åˆæœŸè¨­å®šå€¤..."
#: editor/import_dock.cpp
#, fuzzy
@@ -3670,8 +3652,8 @@ msgstr "トランジション(é·ç§»ï¼‰ãƒŽãƒ¼ãƒ‰"
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
-msgid "Import Animations.."
-msgstr "アニメーションをインãƒãƒ¼ãƒˆ.."
+msgid "Import Animations..."
+msgstr "アニメーションをインãƒãƒ¼ãƒˆ..."
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
@@ -3679,8 +3661,8 @@ msgid "Edit Node Filters"
msgstr "ノードフィルターã®ç·¨é›†"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "フィルター.."
+msgid "Filters..."
+msgstr "フィルター..."
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
@@ -3711,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 "ホストã‹ã‚‰å¿œç­”ãŒã‚ã‚Šã¾ã›ã‚“:"
@@ -3762,8 +3742,8 @@ msgstr "å–得中:"
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
-msgid "Resolving.."
-msgstr "解決中.."
+msgid "Resolving..."
+msgstr "解決中..."
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
@@ -3831,8 +3811,8 @@ msgid "Site:"
msgstr "サイト:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "サãƒãƒ¼ãƒˆ.."
+msgid "Support..."
+msgstr "サãƒãƒ¼ãƒˆ..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -4037,6 +4017,7 @@ msgid "Use Rotation Snap"
msgstr "回転スナップ機能を使ã†"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
#, fuzzy
msgid "Configure Snap..."
msgstr "スナップ機能ã®è¨­å®š"
@@ -4516,8 +4497,8 @@ msgstr "凸型兄弟コリジョンを生æˆ"
#: editor/plugins/mesh_instance_editor_plugin.cpp
#, fuzzy
-msgid "Create Outline Mesh.."
-msgstr "アウトラインメッシュを生æˆ.."
+msgid "Create Outline Mesh..."
+msgstr "アウトラインメッシュを生æˆ..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
#, fuzzy
@@ -4717,7 +4698,7 @@ msgstr "八分木テクスãƒãƒ£ã‚’生æˆ"
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
msgid "Creating polymesh..."
-msgstr "アウトラインメッシュを生æˆ.."
+msgstr "アウトラインメッシュを生æˆ..."
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
@@ -4758,8 +4739,8 @@ msgstr "イメージ読ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼:"
#: editor/plugins/particles_2d_editor_plugin.cpp
#, fuzzy
-msgid "No pixels with transparency > 128 in image.."
-msgstr "イメージ内ã«é€æ˜Žåº¦>128ã®ãƒ”クセルãŒã‚ã‚Šã¾ã›ã‚“.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "イメージ内ã«é€æ˜Žåº¦>128ã®ãƒ”クセルãŒã‚ã‚Šã¾ã›ã‚“..."
#: editor/plugins/particles_2d_editor_plugin.cpp
#, fuzzy
@@ -5179,8 +5160,8 @@ msgid "Import Theme"
msgstr "テーマã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "テーマをåå‰ã‚’ã¤ã‘ã¦ä¿å­˜.."
+msgid "Save Theme As..."
+msgstr "テーマをåå‰ã‚’ã¤ã‘ã¦ä¿å­˜..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -5218,7 +5199,7 @@ msgstr "ファイル"
#: editor/plugins/script_editor_plugin.cpp
msgid "New"
-msgstr "æ–°ã—ã„"
+msgstr "æ–°è¦ä½œæˆ"
#: editor/plugins/script_editor_plugin.cpp
msgid "Save All"
@@ -5286,8 +5267,8 @@ msgstr "ãŠæ°—ã«å…¥ã‚Šã‚’切り替ãˆã‚‹"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
#, fuzzy
-msgid "Find.."
-msgstr "検索.."
+msgid "Find..."
+msgstr "検索..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -5508,18 +5489,18 @@ msgid "Find Previous"
msgstr "å‰ã‚’検索"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "ç½®ãæ›ãˆ.."
+msgid "Replace..."
+msgstr "ç½®ãæ›ãˆ..."
#: editor/plugins/script_text_editor.cpp
#, fuzzy
-msgid "Goto Function.."
-msgstr "関数~ã«ç§»å‹•.."
+msgid "Goto Function..."
+msgstr "関数~ã«ç§»å‹•..."
#: editor/plugins/script_text_editor.cpp
#, fuzzy
-msgid "Goto Line.."
-msgstr "~行ã«ç§»å‹•.."
+msgid "Goto Line..."
+msgstr "~行ã«ç§»å‹•..."
#: editor/plugins/script_text_editor.cpp
#, fuzzy
@@ -6013,13 +5994,8 @@ msgstr "トランスフォーム"
#: editor/plugins/spatial_editor_plugin.cpp
#, fuzzy
-msgid "Configure Snap.."
-msgstr "スナップ機能ã®è¨­å®š"
-
-#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
-msgid "Transform Dialog.."
-msgstr "トランスフォームã®ãƒ€ã‚¤ã‚¢ãƒ­ã‚°.."
+msgid "Transform Dialog..."
+msgstr "トランスフォームã®ãƒ€ã‚¤ã‚¢ãƒ­ã‚°..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -6288,7 +6264,7 @@ msgid "Remove All"
msgstr "削除"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr "テーマを編集..."
#: editor/plugins/theme_editor_plugin.cpp
@@ -6361,8 +6337,9 @@ msgid "Options"
msgstr "オプション"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr ""
+#, fuzzy
+msgid "Has,Many,Options"
+msgstr "オプション"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -6496,7 +6473,7 @@ msgstr "シーンã‹ã‚‰ãƒžãƒ¼ã‚¸ã—ã¾ã™ã‹ï¼Ÿ"
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
msgid "Tile Set"
-msgstr "タイルセット.."
+msgstr "タイルセット..."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -6565,8 +6542,8 @@ msgid "Presets"
msgstr "åˆæœŸè¨­å®šå€¤"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "追加.."
+msgid "Add..."
+msgstr "追加..."
#: editor/project_export.cpp
msgid "Resources"
@@ -6664,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"
@@ -6674,12 +6650,17 @@ msgstr "インãƒãƒ¼ãƒˆã•ã‚ŒãŸãƒ—ロジェクト"
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "プロジェクトå:"
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
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."
@@ -6713,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"
@@ -6732,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:"
@@ -6749,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:"
@@ -6764,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?"
@@ -6801,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 ""
@@ -6827,7 +6801,7 @@ msgstr "プロジェクトマãƒãƒ¼ã‚¸ãƒ£ãƒ¼"
#: editor/project_manager.cpp
msgid "Project List"
-msgstr "プロジェクトã®ãƒªã‚¹ãƒˆ"
+msgstr "プロジェクト一覧"
#: editor/project_manager.cpp
msgid "Scan"
@@ -6839,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
@@ -6889,8 +6860,8 @@ msgstr "マウスボタン"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6920,8 +6891,8 @@ msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
#, fuzzy
-msgid "Press a Key.."
-msgstr "キーを押ã—ã¦ãã ã•ã„.."
+msgid "Press a Key..."
+msgstr "キーを押ã—ã¦ãã ã•ã„..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -7123,7 +7094,7 @@ msgid "Property:"
msgstr "プロパティ:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -7226,12 +7197,12 @@ msgid "Easing Out-In"
msgstr "イージング(Out-In)"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "ファイル.."
+msgid "File..."
+msgstr "ファイル..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "ディレクトリ.."
+msgid "Dir..."
+msgstr "ディレクトリ..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -7262,7 +7233,7 @@ msgstr "ファイルシステム上ã§è¡¨ç¤º"
#: editor/property_editor.cpp
#, fuzzy
msgid "Convert To %s"
-msgstr "~ã«å¤‰æ›ã™ã‚‹.."
+msgstr "~ã«å¤‰æ›ã™ã‚‹..."
#: editor/property_editor.cpp
#, fuzzy
@@ -7427,8 +7398,8 @@ msgid "This operation can't be done on instanced scenes."
msgstr "ã“ã®å‡¦ç†ã«ã¯ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹åŒ–ã•ã‚ŒãŸã‚·ãƒ¼ãƒ³ãŒå¿…è¦ã§ã™."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "æ–°è¦ã‚·ãƒ¼ãƒ³ã«åå‰ã‚’付ã‘ã¦ä¿å­˜.."
+msgid "Save New Scene As..."
+msgstr "æ–°è¦ã‚·ãƒ¼ãƒ³ã«åå‰ã‚’付ã‘ã¦ä¿å­˜..."
#: editor/scene_tree_dock.cpp
#, fuzzy
@@ -8002,12 +7973,12 @@ msgstr ""
#: modules/gdnative/gdnative_library_editor_plugin.cpp
#, fuzzy
msgid "Platform"
-msgstr "プラットフォームã¸ã‚³ãƒ”ー.."
+msgstr "プラットフォームã¸ã‚³ãƒ”ー..."
#: modules/gdnative/gdnative_library_editor_plugin.cpp
#, fuzzy
msgid "Dynamic Library"
-msgstr "メッシュライブラリ.."
+msgstr "メッシュライブラリ..."
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Add an architecture entry"
@@ -8016,12 +7987,12 @@ msgstr ""
#: modules/gdnative/gdnative_library_editor_plugin.cpp
#, fuzzy
msgid "GDNativeLibrary"
-msgstr "メッシュライブラリ.."
+msgstr "メッシュライブラリ..."
#: modules/gdnative/gdnative_library_singleton_editor.cpp
#, fuzzy
msgid "Library"
-msgstr "メッシュライブラリ.."
+msgstr "メッシュライブラリ..."
#: modules/gdnative/gdnative_library_singleton_editor.cpp
#, fuzzy
@@ -9054,7 +9025,7 @@ msgstr "警告!"
#: scene/gui/dialogs.cpp
msgid "Please Confirm..."
-msgstr "確èªã—ã¦ãã ã•ã„。"
+msgstr "確èª"
#: scene/gui/file_dialog.cpp
#, fuzzy
@@ -9072,15 +9043,14 @@ msgstr ""
"らã¯å®Ÿè¡Œæ™‚ã«éžè¡¨ç¤ºã«ãªã‚Šã¾ã™ã€‚"
#: scene/gui/scroll_container.cpp
-#, fuzzy
msgid ""
"ScrollContainer is intended to work with a single child control.\n"
"Use a container as child (VBox,HBox,etc), or a Control and set the custom "
"minimum size manually."
msgstr ""
-"スクロールコンテナã¯å­ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ãŒå‹•ä½œã«å¿…è¦ã§ã™. VBox,HBoxãªã©ã®ã‚³ãƒ³ãƒ†ãƒŠ"
-"ã‚’å­ã¨ã™ã‚‹ã‹ã€ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã‚’カスタム最å°ã‚µã‚¤ã‚ºã«ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«ã§æŒ‡å®šã—ã¦ä½¿ç”¨ã—ã¦"
-"ãã ã•ã„."
+"ScrollContainerã¯å˜ä¸€ã®å­ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã§å‹•ä½œã™ã‚‹ã‚ˆã†ã«æ„図ã•ã‚Œã¦ã„ã¾ã™ã€‚コンテ"
+"ナ(VBox, HBoxãªã©)ã‚’å­ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ã‹ã€ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã‚’使用ã—ã¦ã‚«ã‚¹ã‚¿ãƒ æœ€å°ã‚µ"
+"イズを手動ã§è¨­å®šã—ã¦ãã ã•ã„。"
#: scene/gui/tree.cpp
#, fuzzy
@@ -9124,6 +9094,13 @@ msgstr "フォント読ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼ã€‚"
msgid "Invalid font size."
msgstr "無効ãªãƒ•ã‚©ãƒ³ãƒˆ サイズã§ã™ã€‚"
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "以å‰ã®ã‚¿ãƒ–"
+
+#~ msgid "Next"
+#~ msgstr "次"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "ä¸æ­£ãªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ï¼ˆ '/' ã¨':'ã¯ä¸å¯ã§ã™ï¼‰."
@@ -9152,9 +9129,6 @@ msgstr "無効ãªãƒ•ã‚©ãƒ³ãƒˆ サイズã§ã™ã€‚"
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "project.godotをプロジェクトパスã«ç”Ÿæˆã§ãã¾ã›ã‚“ã§ã—ãŸ"
-#~ msgid "Next"
-#~ msgstr "次"
-
#~ msgid "Not found!"
#~ msgstr "見ã¤ã‹ã‚Šã¾ã›ã‚“!"
@@ -9306,8 +9280,8 @@ msgstr "無効ãªãƒ•ã‚©ãƒ³ãƒˆ サイズã§ã™ã€‚"
#~ msgstr "%sã«ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆä¸­"
#, fuzzy
-#~ msgid "Setting Up.."
-#~ msgstr "セットアップ中.."
+#~ msgid "Setting Up..."
+#~ msgstr "セットアップ中..."
#, fuzzy
#~ msgid "Error loading scene."
@@ -9375,8 +9349,8 @@ msgstr "無効ãªãƒ•ã‚©ãƒ³ãƒˆ サイズã§ã™ã€‚"
#~ msgstr "インフォーメーション"
#, fuzzy
-#~ msgid "Re-Import.."
-#~ msgstr "å†ã‚¤ãƒ³ãƒãƒ¼ãƒˆ.."
+#~ msgid "Re-Import..."
+#~ msgstr "å†ã‚¤ãƒ³ãƒãƒ¼ãƒˆ..."
#, fuzzy
#~ msgid "No bit masks to import!"
@@ -9871,15 +9845,15 @@ msgstr "無効ãªãƒ•ã‚©ãƒ³ãƒˆ サイズã§ã™ã€‚"
#~ msgstr "ズーム (%):"
#, fuzzy
-#~ msgid "Skeleton.."
-#~ msgstr "スケルトン.."
+#~ msgid "Skeleton..."
+#~ msgstr "スケルトン..."
#~ msgid "Zoom Reset"
#~ msgstr "ズームをリセット"
#, fuzzy
-#~ msgid "Zoom Set.."
-#~ msgstr "ズームをセットã™ã‚‹.."
+#~ msgid "Zoom Set..."
+#~ msgstr "ズームをセットã™ã‚‹..."
#~ msgid "Set a Value"
#~ msgstr "値を設定ã™ã‚‹"
diff --git a/editor/translations/ko.po b/editor/translations/ko.po
index c3fcfbbb72..be6b540a9a 100644
--- a/editor/translations/ko.po
+++ b/editor/translations/ko.po
@@ -2,20 +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-04-18 15:39+0000\n"
-"Last-Translator: Sun Kim <perplexingsun@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"
@@ -23,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"
@@ -39,7 +39,7 @@ msgstr "애니메ì´ì…˜ 키프레임 시간 변경"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr "애니메ì´ì…˜ 트랜지션 변경"
+msgstr "애니메ì´ì…˜ 전환 변경"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
@@ -75,7 +75,7 @@ msgstr "애니메ì´ì…˜ 트랙 ì‚­ì œ"
#: editor/animation_editor.cpp
msgid "Set Transitions to:"
-msgstr "변화 설정:"
+msgstr "전환 설정:"
#: editor/animation_editor.cpp
msgid "Anim Track Rename"
@@ -181,7 +181,7 @@ msgstr "밖-안"
#: editor/animation_editor.cpp
msgid "Transitions"
-msgstr "변화"
+msgstr "전환"
#: editor/animation_editor.cpp
msgid "Optimize Animation"
@@ -322,7 +322,7 @@ msgstr "키"
#: editor/animation_editor.cpp
msgid "Transition"
-msgstr "변화"
+msgstr "전환"
#: editor/animation_editor.cpp
msgid "Scale Ratio:"
@@ -504,8 +504,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "'%s'와 '%s'ì˜ ì—°ê²° í•´ì œ"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "연결하기.."
+msgid "Connect..."
+msgstr "연결하기..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -923,12 +923,12 @@ msgid "Move Audio Bus"
msgstr "오디오 버스 ì´ë™"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "오디오 버스 ë ˆì´ì•„ì›ƒì„ ë‹¤ë¥¸ ì´ë¦„으로 저장.."
+msgid "Save Audio Bus Layout As..."
+msgstr "오디오 버스 ë ˆì´ì•„ì›ƒì„ ë‹¤ë¥¸ ì´ë¦„으로 저장..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "새 ë ˆì´ì•„ì›ƒì„ ì €ìž¥í•  장소.."
+msgid "Location for New Layout..."
+msgstr "새 ë ˆì´ì•„ì›ƒì„ ì €ìž¥í•  장소..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -966,7 +966,7 @@ msgstr "다른 ì´ë¦„으로 저장"
#: editor/editor_audio_buses.cpp
msgid "Save this Bus Layout to a file."
-msgstr "ì´ ë²„ìŠ¤ ë ˆì´ì•„ì›ƒì„ íŒŒì¼ë¡œ 저장합니다.."
+msgstr "ì´ ë²„ìŠ¤ ë ˆì´ì•„ì›ƒì„ íŒŒì¼ë¡œ 저장합니다..."
#: editor/editor_audio_buses.cpp editor/import_dock.cpp
msgid "Load Default"
@@ -1065,12 +1065,12 @@ msgid "Updating Scene"
msgstr "씬 ì—…ë°ì´íŠ¸ 중"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "로컬 ë³€ê²½ì‚¬í•­ì„ ì €ìž¥ 중.."
+msgid "Storing local changes..."
+msgstr "로컬 ë³€ê²½ì‚¬í•­ì„ ì €ìž¥ 중..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "씬 ì—…ë°ì´íŠ¸ 중.."
+msgid "Updating scene..."
+msgstr "씬 ì—…ë°ì´íŠ¸ 중..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1138,8 +1138,8 @@ msgid "Show In File Manager"
msgstr "íŒŒì¼ ë§¤ë‹ˆì €ì—ì„œ 보기"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "새 í´ë”.."
+msgid "New Folder..."
+msgstr "새 í´ë”..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1400,20 +1400,20 @@ 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!"
msgstr "리소스 저장 중 ì—러!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "리소스를 다른 ì´ë¦„으로 저장.."
+msgid "Save Resource As..."
+msgstr "리소스를 다른 ì´ë¦„으로 저장..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "알겠습니다.."
+msgid "I see..."
+msgstr "알겠습니다..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1437,7 +1437,7 @@ msgstr "'%s' 파싱 중 ì—러."
#: editor/editor_node.cpp
msgid "Unexpected end of file '%s'."
-msgstr "예ìƒì¹˜ 못한 파ì¼ì˜ ë '%s' 입니다.."
+msgstr "예ìƒì¹˜ 못한 파ì¼ì˜ ë '%s' 입니다..."
#: editor/editor_node.cpp
msgid "Missing '%s' or its dependencies."
@@ -1639,12 +1639,12 @@ msgid "Open Base Scene"
msgstr "기본 씬 열기"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "빠른 씬 열기.."
+msgid "Quick Open Scene..."
+msgstr "빠른 씬 열기..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "빠른 스í¬ë¦½íŠ¸ 열기.."
+msgid "Quick Open Script..."
+msgstr "빠른 스í¬ë¦½íŠ¸ 열기..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1655,8 +1655,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "닫기 ì „ì— '%s' ì— ë³€ê²½ì‚¬í•­ì„ ì €ìž¥í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "ì”¬ì„ ë‹¤ë¥¸ ì´ë¦„으로 저장.."
+msgid "Save Scene As..."
+msgstr "ì”¬ì„ ë‹¤ë¥¸ ì´ë¦„으로 저장..."
#: editor/editor_node.cpp
msgid "No"
@@ -1707,8 +1707,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "ì´ í–‰ë™ì€ 취소가 불가능합니다. 무시하고 ë˜ëŒë¦¬ì‹œê² ìŠµë‹ˆê¹Œ?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "빠른 씬 실행.."
+msgid "Quick Run Scene..."
+msgstr "빠른 씬 실행..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1862,8 +1862,8 @@ msgid "Previous tab"
msgstr "ì´ì „ 탭"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "íŒŒì¼ í•„í„°ë§.."
+msgid "Filter Files..."
+msgstr "íŒŒì¼ í•„í„°ë§..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1874,12 +1874,12 @@ msgid "New Scene"
msgstr "새 씬"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "새 ìƒì† 씬.."
+msgid "New Inherited Scene..."
+msgstr "새 ìƒì† 씬..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "씬 열기.."
+msgid "Open Scene..."
+msgstr "씬 열기..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1898,16 +1898,16 @@ msgid "Open Recent"
msgstr "최근 ì—´ì—ˆë˜ í•­ëª©"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "변환.."
+msgid "Convert To..."
+msgstr "변환..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "메시 ë¼ì´ë¸ŒëŸ¬ë¦¬.."
+msgid "MeshLibrary..."
+msgstr "메시 ë¼ì´ë¸ŒëŸ¬ë¦¬..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "íƒ€ì¼ ì…‹.."
+msgid "TileSet..."
+msgstr "íƒ€ì¼ ì…‹..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2171,8 +2171,8 @@ msgid "Save the currently edited resource."
msgstr "현재 íŽ¸ì§‘ëœ ë¦¬ì†ŒìŠ¤ 저장."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "다른 ì´ë¦„으로 저장.."
+msgid "Save As..."
+msgstr "다른 ì´ë¦„으로 저장..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2280,8 +2280,8 @@ msgid "Creating Mesh Previews"
msgstr "메시 미리보기 ìƒì„± 중"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "ì¸ë„¤ì¼.."
+msgid "Thumbnail..."
+msgstr "ì¸ë„¤ì¼..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2433,8 +2433,8 @@ msgid "(Current)"
msgstr "(현재)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "미러를 가져오는 중입니다, 잠시만 기다리세요.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "미러를 가져오는 중입니다, 잠시만 기다리세요..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2511,8 +2511,8 @@ msgid "Error requesting url: "
msgstr "url 요청 ì—러: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "ë¯¸ëŸ¬ì— ì—°ê²°ì¤‘.."
+msgid "Connecting to Mirror..."
+msgstr "ë¯¸ëŸ¬ì— ì—°ê²°ì¤‘..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2528,8 +2528,8 @@ msgstr "í•´ê²°í•  수 ì—†ìŒ"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "연결중.."
+msgid "Connecting..."
+msgstr "연결중..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2541,8 +2541,8 @@ msgstr "ì—°ê²°ë¨"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "요청중.."
+msgid "Requesting..."
+msgstr "요청중..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2674,12 +2674,12 @@ msgid "Collapse all"
msgstr "ëª¨ë‘ ì ‘ê¸°"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "ì´ë¦„ 변경.."
+msgid "Rename..."
+msgstr "ì´ë¦„ 변경..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "ì´ë™.."
+msgid "Move To..."
+msgstr "ì´ë™..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2690,16 +2690,16 @@ msgid "Instance"
msgstr "ì¸ìŠ¤í„´ìŠ¤"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "ì¢…ì† ê´€ê³„ 편집.."
+msgid "Edit Dependencies..."
+msgstr "ì¢…ì† ê´€ê³„ 편집..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "ì†Œìœ ìž ë³´ê¸°.."
+msgid "View Owners..."
+msgstr "ì†Œìœ ìž ë³´ê¸°..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "복제.."
+msgid "Duplicate..."
+msgstr "복제..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2724,10 +2724,10 @@ msgstr "ì„ íƒëœ ì”¬ì„ ì„ íƒëœ ë…¸ë“œì˜ ìžì‹ìœ¼ë¡œ ì¸ìŠ¤í„´ìŠ¤ 합니다
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"íŒŒì¼ ìŠ¤ìº”ì¤‘,\n"
-"잠시만 기다려주세요.."
+"잠시만 기다려주세요..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2792,8 +2792,8 @@ msgid "Import Scene"
msgstr "씬 가져오기"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "씬 가져오는 중.."
+msgid "Importing Scene..."
+msgstr "씬 가져오는 중..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2804,8 +2804,8 @@ msgid "Generating for Mesh: "
msgstr "메시를 위해 ìƒì„± 중: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "ì‚¬ìš©ìž ì •ì˜ ìŠ¤í¬ë¦½íŠ¸ 실행중.."
+msgid "Running Custom Script..."
+msgstr "ì‚¬ìš©ìž ì •ì˜ ìŠ¤í¬ë¦½íŠ¸ 실행중..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2821,8 +2821,8 @@ msgid "Error running post-import script:"
msgstr "가져오기 후 실행할 스í¬ë¦½íŠ¸ 실행 중 ì—러:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "저장 중.."
+msgid "Saving..."
+msgstr "저장 중..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2841,8 +2841,8 @@ msgid "Import As:"
msgstr "ë‹¤ìŒ í˜•ì‹ìœ¼ë¡œ 가져오기:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "프리셋.."
+msgid "Preset..."
+msgstr "프리셋..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3256,19 +3256,19 @@ msgstr "시간 íƒìƒ‰ 노드"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Transition Node"
-msgstr "변화 노드"
+msgstr "전환 노드"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "애니메ì´ì…˜ 가져오기.."
+msgid "Import Animations..."
+msgstr "애니메ì´ì…˜ 가져오기..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "노드 필터 편집"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "í•„í„°.."
+msgid "Filters..."
+msgstr "í•„í„°..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3335,8 +3335,8 @@ msgid "Fetching:"
msgstr "가져오는 중:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "해결 중.."
+msgid "Resolving..."
+msgstr "해결 중..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3402,8 +3402,8 @@ msgid "Site:"
msgstr "사ì´íŠ¸:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "지ì›.."
+msgid "Support..."
+msgstr "지ì›..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3596,6 +3596,7 @@ msgid "Use Rotation Snap"
msgstr "회전 스냅 사용"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "스냅 설정..."
@@ -3692,14 +3693,12 @@ msgid "Show Guides"
msgstr "ê°€ì´ë“œ 보기"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
msgstr "ì›ì  보기"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1ê°œ ë·°í¬íŠ¸"
+msgstr "ë·°í¬íŠ¸ 보기"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -3992,7 +3991,7 @@ msgstr "ë©”ì‹œì— ì™¸ê³½ì„ ì„ ë§Œë“¤ê¸° 위한 서피스가 없습니다!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "메시 기본 íƒ€ìž…ì´ PRIMITIVE_TRIANGLESì´ ì•„ë‹™ë‹ˆë‹¤!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4023,8 +4022,8 @@ msgid "Create Convex Collision Sibling"
msgstr "Convex ì¶©ëŒ í˜•ì œ 만들기"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "외곽선 메시 만들기.."
+msgid "Create Outline Mesh..."
+msgstr "외곽선 메시 만들기..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4228,8 +4227,8 @@ msgid "Error loading image:"
msgstr "ì´ë¯¸ì§€ 로드 ì—러:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "ì´ë¯¸ì§€ì— 투명ë„ê°€ 128보다 í° í”½ì…€ì´ ì—†ìŠµë‹ˆë‹¤.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "ì´ë¯¸ì§€ì— 투명ë„ê°€ 128보다 í° í”½ì…€ì´ ì—†ìŠµë‹ˆë‹¤..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4589,8 +4588,8 @@ msgid "Import Theme"
msgstr "테마 가져오기"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "테마 다른 ì´ë¦„으로 저장.."
+msgid "Save Theme As..."
+msgstr "테마 다른 ì´ë¦„으로 저장..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4686,8 +4685,8 @@ msgstr "스í¬ë¦½íŠ¸ íŒ¨ë„ í† ê¸€"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "찾기.."
+msgid "Find..."
+msgstr "찾기..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4894,16 +4893,16 @@ msgid "Find Previous"
msgstr "ì´ì „ 찾기"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "변경.."
+msgid "Replace..."
+msgstr "변경..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "함수로 ì´ë™.."
+msgid "Goto Function..."
+msgstr "함수로 ì´ë™..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "ë¼ì¸ìœ¼ë¡œ ì´ë™.."
+msgid "Goto Line..."
+msgstr "ë¼ì¸ìœ¼ë¡œ ì´ë™..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5356,12 +5355,8 @@ msgid "Transform"
msgstr "변형"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "스냅 설정.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "변형 다ì´ì–¼ë¡œê·¸.."
+msgid "Transform Dialog..."
+msgstr "변형 다ì´ì–¼ë¡œê·¸..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5613,8 +5608,8 @@ msgid "Remove All"
msgstr "ëª¨ë‘ ì‚­ì œ"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "테마 편집.."
+msgid "Edit theme..."
+msgstr "테마 편집..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5661,14 +5656,12 @@ msgid "Checked Item"
msgstr "항목 확ì¸ë¨"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "항목 추가"
+msgstr "ë¼ë””오 항목"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "항목 확ì¸ë¨"
+msgstr "ë¼ë””오 항목 확ì¸ë¨"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5683,8 +5676,8 @@ msgid "Options"
msgstr "옵션"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr "가진다,많ì€,여러,옵션들!"
+msgid "Has,Many,Options"
+msgstr "가진다,많ì€,옵션들"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5875,8 +5868,8 @@ msgid "Presets"
msgstr "프리셋"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "추가.."
+msgid "Add..."
+msgstr "추가..."
#: editor/project_export.cpp
msgid "Resources"
@@ -5965,6 +5958,10 @@ msgid "Imported Project"
msgstr "가져온 프로ì íŠ¸"
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "ì¸ì‹í• ìˆ˜ 없는 프로ì íŠ¸ 명입니다."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "í´ë”를 만들 수 없습니다."
@@ -6163,9 +6160,11 @@ msgstr "마우스 버튼"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
+"ì¸ì‹í• ìˆ˜ 없는 ì•¡ì…˜ ì´ë¦„입니다. 공백ì´ê±°ë‚˜, '/' , ':', '=', '\\', '\"' ê°€ í¬í•¨"
+"ë˜ë©´ 안 ë©ë‹ˆë‹¤."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6192,8 +6191,8 @@ msgid "Control+"
msgstr "컨트롤+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "키를 눌러주세요.."
+msgid "Press a Key..."
+msgstr "키를 눌러주세요..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6376,8 +6375,8 @@ msgid "Property:"
msgstr "ì†ì„±:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
-msgstr "재정ì˜.."
+msgid "Override For..."
+msgstr "재정ì˜..."
#: editor/project_settings_editor.cpp
msgid "Input Map"
@@ -6472,12 +6471,12 @@ msgid "Easing Out-In"
msgstr "ê°€ì†-ê°ì†"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "파ì¼.."
+msgid "File..."
+msgstr "파ì¼..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "디렉토리.."
+msgid "Dir..."
+msgstr "디렉토리..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6647,8 +6646,8 @@ msgid "This operation can't be done on instanced scenes."
msgstr "ì´ ìž‘ì—…ì€ ì¸ìŠ¤í„´ìŠ¤ëœ 씬ì—서는 불가합니다."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "새 ì”¬ì„ ë‹¤ë¥¸ ì´ë¦„으로 저장.."
+msgid "Save New Scene As..."
+msgstr "새 ì”¬ì„ ë‹¤ë¥¸ ì´ë¦„으로 저장..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
@@ -7363,7 +7362,7 @@ msgstr "거리 ì„ íƒ:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "í´ëž˜ìŠ¤ ì´ë¦„ì€ í‚¤ì›Œë“œê°€ ë  ìˆ˜ 없습니다"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -8064,7 +8063,7 @@ msgstr "Path ì†ì„±ì€ 유효한 Spatial 노드를 가리켜야 합니다."
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "WorldEnvironment는 Environment 리소스가 필요합니다."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8076,6 +8075,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 ""
+"ì´ WorldEnvironment는 무시ë©ë‹ˆë‹¤. (3D ì”¬ì„ ìœ„í•´) Camera를 추가하거나 아니면 "
+"(2D ì”¬ì„ ìœ„í•´) ì´ í™˜ê²½ì˜ ë°°ê²½ 모드를 Canvasë¡œ 설정하세요."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8172,6 +8173,13 @@ msgstr "í°íŠ¸ 로딩 ì—러."
msgid "Invalid font size."
msgstr "유효하지 ì•Šì€ í°íŠ¸ í¬ê¸°."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "ì´ì „ 탭"
+
+#~ msgid "Next"
+#~ msgstr "다ìŒ"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "유효하지 ì•Šì€ ì•¡ì…˜ ('/' ë˜ëŠ” ':' ë¬¸ìž ì‚¬ìš© 불가)."
@@ -8197,9 +8205,6 @@ msgstr "유효하지 ì•Šì€ í°íŠ¸ í¬ê¸°."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "프로ì íŠ¸ ê²½ë¡œì— project.godot 파ì¼ì„ ì°¾ì„ ìˆ˜ 없습니다."
-#~ msgid "Next"
-#~ msgstr "다ìŒ"
-
#~ msgid "Not found!"
#~ msgstr "ì°¾ì„ ìˆ˜ ì—†ìŒ!"
@@ -8333,8 +8338,8 @@ msgstr "유효하지 ì•Šì€ í°íŠ¸ í¬ê¸°."
#~ msgid "Exporting for %s"
#~ msgstr "%s 내보내기"
-#~ msgid "Setting Up.."
-#~ msgstr "설정 중.."
+#~ msgid "Setting Up..."
+#~ msgstr "설정 중..."
#~ msgid "Error loading scene."
#~ msgstr "씬 로딩 중 ì—러."
@@ -8388,8 +8393,8 @@ msgstr "유효하지 ì•Šì€ í°íŠ¸ í¬ê¸°."
#~ msgid "Info"
#~ msgstr "ì •ë³´"
-#~ msgid "Re-Import.."
-#~ msgstr "다시 가져오기.."
+#~ msgid "Re-Import..."
+#~ msgstr "다시 가져오기..."
#~ msgid "No bit masks to import!"
#~ msgstr "가져올 비트 마스í¬ê°€ 없습니다!"
@@ -8781,14 +8786,14 @@ msgstr "유효하지 ì•Šì€ í°íŠ¸ í¬ê¸°."
#~ msgid "Zoom (%):"
#~ msgstr "확대 (%):"
-#~ msgid "Skeleton.."
-#~ msgstr "스켈레톤.."
+#~ msgid "Skeleton..."
+#~ msgstr "스켈레톤..."
#~ msgid "Zoom Reset"
#~ msgstr "확대 초기화"
-#~ msgid "Zoom Set.."
-#~ msgstr "확대 설정.."
+#~ msgid "Zoom Set..."
+#~ msgstr "확대 설정..."
#~ msgid "Set a Value"
#~ msgstr "값 설정"
@@ -9219,8 +9224,8 @@ msgstr "유효하지 ì•Šì€ í°íŠ¸ í¬ê¸°."
#~ msgid "Export Project PCK"
#~ msgstr "프로ì íŠ¸ PCK 내보내기"
-#~ msgid "Export.."
-#~ msgstr "내보내기.."
+#~ msgid "Export..."
+#~ msgstr "내보내기..."
#~ msgid "Project Export"
#~ msgstr "프로ì íŠ¸ 내보내기"
@@ -9333,8 +9338,8 @@ msgstr "유효하지 ì•Šì€ í°íŠ¸ í¬ê¸°."
#~ msgid "Reload Tool Script (Soft)"
#~ msgstr "툴 스í¬ë¦½íŠ¸ 다시 로드 (소프트)"
-#~ msgid "Edit Connections.."
-#~ msgstr "연결 편집.."
+#~ msgid "Edit Connections..."
+#~ msgstr "연결 편집..."
#~ msgid "Set Params"
#~ msgstr "ì†ì„± ì ìš©"
diff --git a/editor/translations/lt.po b/editor/translations/lt.po
index 6504e570f7..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"
@@ -503,7 +501,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Prijungti '%s' prie '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -914,11 +912,11 @@ msgid "Move Audio Bus"
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1054,11 +1052,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1127,7 +1125,7 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr ""
#: editor/editor_file_dialog.cpp
@@ -1389,12 +1387,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1599,11 +1597,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1615,7 +1613,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr ""
#: editor/editor_node.cpp
@@ -1667,7 +1665,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1812,7 +1810,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1824,11 +1822,11 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1848,15 +1846,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -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."
@@ -2101,7 +2099,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2210,8 +2208,8 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Miniatūra.."
+msgid "Thumbnail..."
+msgstr "Miniatūra..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2362,7 +2360,7 @@ msgid "(Current)"
msgstr "(Esama)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2439,7 +2437,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2456,7 +2454,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2469,7 +2467,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2605,11 +2603,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2621,16 +2619,16 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Duplikuoti"
#: editor/filesystem_dock.cpp
@@ -2656,7 +2654,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2722,7 +2720,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2734,7 +2732,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2750,7 +2748,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2770,7 +2768,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3186,8 +3184,8 @@ msgid "Transition Node"
msgstr "Transition Nodas"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Importuoti Animacijas.."
+msgid "Import Animations..."
+msgstr "Importuoti Animacijas..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
@@ -3195,8 +3193,8 @@ msgstr "Redaguoti Nodų Filtrus"
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
-msgid "Filters.."
-msgstr "Filtrai.."
+msgid "Filters..."
+msgstr "Filtrai..."
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
@@ -3264,7 +3262,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3331,7 +3329,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3518,6 +3516,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3939,7 +3938,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4144,7 +4143,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4505,7 +4504,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4602,7 +4601,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4808,15 +4807,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5268,11 +5267,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5525,7 +5520,7 @@ msgid "Remove All"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5593,7 +5588,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5781,7 +5776,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5871,6 +5866,11 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Netinkamas Å¡rifto dydis."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -6060,8 +6060,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6089,7 +6089,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6273,7 +6273,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6369,11 +6369,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6544,7 +6544,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
diff --git a/editor/translations/ms.po b/editor/translations/ms.po
new file mode 100644
index 0000000000..19d8b6b7d8
--- /dev/null
+++ b/editor/translations/ms.po
@@ -0,0 +1,7956 @@
+# Malay translation of the Godot Engine editor
+# 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.
+#
+# Sam Vanguard <syafz119@gmail.com>, 2018.
+# Shaqir Rafiq <moshamoradev@gmail.com>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Godot Engine editor\n"
+"PO-Revision-Date: 2018-06-05 19:27+0000\n"
+"Last-Translator: Shaqir Rafiq <moshamoradev@gmail.com>\n"
+"Language-Team: Malay <https://hosted.weblate.org/projects/godot-engine/godot/"
+"ms/>\n"
+"Language: ms\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\n"
+
+#: editor/animation_editor.cpp
+msgid "Disabled"
+msgstr "Tidak Aktif"
+
+#: editor/animation_editor.cpp
+msgid "All Selection"
+msgstr "Semua Pilihan"
+
+#: editor/animation_editor.cpp
+#, fuzzy
+msgid "Anim Change Keyframe Time"
+msgstr "Anim Ubah Masa Keyframe"
+
+#: editor/animation_editor.cpp
+msgid "Anim Change Transition"
+msgstr "Anim Ubah Peralihan"
+
+#: editor/animation_editor.cpp
+msgid "Anim Change Transform"
+msgstr "Anim Ubah Penukaran"
+
+#: editor/animation_editor.cpp
+msgid "Anim Change Keyframe Value"
+msgstr "Anim Ubah Nilai Keyframe"
+
+#: editor/animation_editor.cpp
+msgid "Anim Change Call"
+msgstr "Anim Ubah Panggilan"
+
+#: editor/animation_editor.cpp
+msgid "Anim Add Track"
+msgstr "Anim Tambah Trek"
+
+#: editor/animation_editor.cpp
+msgid "Anim Duplicate Keys"
+msgstr "Anim Menduakan Kunci"
+
+#: editor/animation_editor.cpp
+msgid "Move Anim Track Up"
+msgstr "Ubah Trek Anim Ke Atas"
+
+#: editor/animation_editor.cpp
+msgid "Move Anim Track Down"
+msgstr "Ubah Trek Anim Ke Bawah"
+
+#: editor/animation_editor.cpp
+msgid "Remove Anim Track"
+msgstr "Buang Trek Anim"
+
+#: editor/animation_editor.cpp
+msgid "Set Transitions to:"
+msgstr "Set Peralihan ke:"
+
+#: editor/animation_editor.cpp
+msgid "Anim Track Rename"
+msgstr "Ubah Nama Trek Anim"
+
+#: editor/animation_editor.cpp
+msgid "Anim Track Change Interpolation"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Track Change Value Mode"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Track Change Wrap Mode"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Edit Node Curve"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Edit Selection Curve"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Delete Keys"
+msgstr ""
+
+#: editor/animation_editor.cpp editor/plugins/tile_map_editor_plugin.cpp
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Duplicate Selection"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Duplicate Transposed"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Remove Selection"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Continuous"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Discrete"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Trigger"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Add Key"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Move Keys"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Scale Selection"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Scale From Cursor"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Goto Next Step"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Goto Prev Step"
+msgstr ""
+
+#: editor/animation_editor.cpp editor/plugins/curve_editor_plugin.cpp
+#: editor/property_editor.cpp
+msgid "Linear"
+msgstr ""
+
+#: editor/animation_editor.cpp editor/plugins/theme_editor_plugin.cpp
+msgid "Constant"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "In"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Out"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "In-Out"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Out-In"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Transitions"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Optimize Animation"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Clean-Up Animation"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Create NEW track for %s and insert key?"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Create %d NEW tracks and insert keys?"
+msgstr ""
+
+#: editor/animation_editor.cpp editor/create_dialog.cpp
+#: editor/editor_audio_buses.cpp editor/plugins/abstract_polygon_2d_editor.cpp
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+#: editor/plugins/particles_editor_plugin.cpp editor/script_create_dialog.cpp
+msgid "Create"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Create & Insert"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Insert Track & Key"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Insert Key"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Change Anim Len"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Change Anim Loop"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Create Typed Value Key"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Insert"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Scale Keys"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Add Call Track"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Animation zoom."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Length (s):"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Animation length (in seconds)."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Step (s):"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Cursor step snap (in seconds)."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Enable/Disable looping in animation."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Add new tracks."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Move current track up."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Move current track down."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Remove selected track."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Track tools"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Enable editing of individual keys by clicking them."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim. Optimizer"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Max. Linear Error:"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Max. Angular Error:"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Max Optimizable Angle:"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Optimize"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Select an AnimationPlayer from the Scene Tree to edit animations."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Key"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Transition"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Scale Ratio:"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Call Functions in Which Node?"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Remove invalid keys"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Remove unresolved and empty tracks"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Clean-up all animations"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Clean-Up Animation(s) (NO UNDO!)"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Clean-Up"
+msgstr ""
+
+#: editor/array_property_edit.cpp
+msgid "Resize Array"
+msgstr ""
+
+#: editor/array_property_edit.cpp
+msgid "Change Array Value Type"
+msgstr ""
+
+#: editor/array_property_edit.cpp
+msgid "Change Array Value"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Go to Line"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Line Number:"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "No Matches"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Replaced %d occurrence(s)."
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Match Case"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Whole Words"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Replace"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Replace All"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Selection Only"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Zoom In"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Zoom Out"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Reset Zoom"
+msgstr ""
+
+#: editor/code_editor.cpp editor/script_editor_debugger.cpp
+msgid "Line:"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Col:"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Method in target Node must be specified!"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid ""
+"Target method not found! Specify a valid method or attach a script to target "
+"Node."
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Connect To Node:"
+msgstr ""
+
+#: editor/connections_dialog.cpp editor/editor_autoload_settings.cpp
+#: editor/groups_editor.cpp editor/plugins/item_list_editor_plugin.cpp
+#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp
+msgid "Add"
+msgstr ""
+
+#: editor/connections_dialog.cpp editor/dependency_editor.cpp
+#: editor/plugins/animation_tree_editor_plugin.cpp
+#: editor/plugins/theme_editor_plugin.cpp editor/project_manager.cpp
+#: editor/project_settings_editor.cpp
+msgid "Remove"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Add Extra Call Argument:"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Extra Call Arguments:"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Path to Node:"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Make Function"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Deferred"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Oneshot"
+msgstr ""
+
+#: editor/connections_dialog.cpp editor/dependency_editor.cpp
+#: editor/export_template_manager.cpp
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/plugins/script_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp editor/project_export.cpp
+#: editor/project_settings_editor.cpp editor/property_editor.cpp
+#: editor/run_settings_dialog.cpp editor/settings_config_dialog.cpp
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Close"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Connect"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Connect '%s' to '%s'"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Connecting Signal:"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Disconnect '%s' from '%s'"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Connect..."
+msgstr ""
+
+#: editor/connections_dialog.cpp
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Disconnect"
+msgstr ""
+
+#: editor/connections_dialog.cpp editor/editor_help.cpp editor/node_dock.cpp
+msgid "Signals"
+msgstr ""
+
+#: editor/create_dialog.cpp
+msgid "Change %s Type"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/project_settings_editor.cpp
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change"
+msgstr ""
+
+#: editor/create_dialog.cpp
+msgid "Create New %s"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
+#: editor/filesystem_dock.cpp
+msgid "Favorites:"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
+msgid "Recent:"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/editor_node.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
+#: editor/quick_open.cpp
+msgid "Search:"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/editor_help.cpp
+#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
+#: editor/quick_open.cpp
+msgid "Matches:"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/editor_help.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp editor/property_selector.cpp
+#: editor/script_editor_debugger.cpp
+msgid "Description:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Search Replacement For:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Dependencies For:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid ""
+"Scene '%s' is currently being edited.\n"
+"Changes will not take effect unless reloaded."
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid ""
+"Resource '%s' is in use.\n"
+"Changes will take effect when reloaded."
+msgstr ""
+
+#: editor/dependency_editor.cpp
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Dependencies"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Resource"
+msgstr ""
+
+#: editor/dependency_editor.cpp editor/editor_autoload_settings.cpp
+#: editor/project_manager.cpp editor/project_settings_editor.cpp
+#: editor/script_create_dialog.cpp
+msgid "Path"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Dependencies:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Fix Broken"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Dependency Editor"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Search Replacement Resource:"
+msgstr ""
+
+#: editor/dependency_editor.cpp editor/editor_file_dialog.cpp
+#: editor/editor_help.cpp editor/editor_node.cpp editor/filesystem_dock.cpp
+#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
+#: editor/quick_open.cpp scene/gui/file_dialog.cpp
+msgid "Open"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Owners Of:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Remove selected files from the project? (no undo)"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid ""
+"The files being removed are required by other resources in order for them to "
+"work.\n"
+"Remove them anyway? (no undo)"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Cannot remove:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Error loading:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Scene failed to load due to missing dependencies:"
+msgstr ""
+
+#: editor/dependency_editor.cpp editor/editor_node.cpp
+msgid "Open Anyway"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Which action should be taken?"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Fix Dependencies"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Errors loading!"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Permanently delete %d item(s)? (No undo!)"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Owns"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Resources Without Explicit Ownership:"
+msgstr ""
+
+#: editor/dependency_editor.cpp editor/editor_node.cpp
+msgid "Orphan Resource Explorer"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Delete selected files?"
+msgstr ""
+
+#: editor/dependency_editor.cpp editor/editor_audio_buses.cpp
+#: editor/editor_file_dialog.cpp editor/editor_node.cpp
+#: editor/filesystem_dock.cpp editor/plugins/item_list_editor_plugin.cpp
+#: editor/project_export.cpp editor/project_settings_editor.cpp
+#: editor/scene_tree_dock.cpp
+msgid "Delete"
+msgstr ""
+
+#: editor/dictionary_property_edit.cpp
+msgid "Change Dictionary Key"
+msgstr ""
+
+#: editor/dictionary_property_edit.cpp
+msgid "Change Dictionary Value"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Thanks from the Godot community!"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Thanks!"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Godot Engine contributors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Project Founders"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Lead Developer"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Project Manager "
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Developers"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Authors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Platinum Sponsors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Gold Sponsors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Mini Sponsors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Gold Donors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Silver Donors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Bronze Donors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Donors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "License"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Thirdparty License"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid ""
+"Godot Engine relies on a number of thirdparty free and open source "
+"libraries, all compatible with the terms of its MIT license. The following "
+"is an exhaustive list of all such thirdparty components with their "
+"respective copyright statements and license terms."
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "All Components"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Components"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Licenses"
+msgstr ""
+
+#: editor/editor_asset_installer.cpp editor/project_manager.cpp
+msgid "Error opening package file, not in zip format."
+msgstr ""
+
+#: editor/editor_asset_installer.cpp
+msgid "Uncompressing Assets"
+msgstr ""
+
+#: editor/editor_asset_installer.cpp editor/project_manager.cpp
+msgid "Package Installed Successfully!"
+msgstr ""
+
+#: editor/editor_asset_installer.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Success!"
+msgstr ""
+
+#: editor/editor_asset_installer.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Install"
+msgstr ""
+
+#: editor/editor_asset_installer.cpp
+msgid "Package Installer"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Speakers"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Add Effect"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Rename Audio Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Change Audio Bus Volume"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Toggle Audio Bus Solo"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Toggle Audio Bus Mute"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Toggle Audio Bus Bypass Effects"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Select Audio Bus Send"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Add Audio Bus Effect"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Move Bus Effect"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Delete Bus Effect"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Audio Bus, Drag and Drop to rearrange."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Solo"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Mute"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Bypass"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Bus options"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp editor/filesystem_dock.cpp
+#: editor/plugins/tile_map_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "Duplicate"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Reset Volume"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Delete Effect"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Audio"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Add Audio Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Master bus can't be deleted!"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Delete Audio Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Duplicate Audio Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Reset Bus Volume"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Move Audio Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Save Audio Bus Layout As..."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Location for New Layout..."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Open Audio Bus Layout"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "There is no 'res://default_bus_layout.tres' file."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Invalid file, not an audio bus layout."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Add Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Create a new Bus Layout."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp editor/property_editor.cpp
+#: editor/script_create_dialog.cpp
+msgid "Load"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Load an existing Bus Layout."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Save As"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Save this Bus Layout to a file."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp editor/import_dock.cpp
+msgid "Load Default"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Load the default Bus Layout."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Invalid name."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Valid characters:"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Invalid name. Must not collide with an existing engine class name."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Invalid name. Must not collide with an existing buit-in type name."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Invalid name. Must not collide with an existing global constant name."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Invalid Path."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "File does not exist."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Not in resource path."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Add AutoLoad"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Autoload '%s' already exists!"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Rename Autoload"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Toggle AutoLoad Globals"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Move Autoload"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Remove Autoload"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Enable"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Rearrange Autoloads"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp editor/editor_file_dialog.cpp
+#: scene/gui/file_dialog.cpp
+msgid "Path:"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Node Name:"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp editor/editor_profiler.cpp
+#: editor/project_manager.cpp editor/settings_config_dialog.cpp
+msgid "Name"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Singleton"
+msgstr ""
+
+#: editor/editor_data.cpp
+msgid "Updating Scene"
+msgstr ""
+
+#: editor/editor_data.cpp
+msgid "Storing local changes..."
+msgstr ""
+
+#: editor/editor_data.cpp
+msgid "Updating scene..."
+msgstr ""
+
+#: editor/editor_data.cpp
+msgid "[empty]"
+msgstr ""
+
+#: editor/editor_data.cpp
+msgid "[unsaved]"
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp
+msgid "Please select a base directory first"
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp
+msgid "Choose a Directory"
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
+#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp
+msgid "Create Folder"
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
+#: editor/editor_plugin_settings.cpp editor/filesystem_dock.cpp
+#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
+#: scene/gui/file_dialog.cpp
+msgid "Name:"
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
+#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp
+msgid "Could not create folder."
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp
+msgid "Choose"
+msgstr ""
+
+#: editor/editor_export.cpp
+msgid "Storing File:"
+msgstr ""
+
+#: editor/editor_export.cpp
+msgid "Packing"
+msgstr ""
+
+#: editor/editor_export.cpp platform/javascript/export/export.cpp
+msgid "Template file not found:"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "File Exists, Overwrite?"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Select Current Folder"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
+msgid "Copy Path"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
+msgid "Show In File Manager"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
+msgid "New Folder..."
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Refresh"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "All Recognized"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "All Files (*)"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Open a File"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Open File(s)"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Open a Directory"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Open a File or Directory"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp editor/editor_node.cpp
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/script_editor_plugin.cpp scene/gui/file_dialog.cpp
+msgid "Save"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Save a File"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Go Back"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Go Forward"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Go Up"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Toggle Hidden Files"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Toggle Favorite"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Toggle Mode"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Focus Path"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Move Favorite Up"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Move Favorite Down"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Go to parent folder"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Directories & Files:"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Preview:"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp editor/script_editor_debugger.cpp
+#: scene/gui/file_dialog.cpp
+msgid "File:"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Must use a valid extension."
+msgstr ""
+
+#: editor/editor_file_system.cpp
+msgid "ScanSources"
+msgstr ""
+
+#: editor/editor_file_system.cpp
+msgid "(Re)Importing Assets"
+msgstr ""
+
+#: editor/editor_help.cpp editor/editor_node.cpp
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Search Help"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Class List:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Search Classes"
+msgstr ""
+
+#: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp
+msgid "Top"
+msgstr ""
+
+#: editor/editor_help.cpp editor/property_editor.cpp
+msgid "Class:"
+msgstr ""
+
+#: editor/editor_help.cpp editor/scene_tree_editor.cpp
+msgid "Inherits:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Inherited by:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Brief Description:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Members"
+msgstr ""
+
+#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
+msgid "Members:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Public Methods"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Public Methods:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "GUI Theme Items"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "GUI Theme Items:"
+msgstr ""
+
+#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
+msgid "Signals:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Enumerations"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Enumerations:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "enum "
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Constants"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Constants:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Description"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Online Tutorials:"
+msgstr ""
+
+#: editor/editor_help.cpp
+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 ""
+
+#: editor/editor_help.cpp
+msgid "Properties"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Property Description:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid ""
+"There is currently no description for this property. Please help us by "
+"[color=$color][url=$url]contributing one[/url][/color]!"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Methods"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Method Description:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid ""
+"There is currently no description for this method. Please help us by [color="
+"$color][url=$url]contributing one[/url][/color]!"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Search Text"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Find"
+msgstr ""
+
+#: editor/editor_log.cpp
+msgid "Output:"
+msgstr ""
+
+#: editor/editor_log.cpp editor/plugins/animation_tree_editor_plugin.cpp
+#: editor/property_editor.cpp editor/script_editor_debugger.cpp
+#: modules/gdnative/gdnative_library_editor_plugin.cpp scene/gui/line_edit.cpp
+#: scene/gui/text_edit.cpp
+msgid "Clear"
+msgstr ""
+
+#: editor/editor_log.cpp
+msgid "Clear Output"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
+msgid "Error saving resource!"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
+msgid "Save Resource As..."
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
+#: editor/scene_tree_dock.cpp
+msgid "I see..."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Can't open file for writing:"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Requested file format unknown:"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error while saving."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Can't open '%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error while parsing '%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Unexpected end of file '%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Missing '%s' or its dependencies."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error while loading '%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Saving Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Analyzing"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Creating Thumbnail"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "This operation can't be done without a tree root."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Couldn't save scene. Likely dependencies (instances or inheritance) couldn't "
+"be satisfied."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Failed to load resource."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Can't load MeshLibrary for merging!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error saving MeshLibrary!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Can't load TileSet for merging!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error saving TileSet!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error trying to save layout!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Default editor layout overridden."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Layout name not found!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Restored default layout to base settings."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This resource belongs to a scene that was imported, so it's not editable.\n"
+"Please read the documentation relevant to importing scenes to better "
+"understand this workflow."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This resource belongs to a scene that was instanced or inherited.\n"
+"Changes to it will not be kept when saving the current scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This resource was imported, so it's not editable. Change its settings in the "
+"import panel and then re-import."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This scene was imported, so changes to it will not be kept.\n"
+"Instancing it or inheriting will allow making changes to it.\n"
+"Please read the documentation relevant to importing scenes to better "
+"understand this workflow."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This is a remote object so changes to it will not be kept.\n"
+"Please read the documentation relevant to debugging to better understand "
+"this workflow."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Expand all properties"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Collapse all properties"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Copy Params"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Paste Params"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "Paste Resource"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Copy Resource"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Make Built-In"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Make Sub-Resources Unique"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open in Help"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "There is no defined scene to run."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"No main scene has ever been defined, select one?\n"
+"You can change it later in \"Project Settings\" under the 'application' "
+"category."
+msgstr ""
+
+#: editor/editor_node.cpp
+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 ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Selected scene '%s' is not a scene file, select a valid one?\n"
+"You can change it later in \"Project Settings\" under the 'application' "
+"category."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Current scene was never saved, please save it prior to running."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Could not start subprocess!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open Base Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Quick Open Scene..."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Quick Open Script..."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save & Close"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save changes to '%s' before closing?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save Scene As..."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "No"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Yes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "This scene has never been saved. Save before running?"
+msgstr ""
+
+#: editor/editor_node.cpp editor/scene_tree_dock.cpp
+msgid "This operation can't be done without a scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Export Mesh Library"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "This operation can't be done without a root node."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Export Tile Set"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "This operation can't be done without a selected node."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Current scene not saved. Open anyway?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Can't reload a scene that was never saved."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Revert"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "This action cannot be undone. Revert anyway?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Quick Run Scene..."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Quit"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Exit the editor?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open Project Manager?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save & Quit"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save changes to the following scene(s) before quitting?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save changes the following scene(s) before opening Project Manager?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This option is deprecated. Situations where refresh must be forced are now "
+"considered a bug. Please report."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Pick a Main Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Unable to enable addon plugin at: '%s' parsing of config failed."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Unable to find script field for addon plugin at: 'res://addons/%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Unable to load addon script from path: '%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Unable to load addon script from path: '%s' Base type is not EditorPlugin."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Unable to load addon script from path: '%s' Script is not in tool mode."
+msgstr ""
+
+#: 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 ""
+
+#: 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 ""
+
+#: 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 ""
+
+#: editor/editor_node.cpp
+msgid "Scene '%s' has broken dependencies:"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Clear Recent Scenes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save Layout"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Delete Layout"
+msgstr ""
+
+#: editor/editor_node.cpp editor/import_dock.cpp
+#: editor/script_create_dialog.cpp
+msgid "Default"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Switch Scene Tab"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "%d more files or folders"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "%d more folders"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "%d more files"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Dock Position"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Distraction Free Mode"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Toggle distraction-free mode."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Add a new scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Go to previously opened scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Next tab"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Previous tab"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Filter Files..."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Operations with scene files."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "New Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "New Inherited Scene..."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open Scene..."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save all Scenes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Close Scene"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
+msgid "Open Recent"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Convert To..."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "MeshLibrary..."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "TileSet..."
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
+#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
+msgid "Undo"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
+#: scene/gui/line_edit.cpp
+msgid "Redo"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Revert Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Miscellaneous project or scene-wide tools."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Project"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Project Settings"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Run Script"
+msgstr ""
+
+#: editor/editor_node.cpp editor/project_export.cpp
+msgid "Export"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Tools"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Quit to Project List"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
+msgid "Debug"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Deploy with Remote Debug"
+msgstr ""
+
+#: 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 ""
+
+#: editor/editor_node.cpp
+msgid "Small Deploy with Network FS"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"When this option is enabled, export or deploy will produce a minimal "
+"executable.\n"
+"The filesystem will be provided from the project by the editor over the "
+"network.\n"
+"On Android, deploy will use the USB cable for faster performance. This "
+"option speeds up testing for games with a large footprint."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Visible Collision Shapes"
+msgstr ""
+
+#: 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 ""
+
+#: editor/editor_node.cpp
+msgid "Visible Navigation"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Navigation meshes and polygons will be visible on the running game if this "
+"option is turned on."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Sync Scene Changes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"When this option is turned on, any changes made to the scene in the editor "
+"will be replicated in the running game.\n"
+"When used remotely on a device, this is more efficient with network "
+"filesystem."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Sync Script Changes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"When this option is turned on, any script that is saved will be reloaded on "
+"the running game.\n"
+"When used remotely on a device, this is more efficient with network "
+"filesystem."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Editor"
+msgstr ""
+
+#: editor/editor_node.cpp editor/settings_config_dialog.cpp
+msgid "Editor Settings"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Editor Layout"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Toggle Fullscreen"
+msgstr ""
+
+#: editor/editor_node.cpp editor/project_export.cpp
+msgid "Manage Export Templates"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Help"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
+msgid "Classes"
+msgstr ""
+
+#: 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 ""
+
+#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
+msgid "Online Docs"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Q&A"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Issue Tracker"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
+msgid "Community"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "About"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play the project."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Pause the scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Pause Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Stop the scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Stop"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play the edited scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play custom scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play Custom Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Spins when the editor window repaints!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Update Always"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Update Changes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Disable Update Spinner"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Inspector"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Create a new resource in memory and edit it."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Load an existing resource from disk and edit it."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save the currently edited resource."
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
+msgid "Save As..."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Go to the previous edited object in history."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Go to the next edited object in history."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "History of recently edited objects."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Object properties."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Changes may be lost!"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
+#: editor/project_manager.cpp
+msgid "Import"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Node"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "FileSystem"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Output"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Don't Save"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Import Templates From ZIP File"
+msgstr ""
+
+#: editor/editor_node.cpp editor/project_export.cpp
+msgid "Export Project"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Export Library"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Merge With Existing"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Password:"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open & Run a Script"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "New Inherited"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Load Errors"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/tile_map_editor_plugin.cpp
+msgid "Select"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open 2D Editor"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open 3D Editor"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open Script Editor"
+msgstr ""
+
+#: editor/editor_node.cpp editor/project_manager.cpp
+msgid "Open Asset Library"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open the next Editor"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open the previous Editor"
+msgstr ""
+
+#: editor/editor_plugin.cpp
+msgid "Creating Mesh Previews"
+msgstr ""
+
+#: editor/editor_plugin.cpp
+msgid "Thumbnail..."
+msgstr ""
+
+#: editor/editor_plugin_settings.cpp
+msgid "Installed Plugins:"
+msgstr ""
+
+#: editor/editor_plugin_settings.cpp
+msgid "Update"
+msgstr ""
+
+#: editor/editor_plugin_settings.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Version:"
+msgstr ""
+
+#: editor/editor_plugin_settings.cpp
+msgid "Author:"
+msgstr ""
+
+#: editor/editor_plugin_settings.cpp
+msgid "Status:"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Stop Profiling"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Start Profiling"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Measure:"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Frame Time (sec)"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Average Time (sec)"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Frame %"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Physics Frame %"
+msgstr ""
+
+#: editor/editor_profiler.cpp editor/script_editor_debugger.cpp
+msgid "Time:"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Inclusive"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Self"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Frame #:"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Time"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Calls"
+msgstr ""
+
+#: editor/editor_run_native.cpp
+msgid "Select device from the list"
+msgstr ""
+
+#: 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 ""
+
+#: editor/editor_run_script.cpp
+msgid "Write your logic in the _run() method."
+msgstr ""
+
+#: editor/editor_run_script.cpp
+msgid "There is an edited scene already."
+msgstr ""
+
+#: editor/editor_run_script.cpp
+msgid "Couldn't instance script:"
+msgstr ""
+
+#: editor/editor_run_script.cpp
+msgid "Did you forget the 'tool' keyword?"
+msgstr ""
+
+#: editor/editor_run_script.cpp
+msgid "Couldn't run script:"
+msgstr ""
+
+#: editor/editor_run_script.cpp
+msgid "Did you forget the '_run' method?"
+msgstr ""
+
+#: editor/editor_settings.cpp
+msgid "Default (Same as Editor)"
+msgstr ""
+
+#: editor/editor_sub_scene.cpp
+msgid "Select Node(s) to Import"
+msgstr ""
+
+#: editor/editor_sub_scene.cpp
+msgid "Scene Path:"
+msgstr ""
+
+#: editor/editor_sub_scene.cpp
+msgid "Import From Node:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Re-Download"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Uninstall"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "(Installed)"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Download"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "(Missing)"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "(Current)"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Retrieving mirrors, please wait..."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Remove template version '%s'?"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Can't open export templates zip."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Invalid version.txt format inside templates."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "No version.txt found inside templates."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Error creating path for templates:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Extracting Export Templates"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Importing:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid ""
+"No download links found for this version. Direct download is only available "
+"for official releases."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Can't resolve."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Can't connect."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "No response."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Request Failed."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Redirect Loop."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Failed:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Download Complete."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Error requesting url: "
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Connecting to Mirror..."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Disconnected"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Resolving"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Can't Resolve"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Connecting..."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Can't Connect"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Connected"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Requesting..."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Downloading"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Connection Error"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "SSL Handshake Error"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Current Version:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Installed Versions:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Install From File"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Remove Template"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Select template file"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Export Template Manager"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Download Templates"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Select mirror from list: "
+msgstr ""
+
+#: editor/file_type_cache.cpp
+msgid "Can't open file_type_cache.cch for writing, not saving file type cache!"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Cannot navigate to '%s' as it has not been found in the file system!"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "View items as a grid of thumbnails"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "View items as a list"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Status: Import of file failed. Please fix file and reimport manually."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Cannot move/rename resources root."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Cannot move a folder into itself."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Error moving:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Error duplicating:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Unable to update dependencies:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "No name provided"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Provided name contains invalid characters"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "No name provided."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Name contains invalid characters."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "A file or folder with this name already exists."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Renaming file:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Renaming folder:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Duplicating file:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Duplicating folder:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Expand all"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Collapse all"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Rename..."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Move To..."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Open Scene(s)"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Instance"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Edit Dependencies..."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "View Owners..."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Duplicate..."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Previous Directory"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Next Directory"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Re-Scan Filesystem"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Toggle folder status as Favorite"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Instance the selected scene(s) as child of the selected node."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid ""
+"Scanning Files,\n"
+"Please Wait..."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Move"
+msgstr ""
+
+#: editor/filesystem_dock.cpp editor/plugins/animation_tree_editor_plugin.cpp
+#: editor/project_manager.cpp
+msgid "Rename"
+msgstr ""
+
+#: editor/groups_editor.cpp
+msgid "Add to Group"
+msgstr ""
+
+#: editor/groups_editor.cpp
+msgid "Remove from Group"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import as Single Scene"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Animations"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Materials"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Objects"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Objects+Materials"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Objects+Animations"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Materials+Animations"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Objects+Materials+Animations"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import as Multiple Scenes"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import as Multiple Scenes+Materials"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+#: editor/plugins/cube_grid_theme_editor_plugin.cpp
+msgid "Import Scene"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Importing Scene..."
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Generating Lightmaps"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Generating for Mesh: "
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Running Custom Script..."
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Couldn't load post-import script:"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Invalid/broken script for post-import (check console):"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Error running post-import script:"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Saving..."
+msgstr ""
+
+#: editor/import_dock.cpp
+msgid "Set as Default for '%s'"
+msgstr ""
+
+#: editor/import_dock.cpp
+msgid "Clear Default for '%s'"
+msgstr ""
+
+#: editor/import_dock.cpp
+msgid " Files"
+msgstr ""
+
+#: editor/import_dock.cpp
+msgid "Import As:"
+msgstr ""
+
+#: editor/import_dock.cpp editor/property_editor.cpp
+msgid "Preset..."
+msgstr ""
+
+#: editor/import_dock.cpp
+msgid "Reimport"
+msgstr ""
+
+#: editor/multi_node_edit.cpp
+msgid "MultiNode Set"
+msgstr ""
+
+#: editor/node_dock.cpp
+msgid "Groups"
+msgstr ""
+
+#: editor/node_dock.cpp
+msgid "Select a Node to edit Signals and Groups."
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Create Poly"
+msgstr ""
+
+#: 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 ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+msgid "Insert Point"
+msgstr ""
+
+#: 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 ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+msgid "Remove Poly And Point"
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+msgid "Create a new polygon from scratch"
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+msgid ""
+"Edit existing polygon:\n"
+"LMB: Move Point.\n"
+"Ctrl+LMB: Split Segment.\n"
+"RMB: Erase Point."
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+msgid "Delete points"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Toggle Autoplay"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "New Animation Name:"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "New Anim"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Change Animation Name:"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Delete Animation?"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Remove Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "ERROR: Invalid animation name!"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "ERROR: Animation name already exists!"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Rename Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Add Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Blend Next Changed"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Change Blend Time"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Load Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Duplicate Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "ERROR: No animation to copy!"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "ERROR: No animation resource on clipboard!"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Pasted Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Paste Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "ERROR: No animation to edit!"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Play selected animation backwards from current pos. (A)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Play selected animation backwards from end. (Shift+A)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Stop animation playback. (S)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Play selected animation from start. (Shift+D)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Play selected animation from current pos. (D)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Animation position (in seconds)."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Scale animation playback globally for the node."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Create new animation in player."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Load animation from disk."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Load an animation from disk."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Save the current animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Display list of animations in player."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Autoplay on Load"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Edit Target Blend Times"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Animation Tools"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Copy Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Onion Skinning"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Enable Onion Skinning"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Directions"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Past"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Future"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Depth"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "1 step"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "2 steps"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "3 steps"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Differences Only"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Force White Modulate"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Include Gizmos (3D)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Create New Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Animation Name:"
+msgstr ""
+
+#: 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 ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Blend Times:"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Next (Auto Queue):"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Cross-Animation Blend Times"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Animation"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "New name:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Edit Filters"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Scale:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Fade In (s):"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Fade Out (s):"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Mix"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Auto Restart:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Restart (s):"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Random Restart (s):"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Start!"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Amount:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend 0:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend 1:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "X-Fade Time (s):"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Current:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Add Input"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Clear Auto-Advance"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Set Auto-Advance"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Delete Input"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Animation tree is valid."
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Animation tree is invalid."
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Animation Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "OneShot Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Mix Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend2 Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend3 Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend4 Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "TimeScale Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "TimeSeek Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Transition Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Import Animations..."
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Edit Node Filters"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Filters..."
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "AnimationTree"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Free"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Contents:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "View Files"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Can't resolve hostname:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Connection error, please try again."
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Can't connect to host:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "No response from host:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Request failed, return code:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Request failed, too many redirects"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Bad download hash, assuming file has been tampered with."
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Expected:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Got:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Failed sha256 hash check"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Asset Download Error:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Fetching:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Resolving..."
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Error making request"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Idle"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Retry"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Download Error"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Download for this asset is already in progress!"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "first"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "prev"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "next"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "last"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "All"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+#: editor/project_settings_editor.cpp
+msgid "Plugins"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Sort:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Reverse"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+#: editor/project_settings_editor.cpp
+msgid "Category:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Site:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Support..."
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Official"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Testing"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Assets ZIP File"
+msgstr ""
+
+#: editor/plugins/baked_lightmap_editor_plugin.cpp
+msgid ""
+"Can't determine a save path for lightmap images.\n"
+"Save your scene (for images to be saved in the same dir), or pick a save "
+"path from the BakedLightmap properties."
+msgstr ""
+
+#: 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 ""
+
+#: editor/plugins/baked_lightmap_editor_plugin.cpp
+msgid "Failed creating lightmap images, make sure path is writable."
+msgstr ""
+
+#: editor/plugins/baked_lightmap_editor_plugin.cpp
+msgid "Bake Lightmaps"
+msgstr ""
+
+#: editor/plugins/camera_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Preview"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Configure Snap"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Grid Offset:"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Grid Step:"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Rotation Offset:"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Rotation Step:"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Move Pivot"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Move Action"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Move vertical guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Create new vertical guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Remove vertical guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Move horizontal guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Create new horizontal guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Remove horizontal guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Create new horizontal and vertical guides"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Edit IK Chain"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Edit CanvasItem"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Anchors only"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Change Anchors and Margins"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Change Anchors"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Paste Pose"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Select Mode"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Drag: Rotate"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Alt+Drag: Move"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving)."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Alt+RMB: Depth list selection"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Move Mode"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Rotate Mode"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid ""
+"Show a list of all objects at the position clicked\n"
+"(same as Alt+RMB in select mode)."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Click to change object's rotation pivot."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Pan Mode"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Toggles snapping"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Use Snap"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snapping options"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to grid"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Use Rotation Snap"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Configure Snap..."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap Relative"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Use Pixel Snap"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Smart snapping"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to parent"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to node anchor"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to node sides"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to other nodes"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to guides"
+msgstr ""
+
+#: 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 ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Unlock the selected object (can be moved)."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Makes sure the object's children are not selectable."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Restores the object's children's ability to be selected."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Make Bones"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Clear Bones"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Bones"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Make IK Chain"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Clear IK Chain"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Show Grid"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Helpers"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Rulers"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Guides"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Center Selection"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Frame Selection"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Layout"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Insert Keys"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Insert Key"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Insert Key (Existing Tracks)"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Copy Pose"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Clear Pose"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Drag pivot from mouse position"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Set pivot at mouse position"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Multiply grid step by 2"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Divide grid step by 2"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Add %s"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Adding %s..."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "Ok"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Cannot instantiate multiple nodes without root."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "Create Node"
+msgstr ""
+
+#: 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 ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Change default type"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid ""
+"Drag & drop + Shift : Add node as sibling\n"
+"Drag & drop + Alt : Change node type"
+msgstr ""
+
+#: editor/plugins/collision_polygon_editor_plugin.cpp
+msgid "Create Poly3D"
+msgstr ""
+
+#: editor/plugins/collision_shape_2d_editor_plugin.cpp
+msgid "Set Handle"
+msgstr ""
+
+#: editor/plugins/cube_grid_theme_editor_plugin.cpp
+msgid "Remove item %d?"
+msgstr ""
+
+#: 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 ""
+
+#: editor/plugins/cube_grid_theme_editor_plugin.cpp
+msgid "Remove Selected Item"
+msgstr ""
+
+#: editor/plugins/cube_grid_theme_editor_plugin.cpp
+msgid "Import from Scene"
+msgstr ""
+
+#: editor/plugins/cube_grid_theme_editor_plugin.cpp
+msgid "Update from Scene"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Flat0"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Flat1"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Ease in"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Ease out"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Smoothstep"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Modify Curve Point"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Modify Curve Tangent"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Load Curve Preset"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Add point"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Remove point"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Left linear"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Right linear"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Load preset"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Remove Curve Point"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Toggle Curve Linear Tangent"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Hold Shift to edit tangents individually"
+msgstr ""
+
+#: editor/plugins/gi_probe_editor_plugin.cpp
+msgid "Bake GI Probe"
+msgstr ""
+
+#: editor/plugins/gradient_editor_plugin.cpp
+msgid "Add/Remove Color Ramp Point"
+msgstr ""
+
+#: editor/plugins/gradient_editor_plugin.cpp
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Modify Color Ramp"
+msgstr ""
+
+#: editor/plugins/item_list_editor_plugin.cpp
+msgid "Item %d"
+msgstr ""
+
+#: editor/plugins/item_list_editor_plugin.cpp
+msgid "Items"
+msgstr ""
+
+#: editor/plugins/item_list_editor_plugin.cpp
+msgid "Item List Editor"
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid ""
+"No OccluderPolygon2D resource on this node.\n"
+"Create and assign one?"
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Create Occluder Polygon"
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Create a new polygon from scratch."
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Edit existing polygon:"
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "LMB: Move Point."
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Ctrl+LMB: Split Segment."
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "RMB: Erase Point."
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh is empty!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Static Trimesh Body"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Static Convex Body"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "This doesn't work on scene root!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Trimesh Shape"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Convex Shape"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Navigation Mesh"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Contained Mesh is not of type ArrayMesh."
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "UV Unwrap failed, mesh may not be manifold?"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "No mesh to debug."
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Model has no UV in this layer"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "MeshInstance lacks a Mesh!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh has not surface to create outlines from!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Could not create outline!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Outline"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Trimesh Static Body"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Convex Static Body"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Trimesh Collision Sibling"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Convex Collision Sibling"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Outline Mesh..."
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "View UV1"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "View UV2"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Unwrap UV2 for Lightmap/AO"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Outline Mesh"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Outline Size:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "No mesh source specified (and no MultiMesh set in node)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "No mesh source specified (and MultiMesh contains no Mesh)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Mesh source is invalid (invalid path)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Mesh source is invalid (not a MeshInstance)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Mesh source is invalid (contains no Mesh resource)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "No surface source specified."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Surface source is invalid (invalid path)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Surface source is invalid (no geometry)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Surface source is invalid (no faces)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Parent has no solid faces to populate."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Couldn't map area."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Select a Source Mesh:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Select a Target Surface:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Populate Surface"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Populate MultiMesh"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Target Surface:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Source Mesh:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "X-Axis"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Y-Axis"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Z-Axis"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Mesh Up Axis:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Random Rotation:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Random Tilt:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Random Scale:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Populate"
+msgstr ""
+
+#: editor/plugins/navigation_mesh_editor_plugin.cpp
+msgid "Bake!"
+msgstr ""
+
+#: editor/plugins/navigation_mesh_editor_plugin.cpp
+msgid "Bake the navigation mesh."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_editor_plugin.cpp
+msgid "Clear the navigation mesh."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Setting up Configuration..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Calculating grid size..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Creating heightfield..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Marking walkable triangles..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Constructing compact heightfield..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Eroding walkable area..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Partitioning..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Creating contours..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Creating polymesh..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Converting to native navigation mesh..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Navigation Mesh Generator Setup:"
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Parsing Geometry..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Done!"
+msgstr ""
+
+#: editor/plugins/navigation_polygon_editor_plugin.cpp
+msgid "Create Navigation Polygon"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Generating AABB"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Can only set point into a ParticlesMaterial process material"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Error loading image:"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "No pixels with transparency > 128 in image..."
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Generate Visibility Rect"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Load Emission Mask"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Clear Emission Mask"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Particles"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Generated Point Count:"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Generation Time (sec):"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Emission Mask"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Capture from Pixel"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Emission Colors"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Node does not contain geometry."
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Node does not contain geometry (faces)."
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "A processor material of type 'ParticlesMaterial' is required."
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Faces contain no area!"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "No faces!"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Generate AABB"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Create Emission Points From Mesh"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Create Emission Points From Node"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Create Emitter"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Emission Points:"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Surface Points"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Surface Points+Normal (Directed)"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Volume"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Emission Source: "
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Generate Visibility AABB"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Remove Point from Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Remove Out-Control from Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Remove In-Control from Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Add Point to Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Move Point in Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Move In-Control in Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Move Out-Control in Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Select Points"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Shift+Drag: Select Control Points"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Click: Add Point"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Right Click: Delete Point"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Select Control Points (Shift+Drag)"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Add Point (in empty space)"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Split Segment (in curve)"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Delete Point"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Close Curve"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Curve Point #"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Set Curve Point Position"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Set Curve In Position"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Set Curve Out Position"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Split Path"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Remove Path Point"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Remove Out-Control Point"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Remove In-Control Point"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Create UV Map"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Transform UV Map"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Polygon 2D UV Editor"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Move Point"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Ctrl: Rotate"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Shift: Move All"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Shift+Ctrl: Scale"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Move Polygon"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Rotate Polygon"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Scale Polygon"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp
+#: editor/plugins/shader_editor_plugin.cpp editor/project_manager.cpp
+#: editor/project_settings_editor.cpp editor/property_editor.cpp
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Edit"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Polygon->UV"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "UV->Polygon"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Clear UV"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Snap"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Enable Snap"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Grid"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "ERROR: Couldn't load resource!"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "Add Resource"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "Rename Resource"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Delete Resource"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "Resource clipboard is empty!"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/scene_tree_dock.cpp editor/scene_tree_editor.cpp
+msgid "Open in Editor"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/scene_tree_editor.cpp
+msgid "Instance:"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp
+#: editor/scene_tree_editor.cpp editor/script_editor_debugger.cpp
+msgid "Type:"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Load Resource"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp editor/property_editor.cpp
+#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
+msgid "Paste"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "ResourcePreloader"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Clear Recent Files"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Close and save changes?"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Error while saving theme"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Error saving"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Error importing theme"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Error importing"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Import Theme"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Save Theme As..."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid " Class Reference"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Sort"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp editor/scene_tree_dock.cpp
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Move Up"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp editor/scene_tree_dock.cpp
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Move Down"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Next script"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Previous script"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "File"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "New"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Save All"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Soft Reload Script"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Copy Script Path"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Show In File System"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "History Prev"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "History Next"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Reload Theme"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Save Theme"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Save Theme As"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Close Docs"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Close All"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Close Other Tabs"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp
+msgid "Run"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Toggle Scripts Panel"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp
+msgid "Find..."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp
+msgid "Find Next"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
+msgid "Step Over"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
+msgid "Step Into"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
+msgid "Break"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp
+#: editor/script_editor_debugger.cpp
+msgid "Continue"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Keep Debugger Open"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Debug with external editor"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Open Godot online documentation"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Search the class hierarchy."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Search the reference documentation."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Go to previous edited document."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Go to next edited document."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Discard"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Create Script"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid ""
+"The following files are newer on disk.\n"
+"What action should be taken?:"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Reload"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Resave"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
+msgid "Debugger"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid ""
+"Built-in scripts can only be edited when the scene they belong to is loaded"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Only resources from filesystem can be dropped."
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Pick Color"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Convert Case"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Uppercase"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Lowercase"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Capitalize"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp
+#: scene/gui/text_edit.cpp
+msgid "Cut"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp editor/property_editor.cpp
+#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
+msgid "Copy"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp
+#: scene/gui/text_edit.cpp
+msgid "Select All"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Delete Line"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Indent Left"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Indent Right"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Toggle Comment"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Fold/Unfold Line"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Fold All Lines"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Unfold All Lines"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Clone Down"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Complete Symbol"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Trim Trailing Whitespace"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Convert Indent To Spaces"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Convert Indent To Tabs"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Auto Indent"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Toggle Breakpoint"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Remove All Breakpoints"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Goto Next Breakpoint"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Goto Previous Breakpoint"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Convert To Uppercase"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Convert To Lowercase"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Find Previous"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Replace..."
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Goto Function..."
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Goto Line..."
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Contextual Help"
+msgstr ""
+
+#: editor/plugins/shader_editor_plugin.cpp
+msgid "Shader"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Scalar Constant"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Vec Constant"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change RGB Constant"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Scalar Operator"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Vec Operator"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Vec Scalar Operator"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change RGB Operator"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Toggle Rot Only"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Scalar Function"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Vec Function"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Scalar Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Vec Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change RGB Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Default Value"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change XForm Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Texture Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Cubemap Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Comment"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Add/Remove to Color Ramp"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Add/Remove to Curve Map"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Modify Curve Map"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Input Name"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Connect Graph Nodes"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Disconnect Graph Nodes"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Remove Shader Graph Node"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Move Shader Graph Node"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Duplicate Graph Node(s)"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Delete Shader Graph Node(s)"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Error: Cyclic Connection Link"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Error: Missing Input Connections"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Add Shader Graph Node"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Orthogonal"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Perspective"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Transform Aborted."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "X-Axis Transform."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Y-Axis Transform."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Z-Axis Transform."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Plane Transform."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Scaling: "
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Translating: "
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rotating %s degrees."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Keying is disabled (no key inserted)."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Animation Key Inserted."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Objects Drawn"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Material Changes"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Shader Changes"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Surface Changes"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Draw Calls"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Vertices"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "FPS"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Top View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Bottom View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Bottom"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Left View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Left"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Right View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Right"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Front View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Front"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rear View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rear"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Align with view"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "OK :("
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "No parent to instance a child at."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "This operation requires a single selected node."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Display Normal"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Display Wireframe"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Display Overdraw"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Display Unshaded"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Environment"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Gizmos"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Information"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View FPS"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Half Resolution"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Audio Listener"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Doppler Enable"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Left"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Right"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Forward"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Backwards"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Up"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Down"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Speed Modifier"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "XForm Dialog"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Select Mode (Q)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid ""
+"Drag: Rotate\n"
+"Alt+Drag: Move\n"
+"Alt+RMB: Depth list selection"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Move Mode (W)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rotate Mode (E)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Scale Mode (R)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Local Coords"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Local Space Mode (%s)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Snap Mode (%s)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Bottom View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Top View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rear View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Front View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Left View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Right View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Switch Perspective/Orthogonal view"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Insert Animation Key"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Focus Origin"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Focus Selection"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Align Selection With View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Tool Select"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Tool Move"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Tool Rotate"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Tool Scale"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Toggle Freelook"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Transform"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Transform Dialog..."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "1 Viewport"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "2 Viewports"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "2 Viewports (Alt)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "3 Viewports"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "3 Viewports (Alt)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "4 Viewports"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Origin"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Grid"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Settings"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Skeleton Gizmo visibility"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Snap Settings"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Translate Snap:"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rotate Snap (deg.):"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Scale Snap (%):"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Viewport Settings"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Perspective FOV (deg.):"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Z-Near:"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Z-Far:"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Transform Change"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Translate:"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rotate (deg.):"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Scale (ratio):"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Transform Type"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Pre"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Post"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "ERROR: Couldn't load frame resource!"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Add Frame"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Resource clipboard is empty or not a texture!"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Paste Frame"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Add Empty"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Change Animation Loop"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Change Animation FPS"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "(empty)"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Animations"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Speed (FPS):"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Loop"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Animation Frames"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Insert Empty (Before)"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Insert Empty (After)"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Move (Before)"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Move (After)"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "SpriteFrames"
+msgstr ""
+
+#: editor/plugins/style_box_editor_plugin.cpp
+msgid "StyleBox Preview:"
+msgstr ""
+
+#: editor/plugins/style_box_editor_plugin.cpp
+msgid "StyleBox"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Set Region Rect"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Snap Mode:"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "<None>"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Pixel Snap"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Grid Snap"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Auto Slice"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Offset:"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Step:"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Separation:"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Texture Region"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Texture Region Editor"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Can't save theme to file:"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Add All Items"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Add All"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Remove Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Remove All Items"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Remove All"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Edit theme..."
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Theme editing menu."
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Add Class Items"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Remove Class Items"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Create Empty Template"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Create Empty Editor Template"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Create From Current Editor Theme"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "CheckBox Radio1"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "CheckBox Radio2"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Check Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Has"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Many"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
+msgid "Options"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Has,Many,Options"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Tab 1"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Tab 2"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Tab 3"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Data Type:"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Icon"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Style"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Font"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Color"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Theme"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Erase Selection"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Paint TileMap"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Line Draw"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Rectangle Paint"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Bucket Fill"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Erase TileMap"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Erase selection"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Find tile"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Transpose"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Mirror X"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Mirror Y"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Paint Tile"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Pick Tile"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Rotate 0 degrees"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Rotate 90 degrees"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Rotate 180 degrees"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Rotate 270 degrees"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Could not find tile:"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Item name or ID:"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Create from scene?"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Merge from scene?"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Tile Set"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Create from Scene"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Merge from Scene"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp editor/script_editor_debugger.cpp
+msgid "Error"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Autotiles"
+msgstr ""
+
+#: 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 ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid ""
+"LMB: set bit on.\n"
+"RMB: set bit off."
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Select current edited sub-tile."
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Select sub-tile to change its priority."
+msgstr ""
+
+#: editor/progress_dialog.cpp scene/gui/dialogs.cpp
+msgid "Cancel"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Runnable"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Delete patch '%s' from list?"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Delete preset '%s'?"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export templates for this platform are missing/corrupted: "
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Presets"
+msgstr ""
+
+#: editor/project_export.cpp editor/project_settings_editor.cpp
+msgid "Add..."
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Resources"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export all resources in the project"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export selected scenes (and dependencies)"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export selected resources (and dependencies)"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export Mode:"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Resources to export:"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid ""
+"Filters to export non-resource files (comma separated, e.g: *.json, *.txt)"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid ""
+"Filters to exclude files from project (comma separated, e.g: *.json, *.txt)"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Patches"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Make Patch"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Features"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Custom (comma-separated):"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Feature List:"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export PCK/Zip"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export templates for this platform are missing:"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export templates for this platform are missing/corrupted:"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export With Debug"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "The path does not exist."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Please choose a 'project.godot' file."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Please choose an empty folder."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Imported Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Couldn't create folder."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "There is already a folder in this path with the specified name."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "It would be a good idea to name your project."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Invalid project path (changed anything?)."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid ""
+"Couldn't load project.godot in project path (error %d). It may be missing or "
+"corrupted."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Couldn't edit project.godot in project path."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Couldn't create project.godot in project path."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "The following files failed extraction from package:"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Rename Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "New Game Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Import Existing Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Import & Edit"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Create New Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Create & Edit"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Install Project:"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Install & Edit"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Project Name:"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Create folder"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Project Path:"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Browse"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Unnamed Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Can't open project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Are you sure to open more than one project?"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid ""
+"Can't run project: no main scene defined.\n"
+"Please edit the project and set the main scene in \"Project Settings\" under "
+"the \"Application\" category."
+msgstr ""
+
+#: 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 ""
+
+#: editor/project_manager.cpp
+msgid "Are you sure to run more than one project?"
+msgstr ""
+
+#: editor/project_manager.cpp
+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 ""
+
+#: editor/project_manager.cpp
+msgid ""
+"You are about the scan %s folders for existing Godot projects. Do you "
+"confirm?"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Project Manager"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Project List"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Scan"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Select a Folder to Scan"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "New Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Templates"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Exit"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Restart Now"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Can't run project"
+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 ""
+
+#: editor/project_settings_editor.cpp
+msgid "Key "
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Joy Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Joy Axis"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Mouse Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid ""
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Action '%s' already exists!"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Rename Input Action Event"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Input Action Event"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
+msgid "Shift+"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
+msgid "Alt+"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
+msgid "Control+"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
+msgid "Press a Key..."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Mouse Button Index:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Left Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Right Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Middle Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Wheel Up Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Wheel Down Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Button 6"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Button 7"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Button 8"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Button 9"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Joypad Axis Index:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Axis"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Joypad Button Index:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Erase Input Action"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Erase Input Action Event"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Event"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Device"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Left Button."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Right Button."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Middle Button."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Wheel Up."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Wheel Down."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Global Property"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Select a setting item first!"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "No property '%s' exists."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Setting '%s' is internal, and it can't be deleted."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Delete Item"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Already existing"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Input Action"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Error saving settings."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Settings saved OK."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Override for Feature"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Translation"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Remove Translation"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Remapped Path"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Resource Remap Add Remap"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Change Resource Remap Language"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Remove Resource Remap"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Remove Resource Remap Option"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Changed Locale Filter"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Changed Locale Filter Mode"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Project Settings (project.godot)"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
+msgid "General"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/property_editor.cpp
+msgid "Property:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Override For..."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Input Map"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Action:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Device:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Index:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Localization"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Translations"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Translations:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Remaps"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Resources:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Remaps by Locale:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Locale"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Locales Filter"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Show all locales"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Show only selected locales"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Filter mode:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Locales:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "AutoLoad"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Pick a Viewport"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Ease In"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Ease Out"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Zero"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Easing In-Out"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Easing Out-In"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "File..."
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Dir..."
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Assign"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Select Node"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "New Script"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "New %s"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Make Unique"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Show in File System"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Convert To %s"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Error loading file: Not a resource!"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Selected node is not a Viewport!"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Pick a Node"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Bit %d, val %d."
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "On"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "[Empty]"
+msgstr ""
+
+#: editor/property_editor.cpp modules/visual_script/visual_script_editor.cpp
+msgid "Set"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Properties:"
+msgstr ""
+
+#: editor/property_selector.cpp
+msgid "Select Property"
+msgstr ""
+
+#: editor/property_selector.cpp
+msgid "Select Virtual Method"
+msgstr ""
+
+#: editor/property_selector.cpp
+msgid "Select Method"
+msgstr ""
+
+#: editor/pvrtc_compress.cpp
+msgid "Could not execute PVRTC tool:"
+msgstr ""
+
+#: editor/pvrtc_compress.cpp
+msgid "Can't load back converted image using PVRTC tool:"
+msgstr ""
+
+#: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp
+msgid "Reparent Node"
+msgstr ""
+
+#: editor/reparent_dialog.cpp
+msgid "Reparent Location (Select new Parent):"
+msgstr ""
+
+#: editor/reparent_dialog.cpp
+msgid "Keep Global Transform"
+msgstr ""
+
+#: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp
+msgid "Reparent"
+msgstr ""
+
+#: editor/run_settings_dialog.cpp
+msgid "Run Mode:"
+msgstr ""
+
+#: editor/run_settings_dialog.cpp
+msgid "Current Scene"
+msgstr ""
+
+#: editor/run_settings_dialog.cpp
+msgid "Main Scene"
+msgstr ""
+
+#: editor/run_settings_dialog.cpp
+msgid "Main Scene Arguments:"
+msgstr ""
+
+#: editor/run_settings_dialog.cpp
+msgid "Scene Run Settings"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp editor/script_create_dialog.cpp
+#: scene/gui/dialogs.cpp
+msgid "OK"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "No parent to instance the scenes at."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Error loading scene from %s"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid ""
+"Cannot instance the scene '%s' because the current scene exists within one "
+"of its nodes."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Instance Scene(s)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "This operation can't be done on the tree root."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Move Node In Parent"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Move Nodes In Parent"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Duplicate Node(s)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Delete Node(s)?"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Can not perform with the root node."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "This operation can't be done on instanced scenes."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Save New Scene As..."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Editable Children"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Load As Placeholder"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Discard Instancing"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Makes Sense!"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Can't operate on nodes from a foreign scene!"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Can't operate on nodes the current scene inherits from!"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Remove Node(s)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid ""
+"Couldn't save new scene. Likely dependencies (instances) couldn't be "
+"satisfied."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Error saving scene."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Error duplicating scene to save it."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Sub-Resources"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Clear Inheritance"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Delete Node(s)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Add Child Node"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Instance Child Scene"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Change Type"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Attach Script"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Clear Script"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Merge From Scene"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Save Branch as Scene"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Copy Node Path"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Delete (No Confirm)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Add/Create a New Node"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid ""
+"Instance a scene file as a Node. Creates an inherited scene if no root node "
+"exists."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Filter nodes"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Attach a new or existing script for the selected node."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Clear a script for the selected node."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Remote"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Local"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Clear Inheritance? (No Undo!)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Clear!"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Toggle Spatial Visible"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Toggle CanvasItem Visible"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Node configuration warning:"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid ""
+"Node has connection(s) and group(s)\n"
+"Click to show signals dock."
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid ""
+"Node has connections.\n"
+"Click to show signals dock."
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid ""
+"Node is in group(s).\n"
+"Click to show groups dock."
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Open script"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid ""
+"Node is locked.\n"
+"Click to unlock"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid ""
+"Children are not selectable.\n"
+"Click to make selectable"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Toggle Visibility"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Invalid node name, the following characters are not allowed:"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Rename Node"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Scene Tree (Nodes):"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Node Configuration Warning!"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Select a Node"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Error loading template '%s'"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Error - Could not create script in filesystem."
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Error loading script from %s"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "N/A"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Path is empty"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Path is not local"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Invalid base path"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Directory of the same name exists"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "File exists, will be reused"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Invalid extension"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Wrong extension chosen"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Invalid Path"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Invalid class name"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Invalid inherited parent name or path"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Script valid"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Allowed: a-z, A-Z, 0-9 and _"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Built-in script (into scene file)"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Create new script file"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Load existing script file"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Language"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Inherits"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Class Name"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Template"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Built-in Script"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Attach Node Script"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Remote "
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Bytes:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Warning"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Error:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Source:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Function:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Pick one or more items from the list to display the graph."
+msgstr ""
+
+#: editor/script_editor_debugger.cpp modules/mono/editor/mono_bottom_panel.cpp
+msgid "Errors"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Child Process Connected"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Copy Error"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Inspect Previous Instance"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Inspect Next Instance"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Stack Frames"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Variable"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Errors:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Stack Trace (if applicable):"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Profiler"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Monitor"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Value"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Monitors"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "List of Video Memory Usage by Resource:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Total:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Video Mem"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Resource Path"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Type"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Format"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Usage"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Misc"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Clicked Control:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Clicked Control Type:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Live Edit Root:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Set From Tree"
+msgstr ""
+
+#: editor/settings_config_dialog.cpp
+msgid "Shortcuts"
+msgstr ""
+
+#: editor/settings_config_dialog.cpp
+msgid "Binding"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Light Radius"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change AudioStreamPlayer3D Emission Angle"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Camera FOV"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Camera Size"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Sphere Shape Radius"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Box Shape Extents"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Capsule Shape Radius"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Capsule Shape Height"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Ray Shape Length"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Notifier Extents"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Particles AABB"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Probe Extents"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Select the dynamic library for this entry"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Select dependencies of the library for this entry"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Remove current entry"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Double click to create a new entry"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Platform:"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Platform"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Dynamic Library"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Add an architecture entry"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "GDNativeLibrary"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_singleton_editor.cpp
+msgid "Library"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_singleton_editor.cpp
+msgid "Status"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_singleton_editor.cpp
+msgid "Libraries: "
+msgstr ""
+
+#: modules/gdnative/register_types.cpp
+msgid "GDNative"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+#: modules/visual_script/visual_script_builtin_funcs.cpp
+msgid "Invalid type argument to convert(), use TYPE_* constants."
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp modules/mono/glue/glue_header.h
+#: modules/visual_script/visual_script_builtin_funcs.cpp
+msgid "Not enough bytes for decoding bytes, or invalid format."
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "step argument is zero!"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Not a script with an instance"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Not based on a script"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Not based on a resource file"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Invalid instance dictionary format (missing @path)"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Invalid instance dictionary format (can't load script at @path)"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Invalid instance dictionary format (invalid script at @path)"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Invalid instance dictionary (invalid subclasses)"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Object can't provide a length."
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Next Plane"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Previous Plane"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Plane:"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Next Floor"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Previous Floor"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Floor:"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "GridMap Delete Selection"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "GridMap Duplicate Selection"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Grid Map"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Snap View"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Clip Disabled"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Clip Above"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Clip Below"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Edit X Axis"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Edit Y Axis"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Edit Z Axis"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Rotate X"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Rotate Y"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Rotate Z"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Back Rotate X"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Back Rotate Y"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Back Rotate Z"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Clear Rotation"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Create Area"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Create Exterior Connector"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Erase Area"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Clear Selection"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "GridMap Settings"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Pick Distance:"
+msgstr ""
+
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Generating solution..."
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Generating C# project..."
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Failed to create solution."
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Failed to save solution."
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Done"
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Failed to create C# project."
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Mono"
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "About C# support"
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Create C# solution"
+msgstr ""
+
+#: modules/mono/editor/mono_bottom_panel.cpp
+msgid "Builds"
+msgstr ""
+
+#: modules/mono/editor/mono_bottom_panel.cpp
+msgid "Build Project"
+msgstr ""
+
+#: modules/mono/editor/mono_bottom_panel.cpp
+msgid "Warnings"
+msgstr ""
+
+#: modules/mono/mono_gd/gd_mono_utils.cpp
+msgid "End of inner exception stack trace"
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid ""
+"A node yielded without working memory, please read the docs on how to yield "
+"properly!"
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid ""
+"Node yielded, but did not return a function state in the first working "
+"memory."
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid ""
+"Return value must be assigned to first element of node working memory! Fix "
+"your node please."
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid "Node returned an invalid sequence output: "
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid "Found sequence bit but not the node in the stack, report bug!"
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid "Stack overflow with stack depth: "
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Signal Arguments"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Argument Type"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Argument name"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Set Variable Default Value"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Set Variable Type"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Functions:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Variables:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Name is not a valid identifier:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Name already in use by another func/var/signal:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Rename Function"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Rename Variable"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Rename Signal"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Function"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Variable"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Signal"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Expression"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Node"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Remove VisualScript Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Duplicate VisualScript Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold %s to drop a Getter. Hold Shift to drop a generic signature."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold %s to drop a simple reference to the node."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold Ctrl to drop a simple reference to the node."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold %s to drop a Variable Setter."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold Ctrl to drop a Variable Setter."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Preload Node"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Node(s) From Tree"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Getter Property"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Setter Property"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Base Type"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Move Node(s)"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Remove VisualScript Node"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Connect Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Condition"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Sequence"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Switch"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Iterator"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "While"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Return"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Call"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Get"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Script already has function '%s'"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Input Value"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Can't copy the function node."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Clipboard is empty!"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Paste VisualScript Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Remove Function"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Edit Variable"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Remove Variable"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Edit Signal"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Remove Signal"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Editing Variable:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Editing Signal:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Base Type:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Available Nodes:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Select or create a function to edit graph"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Edit Signal Arguments:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Edit Variable:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Delete Selected"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Find Node Type"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Copy Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Cut Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Paste Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_flow_control.cpp
+msgid "Input type not iterable: "
+msgstr ""
+
+#: modules/visual_script/visual_script_flow_control.cpp
+msgid "Iterator became invalid"
+msgstr ""
+
+#: modules/visual_script/visual_script_flow_control.cpp
+msgid "Iterator became invalid: "
+msgstr ""
+
+#: modules/visual_script/visual_script_func_nodes.cpp
+msgid "Invalid index property name."
+msgstr ""
+
+#: modules/visual_script/visual_script_func_nodes.cpp
+msgid "Base object is not a Node!"
+msgstr ""
+
+#: modules/visual_script/visual_script_func_nodes.cpp
+msgid "Path does not lead Node!"
+msgstr ""
+
+#: modules/visual_script/visual_script_func_nodes.cpp
+msgid "Invalid index property name '%s' in node %s."
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid ": Invalid argument of type: "
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid ": Invalid arguments: "
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid "VariableGet not found in script: "
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid "VariableSet not found in script: "
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid "Custom node has no _step() method, can't process graph."
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid ""
+"Invalid return value from _step(), must be integer (seq out), or string "
+"(error)."
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Run in Browser"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Run exported HTML in the system's default browser."
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Could not write file:"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Could not open template for export:"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Invalid export template:"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Could not read custom HTML shell:"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Could not read boot splash image file:"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Using default boot splash image."
+msgstr ""
+
+#: scene/2d/animated_sprite.cpp
+msgid ""
+"A SpriteFrames resource must be created or set in the 'Frames' property in "
+"order for AnimatedSprite to display frames."
+msgstr ""
+
+#: scene/2d/canvas_modulate.cpp
+msgid ""
+"Only one visible CanvasModulate is allowed per scene (or set of instanced "
+"scenes). The first created one will work, while the rest will be ignored."
+msgstr ""
+
+#: scene/2d/collision_object_2d.cpp
+msgid ""
+"This node has no children shapes, so it can't interact with the space.\n"
+"Consider adding CollisionShape2D or CollisionPolygon2D children nodes to "
+"define its shape."
+msgstr ""
+
+#: scene/2d/collision_polygon_2d.cpp
+msgid ""
+"CollisionPolygon2D only serves to provide a collision shape to a "
+"CollisionObject2D derived node. Please only use it as a child of Area2D, "
+"StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."
+msgstr ""
+
+#: scene/2d/collision_polygon_2d.cpp
+msgid "An empty CollisionPolygon2D has no effect on collision."
+msgstr ""
+
+#: scene/2d/collision_shape_2d.cpp
+msgid ""
+"CollisionShape2D only serves to provide a collision shape to a "
+"CollisionObject2D derived node. Please only use it as a child of Area2D, "
+"StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."
+msgstr ""
+
+#: scene/2d/collision_shape_2d.cpp
+msgid ""
+"A shape must be provided for CollisionShape2D to function. Please create a "
+"shape resource for it!"
+msgstr ""
+
+#: scene/2d/light_2d.cpp
+msgid ""
+"A texture with the shape of the light must be supplied to the 'texture' "
+"property."
+msgstr ""
+
+#: scene/2d/light_occluder_2d.cpp
+msgid ""
+"An occluder polygon must be set (or drawn) for this occluder to take effect."
+msgstr ""
+
+#: scene/2d/light_occluder_2d.cpp
+msgid "The occluder polygon for this occluder is empty. Please draw a polygon!"
+msgstr ""
+
+#: scene/2d/navigation_polygon.cpp
+msgid ""
+"A NavigationPolygon resource must be set or created for this node to work. "
+"Please set a property or draw a polygon."
+msgstr ""
+
+#: scene/2d/navigation_polygon.cpp
+msgid ""
+"NavigationPolygonInstance must be a child or grandchild to a Navigation2D "
+"node. It only provides navigation data."
+msgstr ""
+
+#: scene/2d/parallax_layer.cpp
+msgid ""
+"ParallaxLayer node only works when set as child of a ParallaxBackground node."
+msgstr ""
+
+#: scene/2d/particles_2d.cpp scene/3d/particles.cpp
+msgid ""
+"A material to process the particles is not assigned, so no behavior is "
+"imprinted."
+msgstr ""
+
+#: scene/2d/path_2d.cpp
+msgid "PathFollow2D only works when set as a child of a Path2D node."
+msgstr ""
+
+#: scene/2d/physics_body_2d.cpp
+msgid ""
+"Size changes to RigidBody2D (in character or rigid modes) will be overridden "
+"by the physics engine when running.\n"
+"Change the size in children collision shapes instead."
+msgstr ""
+
+#: scene/2d/remote_transform_2d.cpp
+msgid "Path property must point to a valid Node2D node to work."
+msgstr ""
+
+#: scene/2d/visibility_notifier_2d.cpp
+msgid ""
+"VisibilityEnable2D works best when used with the edited scene root directly "
+"as parent."
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid "ARVRCamera must have an ARVROrigin node as its parent"
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid "ARVRController must have an ARVROrigin node as its parent"
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid ""
+"The controller id must not be 0 or this controller will not be bound to an "
+"actual controller"
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid "ARVRAnchor must have an ARVROrigin node as its parent"
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid ""
+"The anchor id must not be 0 or this anchor will not be bound to an actual "
+"anchor"
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid "ARVROrigin requires an ARVRCamera child node"
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp
+msgid "%d%%"
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp
+msgid "(Time Left: %d:%02d s)"
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp
+msgid "Plotting Meshes: "
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp
+msgid "Plotting Lights:"
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp scene/3d/gi_probe.cpp
+msgid "Finishing Plot"
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp
+msgid "Lighting Meshes: "
+msgstr ""
+
+#: scene/3d/collision_object.cpp
+msgid ""
+"This node has no children shapes, so it can't interact with the space.\n"
+"Consider adding CollisionShape or CollisionPolygon children nodes to define "
+"its shape."
+msgstr ""
+
+#: scene/3d/collision_polygon.cpp
+msgid ""
+"CollisionPolygon only serves to provide a collision shape to a "
+"CollisionObject derived node. Please only use it as a child of Area, "
+"StaticBody, RigidBody, KinematicBody, etc. to give them a shape."
+msgstr ""
+
+#: scene/3d/collision_polygon.cpp
+msgid "An empty CollisionPolygon has no effect on collision."
+msgstr ""
+
+#: scene/3d/collision_shape.cpp
+msgid ""
+"CollisionShape only serves to provide a collision shape to a CollisionObject "
+"derived node. Please only use it as a child of Area, StaticBody, RigidBody, "
+"KinematicBody, etc. to give them a shape."
+msgstr ""
+
+#: scene/3d/collision_shape.cpp
+msgid ""
+"A shape must be provided for CollisionShape to function. Please create a "
+"shape resource for it!"
+msgstr ""
+
+#: scene/3d/gi_probe.cpp
+msgid "Plotting Meshes"
+msgstr ""
+
+#: scene/3d/navigation_mesh.cpp
+msgid "A NavigationMesh resource must be set or created for this node to work."
+msgstr ""
+
+#: scene/3d/navigation_mesh.cpp
+msgid ""
+"NavigationMeshInstance must be a child or grandchild to a Navigation node. "
+"It only provides navigation data."
+msgstr ""
+
+#: scene/3d/particles.cpp
+msgid ""
+"Nothing is visible because meshes have not been assigned to draw passes."
+msgstr ""
+
+#: scene/3d/physics_body.cpp
+msgid ""
+"Size changes to RigidBody (in character or rigid modes) will be overridden "
+"by the physics engine when running.\n"
+"Change the size in children collision shapes instead."
+msgstr ""
+
+#: scene/3d/remote_transform.cpp
+msgid "Path property must point to a valid Spatial node to work."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
+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 ""
+
+#: scene/3d/sprite_3d.cpp
+msgid ""
+"A SpriteFrames resource must be created or set in the 'Frames' property in "
+"order for AnimatedSprite3D to display frames."
+msgstr ""
+
+#: scene/3d/vehicle_body.cpp
+msgid ""
+"VehicleWheel serves to provide a wheel system to a VehicleBody. Please use "
+"it as a child of a VehicleBody."
+msgstr ""
+
+#: scene/gui/color_picker.cpp
+msgid "Raw Mode"
+msgstr ""
+
+#: scene/gui/color_picker.cpp
+msgid "Add current color as a preset"
+msgstr ""
+
+#: scene/gui/dialogs.cpp
+msgid "Alert!"
+msgstr ""
+
+#: scene/gui/dialogs.cpp
+msgid "Please Confirm..."
+msgstr ""
+
+#: scene/gui/file_dialog.cpp
+msgid "Select this Folder"
+msgstr ""
+
+#: scene/gui/popup.cpp
+msgid ""
+"Popups will hide by default unless you call popup() or any of the popup*() "
+"functions. Making them visible for editing is fine though, but they will "
+"hide upon running."
+msgstr ""
+
+#: scene/gui/scroll_container.cpp
+msgid ""
+"ScrollContainer is intended to work with a single child control.\n"
+"Use a container as child (VBox,HBox,etc), or a Control and set the custom "
+"minimum size manually."
+msgstr ""
+
+#: scene/gui/tree.cpp
+msgid "(Other)"
+msgstr ""
+
+#: scene/main/scene_tree.cpp
+msgid ""
+"Default Environment as specified in Project Settings (Rendering -> "
+"Environment -> Default Environment) could not be loaded."
+msgstr ""
+
+#: scene/main/viewport.cpp
+msgid ""
+"This viewport is not set as render target. If you intend for it to display "
+"its contents directly to the screen, make it a child of a Control so it can "
+"obtain a size. Otherwise, make it a RenderTarget and assign its internal "
+"texture to some node for display."
+msgstr ""
+
+#: scene/resources/dynamic_font.cpp
+msgid "Error initializing FreeType."
+msgstr ""
+
+#: scene/resources/dynamic_font.cpp
+msgid "Unknown font format."
+msgstr ""
+
+#: scene/resources/dynamic_font.cpp
+msgid "Error loading font."
+msgstr ""
+
+#: scene/resources/dynamic_font.cpp
+msgid "Invalid font size."
+msgstr ""
diff --git a/editor/translations/nb.po b/editor/translations/nb.po
index 17123dc8fc..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,13 +498,12 @@ 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.."
-msgstr "Koble Til.."
+msgid "Connect..."
+msgstr "Koble Til..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -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å"
@@ -935,12 +925,12 @@ msgid "Move Audio Bus"
msgstr "Flytt Audio Bus"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "Lagre Audio Bus Oppsett som..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Plassering for nytt oppsett.."
+msgid "Location for New Layout..."
+msgstr "Plassering for nytt oppsett..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1078,12 +1068,12 @@ msgid "Updating Scene"
msgstr "Oppdaterer Scene"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Lagrer lokale endringer.."
+msgid "Storing local changes..."
+msgstr "Lagrer lokale endringer..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Oppdaterer scene.."
+msgid "Updating scene..."
+msgstr "Oppdaterer scene..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -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?"
@@ -1152,8 +1141,8 @@ msgid "Show In File Manager"
msgstr "Vis I Filutforsker"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Ny Mappe.."
+msgid "New Folder..."
+msgstr "Ny Mappe..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -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,27 +1398,25 @@ 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!"
msgstr "Feil ved lagring av ressurs!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Lagre Ressurs Som.."
+msgid "Save Resource As..."
+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.."
+msgid "I see..."
+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
@@ -1665,12 +1646,12 @@ msgid "Open Base Scene"
msgstr "Ã…pne Base Scene"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Hurtigåpne Scene.."
+msgid "Quick Open Scene..."
+msgstr "Hurtigåpne Scene..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Hurtigåpne Skript.."
+msgid "Quick Open Script..."
+msgstr "Hurtigåpne Skript..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1681,8 +1662,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Lagre endringer til '%s' før lukking?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Lagre Scene Som.."
+msgid "Save Scene As..."
+msgstr "Lagre Scene Som..."
#: editor/editor_node.cpp
msgid "No"
@@ -1733,8 +1714,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Denne handlingen kan ikke angres. GÃ¥ tilbake likevel?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Hurtigkjør Scene.."
+msgid "Quick Run Scene..."
+msgstr "Hurtigkjør Scene..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1887,8 +1868,8 @@ msgid "Previous tab"
msgstr "Forrige fane"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Filtrer Filer.."
+msgid "Filter Files..."
+msgstr "Filtrer Filer..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1899,12 +1880,12 @@ msgid "New Scene"
msgstr "Ny Scene"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Ny Arvet Scene.."
+msgid "New Inherited Scene..."
+msgstr "Ny Arvet Scene..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Ã…pne Scene.."
+msgid "Open Scene..."
+msgstr "Ã…pne Scene..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1923,17 +1904,17 @@ msgid "Open Recent"
msgstr "Ã…pne Nylig"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Konverter Til.."
+msgid "Convert To..."
+msgstr "Konverter Til..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshBibliotek.."
+msgid "MeshLibrary..."
+msgstr "MeshBibliotek..."
#: editor/editor_node.cpp
#, fuzzy
-msgid "TileSet.."
-msgstr "TileSet.."
+msgid "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."
@@ -2205,8 +2185,8 @@ msgid "Save the currently edited resource."
msgstr "Lagre den nylige redigerte ressursen."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Lagre Som.."
+msgid "Save As..."
+msgstr "Lagre Som..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2315,8 +2295,8 @@ msgid "Creating Mesh Previews"
msgstr "Lager Forhåndsvisning av Mesh"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Miniatyrbilde.."
+msgid "Thumbnail..."
+msgstr "Miniatyrbilde..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2473,8 +2453,8 @@ msgid "(Current)"
msgstr "(Gjeldende)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "Henter fillager, vennligst vent.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "Henter fillager, vennligst vent..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2556,8 +2536,8 @@ msgid "Error requesting url: "
msgstr "Error ved forespørsel av url: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "Kobler til Fillager.."
+msgid "Connecting to Mirror..."
+msgstr "Kobler til Fillager..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2573,8 +2553,8 @@ msgstr "Kan ikke Løses"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Kobler til.."
+msgid "Connecting..."
+msgstr "Kobler til..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2588,8 +2568,8 @@ msgstr "Tilkoblet"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
-msgid "Requesting.."
-msgstr "Ber om.."
+msgid "Requesting..."
+msgstr "Ber om..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2734,12 +2714,12 @@ msgid "Collapse all"
msgstr "Kollaps alle"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Endre Navn.."
+msgid "Rename..."
+msgstr "Endre Navn..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "Flytt Til.."
+msgid "Move To..."
+msgstr "Flytt Til..."
#: editor/filesystem_dock.cpp
#, fuzzy
@@ -2751,16 +2731,16 @@ msgid "Instance"
msgstr "Instans"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Endre Avhengigheter.."
+msgid "Edit Dependencies..."
+msgstr "Endre Avhengigheter..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Vis Eiere.."
+msgid "View Owners..."
+msgstr "Vis Eiere..."
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Duplisér"
#: editor/filesystem_dock.cpp
@@ -2787,10 +2767,10 @@ msgstr "Instanser den valgte scene(r) som barn av den valgte noden."
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Skanner Filer,\n"
-"Vennligst Vent.."
+"Vennligst Vent..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2855,8 +2835,8 @@ msgid "Import Scene"
msgstr "Importer Scene"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Importerer Scene.."
+msgid "Importing Scene..."
+msgstr "Importerer Scene..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2867,8 +2847,8 @@ msgid "Generating for Mesh: "
msgstr "Genererer for Maske: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "Kjører Tilpasser Skript.."
+msgid "Running Custom Script..."
+msgstr "Kjører Tilpasser Skript..."
#: editor/import/resource_importer_scene.cpp
#, fuzzy
@@ -2884,8 +2864,8 @@ msgid "Error running post-import script:"
msgstr "Error ved kjøring av post-import script:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Lagrer.."
+msgid "Saving..."
+msgstr "Lagrer..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2905,8 +2885,8 @@ msgstr "Importer Som:"
#: editor/import_dock.cpp editor/property_editor.cpp
#, fuzzy
-msgid "Preset.."
-msgstr "Preset.."
+msgid "Preset..."
+msgstr "Preset..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3336,16 +3316,16 @@ msgid "Transition Node"
msgstr "Overgang Node"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Importer Animasjoner.."
+msgid "Import Animations..."
+msgstr "Importer Animasjoner..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "Rediger Node-Filtre"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "Filtre.."
+msgid "Filters..."
+msgstr "Filtre..."
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
@@ -3414,8 +3394,8 @@ msgid "Fetching:"
msgstr "Henter:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "Løser.."
+msgid "Resolving..."
+msgstr "Løser..."
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
@@ -3484,8 +3464,8 @@ msgid "Site:"
msgstr "Side:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Support.."
+msgid "Support..."
+msgstr "Support..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3675,6 +3655,7 @@ msgid "Use Rotation Snap"
msgstr "Bruk Rotasjons-Snap"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "Konfigurer Snap..."
@@ -3837,7 +3818,7 @@ msgstr "Legg til %s"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Adding %s..."
-msgstr "Legger til %s.."
+msgstr "Legger til %s..."
#: editor/plugins/canvas_item_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Ok"
@@ -4106,7 +4087,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4313,8 +4294,8 @@ msgid "Error loading image:"
msgstr "Feil ved innlasting av bilde:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Ingen piksler med gjennomsiktighet > 128 i bilde.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Ingen piksler med gjennomsiktighet > 128 i bilde..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4682,8 +4663,8 @@ msgid "Import Theme"
msgstr "Importer Tema"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Lagre Tema Som.."
+msgid "Save Theme As..."
+msgstr "Lagre Tema Som..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4779,8 +4760,8 @@ msgstr "Veksle skriptpanel"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Finn.."
+msgid "Find..."
+msgstr "Finn..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4987,15 +4968,15 @@ msgid "Find Previous"
msgstr "Finn forrige"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Erstatt.."
+msgid "Replace..."
+msgstr "Erstatt..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5450,11 +5431,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5709,7 +5686,7 @@ msgid "Remove All"
msgstr "Fjern Funksjon"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5778,8 +5755,9 @@ msgid "Options"
msgstr "Innstillinger"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr ""
+#, fuzzy
+msgid "Has,Many,Options"
+msgstr "Innstillinger"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5905,7 +5883,7 @@ msgstr ""
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
msgid "Tile Set"
-msgstr "TileSet.."
+msgstr "TileSet..."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -5969,7 +5947,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -6060,6 +6038,11 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Prosjektnavn:"
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "Kunne ikke opprette mappe."
@@ -6251,8 +6234,8 @@ msgstr "Museknapp"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6280,7 +6263,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6466,7 +6449,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6563,11 +6546,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6740,7 +6723,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -8211,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."
@@ -8227,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 9927fd8e8a..bfedf322b3 100644
--- a/editor/translations/nl.po
+++ b/editor/translations/nl.po
@@ -11,9 +11,12 @@
# Daeran Wereld <daeran@gmail.com>, 2017.
# Dzejkop <jakubtrad@gmail.com>, 2017.
# Ferdinand de Coninck <ferdinand.deconinck@gmail.com>, 2018.
+# frank <frankvprive@gmail.com>, 2018.
+# Johannes Smit <smitjohannes96@gmail.com>, 2018.
# Jorn Theunissen <jorn-theunissen@hotmail.com>, 2018.
# Maikel <maikel_martens_1@hotmail.com>, 2017.
# millenniumproof <millenniumproof@gmail.com>, 2018.
+# nee <lespam@protonmail.com>, 2018.
# Pieter-Jan Briers <pieterjan.briers@gmail.com>, 2017-2018.
# Robin Arys <robinarys@hotmail.com>, 2017.
# Senno Kaasjager <senno.kaasjager@gmail.com>, 2017.
@@ -25,15 +28,15 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-04-06 15:35+0000\n"
-"Last-Translator: millenniumproof <millenniumproof@gmail.com>\n"
+"PO-Revision-Date: 2018-05-21 18:36+0000\n"
+"Last-Translator: Johannes Smit <smitjohannes96@gmail.com>\n"
"Language-Team: Dutch <https://hosted.weblate.org/projects/godot-engine/godot/"
"nl/>\n"
"Language: nl\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\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -514,8 +517,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Ontkoppel '%s' van '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "Verbind.."
+msgid "Connect..."
+msgstr "Verbind..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -937,12 +940,12 @@ msgid "Move Audio Bus"
msgstr "Verplaats audiobus"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Sla Audio Bus Layout Op Als.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Sla Audio Bus Layout Op Als..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Locatie voor Nieuwe Layout.."
+msgid "Location for New Layout..."
+msgstr "Locatie voor Nieuwe Layout..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1080,12 +1083,12 @@ msgid "Updating Scene"
msgstr "Scene aan het Updaten"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Lokale wijziging aan het opslaan.."
+msgid "Storing local changes..."
+msgstr "Lokale wijziging aan het opslaan..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Scene aan het updaten.."
+msgid "Updating scene..."
+msgstr "Scene aan het updaten..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1153,8 +1156,8 @@ msgid "Show In File Manager"
msgstr "Weergeven in Bestandsbeheer"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Nieuwe Map.."
+msgid "New Folder..."
+msgstr "Nieuwe Map..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1415,20 +1418,20 @@ msgstr "Maak Uitvoer Leeg"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Project exporteren faalt door foutcode %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Error bij het opslaan van resource!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Resource Opslaan Als.."
+msgid "Save Resource As..."
+msgstr "Resource Opslaan Als..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "Ik snap het.."
+msgid "I see..."
+msgstr "Ik snap het..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1660,11 +1663,11 @@ msgid "Open Base Scene"
msgstr "Open Basisscene"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr "Open Scene Snel..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr "Open Script Snel..."
#: editor/editor_node.cpp
@@ -1676,7 +1679,7 @@ msgid "Save changes to '%s' before closing?"
msgstr "Sla wijzigen aan '%s' op voor het afsluiten?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr "Sla Scene Op Als..."
#: editor/editor_node.cpp
@@ -1729,7 +1732,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Deze actie kan niet ongedaan gemaakt worden. Toch herstellen?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr "Snel Scene Uitvoeren..."
#: editor/editor_node.cpp
@@ -1889,7 +1892,7 @@ msgid "Previous tab"
msgstr "Vorig tabblad"
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr "Bestanden Filteren..."
#: editor/editor_node.cpp
@@ -1901,11 +1904,11 @@ msgid "New Scene"
msgstr "Nieuwe Scene"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr "Nieuwe Geërfde Scene..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr "Scene Openen..."
#: editor/editor_node.cpp
@@ -1925,15 +1928,15 @@ msgid "Open Recent"
msgstr "Recente Scenes Openen"
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr "Converteer Naar..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr "MeshLibrary..."
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2198,8 +2201,8 @@ msgid "Save the currently edited resource."
msgstr "De bewerkte bron opslaan."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Opslaan Als.."
+msgid "Save As..."
+msgstr "Opslaan Als..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2307,8 +2310,8 @@ msgid "Creating Mesh Previews"
msgstr "Creëren van Mesh Previews"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Voorbeeld.."
+msgid "Thumbnail..."
+msgstr "Voorbeeld..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2460,8 +2463,8 @@ msgid "(Current)"
msgstr "(Huidig)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "Mirrors ophalen, even wachten a.u.b.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "Mirrors ophalen, even wachten a.u.b..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2538,8 +2541,8 @@ msgid "Error requesting url: "
msgstr "Fout bij het opvragen van een URL: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "Verbinden met Mirror.."
+msgid "Connecting to Mirror..."
+msgstr "Verbinden met Mirror..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2555,8 +2558,8 @@ msgstr "Kan niet oplossen"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Verbinden.."
+msgid "Connecting..."
+msgstr "Verbinden..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2568,7 +2571,7 @@ msgstr "Verbonden"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "Opvragen..."
#: editor/export_template_manager.cpp
@@ -2706,12 +2709,12 @@ msgid "Collapse all"
msgstr "Klap alles in"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Hernoemen.."
+msgid "Rename..."
+msgstr "Hernoemen..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "Verplaats Naar.."
+msgid "Move To..."
+msgstr "Verplaats Naar..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2722,16 +2725,16 @@ msgid "Instance"
msgstr "Instantie"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Afhankelijkheden aanpassen.."
+msgid "Edit Dependencies..."
+msgstr "Afhankelijkheden aanpassen..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Bekijk eigenaren.."
+msgid "View Owners..."
+msgstr "Bekijk eigenaren..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Dupliceren.."
+msgid "Duplicate..."
+msgstr "Dupliceren..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2758,10 +2761,10 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Bestanden Scannen,\n"
-"Wacht Alstublieft.."
+"Wacht Alstublieft..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2826,8 +2829,8 @@ msgid "Import Scene"
msgstr "Importeer Scene"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Scene Importeren.."
+msgid "Importing Scene..."
+msgstr "Scene Importeren..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2838,8 +2841,8 @@ msgid "Generating for Mesh: "
msgstr "Bouw voor Mesh: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "Aangepast script uitvoeren .."
+msgid "Running Custom Script..."
+msgstr "Aangepast script uitvoeren ..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2854,8 +2857,8 @@ msgid "Error running post-import script:"
msgstr "Fout bij uitvoeren post-import script:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Opslaan.."
+msgid "Saving..."
+msgstr "Opslaan..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2874,8 +2877,8 @@ msgid "Import As:"
msgstr "Importereen Als:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Voorinstelling.."
+msgid "Preset..."
+msgstr "Voorinstelling..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3294,15 +3297,15 @@ msgid "Transition Node"
msgstr "Transition Node"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Importeer Animaties.."
+msgid "Import Animations..."
+msgstr "Importeer Animaties..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "Wijzig Node Filters"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr "Filters..."
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3370,8 +3373,8 @@ msgid "Fetching:"
msgstr "Ophalen:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "Oplossen .."
+msgid "Resolving..."
+msgstr "Oplossen ..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3437,8 +3440,8 @@ msgid "Site:"
msgstr "Site:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Ondersteuning.."
+msgid "Support..."
+msgstr "Ondersteuning..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3635,8 +3638,9 @@ msgid "Use Rotation Snap"
msgstr "Gebruik Rotatie Snap"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
-msgstr "Configureer Uitlijnen..."
+msgstr "Configureer Snap..."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap Relative"
@@ -3735,14 +3739,12 @@ msgid "Show Guides"
msgstr "Toon hulplijnen"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "Raster Weergeven"
+msgstr "Toon Oorsprongspunt"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "Toon helpers"
+msgstr "Toon Aanzicht Portaal"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -3854,11 +3856,11 @@ msgstr "Verwijder Geselecteerde Item"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Import from Scene"
-msgstr "Importeer vanaf de Scene"
+msgstr "Importeer Vanuit Scene"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Update from Scene"
-msgstr "Werk bij vanaf de Scene"
+msgstr "Update Vanuit Scene"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Flat0"
@@ -3869,13 +3871,12 @@ msgid "Flat1"
msgstr "Plat1"
#: editor/plugins/curve_editor_plugin.cpp
-#, fuzzy
msgid "Ease in"
-msgstr "Schaal Selectie"
+msgstr "Rustig Aanzetten"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Ease out"
-msgstr "Neem af naar buiten"
+msgstr "Rustig Afzetten"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Smoothstep"
@@ -4043,7 +4044,7 @@ msgstr "Mesh heeft geen oppervlakte om omlijning van te maken!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "Mesh grondtype is niet PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4075,8 +4076,8 @@ msgstr "Creëer Convex Botsing Broer"
#: editor/plugins/mesh_instance_editor_plugin.cpp
#, fuzzy
-msgid "Create Outline Mesh.."
-msgstr "Creëer Omlijning Mesh.."
+msgid "Create Outline Mesh..."
+msgstr "Creëer Omlijning Mesh..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4227,7 +4228,7 @@ msgstr "Hoogteveld aan het creëeren..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Marking walkable triangles..."
-msgstr "Markeer loopbare driehoeken.."
+msgstr "Markeer loopbare driehoeken..."
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
@@ -4284,8 +4285,8 @@ msgid "Error loading image:"
msgstr "Error bij het laden van afbeelding:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Geen pixels met transparantie > 128 in afbeelding.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Geen pixels met transparantie > 128 in afbeelding..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4645,8 +4646,8 @@ msgid "Import Theme"
msgstr "Importeer Thema"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Thema Opslaan Als.."
+msgid "Save Theme As..."
+msgstr "Thema Opslaan Als..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4742,8 +4743,8 @@ msgstr "Schakel Scripten Paneel"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Vind.."
+msgid "Find..."
+msgstr "Vind..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4952,16 +4953,16 @@ msgid "Find Previous"
msgstr "Vind Vorige"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Vervang.."
+msgid "Replace..."
+msgstr "Vervang..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Ga Naar Functie.."
+msgid "Goto Function..."
+msgstr "Ga Naar Functie..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Ga Naar Regel.."
+msgid "Goto Line..."
+msgstr "Ga Naar Regel..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5305,12 +5306,11 @@ msgstr "Vrijekijk Snelheid Modificator"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "XForm Dialog"
-msgstr ""
+msgstr "XForm Dialoog"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Select Mode (Q)"
-msgstr "Selecteer Modus"
+msgstr "Selectiestand (Q)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid ""
@@ -5318,22 +5318,25 @@ msgid ""
"Alt+Drag: Move\n"
"Alt+RMB: Depth list selection"
msgstr ""
+"Slepen: Roteren\n"
+"Atl+Slepen: Verplaatsen\n"
+"Alt+RMB: Diepte selectie"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Move Mode (W)"
-msgstr ""
+msgstr "Beweegstand (W)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotate Mode (E)"
-msgstr ""
+msgstr "Rotatiestand (E)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scale Mode (R)"
-msgstr ""
+msgstr "Schaalstand (R)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Coords"
-msgstr ""
+msgstr "Lokale Coördinaten"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Space Mode (%s)"
@@ -5346,64 +5349,63 @@ msgstr "Op hulplijnen uitlijnen"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Bottom View"
-msgstr ""
+msgstr "Onderaanzicht"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Top View"
-msgstr ""
+msgstr "Bovenaanzicht"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rear View"
-msgstr ""
+msgstr "Achteraanzicht"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Front View"
-msgstr ""
+msgstr "Vooraanzicht"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Left View"
-msgstr ""
+msgstr "Linker Zijaanzicht"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right View"
-msgstr ""
+msgstr "Rechter Zijaanzicht"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal view"
-msgstr ""
+msgstr "Schakel Perspectief/Orthogonaal aanzicht"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Insert Animation Key"
-msgstr ""
+msgstr "Voeg Animatiesleutel toe"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Focus Origin"
-msgstr ""
+msgstr "Focus op Oorsprongspunt"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Focus Selection"
-msgstr ""
+msgstr "Focus Selectie"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Align Selection With View"
-msgstr ""
+msgstr "Arrangeer Selectie naar Aanzicht"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Tool Select"
-msgstr "Alle Selectie"
+msgstr "Gereedschappen"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Tool Move"
-msgstr ""
+msgstr "Beweeg Gereedschap"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Tool Rotate"
-msgstr ""
+msgstr "Roteer Gereedschap"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Tool Scale"
-msgstr ""
+msgstr "Verschalen Gereedschap"
#: editor/plugins/spatial_editor_plugin.cpp
#, fuzzy
@@ -5412,52 +5414,48 @@ msgstr "Toggle Favoriet"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform"
-msgstr ""
+msgstr "Transformatie"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr ""
+msgid "Transform Dialog..."
+msgstr "Transformatie Dialoog..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
-msgstr ""
+msgstr "1 Aanzicht Portaal"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "2 Viewports"
-msgstr ""
+msgstr "2 Aanzicht Portalen"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "2 Viewports (Alt)"
-msgstr ""
+msgstr "2 Aanzicht Portalen (Alt)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "3 Viewports"
-msgstr ""
+msgstr "3 Aanzicht Portalen"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "3 Viewports (Alt)"
-msgstr ""
+msgstr "3 Aanzicht Portalen (Alt)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "4 Viewports"
-msgstr ""
+msgstr "4 Aanzicht Portalen"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Origin"
-msgstr ""
+msgstr "Bekijk Oorsprongspunt"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Grid"
-msgstr ""
+msgstr "Bekijk Raster"
#: editor/plugins/spatial_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Settings"
-msgstr ""
+msgstr "Instellingen"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Skeleton Gizmo visibility"
@@ -5465,71 +5463,71 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Snap Settings"
-msgstr ""
+msgstr "Snap instellingen"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Translate Snap:"
-msgstr ""
+msgstr "Verplaats Snap:"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotate Snap (deg.):"
-msgstr ""
+msgstr "Draai Snap (grad.):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scale Snap (%):"
-msgstr ""
+msgstr "Verander Grootte van Snap (%):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Viewport Settings"
-msgstr ""
+msgstr "Instellingen Aanzicht Portaal"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Perspective FOV (deg.):"
-msgstr ""
+msgstr "Perspectief FOV (grad.):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Z-Near:"
-msgstr ""
+msgstr "Bekijk Z-Near:"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Z-Far:"
-msgstr ""
+msgstr "Bekijk Z-Far:"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Change"
-msgstr ""
+msgstr "Transformatie Verandering"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Translate:"
-msgstr ""
+msgstr "Verplaats:"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotate (deg.):"
-msgstr ""
+msgstr "Rotatie (graden):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scale (ratio):"
-msgstr ""
+msgstr "Verschalen (ratio):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Type"
-msgstr ""
+msgstr "Transformatie Type"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Pre"
-msgstr ""
+msgstr "Pre"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Post"
-msgstr ""
+msgstr "Post"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "ERROR: Couldn't load frame resource!"
-msgstr ""
+msgstr "FOUT: Kan frame benodigdheden niet laden!"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Add Frame"
-msgstr ""
+msgstr "Voeg Frame toe"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Resource clipboard is empty or not a texture!"
@@ -5537,60 +5535,59 @@ msgstr ""
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Paste Frame"
-msgstr ""
+msgstr "Frame Plakken"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Add Empty"
-msgstr ""
+msgstr "Lege Toevoegen"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Change Animation Loop"
-msgstr ""
+msgstr "Verander Animatie Lus"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Change Animation FPS"
-msgstr ""
+msgstr "Verander Animatie FPS"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "(empty)"
-msgstr ""
+msgstr "(leeg)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Animations"
-msgstr ""
+msgstr "Animaties"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Speed (FPS):"
-msgstr ""
+msgstr "Snelheid (FPS):"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Loop"
-msgstr ""
+msgstr "Lus"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Animation Frames"
-msgstr ""
+msgstr "Animatie Frames"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Insert Empty (Before)"
-msgstr ""
+msgstr "Lege Toevoegen (Hiervoor)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Insert Empty (After)"
-msgstr ""
+msgstr "Lege Toevoegen (Hierna)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
-#, fuzzy
msgid "Move (Before)"
-msgstr "Kopiëer Nodes"
+msgstr "Verplaats (Hiervoor)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Move (After)"
-msgstr ""
+msgstr "Verplaats (Hierna)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "SpriteFrames"
-msgstr ""
+msgstr "Sprite-Frames"
#: editor/plugins/style_box_editor_plugin.cpp
msgid "StyleBox Preview:"
@@ -5610,7 +5607,7 @@ msgstr ""
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "<None>"
-msgstr ""
+msgstr "<Geen>"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Pixel Snap"
@@ -5637,50 +5634,48 @@ msgstr ""
#: editor/plugins/texture_region_editor_plugin.cpp
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Separation:"
-msgstr ""
+msgstr "Afzondering:"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Texture Region"
-msgstr ""
+msgstr "Textuur Regio"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Texture Region Editor"
-msgstr ""
+msgstr "Textuur Regio Editor"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Can't save theme to file:"
-msgstr ""
+msgstr "Kan thema niet opslaan in bestand:"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Add All Items"
-msgstr ""
+msgstr "Alle Items Toevoegen"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Add All"
-msgstr ""
+msgstr "Allen Toevoegen"
#: editor/plugins/theme_editor_plugin.cpp
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Remove Item"
-msgstr ""
+msgstr "Verwijder Item"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Remove All Items"
-msgstr "Verwijder Selectie"
+msgstr "Verwijder Alle Items"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Remove All"
-msgstr "Verwijderen"
+msgstr "Verwijder Alles"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr ""
+msgid "Edit theme..."
+msgstr "Bewerk Thema..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
-msgstr ""
+msgstr "Thema Bewerkingsmenu."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Add Class Items"
@@ -5692,15 +5687,15 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
msgid "Create Empty Template"
-msgstr ""
+msgstr "Creëer Leeg Sjabloon"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Create Empty Editor Template"
-msgstr ""
+msgstr "Creëer Lege Sjabloon Editor"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Create From Current Editor Theme"
-msgstr ""
+msgstr "Creëer Derivatie Huidig Editor Thema"
#: editor/plugins/theme_editor_plugin.cpp
msgid "CheckBox Radio1"
@@ -5712,7 +5707,7 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
msgid "Item"
-msgstr ""
+msgstr "Item"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Check Item"
@@ -5733,69 +5728,68 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
-msgstr ""
+msgstr "Had"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Many"
-msgstr ""
+msgstr "Veel"
#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
msgid "Options"
-msgstr ""
+msgstr "Opties"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr ""
+#, fuzzy
+msgid "Has,Many,Options"
+msgstr "Opties"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
-msgstr ""
+msgstr "Tabblad 1"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 2"
-msgstr ""
+msgstr "Tabblad 2"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 3"
-msgstr ""
+msgstr "Tabblad 3"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Data Type:"
-msgstr ""
+msgstr "Data Type:"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Icon"
-msgstr ""
+msgstr "Icoon"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Style"
-msgstr ""
+msgstr "Stijl"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Font"
-msgstr ""
+msgstr "Lettertype"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Color"
-msgstr ""
+msgstr "Kleur"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme"
-msgstr ""
+msgstr "Thema"
#: editor/plugins/tile_map_editor_plugin.cpp
-#, fuzzy
msgid "Erase Selection"
-msgstr "Schaal Selectie"
+msgstr "Selectie Verwijderen"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Paint TileMap"
msgstr ""
#: editor/plugins/tile_map_editor_plugin.cpp
-#, fuzzy
msgid "Line Draw"
-msgstr "Lineair"
+msgstr "Teken Lijn"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rectangle Paint"
@@ -5811,23 +5805,23 @@ msgstr ""
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Erase selection"
-msgstr ""
+msgstr "Verwijder Selectie"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Find tile"
-msgstr ""
+msgstr "Vind Tegel"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Transpose"
-msgstr ""
+msgstr "Transponeren"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Mirror X"
-msgstr ""
+msgstr "Spiegel X"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Mirror Y"
-msgstr ""
+msgstr "Spiegel Y"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Paint Tile"
@@ -5835,39 +5829,39 @@ msgstr ""
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Pick Tile"
-msgstr ""
+msgstr "Kies Tegel"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 0 degrees"
-msgstr ""
+msgstr "0 Graden Roteren"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 90 degrees"
-msgstr ""
+msgstr "90 Graden Roteren"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 180 degrees"
-msgstr ""
+msgstr "180 Graden Roteren"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 270 degrees"
-msgstr ""
+msgstr "270 Graden Roteren"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Could not find tile:"
-msgstr ""
+msgstr "Niet gevonden titel:"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Item name or ID:"
-msgstr ""
+msgstr "Item naam of identificatiecode:"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from scene?"
-msgstr ""
+msgstr "Creëer vanuit scene?"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Merge from scene?"
-msgstr ""
+msgstr "Vervoegen vanuit scene?"
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
@@ -5876,15 +5870,15 @@ msgstr "TileSet..."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
-msgstr ""
+msgstr "Creëer vanuit Scene"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Merge from Scene"
-msgstr ""
+msgstr "Vervoeg vanuit Scene"
#: editor/plugins/tile_set_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Error"
-msgstr ""
+msgstr "Fout"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Autotiles"
@@ -5903,44 +5897,40 @@ msgid ""
msgstr ""
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Select current edited sub-tile."
-msgstr "De bewerkte bron opslaan."
+msgstr "Selecteer zojuist bewerkte sub-tegel."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Select sub-tile to change its priority."
-msgstr ""
+msgstr "Selecteer een sub-tegel om zijn prioriteit te veranderen."
#: editor/progress_dialog.cpp scene/gui/dialogs.cpp
msgid "Cancel"
-msgstr "Annuleren"
+msgstr "Annuleer"
#: editor/project_export.cpp
-#, fuzzy
msgid "Runnable"
-msgstr "Inschakelen"
+msgstr "Uitvoerbaar"
#: editor/project_export.cpp
-#, fuzzy
msgid "Delete patch '%s' from list?"
-msgstr "Verwijder"
+msgstr "Verwijder patch '%s' van lijst?"
#: editor/project_export.cpp
-#, fuzzy
msgid "Delete preset '%s'?"
-msgstr "Verwijder geselecteerde bestanden?"
+msgstr "Verwijder voorinstelling '%s'?"
#: editor/project_export.cpp
msgid "Export templates for this platform are missing/corrupted: "
-msgstr ""
+msgstr "Exportsjablonen voor dit platform zijn vermist/corrupt: "
#: editor/project_export.cpp
msgid "Presets"
-msgstr ""
+msgstr "Voorinstelling"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr ""
+msgid "Add..."
+msgstr "Toevoegen..."
#: editor/project_export.cpp
msgid "Resources"
@@ -5952,7 +5942,7 @@ msgstr ""
#: editor/project_export.cpp
msgid "Export selected scenes (and dependencies)"
-msgstr ""
+msgstr "Exporteer geselecteerde scenes (en afhankelijkheden)"
#: editor/project_export.cpp
msgid "Export selected resources (and dependencies)"
@@ -5977,9 +5967,8 @@ msgid ""
msgstr ""
#: editor/project_export.cpp
-#, fuzzy
msgid "Patches"
-msgstr "Matches:"
+msgstr "Patches"
#: editor/project_export.cpp
msgid "Make Patch"
@@ -5987,24 +5976,23 @@ msgstr ""
#: editor/project_export.cpp
msgid "Features"
-msgstr ""
+msgstr "Kenmerken"
#: editor/project_export.cpp
msgid "Custom (comma-separated):"
msgstr ""
#: editor/project_export.cpp
-#, fuzzy
msgid "Feature List:"
-msgstr "Methode Lijst:"
+msgstr "Kenmerkenlijst:"
#: editor/project_export.cpp
msgid "Export PCK/Zip"
-msgstr ""
+msgstr "Exporteer PCK/Zip"
#: editor/project_export.cpp
msgid "Export templates for this platform are missing:"
-msgstr ""
+msgstr "Vermiste Exportsjablonen voor dit platform:"
#: editor/project_export.cpp
msgid "Export templates for this platform are missing/corrupted:"
@@ -6015,30 +6003,33 @@ msgid "Export With Debug"
msgstr ""
#: editor/project_manager.cpp
-#, fuzzy
msgid "The path does not exist."
-msgstr "Bestand bestaat niet."
+msgstr "Dit pad bestaat niet."
#: editor/project_manager.cpp
msgid "Please choose a 'project.godot' file."
-msgstr ""
+msgstr "Kies alstublieft een 'project.godot' bestand."
#: editor/project_manager.cpp
msgid "Please choose an empty folder."
-msgstr ""
+msgstr "Kies alstublieft een lege map."
#: editor/project_manager.cpp
msgid "Imported Project"
-msgstr ""
+msgstr "Geïmporteerd Project"
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Ongeldige naam."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
-msgstr "Map kon niet gemaakt worden."
+msgstr "Kon map niet creëren."
#: editor/project_manager.cpp
msgid "There is already a folder in this path with the specified name."
-msgstr ""
+msgstr "Er is al een map in dit pad met dezelfde naam."
#: editor/project_manager.cpp
msgid "It would be a good idea to name your project."
@@ -6126,7 +6117,7 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
msgid "Can't open project"
-msgstr "Verbind.."
+msgstr "Verbind..."
#: editor/project_manager.cpp
msgid "Are you sure to open more than one project?"
@@ -6201,7 +6192,7 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
msgid "Can't run project"
-msgstr "Verbind.."
+msgstr "Verbind..."
#: editor/project_manager.cpp
msgid ""
@@ -6227,8 +6218,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6256,7 +6247,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6443,7 +6434,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6517,7 +6508,7 @@ msgstr ""
#: editor/property_editor.cpp
msgid "Pick a Viewport"
-msgstr ""
+msgstr "Kies een Aanzicht portaal"
#: editor/property_editor.cpp
msgid "Ease In"
@@ -6540,11 +6531,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6582,8 +6573,9 @@ msgid "Error loading file: Not a resource!"
msgstr ""
#: editor/property_editor.cpp
+#, fuzzy
msgid "Selected node is not a Viewport!"
-msgstr ""
+msgstr "Geselecteerde ..... is geen Aanzicht Portaal!"
#: editor/property_editor.cpp
#, fuzzy
@@ -6718,7 +6710,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -8258,10 +8250,10 @@ msgid ""
"obtain a size. Otherwise, make it a RenderTarget and assign its internal "
"texture to some node for display."
msgstr ""
-"Deze viewport is niet ingesteld als render target. Maak het een kind van een "
-"Control zodat het een grootte kan ontvangen, als je de bedoeling hebt zijn "
-"inhoud direct op het scherm te weergeven. Anders, maak er een RenderTarget "
-"van en wijs zijn interne texture toe aan een node om te tonen."
+"Dit Aanzicht Portaal is niet ingesteld als render target. Maak het een kind "
+"van een Control zodat het een grootte kan ontvangen, als je de bedoeling "
+"hebt zijn inhoud direct op het scherm te weergeven. Anders, maak er een "
+"RenderTarget van en wijs zijn interne texture toe aan een node om te tonen."
#: scene/resources/dynamic_font.cpp
msgid "Error initializing FreeType."
@@ -8280,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:"
@@ -8293,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!"
@@ -8339,7 +8335,7 @@ msgstr "Ongeldige lettertype grootte."
#, fuzzy
#~ msgid "Setting '"
-#~ msgstr "Aan Het Opzetten.."
+#~ msgstr "Aan Het Opzetten..."
#, fuzzy
#~ msgid "Selection -> Duplicate"
@@ -8390,8 +8386,8 @@ msgstr "Ongeldige lettertype grootte."
#~ msgid "Exporting for %s"
#~ msgstr "Aan het exporteren voor %s"
-#~ msgid "Setting Up.."
-#~ msgstr "Aan Het Opzetten.."
+#~ msgid "Setting Up..."
+#~ msgstr "Aan Het Opzetten..."
#~ msgid "Re-Importing"
#~ msgstr "Aan Het Herimporteren"
diff --git a/editor/translations/pl.po b/editor/translations/pl.po
index 3c8ee72cec..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.
@@ -11,6 +10,7 @@
# Dariusz Król <rexioweb@gmail.com>, 2018.
# heya10 <igor.gielzak@gmail.com>, 2017.
# holistyczny interlokutor <jakubowesmieci@gmail.com>, 2017.
+# Igor <igor.gielzak@gmail.com>, 2018.
# Kajetan Kuszczyński <kajetanek99@gmail.com>, 2016.
# Kamil Lewan <lewan.kamil@gmail.com>, 2016.
# Karol Walasek <coreconviction@gmail.com>, 2016.
@@ -19,16 +19,16 @@
# NeverK <neverkoxu@gmail.com>, 2018.
# Rafal Brozio <rafal.brozio@gmail.com>, 2016.
# Rafał Ziemniak <synaptykq@gmail.com>, 2017.
+# RM <synaptykq@gmail.com>, 2018.
# Sebastian Krzyszkowiak <dos@dosowisko.net>, 2017.
# 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-04-23 15:40+0000\n"
-"Last-Translator: Dariusz Król <rexioweb@gmail.com>\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"
"Language: pl\n"
@@ -36,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-dev\n"
+"X-Generator: Weblate 3.1-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -48,19 +48,19 @@ msgstr "Wszystkie zaznaczenia"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Time"
-msgstr "Zmień czas klatki kluczowej"
+msgstr "Zmiana czasu klatki kluczowej"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr "Zmiana przejścia animacji"
+msgstr "Zmiana przejścia"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
-msgstr "Animacja transformacji"
+msgstr "Zmiana transformacji"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Value"
-msgstr "Zmień wartość klatki kluczowej"
+msgstr "Zmiana wartości klatki kluczowej"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
@@ -84,7 +84,7 @@ msgstr "Przesuń ścieżkę animacji w dół"
#: editor/animation_editor.cpp
msgid "Remove Anim Track"
-msgstr "Usuń animację"
+msgstr "Usuń ścieżkę animacji"
#: editor/animation_editor.cpp
msgid "Set Transitions to:"
@@ -92,7 +92,7 @@ msgstr "Ustaw przejścia na:"
#: editor/animation_editor.cpp
msgid "Anim Track Rename"
-msgstr "Zmień nazwę animacji"
+msgstr "Zmień nazwę ściezki animacji"
#: editor/animation_editor.cpp
msgid "Anim Track Change Interpolation"
@@ -517,7 +517,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Rozłącz '%s' z '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "Połącz..."
#: editor/connections_dialog.cpp
@@ -937,11 +937,11 @@ msgid "Move Audio Bus"
msgstr "Przemieść magistralę audio"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "Zapisz układ magistrali audio jako..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr "Lokalizacja nowego układu..."
#: editor/editor_audio_buses.cpp
@@ -1078,12 +1078,12 @@ msgid "Updating Scene"
msgstr "Aktualizowanie Sceny"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Zachowywanie lokalnych zmian.."
+msgid "Storing local changes..."
+msgstr "Zachowywanie lokalnych zmian..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Aktualizacja sceny .."
+msgid "Updating scene..."
+msgstr "Aktualizacja sceny ..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1151,7 +1151,7 @@ msgid "Show In File Manager"
msgstr "Pokaż w menadżerze plików"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr "Utwórz katalog..."
#: editor/editor_file_dialog.cpp
@@ -1343,9 +1343,8 @@ msgid "Description"
msgstr "Opis"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Online Tutorials:"
-msgstr "Poradniki"
+msgstr "Poradniki online:"
#: editor/editor_help.cpp
#, fuzzy
@@ -1354,8 +1353,9 @@ msgid ""
"$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/"
"url][/color]."
msgstr ""
-"Obecnie nie ma opisu dla tej metody. Pomóż nam, [color=$color][url="
-"$url]wysyłając ją[/url][/color]!"
+"Obecnie nie ma żadnych samouczków dla tej klasy, możesz [color=$color][url="
+"$url]dodać jeden[/url][/kolor] lub [color=$color] [url=$url2]poprosić o "
+"jeden[/url][/barl]."
#: editor/editor_help.cpp
msgid "Properties"
@@ -1414,20 +1414,20 @@ msgstr "Wyczyść dane wyjściowe"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Eksport projektu nie powiódł się, kod błędu to %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "BÅ‚Ä…d podczas zapisu zasobu!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "Zapisz zasób jako..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "WidzÄ™.."
+msgid "I see..."
+msgstr "WidzÄ™..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1657,12 +1657,12 @@ msgid "Open Base Scene"
msgstr "Otwórz scenę bazową"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Szybkie otwieranie sceny.."
+msgid "Quick Open Scene..."
+msgstr "Szybkie otwieranie sceny..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Szybkie otwieranie skryptu.."
+msgid "Quick Open Script..."
+msgstr "Szybkie otwieranie skryptu..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1673,8 +1673,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Zapisać zmiany w '%s' przed zamknięciem?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Zapisz scenÄ™ jako.."
+msgid "Save Scene As..."
+msgstr "Zapisz scenÄ™ jako..."
#: editor/editor_node.cpp
msgid "No"
@@ -1725,8 +1725,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Tego nie można cofnąć. Przywrócić mimo to?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Szybkie uruchomienie sceny.."
+msgid "Quick Run Scene..."
+msgstr "Szybkie uruchomienie sceny..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1884,7 +1884,7 @@ msgid "Previous tab"
msgstr "Poprzednia zakładka"
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr "Filtrowanie plików..."
#: editor/editor_node.cpp
@@ -1896,12 +1896,12 @@ msgid "New Scene"
msgstr "Nowa scena"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr "Nowa scena dziedziczÄ…ca..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Otwórz scenę.."
+msgid "Open Scene..."
+msgstr "Otwórz scenę..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1920,15 +1920,15 @@ msgid "Open Recent"
msgstr "Ostatnio otwierane"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Konwertuje na.."
+msgid "Convert To..."
+msgstr "Konwertuje na..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr "MeshLibrary..."
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2114,7 +2114,7 @@ msgstr "Społeczność"
#: editor/editor_node.cpp
msgid "About"
-msgstr "O programie"
+msgstr "O silniku"
#: editor/editor_node.cpp
msgid "Play the project."
@@ -2189,7 +2189,7 @@ msgid "Save the currently edited resource."
msgstr "Zapisz aktualnie edytowany zasób."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr "Zapisz jako..."
#: editor/editor_node.cpp
@@ -2298,8 +2298,8 @@ msgid "Creating Mesh Previews"
msgstr "Tworzenie podglÄ…du Mesh"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Miniatura.."
+msgid "Thumbnail..."
+msgstr "Miniatura..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2452,8 +2452,8 @@ msgid "(Current)"
msgstr "(Bieżący)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "Pobieranie informacji o serwerach lustrzanych, proszę czekać.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "Pobieranie informacji o serwerach lustrzanych, proszę czekać..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2530,8 +2530,8 @@ msgid "Error requesting url: "
msgstr "Błąd podczas żądania adresu url: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "ÅÄ…czenie z serwerem lustrzanym.."
+msgid "Connecting to Mirror..."
+msgstr "ÅÄ…czenie z serwerem lustrzanym..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2547,8 +2547,8 @@ msgstr "Nie można rozwiązać"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "ÅÄ…czenie.."
+msgid "Connecting..."
+msgstr "ÅÄ…czenie..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2560,7 +2560,7 @@ msgstr "Podłączony"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "Żądanie danych..."
#: editor/export_template_manager.cpp
@@ -2696,11 +2696,11 @@ msgid "Collapse all"
msgstr "Zwiń foldery"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr "Zmień nazwę..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr "PrzenieÅ› Do..."
#: editor/filesystem_dock.cpp
@@ -2712,16 +2712,16 @@ msgid "Instance"
msgstr "Instancja"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr "Edytuj Zależności..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Pokaż właścicieli.."
+msgid "View Owners..."
+msgstr "Pokaż właścicieli..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Duplikuj.."
+msgid "Duplicate..."
+msgstr "Duplikuj..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2746,7 +2746,7 @@ msgstr "Utwórz instancje wybranej sceny/scen jako dziecko wybranego węzła."
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Skanowanie plików,\n"
"Proszę czekać..."
@@ -2814,8 +2814,8 @@ msgid "Import Scene"
msgstr "Importuj ScenÄ™"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Importowanie Sceny.."
+msgid "Importing Scene..."
+msgstr "Importowanie Sceny..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2826,7 +2826,7 @@ msgid "Generating for Mesh: "
msgstr "Generowanie dla siatki: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr "Uruchamiam skrypt..."
#: editor/import/resource_importer_scene.cpp
@@ -2844,8 +2844,8 @@ msgid "Error running post-import script:"
msgstr "BÅ‚Ä…d podczas uruchamiania skryptu po imporcie:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Zapisywanie.."
+msgid "Saving..."
+msgstr "Zapisywanie..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2864,8 +2864,8 @@ msgid "Import As:"
msgstr "Importuj jako:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Ustawienie predefiniowane.."
+msgid "Preset..."
+msgstr "Ustawienie predefiniowane..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3081,12 +3081,11 @@ msgstr "Tryb Å‚usek cebuli"
#: editor/plugins/animation_player_editor_plugin.cpp
#, fuzzy
msgid "Enable Onion Skinning"
-msgstr "WÅ‚Ä…cz tryb Å‚usek cebuli"
+msgstr "WÅ‚Ä…cz tryb warstw cebuli"
#: editor/plugins/animation_player_editor_plugin.cpp
-#, fuzzy
msgid "Directions"
-msgstr "Kategorie:"
+msgstr "Kierunki"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Past"
@@ -3290,16 +3289,16 @@ msgid "Transition Node"
msgstr "Węzeł Przejścia"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Zaimportuj animacje.."
+msgid "Import Animations..."
+msgstr "Zaimportuj animacje..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "Edytuj filtry węzłów"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "Filtry.."
+msgid "Filters..."
+msgstr "Filtry..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3342,9 +3341,10 @@ msgid "Request failed, too many redirects"
msgstr "Żądanie nieudane, zbyt dużo przekierowań"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Bad download hash, assuming file has been tampered with."
-msgstr "Zły hash pobranego pliku. Zakładamy, że ktoś przy nim majstrował."
+msgstr ""
+"Zły hash pobranego pliku. Zakładamy, że ktoś przy nim majstrował, lub został "
+"niepoprawnie pobrany."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Expected:"
@@ -3367,13 +3367,12 @@ msgid "Fetching:"
msgstr "Pobieranie:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr "RozwiÄ…zywanie..."
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Error making request"
-msgstr "Wystąpił błąd podczas tworzenia żądania"
+msgstr "Wystąpił błąd podczas żądania"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Idle"
@@ -3435,8 +3434,8 @@ msgid "Site:"
msgstr "Źródło:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Wsparcie.."
+msgid "Support..."
+msgstr "Wsparcie..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3475,9 +3474,8 @@ msgstr ""
"jedynie do odczytu."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
-#, fuzzy
msgid "Bake Lightmaps"
-msgstr "Wypal Lightmaps"
+msgstr "Stwórz Lightmaps"
#: editor/plugins/camera_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
@@ -3585,9 +3583,8 @@ msgstr ""
"poruszania)."
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Alt+RMB: Depth list selection"
-msgstr "Alt+PPM: Lista wyboru głębi"
+msgstr "Alt + RMB: Głębokość listy"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Mode"
@@ -3635,8 +3632,9 @@ msgid "Use Rotation Snap"
msgstr "Użyj kroków obrotu"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
-msgstr "Konfiguruj przyciÄ…ganie.."
+msgstr "Konfiguruj przyciÄ…ganie..."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap Relative"
@@ -3731,14 +3729,12 @@ msgid "Show Guides"
msgstr "Pokaż prowadnice"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
msgstr "Pokaż pozycję początkową"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 widok"
+msgstr "Pokaż widok"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -3782,11 +3778,11 @@ msgstr "Ustaw pivot w pozycji myszy"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Multiply grid step by 2"
-msgstr ""
+msgstr "Podwój wielkość siatki"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Divide grid step by 2"
-msgstr ""
+msgstr "Zmniejsz wielkość siatki dwukrotnie"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Add %s"
@@ -3802,7 +3798,7 @@ msgstr "Ok"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Cannot instantiate multiple nodes without root."
-msgstr ""
+msgstr "Nie można utworzyć wielu wezłów bez węzła głównego."
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
@@ -3858,11 +3854,11 @@ msgstr "Aktualizuj ze sceny"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Flat0"
-msgstr ""
+msgstr "Flat0"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Flat1"
-msgstr ""
+msgstr "Flat1"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Ease in"
@@ -4042,7 +4038,6 @@ msgid "Could not create outline!"
msgstr "Nie udało się utworzyć zarysu!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-#, fuzzy
msgid "Create Outline"
msgstr "Utwórz zarys"
@@ -4067,8 +4062,8 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "Utwórz siatkę zarysu.."
+msgid "Create Outline Mesh..."
+msgstr "Utwórz siatkę zarysu..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4282,8 +4277,8 @@ msgid "Error loading image:"
msgstr "BÅ‚Ä…d wczytywania obrazu:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Brak pikseli z przeźroczystością > 128 w obrazie.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Brak pikseli z przeźroczystością > 128 w obrazie..."
#: editor/plugins/particles_2d_editor_plugin.cpp
#, fuzzy
@@ -4653,8 +4648,8 @@ msgid "Import Theme"
msgstr "Zaimportuj motyw"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Zapisz motyw jako.."
+msgid "Save Theme As..."
+msgstr "Zapisz motyw jako..."
#: editor/plugins/script_editor_plugin.cpp
#, fuzzy
@@ -4752,8 +4747,8 @@ msgstr "Przełącz panel skryptów"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Znajdź.."
+msgid "Find..."
+msgstr "Znajdź..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4963,16 +4958,16 @@ msgid "Find Previous"
msgstr "Znajdź poprzedni"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Zamień.."
+msgid "Replace..."
+msgstr "Zamień..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Przejdź do funkcji.."
+msgid "Goto Function..."
+msgstr "Przejdź do funkcji..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Przejdź do linii.."
+msgid "Goto Line..."
+msgstr "Przejdź do linii..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5061,11 +5056,11 @@ msgstr ""
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Add/Remove to Curve Map"
-msgstr ""
+msgstr "Dodaj/Usuń do mapy krzywej"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Modify Curve Map"
-msgstr ""
+msgstr "Edytuj mape krzywej"
#: editor/plugins/shader_graph_editor_plugin.cpp
#, fuzzy
@@ -5299,34 +5294,31 @@ msgstr "Efekt Dopplera"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Left"
-msgstr ""
+msgstr "\"Wolny widok\" w lewo"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Right"
-msgstr ""
+msgstr "\"Wolny widok\" w prawo"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Freelook Forward"
-msgstr "Dalej"
+msgstr "\"Wolny widok\" w przód"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Freelook Backwards"
-msgstr "Wstecz"
+msgstr "\"Wolny widok\" w tył"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Up"
-msgstr ""
+msgstr "\"Wolny widok\" w góre"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Freelook Down"
-msgstr "Kółko myszy w dół."
+msgstr "\"Wolny widok\" w dół"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Speed Modifier"
-msgstr ""
+msgstr "Zmiennik prędkości \"Wolnego widoku\""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "XForm Dialog"
@@ -5342,6 +5334,9 @@ msgid ""
"Alt+Drag: Move\n"
"Alt+RMB: Depth list selection"
msgstr ""
+"Pociągnięcie: Obrót\n"
+"Alt+Pociągnięcie: Poruszenie\n"
+"Alt+PPM: Lista wyboru głębi"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Move Mode (W)"
@@ -5357,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)"
@@ -5440,12 +5435,8 @@ msgid "Transform"
msgstr "Przekształcanie"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Konfiguruj krokowanie.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "Okno transformowania.."
+msgid "Transform Dialog..."
+msgstr "Okno transformowania..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5699,8 +5690,8 @@ msgid "Remove All"
msgstr "Usuń wszystkie"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "Edytuj motyw interfejsu.."
+msgid "Edit theme..."
+msgstr "Edytuj motyw interfejsu..."
#: editor/plugins/theme_editor_plugin.cpp
#, fuzzy
@@ -5772,7 +5763,7 @@ msgstr "Opcje"
#: editor/plugins/theme_editor_plugin.cpp
#, fuzzy
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr "Ma,Wiele,Różnych,Opcji!"
#: editor/plugins/theme_editor_plugin.cpp
@@ -5965,8 +5956,8 @@ msgid "Presets"
msgstr "Profile eksportu"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "Dodaj.."
+msgid "Add..."
+msgstr "Dodaj..."
#: editor/project_export.cpp
msgid "Resources"
@@ -6060,12 +6051,17 @@ msgid "Imported Project"
msgstr "Zaimportowano projekt"
#: editor/project_manager.cpp
+#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Nazwa projektu:"
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "Nie można utworzyć katalogu."
#: editor/project_manager.cpp
msgid "There is already a folder in this path with the specified name."
-msgstr ""
+msgstr "Folder o podanej nazwie istnieje już w tej lokalizacji."
#: editor/project_manager.cpp
msgid "It would be a good idea to name your project."
@@ -6256,10 +6252,13 @@ msgid "Mouse Button"
msgstr "Przycisk myszy"
#: editor/project_settings_editor.cpp
+#, fuzzy
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
+"Niepoprawna nazwa akcji. Nazwa nie może być pusta ani zawierać znaki takie "
+"jak: '/', ':', '=', '\\' lub '\"'"
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6286,8 +6285,8 @@ msgid "Control+"
msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "Naciśnij klawisz.."
+msgid "Press a Key..."
+msgstr "Naciśnij klawisz..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6359,7 +6358,7 @@ msgstr "UrzÄ…dzenie"
#: editor/project_settings_editor.cpp
msgid "Button"
-msgstr "Przycisk"
+msgstr "Button"
#: editor/project_settings_editor.cpp
msgid "Left Button."
@@ -6463,15 +6462,15 @@ msgstr "Ustawienia projektu (project.godot)"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "General"
-msgstr "Ogólny"
+msgstr "Ogólne"
#: editor/project_settings_editor.cpp editor/property_editor.cpp
msgid "Property:"
msgstr "Właściwość:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
-msgstr "Nadpisz dla.."
+msgid "Override For..."
+msgstr "Nadpisz dla..."
#: editor/project_settings_editor.cpp
msgid "Input Map"
@@ -6566,12 +6565,12 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "Plik.."
+msgid "File..."
+msgstr "Plik..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "Katalog.."
+msgid "Dir..."
+msgstr "Katalog..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6682,7 +6681,7 @@ msgstr "Aktualna scena"
#: editor/run_settings_dialog.cpp
msgid "Main Scene"
-msgstr "Główna scena"
+msgstr "Scena główna"
#: editor/run_settings_dialog.cpp
msgid "Main Scene Arguments:"
@@ -6710,6 +6709,7 @@ msgid ""
"Cannot instance the scene '%s' because the current scene exists within one "
"of its nodes."
msgstr ""
+"Nie można utworzyć sceny '%s' ponieważ obecna scena jest jednym z jej wezłów."
#: editor/scene_tree_dock.cpp
#, fuzzy
@@ -6747,7 +6747,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Tej operacji nie można wykonać na dziedziczącej scenie."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr "Zapisz nowÄ… scenÄ™ jako ..."
#: editor/scene_tree_dock.cpp
@@ -7197,7 +7197,7 @@ msgstr "Skróty"
#: editor/settings_config_dialog.cpp
msgid "Binding"
-msgstr ""
+msgstr "WiÄ…zanie"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Light Radius"
@@ -7475,7 +7475,7 @@ msgstr "Wybierz odległość:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "Nazwa klasy nie może być słowem zastrzeżonym"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -7483,7 +7483,7 @@ msgstr "Generowanie solucji..."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating C# project..."
-msgstr ""
+msgstr "Generowanie projektu C#..."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Failed to create solution."
@@ -7507,7 +7507,7 @@ msgstr "Mono"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "About C# support"
-msgstr ""
+msgstr "O wsparciu języka C#"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Create C# solution"
@@ -7708,7 +7708,7 @@ msgstr "Przełącznik"
#: modules/visual_script/visual_script_editor.cpp
msgid "Iterator"
-msgstr ""
+msgstr "Iterator"
#: modules/visual_script/visual_script_editor.cpp
msgid "While"
@@ -7930,6 +7930,10 @@ msgid ""
"Consider adding CollisionShape2D or CollisionPolygon2D children nodes to "
"define its shape."
msgstr ""
+"Ten węzeł nie posiada podwezła, który definiował by jego kształt, więc nie "
+"może wchodzić w interakcje.\n"
+"Powinieneś dodać węzeł \"CollisionShape2D\" lub \"CollisionPolygon2D\" jako "
+"podwęzeł aby zdefiniować kształt."
#: scene/2d/collision_polygon_2d.cpp
msgid ""
@@ -8009,6 +8013,8 @@ msgid ""
"A material to process the particles is not assigned, so no behavior is "
"imprinted."
msgstr ""
+"Nie przypisano materiału do przetwarzania cząsteczek, więc zmiany nie będą "
+"widoczne."
#: scene/2d/path_2d.cpp
msgid "PathFollow2D only works when set as a child of a Path2D node."
@@ -8051,6 +8057,8 @@ msgid ""
"The controller id must not be 0 or this controller will not be bound to an "
"actual controller"
msgstr ""
+"Id kontrolera nie może być '0' w innym przypadku kontroler nie zostanie "
+"przypisany do żadnego rzeczywistego kontrolera"
#: scene/3d/arvr_nodes.cpp
#, fuzzy
@@ -8073,7 +8081,7 @@ msgstr ""
#: scene/3d/baked_lightmap.cpp
msgid "(Time Left: %d:%02d s)"
-msgstr ""
+msgstr "(Pozostały czas: %d:%02d s)"
#: scene/3d/baked_lightmap.cpp
msgid "Plotting Meshes: "
@@ -8284,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 ':')."
@@ -8309,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!"
@@ -8450,8 +8462,8 @@ msgstr "Niepoprawny rozmiar fonta."
#~ msgid "Exporting for %s"
#~ msgstr "Exportowanie do %s"
-#~ msgid "Setting Up.."
-#~ msgstr "Konfigurowanie .."
+#~ msgid "Setting Up..."
+#~ msgstr "Konfigurowanie ..."
#~ msgid "Error loading scene."
#~ msgstr "BÅ‚Ä…d Å‚adowania sceny."
@@ -8509,8 +8521,8 @@ msgstr "Niepoprawny rozmiar fonta."
#~ msgid "Info"
#~ msgstr "Informacje"
-#~ msgid "Re-Import.."
-#~ msgstr "Importuj ponownie.."
+#~ msgid "Re-Import..."
+#~ msgstr "Importuj ponownie..."
#~ msgid "No bit masks to import!"
#~ msgstr "Brak mask bitowych do zaimportowania!"
@@ -8883,13 +8895,13 @@ msgstr "Niepoprawny rozmiar fonta."
#~ msgid "Zoom (%):"
#~ msgstr "Powiększenie (%):"
-#~ msgid "Skeleton.."
-#~ msgstr "Szkielet.."
+#~ msgid "Skeleton..."
+#~ msgstr "Szkielet..."
#~ msgid "Zoom Reset"
#~ msgstr "Wyzeruj przybliżenie"
-#~ msgid "Zoom Set.."
+#~ msgid "Zoom Set..."
#~ msgstr "Ustaw przybliżenie..."
#~ msgid "Set a Value"
@@ -9238,8 +9250,8 @@ msgstr "Niepoprawny rozmiar fonta."
#~ msgid "Export Project PCK"
#~ msgstr "Eksport projektu PCK"
-#~ msgid "Export.."
-#~ msgstr "Eksport.."
+#~ msgid "Export..."
+#~ msgstr "Eksport..."
#~ msgid "Project Export"
#~ msgstr "Eksport projektu"
diff --git a/editor/translations/pr.po b/editor/translations/pr.po
index 94856aa875..0c085024e0 100644
--- a/editor/translations/pr.po
+++ b/editor/translations/pr.po
@@ -500,7 +500,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr ""
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -915,11 +915,11 @@ msgid "Move Audio Bus"
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1055,11 +1055,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1129,7 +1129,7 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr ""
#: editor/editor_file_dialog.cpp
@@ -1394,12 +1394,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1606,11 +1606,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1622,7 +1622,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr ""
#: editor/editor_node.cpp
@@ -1674,7 +1674,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1819,7 +1819,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1831,11 +1831,11 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1855,15 +1855,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2109,7 +2109,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2218,7 +2218,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2370,7 +2370,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2447,7 +2447,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2464,7 +2464,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2479,7 +2479,7 @@ msgstr "Slit th' Node"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2618,11 +2618,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2634,15 +2634,15 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2668,7 +2668,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2734,7 +2734,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2746,7 +2746,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2762,7 +2762,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2782,7 +2782,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3199,7 +3199,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3207,7 +3207,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3275,7 +3275,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3342,7 +3342,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3532,6 +3532,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3957,7 +3958,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4162,7 +4163,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4527,7 +4528,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4625,7 +4626,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4833,15 +4834,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5296,11 +5297,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5556,7 +5553,7 @@ msgid "Remove All"
msgstr "Discharge ye' Signal"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5624,7 +5621,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5813,7 +5810,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5903,6 +5900,11 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Yer index property name be thrown overboard!"
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -6091,8 +6093,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6120,7 +6122,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6306,7 +6308,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6403,11 +6405,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6580,7 +6582,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
diff --git a/editor/translations/pt_BR.po b/editor/translations/pt_BR.po
index 3ad4798ca0..6d26cbc500 100644
--- a/editor/translations/pt_BR.po
+++ b/editor/translations/pt_BR.po
@@ -2,14 +2,14 @@
# 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.
# AWK <arthurwk1800@gmail.com>, 2017.
+# Breno Caldeira <breno.caldeira@gmail.com>, 2018.
# Francesco Perrotti-Garcia <fpg1503@gmail.com>, 2017.
# George Marques <george@gmarqu.es>, 2016.
-# Guilherme Felipe C G Silva <guilhermefelipecgs@gmail.com>, 2017.
+# Guilherme Felipe C G Silva <guilhermefelipecgs@gmail.com>, 2017, 2018.
# João Victor Lima <victordevtb@outlook.com>, 2018.
# João Vitor de Oliveira Carlos <lopogax@gmail.com>, 2018.
# Joaquim Ferreira <joaquimferreira1996@bol.com.br>, 2016.
@@ -23,12 +23,11 @@
# 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-04-30 13:39+0000\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"
@@ -37,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"
@@ -517,13 +516,13 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Desconectar '%s' do '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
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"
@@ -938,12 +937,12 @@ msgid "Move Audio Bus"
msgstr "Mover Canal de Ãudio"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "Salvar Layout de Canais de Ãudio Como..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Localização para o Novo Layout.."
+msgid "Location for New Layout..."
+msgstr "Localização para o Novo Layout..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1081,11 +1080,11 @@ msgid "Updating Scene"
msgstr "Atualizando Cena"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr "Armazenando alterações locais..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr "Atualizando Cena..."
#: editor/editor_data.cpp
@@ -1154,7 +1153,7 @@ msgid "Show In File Manager"
msgstr "Mostrar no Gerenciador de Arquivos"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr "Nova Pasta..."
#: editor/editor_file_dialog.cpp
@@ -1416,19 +1415,19 @@ msgstr "Limpar Saída"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Falha na exportação do projeto com código de erro %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Erro ao salvar Recurso!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "Salvar Recuso como..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr "Entendo..."
#: editor/editor_node.cpp
@@ -1658,11 +1657,11 @@ msgid "Open Base Scene"
msgstr "Abrir Cena Base"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr "Abrir Cena Rapidamente..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr "Abrir Script Rapidamente..."
#: editor/editor_node.cpp
@@ -1674,7 +1673,7 @@ msgid "Save changes to '%s' before closing?"
msgstr "Salvar alterações em '%s' antes de fechar?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr "Salvar Cena Como..."
#: editor/editor_node.cpp
@@ -1726,7 +1725,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Esta ação não pode ser desfeita. Reverter mesmo assim?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr "Rodar Cena Ãgil..."
#: editor/editor_node.cpp
@@ -1887,8 +1886,8 @@ msgid "Previous tab"
msgstr "Guia anterior"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Filtrar Arquivos.."
+msgid "Filter Files..."
+msgstr "Filtrar Arquivos..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1899,11 +1898,11 @@ msgid "New Scene"
msgstr "Nova Cena"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr "Nova Cena Herdada..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr "Abrir Cena..."
#: editor/editor_node.cpp
@@ -1923,15 +1922,15 @@ msgid "Open Recent"
msgstr "Abrir Recente"
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr "Converter Para..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr "MeshLibrary..."
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -1994,7 +1993,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Small Deploy with Network FS"
-msgstr "Pequeno teste com o sistema de arquivos em rede"
+msgstr "Pequena DIstribuição com Sistema de Arquivos de Rede"
#: editor/editor_node.cpp
msgid ""
@@ -2195,7 +2194,7 @@ msgid "Save the currently edited resource."
msgstr "Salva o recurso editado atualmente."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr "Salvar Como..."
#: editor/editor_node.cpp
@@ -2304,7 +2303,7 @@ msgid "Creating Mesh Previews"
msgstr "Criando Previsualizações das Malhas"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr "Miniatura..."
#: editor/editor_plugin_settings.cpp
@@ -2458,7 +2457,7 @@ msgid "(Current)"
msgstr "(Atual)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr "Reconectando, por favor aguarde."
#: editor/export_template_manager.cpp
@@ -2536,7 +2535,7 @@ msgid "Error requesting url: "
msgstr "Erro ao solicitar url: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr "Conectando..."
#: editor/export_template_manager.cpp
@@ -2553,8 +2552,8 @@ msgstr "Não foi possível resolver"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Conectando.."
+msgid "Connecting..."
+msgstr "Conectando..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2566,8 +2565,8 @@ msgstr "Conectado"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Solicitando.."
+msgid "Requesting..."
+msgstr "Solicitando..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2702,11 +2701,11 @@ msgid "Collapse all"
msgstr "Recolher tudo"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr "Renomear..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr "Mover Para..."
#: editor/filesystem_dock.cpp
@@ -2718,15 +2717,15 @@ msgid "Instance"
msgstr "Instância"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Editar Dependências.."
+msgid "Edit Dependencies..."
+msgstr "Editar Dependências..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr "Visualizar Proprietários..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Duplicar..."
#: editor/filesystem_dock.cpp
@@ -2752,7 +2751,7 @@ msgstr "Instanciar a(s) cena(s) selecionada como filho do nó selecionado."
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Escaneando arquivos,\n"
"Por favor aguarde..."
@@ -2820,7 +2819,7 @@ msgid "Import Scene"
msgstr "Importar cena"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr "Importando Cena..."
#: editor/import/resource_importer_scene.cpp
@@ -2832,7 +2831,7 @@ msgid "Generating for Mesh: "
msgstr "Generando para a Malha: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr "Rodando Script Personalizado..."
#: editor/import/resource_importer_scene.cpp
@@ -2848,7 +2847,7 @@ msgid "Error running post-import script:"
msgstr "Erro ao rodar script pós-importação:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr "Salvando..."
#: editor/import_dock.cpp
@@ -2868,7 +2867,7 @@ msgid "Import As:"
msgstr "Importar como:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr "Predefinição..."
#: editor/import_dock.cpp
@@ -3289,7 +3288,7 @@ msgid "Transition Node"
msgstr "Nó Transition"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr "Importar Animações..."
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3297,7 +3296,7 @@ msgid "Edit Node Filters"
msgstr "Editar Filtros de Nó"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr "Filtros..."
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3306,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:"
@@ -3365,7 +3364,7 @@ msgid "Fetching:"
msgstr "Procurando:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr "Resolvendo..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3432,8 +3431,8 @@ msgid "Site:"
msgstr "Site:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Suporte.."
+msgid "Support..."
+msgstr "Suporte..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3630,8 +3629,9 @@ msgid "Use Rotation Snap"
msgstr "Usar Snap de Rotação"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
-msgstr "Configurar Encaixe..."
+msgstr "Configurar Snap..."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap Relative"
@@ -3726,14 +3726,12 @@ msgid "Show Guides"
msgstr "Mostrar guias"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "Ver Origem"
+msgstr "Mostrar Origem"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 Viewport"
+msgstr "Mostrar Viewport"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -4026,7 +4024,7 @@ msgstr "Malha não tem superfície para criar contornos dela!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "Tipo primitivo da Mesh não é PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4057,8 +4055,8 @@ msgid "Create Convex Collision Sibling"
msgstr "Criar Colisão Convexa Irmã"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "Criar Malha de Contorno.."
+msgid "Create Outline Mesh..."
+msgstr "Criar Malha de Contorno..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4265,7 +4263,7 @@ msgid "Error loading image:"
msgstr "Erro ao carregar imagem:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr "Nenhum pixel com transparência > 128 na imagem."
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4626,7 +4624,7 @@ msgid "Import Theme"
msgstr "Importar Tema"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr "Salvar Tema Como..."
#: editor/plugins/script_editor_plugin.cpp
@@ -4723,7 +4721,7 @@ msgstr "Alternar Painel de Scripts"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr "Localizar..."
#: editor/plugins/script_editor_plugin.cpp
@@ -4933,15 +4931,15 @@ msgid "Find Previous"
msgstr "Encontrar Anterior"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr "Substituir..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr "Ir para Função..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr "Ir para linha..."
#: editor/plugins/script_text_editor.cpp
@@ -5395,11 +5393,7 @@ msgid "Transform"
msgstr "Transformação"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Configurar Snap..."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr "Diálogo Transformação..."
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5652,8 +5646,8 @@ msgid "Remove All"
msgstr "Remover Tudo"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "Editar tema.."
+msgid "Edit theme..."
+msgstr "Editar tema..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5681,11 +5675,11 @@ msgstr "Criar a Partir do Tema Atual do Editor"
#: editor/plugins/theme_editor_plugin.cpp
msgid "CheckBox Radio1"
-msgstr "Rádio Checkbox 1"
+msgstr "CheckBox Rádio1"
#: editor/plugins/theme_editor_plugin.cpp
msgid "CheckBox Radio2"
-msgstr "Caixa de Seleção 2"
+msgstr "CheckBox Rádio2"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Item"
@@ -5693,21 +5687,19 @@ msgstr "Item"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Check Item"
-msgstr "Checar Item"
+msgstr "Item Marcável"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Checked Item"
msgstr "Item Checado"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "Adicionar Item"
+msgstr "Item Rádio"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "Item Checado"
+msgstr "Item Rádio Marcado"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5715,15 +5707,15 @@ msgstr "Tem"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Many"
-msgstr "Muitos"
+msgstr "Muitas"
#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
msgid "Options"
msgstr "Opções"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr "Ter,Muitas,Várias,Opções!"
+msgid "Has,Many,Options"
+msgstr "Tem,Muitas,Opções"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5755,7 +5747,7 @@ msgstr "Fonte"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Color"
-msgstr "Color"
+msgstr "Cor"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme"
@@ -5916,7 +5908,7 @@ msgid "Presets"
msgstr "Predefiniçoes"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr "Adicionar..."
#: editor/project_export.cpp
@@ -6012,6 +6004,10 @@ msgid "Imported Project"
msgstr "Projeto Importado"
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "Nome do Projeto Inválido."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "Impossível criar a pasta."
@@ -6212,9 +6208,11 @@ msgstr "Botão do Mous"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"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 '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6241,7 +6239,7 @@ msgid "Control+"
msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr "Pressione uma Tecla..."
#: editor/project_settings_editor.cpp
@@ -6425,7 +6423,7 @@ msgid "Property:"
msgstr "Propriedade:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr "Sobrescrever Para..."
#: editor/project_settings_editor.cpp
@@ -6521,11 +6519,11 @@ msgid "Easing Out-In"
msgstr "Easing Out-In"
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr "Arquivo..."
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr "Dir..."
#: editor/property_editor.cpp
@@ -6698,7 +6696,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Essa operação não pode ser realizada em cenas instanciadas."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr "Salvar Nova Cena Como..."
#: editor/scene_tree_dock.cpp
@@ -7415,7 +7413,7 @@ msgstr "Escolha uma Distância:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "Nome da classe não pode ser uma palavra reservada"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -8129,7 +8127,7 @@ msgstr "A propriedade Caminho deve apontar para um nó Spatial para funcionar."
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "WorldEnvironment precisa de um recurso Environment."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8143,6 +8141,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 ""
+"Este WorldEnvironment está sendo ignorado. Adicione uma Camera (para cenas "
+"3D) ou defina o Background Mode deste ambiente para Canvas (para cenas 2D)."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8240,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 ':')."
@@ -8266,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!"
@@ -8414,7 +8418,7 @@ msgstr "Tamanho de fonte inválido."
#~ msgid "Exporting for %s"
#~ msgstr "Exportando para %s"
-#~ msgid "Setting Up.."
+#~ msgid "Setting Up..."
#~ msgstr "Ajustando..."
#~ msgid "Error loading scene."
@@ -8476,7 +8480,7 @@ msgstr "Tamanho de fonte inválido."
#~ msgid "Info"
#~ msgstr "Informação"
-#~ msgid "Re-Import.."
+#~ msgid "Re-Import..."
#~ msgstr "Re-importar..."
#~ msgid "No bit masks to import!"
@@ -8871,13 +8875,13 @@ msgstr "Tamanho de fonte inválido."
#~ msgid "Zoom (%):"
#~ msgstr "Ampliação (%):"
-#~ msgid "Skeleton.."
+#~ msgid "Skeleton..."
#~ msgstr "Esqueleto..."
#~ msgid "Zoom Reset"
#~ msgstr "Restaurar Ampliação"
-#~ msgid "Zoom Set.."
+#~ msgid "Zoom Set..."
#~ msgstr "Definir Ampliação..."
#~ msgid "Set a Value"
@@ -9297,7 +9301,7 @@ msgstr "Tamanho de fonte inválido."
#~ msgid "Export Project PCK"
#~ msgstr "Exportar PCK do Projeto"
-#~ msgid "Export.."
+#~ msgid "Export..."
#~ msgstr "Exportar..."
#~ msgid "Project Export"
@@ -9404,7 +9408,7 @@ msgstr "Tamanho de fonte inválido."
#~ msgid "Reload Tool Script (Soft)"
#~ msgstr "Recarregar Tool Script (suave)"
-#~ msgid "Edit Connections.."
+#~ msgid "Edit Connections..."
#~ msgstr "Editar Conexões..."
#~ msgid "Set Params"
@@ -9461,5 +9465,5 @@ msgstr "Tamanho de fonte inválido."
#~ msgid "Source Texture:"
#~ msgstr "Textura de Origem:"
-#~ msgid "Merging.."
+#~ msgid "Merging..."
#~ msgstr "Fundindo..."
diff --git a/editor/translations/pt_PT.po b/editor/translations/pt_PT.po
index 84e80718da..71275cd19a 100644
--- a/editor/translations/pt_PT.po
+++ b/editor/translations/pt_PT.po
@@ -2,9 +2,9 @@
# 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.
# João Graça <jgraca95@gmail.com>, 2017.
# João Lopes <linux-man@hotmail.com>, 2017-2018.
# Miguel Gomes <miggas09@gmail.com>, 2017.
@@ -13,11 +13,10 @@
# 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-04-25 09:40+0000\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"
@@ -25,7 +24,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-dev\n"
+"X-Generator: Weblate 3.0\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -505,7 +504,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Desligar '%s' de '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "Ligar..."
#: editor/connections_dialog.cpp
@@ -926,12 +925,12 @@ msgid "Move Audio Bus"
msgstr "Mover barramento de áudio"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Guardar Modelo do barramento de áudio como.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Guardar Modelo do barramento de áudio como..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Localização para o Novo Modelo.."
+msgid "Location for New Layout..."
+msgstr "Localização para o Novo Modelo..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1072,12 +1071,12 @@ msgid "Updating Scene"
msgstr "Atualizando a Cena"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Armazenando alterações locais.."
+msgid "Storing local changes..."
+msgstr "Armazenando alterações locais..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Atualizando a Cena.."
+msgid "Updating scene..."
+msgstr "Atualizando a Cena..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1145,8 +1144,8 @@ msgid "Show In File Manager"
msgstr "Mostrar no Gestor de Ficheiros"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Nova Diretoria.."
+msgid "New Folder..."
+msgstr "Nova Diretoria..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1407,20 +1406,20 @@ msgstr "Limpar Saída"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Exportação do projeto falhou com código de erro %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Erro ao guardar recurso!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Guardar Recurso Como.."
+msgid "Save Resource As..."
+msgstr "Guardar Recurso Como..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "Eu vejo.."
+msgid "I see..."
+msgstr "Eu vejo..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1653,12 +1652,12 @@ msgid "Open Base Scene"
msgstr "Abrir Cena Base"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Abrir Cena de forma rápida.."
+msgid "Quick Open Scene..."
+msgstr "Abrir Cena de forma rápida..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Abrir Script de forma rápida.."
+msgid "Quick Open Script..."
+msgstr "Abrir Script de forma rápida..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1669,8 +1668,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Guardar alterações a '%s' antes de fechar?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Guardar Cena como.."
+msgid "Save Scene As..."
+msgstr "Guardar Cena como..."
#: editor/editor_node.cpp
msgid "No"
@@ -1721,8 +1720,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Esta acção não pode ser desfeita. Reverter na mesma?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Executar Cena de forma rápida.."
+msgid "Quick Run Scene..."
+msgstr "Executar Cena de forma rápida..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1878,8 +1877,8 @@ msgid "Previous tab"
msgstr "Guia anterior"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Filtrar Ficheiro.."
+msgid "Filter Files..."
+msgstr "Filtrar Ficheiro..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1890,12 +1889,12 @@ msgid "New Scene"
msgstr "Nova Cena"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Nova Cena Herdada.."
+msgid "New Inherited Scene..."
+msgstr "Nova Cena Herdada..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Abrir Cena.."
+msgid "Open Scene..."
+msgstr "Abrir Cena..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1914,16 +1913,16 @@ msgid "Open Recent"
msgstr "Abrir Recente"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Converter Para.."
+msgid "Convert To..."
+msgstr "Converter Para..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshLibrary.."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet.."
+msgid "TileSet..."
+msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2110,7 +2109,7 @@ msgstr "Comunidade"
#: editor/editor_node.cpp
msgid "About"
-msgstr "Sobre"
+msgstr "Sobre Nós"
#: editor/editor_node.cpp
msgid "Play the project."
@@ -2185,8 +2184,8 @@ msgid "Save the currently edited resource."
msgstr "Guarde o recurso editado."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Guardar Como.."
+msgid "Save As..."
+msgstr "Guardar Como..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2294,8 +2293,8 @@ msgid "Creating Mesh Previews"
msgstr "A criar pré-visualizações de Mesh"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Miniatura.."
+msgid "Thumbnail..."
+msgstr "Miniatura..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2447,7 +2446,7 @@ msgid "(Current)"
msgstr "(Atual)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr "A readquirir servidores, espere por favor..."
#: editor/export_template_manager.cpp
@@ -2525,7 +2524,7 @@ msgid "Error requesting url: "
msgstr "Erro ao solicitar url: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr "A ligar ao servidor..."
#: editor/export_template_manager.cpp
@@ -2542,8 +2541,8 @@ msgstr "Impossível resolver"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "A ligar.."
+msgid "Connecting..."
+msgstr "A ligar..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2555,7 +2554,7 @@ msgstr "Ligado"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "A solicitar..."
#: editor/export_template_manager.cpp
@@ -2691,12 +2690,12 @@ msgid "Collapse all"
msgstr "Colapsar tudo"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Renomear.."
+msgid "Rename..."
+msgstr "Renomear..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "Mover para.."
+msgid "Move To..."
+msgstr "Mover para..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2707,15 +2706,15 @@ msgid "Instance"
msgstr "Instância"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Editar Dependências.."
+msgid "Edit Dependencies..."
+msgstr "Editar Dependências..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr "Ver proprietários..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Duplicar..."
#: editor/filesystem_dock.cpp
@@ -2741,7 +2740,7 @@ msgstr "Instancie a(s) Cena(s) selecionada(s) como filha(s) do Nó selecionado."
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"A analisar Ficheiros,\n"
"Espere, por favor..."
@@ -2809,8 +2808,8 @@ msgid "Import Scene"
msgstr "Importar Cena"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "A importar Cena.."
+msgid "Importing Scene..."
+msgstr "A importar Cena..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2821,8 +2820,8 @@ msgid "Generating for Mesh: "
msgstr "A gerar para Mesh: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "A executar Script Customizado.."
+msgid "Running Custom Script..."
+msgstr "A executar Script Customizado..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2837,8 +2836,8 @@ msgid "Error running post-import script:"
msgstr "Erro na execução do Script de pós-importação:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "A guardar.."
+msgid "Saving..."
+msgstr "A guardar..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2857,8 +2856,8 @@ msgid "Import As:"
msgstr "Importar Como:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Predefinido.."
+msgid "Preset..."
+msgstr "Predefinido..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3276,7 +3275,7 @@ msgid "Transition Node"
msgstr "Nó Transition"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr "Importar Animações..."
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3284,7 +3283,7 @@ msgid "Edit Node Filters"
msgstr "Editar filtros de Nó"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr "Filtros..."
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3352,7 +3351,7 @@ msgid "Fetching:"
msgstr "Em busca:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr "A resolver..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3419,7 +3418,7 @@ msgid "Site:"
msgstr "Site:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr "Suporte..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3614,6 +3613,7 @@ msgid "Use Rotation Snap"
msgstr "Usar Ajuste na rotação"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "Configurar Ajuste..."
@@ -3710,14 +3710,12 @@ msgid "Show Guides"
msgstr "Mostrar guias"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "Ver origem"
+msgstr "Mostrar Origem"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 Vista"
+msgstr "Mostrar Vista"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -4010,7 +4008,7 @@ msgstr "A Mesh não tem superfície para criar contornos!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "Tipo primitivo de Mesh não é PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4041,7 +4039,7 @@ msgid "Create Convex Collision Sibling"
msgstr "Criar irmão de colisão convexa"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr "Criar Mesh contorno..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4246,7 +4244,7 @@ msgid "Error loading image:"
msgstr "Erro ao carregar imagem:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr "Sem pixeis com transparência > 128 na imagem..."
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4607,7 +4605,7 @@ msgid "Import Theme"
msgstr "Importar tema"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr "Guardar tema como..."
#: editor/plugins/script_editor_plugin.cpp
@@ -4704,7 +4702,7 @@ msgstr "Alternar painel de Scripts"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr "Encontrar..."
#: editor/plugins/script_editor_plugin.cpp
@@ -4914,15 +4912,15 @@ msgid "Find Previous"
msgstr "Encontrar anterior"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr "Substituir..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr "Ir para Função..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr "Ir para linha..."
#: editor/plugins/script_text_editor.cpp
@@ -5376,11 +5374,7 @@ msgid "Transform"
msgstr "Transformar"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Configurar Ajuste..."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr "Diálogo de transformação..."
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5442,7 +5436,7 @@ msgstr "Ajuste de escala (%):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Viewport Settings"
-msgstr "Configuração de vista"
+msgstr "Configuração de Vista"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Perspective FOV (deg.):"
@@ -5633,7 +5627,7 @@ msgid "Remove All"
msgstr "Remover tudo"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr "Editar tema..."
#: editor/plugins/theme_editor_plugin.cpp
@@ -5681,14 +5675,12 @@ msgid "Checked Item"
msgstr "Item verificado"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "Adicionar item"
+msgstr "Item Rádio"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "Item verificado"
+msgstr "Item Rádio marcado"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5703,8 +5695,8 @@ msgid "Options"
msgstr "Opções"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr "Tem,Muitos,Vários,Opções!"
+msgid "Has,Many,Options"
+msgstr "Tem,Muitas,Opções"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5896,7 +5888,7 @@ msgid "Presets"
msgstr "Predefinições"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr "Adicionar..."
#: editor/project_export.cpp
@@ -5991,6 +5983,10 @@ msgid "Imported Project"
msgstr "Projeto importado"
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "Nome do Projeto Inválido."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "Impossível criar pasta."
@@ -6190,9 +6186,11 @@ msgstr "Botão do rato"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"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!"
@@ -6219,7 +6217,7 @@ msgid "Control+"
msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr "Pressione uma tecla..."
#: editor/project_settings_editor.cpp
@@ -6403,7 +6401,7 @@ msgid "Property:"
msgstr "Propriedade:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr "Sobrepor por..."
#: editor/project_settings_editor.cpp
@@ -6476,7 +6474,7 @@ msgstr "Carregamento automático"
#: editor/property_editor.cpp
msgid "Pick a Viewport"
-msgstr "Escolha uma vista"
+msgstr "Escolha uma Vista"
#: editor/property_editor.cpp
msgid "Ease In"
@@ -6499,11 +6497,11 @@ msgid "Easing Out-In"
msgstr "Easing Out-In"
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr "Ficheiro..."
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr "Diretoria..."
#: editor/property_editor.cpp
@@ -6540,7 +6538,7 @@ msgstr "Erro ao carregar Ficheiro: Não é um recurso!"
#: editor/property_editor.cpp
msgid "Selected node is not a Viewport!"
-msgstr "Nó selecionado não é uma vista!"
+msgstr "Nó selecionado não é uma Vista!"
#: editor/property_editor.cpp
msgid "Pick a Node"
@@ -6676,7 +6674,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Esta operação não pode ser feita numa Cena instanciada."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr "Guardar nova Cena como..."
#: editor/scene_tree_dock.cpp
@@ -7394,7 +7392,7 @@ msgstr "Distância de escolha:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "Nome de classe não pode ser uma palavra-chave reservada"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -8108,7 +8106,7 @@ msgstr ""
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "WorldEnvironment precisa de um recurso Environment."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8122,6 +8120,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 ""
+"Este WorldEnvironment Ä— ignorado. Pode adicionar uma Camera (para cenas 3D) "
+"ou definir o Modo Background deste ambiente como Canvas (para cenas 2D)."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8219,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 ':')."
@@ -8244,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 e5b3fcbad7..eaf931092a 100644
--- a/editor/translations/ro.po
+++ b/editor/translations/ro.po
@@ -2,15 +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-02 18:03+0000\n"
-"Last-Translator: Filip <filipanton@tutanota.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"
@@ -18,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"
@@ -256,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."
@@ -498,8 +498,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Deconectați '%s' de la '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "Conectați.."
+msgid "Connect..."
+msgstr "Conectați..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -549,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:"
@@ -793,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!"
@@ -892,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"
@@ -919,16 +919,16 @@ msgid "Move Audio Bus"
msgstr "Mutați Pista Audio"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Salvați Pista Audio Șablon Ca.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Salvați Schema Pistei Audio Ca..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Locație pentru Noul Șablon..."
+msgid "Location for New Layout..."
+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."
@@ -936,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"
@@ -944,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
@@ -953,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
@@ -962,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"
@@ -970,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."
@@ -1064,12 +1064,12 @@ msgid "Updating Scene"
msgstr "Scena se Actualizează"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr "Modificările locale se stochează..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Scena se Actualizează.."
+msgid "Updating scene..."
+msgstr "Scena se Actualizează..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1114,223 +1114,223 @@ msgstr "Fişierul se Stochează:"
#: editor/editor_export.cpp
msgid "Packing"
-msgstr ""
+msgstr "Ambalare"
#: editor/editor_export.cpp platform/javascript/export/export.cpp
msgid "Template file not found:"
-msgstr ""
+msgstr "Fișierul șablon nu a fost găsit:"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "File Exists, Overwrite?"
-msgstr ""
+msgstr "Fișierul există, suprascrieţi?"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Select Current Folder"
-msgstr ""
+msgstr "Selectaţi directorul curent"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
msgid "Copy Path"
-msgstr ""
+msgstr "Copiaţi Calea"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
msgid "Show In File Manager"
-msgstr ""
+msgstr "Arătați în Administratorul de Fișiere"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr ""
+msgid "New Folder..."
+msgstr "Director Nou..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
-msgstr ""
+msgstr "Reîmprospătați"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "All Recognized"
-msgstr ""
+msgstr "Toate Recunoscute"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "All Files (*)"
-msgstr ""
+msgstr "Toate FiÅŸierele (*)"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open a File"
-msgstr ""
+msgstr "Deschideți un Fișier"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open File(s)"
-msgstr ""
+msgstr "Deschideți Fișier(e)"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open a Directory"
-msgstr ""
+msgstr "Deschideţi un Director"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open a File or Directory"
-msgstr ""
+msgstr "Deschideți un Fişier sau Director"
#: editor/editor_file_dialog.cpp editor/editor_node.cpp
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/script_editor_plugin.cpp scene/gui/file_dialog.cpp
msgid "Save"
-msgstr ""
+msgstr "Salvați"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Save a File"
-msgstr ""
+msgstr "Salvați un Fișier"
#: editor/editor_file_dialog.cpp
msgid "Go Back"
-msgstr ""
+msgstr "ÃŽnapoi"
#: editor/editor_file_dialog.cpp
msgid "Go Forward"
-msgstr ""
+msgstr "ÃŽnainte"
#: editor/editor_file_dialog.cpp
msgid "Go Up"
-msgstr ""
+msgstr "Sus"
#: editor/editor_file_dialog.cpp
msgid "Toggle Hidden Files"
-msgstr ""
+msgstr "Comutați Fișiere Ascunse"
#: editor/editor_file_dialog.cpp
msgid "Toggle Favorite"
-msgstr ""
+msgstr "Comutați Favorite"
#: editor/editor_file_dialog.cpp
msgid "Toggle Mode"
-msgstr ""
+msgstr "Modul de Comutare"
#: editor/editor_file_dialog.cpp
msgid "Focus Path"
-msgstr ""
+msgstr "Calea Focală"
#: editor/editor_file_dialog.cpp
msgid "Move Favorite Up"
-msgstr ""
+msgstr "Deplasați Favorit Sus"
#: editor/editor_file_dialog.cpp
msgid "Move Favorite Down"
-msgstr ""
+msgstr "Deplasați Favorit Jos"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Go to parent folder"
-msgstr ""
+msgstr "Accesați Directorul Părinte"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Directories & Files:"
-msgstr ""
+msgstr "Directoare și Fişiere:"
#: editor/editor_file_dialog.cpp
msgid "Preview:"
-msgstr ""
+msgstr "Previzualizați:"
#: editor/editor_file_dialog.cpp editor/script_editor_debugger.cpp
#: scene/gui/file_dialog.cpp
msgid "File:"
-msgstr ""
+msgstr "Fișier:"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Must use a valid extension."
-msgstr ""
+msgstr "Trebuie să utilizaţi o extensie valida."
#: editor/editor_file_system.cpp
msgid "ScanSources"
-msgstr ""
+msgstr "SurseScan"
#: editor/editor_file_system.cpp
msgid "(Re)Importing Assets"
-msgstr ""
+msgstr "(Re)Importând Asset-uri"
#: editor/editor_help.cpp editor/editor_node.cpp
#: editor/plugins/script_editor_plugin.cpp
msgid "Search Help"
-msgstr ""
+msgstr "Căutați în Ajutor"
#: editor/editor_help.cpp
msgid "Class List:"
-msgstr ""
+msgstr "Listă de Clase:"
#: editor/editor_help.cpp
msgid "Search Classes"
-msgstr ""
+msgstr "Căutare Clase"
#: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp
msgid "Top"
-msgstr ""
+msgstr "Sus"
#: editor/editor_help.cpp editor/property_editor.cpp
msgid "Class:"
-msgstr ""
+msgstr "Clasă:"
#: editor/editor_help.cpp editor/scene_tree_editor.cpp
msgid "Inherits:"
-msgstr ""
+msgstr "Moștenește:"
#: editor/editor_help.cpp
msgid "Inherited by:"
-msgstr ""
+msgstr "MoÅŸtenit de:"
#: editor/editor_help.cpp
msgid "Brief Description:"
-msgstr ""
+msgstr "Descriere Scurtă:"
#: editor/editor_help.cpp
msgid "Members"
-msgstr ""
+msgstr "Membri"
#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
msgid "Members:"
-msgstr ""
+msgstr "Membri:"
#: editor/editor_help.cpp
msgid "Public Methods"
-msgstr ""
+msgstr "Metode Publice"
#: editor/editor_help.cpp
msgid "Public Methods:"
-msgstr ""
+msgstr "Metode Publice:"
#: editor/editor_help.cpp
msgid "GUI Theme Items"
-msgstr ""
+msgstr "Obiecte Tema Interfața Grafică"
#: editor/editor_help.cpp
msgid "GUI Theme Items:"
-msgstr ""
+msgstr "Obiecte Tema Interfața Grafică:"
#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
msgid "Signals:"
-msgstr ""
+msgstr "Semnale:"
#: editor/editor_help.cpp
msgid "Enumerations"
-msgstr ""
+msgstr "Enumerări"
#: editor/editor_help.cpp
msgid "Enumerations:"
-msgstr ""
+msgstr "Enumerări:"
#: editor/editor_help.cpp
msgid "enum "
-msgstr ""
+msgstr "enum "
#: editor/editor_help.cpp
msgid "Constants"
-msgstr ""
+msgstr "Constante"
#: editor/editor_help.cpp
msgid "Constants:"
-msgstr ""
+msgstr "Constante:"
#: editor/editor_help.cpp
msgid "Description"
-msgstr ""
+msgstr "Descriere"
#: editor/editor_help.cpp
msgid "Online Tutorials:"
-msgstr ""
+msgstr "Tutoriale Internet:"
#: editor/editor_help.cpp
msgid ""
@@ -1338,164 +1338,174 @@ msgid ""
"$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/"
"url][/color]."
msgstr ""
+"Nu există în prezent nici un tutorial pentru această clasă, puteţi [culoare "
+"= $color] [url = $url] contribui unul [/ URL] [/ color] sau [culoare = "
+"$color] [url = $url2] cerere unul[/ URL] [/ color]."
#: editor/editor_help.cpp
msgid "Properties"
-msgstr ""
+msgstr "Proprietăți"
#: editor/editor_help.cpp
msgid "Property Description:"
-msgstr ""
+msgstr "Descriere Proprietate:"
#: editor/editor_help.cpp
msgid ""
"There is currently no description for this property. Please help us by "
"[color=$color][url=$url]contributing one[/url][/color]!"
msgstr ""
+"Nu există în prezent nici o descriere pentru această proprietate. Te rog "
+"ajută-ne prin a [color = $color] [url = $url] contribui cu una [/ URL] [/ "
+"color]!"
#: editor/editor_help.cpp
msgid "Methods"
-msgstr ""
+msgstr "Metode"
#: editor/editor_help.cpp
msgid "Method Description:"
-msgstr ""
+msgstr "Descrierea metodei:"
#: editor/editor_help.cpp
msgid ""
"There is currently no description for this method. Please help us by [color="
"$color][url=$url]contributing one[/url][/color]!"
msgstr ""
+"Nu există în prezent nici o descriere pentru această metodă. Te rog ajută-ne "
+"de prin a [color = $color] [url = $url] contribui cu una [/ URL] [/ color]!"
#: editor/editor_help.cpp
msgid "Search Text"
-msgstr ""
+msgstr "Căutați Text"
#: editor/editor_help.cpp
msgid "Find"
-msgstr ""
+msgstr "Găsiți"
#: editor/editor_log.cpp
msgid "Output:"
-msgstr ""
+msgstr "Afișare:"
#: editor/editor_log.cpp editor/plugins/animation_tree_editor_plugin.cpp
#: editor/property_editor.cpp editor/script_editor_debugger.cpp
#: modules/gdnative/gdnative_library_editor_plugin.cpp scene/gui/line_edit.cpp
#: scene/gui/text_edit.cpp
msgid "Clear"
-msgstr ""
+msgstr "Curăță"
#: editor/editor_log.cpp
msgid "Clear Output"
-msgstr ""
+msgstr "Curăță Afișarea"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Exportul de proiect nu a reuÅŸit cu un cod de eroare %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
-msgstr ""
+msgstr "Eroare la salvarea resursei!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr ""
+msgid "Save Resource As..."
+msgstr "Salvați Resursa Ca..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr ""
+msgid "I see..."
+msgstr "Am înțeles..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
-msgstr ""
+msgstr "Nu pot deschide fiÅŸierul pentru scris:"
#: editor/editor_node.cpp
msgid "Requested file format unknown:"
-msgstr ""
+msgstr "Formatul fiÅŸierului solicitat este necunoscut:"
#: editor/editor_node.cpp
msgid "Error while saving."
-msgstr ""
+msgstr "Eroare la salvare."
#: editor/editor_node.cpp
msgid "Can't open '%s'."
-msgstr ""
+msgstr "Imposibil de deschis '%s'."
#: editor/editor_node.cpp
msgid "Error while parsing '%s'."
-msgstr ""
+msgstr "Eroare analizând '%s'."
#: editor/editor_node.cpp
msgid "Unexpected end of file '%s'."
-msgstr ""
+msgstr "Sfârșit de fișier neaşteptat '%s'."
#: editor/editor_node.cpp
msgid "Missing '%s' or its dependencies."
-msgstr ""
+msgstr "Lipsește '%s' sau dependenţele sale."
#: editor/editor_node.cpp
msgid "Error while loading '%s'."
-msgstr ""
+msgstr "Eroare în timpul încărcării '%s'."
#: editor/editor_node.cpp
msgid "Saving Scene"
-msgstr ""
+msgstr "Salvând Scena"
#: editor/editor_node.cpp
msgid "Analyzing"
-msgstr ""
+msgstr "Analizând"
#: editor/editor_node.cpp
msgid "Creating Thumbnail"
-msgstr ""
+msgstr "Creând Thumbnail"
#: editor/editor_node.cpp
msgid "This operation can't be done without a tree root."
-msgstr ""
+msgstr "Aceasta operațiune nu se poate face fără o rădăcină de copac."
#: editor/editor_node.cpp
msgid ""
"Couldn't save scene. Likely dependencies (instances or inheritance) couldn't "
"be satisfied."
msgstr ""
+"Nu am putut salva scena. Probabil dependenţe (instanţe sau moşteniri) nu au "
+"putut fi satisfăcute."
#: editor/editor_node.cpp
msgid "Failed to load resource."
-msgstr ""
+msgstr "Încărcarea resursei a eșuat."
#: editor/editor_node.cpp
msgid "Can't load MeshLibrary for merging!"
-msgstr ""
+msgstr "Imposibil de încărcat MeshLibrary pentru unire!"
#: editor/editor_node.cpp
msgid "Error saving MeshLibrary!"
-msgstr ""
+msgstr "Eroare la salvarea MeshLibrary!"
#: editor/editor_node.cpp
msgid "Can't load TileSet for merging!"
-msgstr ""
+msgstr "Imposibil de încărcat TileSet pentru unire!"
#: editor/editor_node.cpp
msgid "Error saving TileSet!"
-msgstr ""
+msgstr "Eroare la salvarea TileSet!"
#: editor/editor_node.cpp
msgid "Error trying to save layout!"
-msgstr ""
+msgstr "Eroare la încercarea de a salva schema!"
#: editor/editor_node.cpp
msgid "Default editor layout overridden."
-msgstr ""
+msgstr "Schemă implicită de editor suprascrisă."
#: editor/editor_node.cpp
msgid "Layout name not found!"
-msgstr ""
+msgstr "Numele schemei nu a fost găsit!"
#: editor/editor_node.cpp
msgid "Restored default layout to base settings."
-msgstr ""
+msgstr "S-a restaurat schema implictă la setările de bază."
#: editor/editor_node.cpp
msgid ""
@@ -1503,18 +1513,26 @@ msgid ""
"Please read the documentation relevant to importing scenes to better "
"understand this workflow."
msgstr ""
+"Această resursă aparţine de o scena care a fost importată, astfel încât nu "
+"este editabilă.\n"
+"Vă rugăm să citiţi documentaţia relevantă pentru importul scene pentru a "
+"înţelege mai bine cum sa lucrați cu acestea."
#: editor/editor_node.cpp
msgid ""
"This resource belongs to a scene that was instanced or inherited.\n"
"Changes to it will not be kept when saving the current scene."
msgstr ""
+"Această resursă este o scena care a fost instanțată sau moştenită.\n"
+"Modificările la acesta nu vor fi păstrate la salvarea scenei curente."
#: editor/editor_node.cpp
msgid ""
"This resource was imported, so it's not editable. Change its settings in the "
"import panel and then re-import."
msgstr ""
+"Această resursă a fost importată, astfel încât nu este editabilă. Modificaţi "
+"setările din panoul de import şi apoi reimportați."
#: editor/editor_node.cpp
msgid ""
@@ -1523,6 +1541,11 @@ msgid ""
"Please read the documentation relevant to importing scenes to better "
"understand this workflow."
msgstr ""
+"Această scenă a fost importată, astfel încât modificările la acesta nu vor "
+"fi păstrate.\n"
+"Instanțarea sau moştenirea vă permite efectuarea de modificări la acesta.\n"
+"Vă rugăm să citiţi documentaţia relevantă pentru importul scene pentru a "
+"înţelege mai bine acest mod de lucru."
#: editor/editor_node.cpp
msgid ""
@@ -1530,46 +1553,50 @@ msgid ""
"Please read the documentation relevant to debugging to better understand "
"this workflow."
msgstr ""
+"Acesta este un obiect îndepărtat, astfel încât modificările la acesta nu vor "
+"fi păstrate.\n"
+"Vă rugăm să citiţi documentaţia relevantă pentru depanare pentru a înţelege "
+"mai bine acest mod de lucru."
#: editor/editor_node.cpp
msgid "Expand all properties"
-msgstr ""
+msgstr "Extinde toate proprietăţile"
#: editor/editor_node.cpp
msgid "Collapse all properties"
-msgstr ""
+msgstr "Restrânge toate proprietăţile"
#: editor/editor_node.cpp
msgid "Copy Params"
-msgstr ""
+msgstr "Copie Parametrii"
#: editor/editor_node.cpp
msgid "Paste Params"
-msgstr ""
+msgstr "Lipiţi Parametrii"
#: editor/editor_node.cpp editor/plugins/resource_preloader_editor_plugin.cpp
msgid "Paste Resource"
-msgstr ""
+msgstr "Lipiți Resursa"
#: editor/editor_node.cpp
msgid "Copy Resource"
-msgstr ""
+msgstr "Copiați Resursa"
#: editor/editor_node.cpp
msgid "Make Built-In"
-msgstr ""
+msgstr "Faceți Încorporat"
#: editor/editor_node.cpp
msgid "Make Sub-Resources Unique"
-msgstr ""
+msgstr "Faceți Sub-Resursa Unică"
#: editor/editor_node.cpp
msgid "Open in Help"
-msgstr ""
+msgstr "Deschideți în Ajutor"
#: editor/editor_node.cpp
msgid "There is no defined scene to run."
-msgstr ""
+msgstr "Nu există nici o scenă definită pentru a execuție."
#: editor/editor_node.cpp
msgid ""
@@ -1577,6 +1604,8 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
+"Nici o scena principala a fost definită, selectați una?\n"
+"Puteți schimba mai târziu, în \"Setări Proiect\" în categoria 'Aplicație'."
#: editor/editor_node.cpp
msgid ""
@@ -1584,6 +1613,8 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
+"Scena selectată ’%s’ nu există, selectați una?\n"
+"PuteÈ›i schimba mai târziu în „Setări Proiect†în categoria „AplicaÈ›ieâ€."
#: editor/editor_node.cpp
msgid ""
@@ -1591,343 +1622,364 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
+"Scena selectată ’%s’ nu este un fișier scenă, selectați una validă?\n"
+"PuteÈ›i schimba mai târziu în „Setări Proiect†în categoria „AplicaÈ›ieâ€."
#: editor/editor_node.cpp
msgid "Current scene was never saved, please save it prior to running."
msgstr ""
+"Scena curentă nu a fost salvată niciodată, salvați-o înainte de rulare."
#: editor/editor_node.cpp
msgid "Could not start subprocess!"
-msgstr ""
+msgstr "Nu s-a putut porni subprocesul!"
#: editor/editor_node.cpp
msgid "Open Scene"
-msgstr ""
+msgstr "Deschide o scenă"
#: editor/editor_node.cpp
msgid "Open Base Scene"
-msgstr ""
+msgstr "Deschide o scenă de bază"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr ""
+msgid "Quick Open Scene..."
+msgstr "Deschide o scenă rapid..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr ""
+msgid "Quick Open Script..."
+msgstr "Deschide un script rapid..."
#: editor/editor_node.cpp
msgid "Save & Close"
-msgstr ""
+msgstr "Salvează și închide"
#: editor/editor_node.cpp
msgid "Save changes to '%s' before closing?"
-msgstr ""
+msgstr "Salvează schimbările la ’%s’ înainte de ieșire?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr ""
+msgid "Save Scene As..."
+msgstr "Salvează scena ca..."
#: editor/editor_node.cpp
msgid "No"
-msgstr ""
+msgstr "Nu"
#: editor/editor_node.cpp
msgid "Yes"
-msgstr ""
+msgstr "Da"
#: editor/editor_node.cpp
msgid "This scene has never been saved. Save before running?"
-msgstr ""
+msgstr "Această scenă nu a fost salvată niciodata. Salvați înainte de rulare?"
#: editor/editor_node.cpp editor/scene_tree_dock.cpp
msgid "This operation can't be done without a scene."
-msgstr ""
+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 ""
+msgid "Quick Run Scene..."
+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 ""
+msgid "Filter Files..."
+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 ""
+msgid "New Inherited Scene..."
+msgstr "Scenă Derivată Nouă..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr ""
+msgid "Open Scene..."
+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 ""
+msgid "Convert To..."
+msgstr "Convertește În..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr ""
+msgid "MeshLibrary..."
+msgstr "Librărie_de_Structuri..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr ""
+msgid "TileSet..."
+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 ""
@@ -1938,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 ""
@@ -1970,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 ""
@@ -1982,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 ""
+msgid "Save As..."
+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 ""
+msgid "Thumbnail..."
+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 ""
+msgid "Retrieving mirrors, please wait..."
+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 ""
+msgid "Connecting to Mirror..."
+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 ""
+msgid "Connecting..."
+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 ""
+msgid "Requesting..."
+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 ""
+msgid "Rename..."
+msgstr "Redenumește..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr ""
+msgid "Move To..."
+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 ""
+msgid "Edit Dependencies..."
+msgstr "Editează Dependințele..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr ""
+msgid "View Owners..."
+msgstr "Vizualizează Proprietarii..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr ""
+msgid "Duplicate..."
+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.."
+"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 ""
+msgid "Importing Scene..."
+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 ""
+msgid "Running Custom Script..."
+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 ""
+msgid "Saving..."
+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 ""
+msgid "Preset..."
+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 ""
@@ -2828,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 ""
+msgid "Import Animations..."
+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 ""
+msgid "Filters..."
+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 ""
+msgid "Resolving..."
+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 ""
+msgid "Support..."
+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 ""
@@ -3351,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
@@ -3487,559 +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 ""
+msgid "Create Outline Mesh..."
+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:"
@@ -4055,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"
@@ -4067,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..."
@@ -4111,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..."
@@ -4143,7 +4245,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4156,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
@@ -4210,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"
@@ -4375,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"
@@ -4387,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
@@ -4407,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"
@@ -4477,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?"
@@ -4504,7 +4606,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4593,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"
@@ -4601,7 +4703,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4807,15 +4909,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5183,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"
@@ -5195,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"
@@ -5255,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"
@@ -5266,11 +5368,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5316,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"
@@ -5360,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"
@@ -5456,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>"
@@ -5464,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"
@@ -5523,7 +5621,7 @@ msgid "Remove All"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5591,7 +5689,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5779,7 +5877,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5869,6 +5967,10 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "Nume de Proiect Nevalid."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -5968,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)"
@@ -6029,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 "
@@ -6055,8 +6165,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6084,7 +6194,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6261,14 +6371,14 @@ 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:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6364,11 +6474,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6469,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"
@@ -6485,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
@@ -6539,7 +6649,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -6590,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)"
@@ -6614,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"
@@ -6652,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"
@@ -6664,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"
@@ -7160,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"
@@ -7212,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"
@@ -7228,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"
@@ -7632,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:"
@@ -7971,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 9ddbc965e5..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,15 +14,15 @@
# 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-04-27 16:39+0000\n"
-"Last-Translator: Chaosus89 <chaosus89@gmail.com>\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"
"Language: 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"
@@ -511,8 +511,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Отключить '%s' от '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "ПриÑоединить.."
+msgid "Connect..."
+msgstr "ПриÑоединить..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -931,12 +931,12 @@ msgid "Move Audio Bus"
msgstr "ПеремеÑтить аудио шину"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Сохранить раÑкладку звуковой шины как.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Сохранить раÑкладку звуковой шины как..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "МеÑтоположение новой раÑкладки.."
+msgid "Location for New Layout..."
+msgstr "МеÑтоположение новой раÑкладки..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1077,12 +1077,12 @@ msgid "Updating Scene"
msgstr "Обновление Ñцены"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Сохранение локальных изменений.."
+msgid "Storing local changes..."
+msgstr "Сохранение локальных изменений..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Обновление Ñцены.."
+msgid "Updating scene..."
+msgstr "Обновление Ñцены..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1150,8 +1150,8 @@ msgid "Show In File Manager"
msgstr "ПроÑмотреть в проводнике"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "ÐÐ¾Ð²Ð°Ñ Ð¿Ð°Ð¿ÐºÐ°.."
+msgid "New Folder..."
+msgstr "ÐÐ¾Ð²Ð°Ñ Ð¿Ð°Ð¿ÐºÐ°..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1412,20 +1412,20 @@ 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!"
msgstr "Ошибка при Ñохранении реÑурÑа!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Сохранить реÑÑƒÑ€Ñ ÐºÐ°Ðº.."
+msgid "Save Resource As..."
+msgstr "Сохранить реÑÑƒÑ€Ñ ÐºÐ°Ðº..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "ЯÑно.."
+msgid "I see..."
+msgstr "ЯÑно..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1655,12 +1655,12 @@ msgid "Open Base Scene"
msgstr "Открыть оÑновную Ñцену"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "БыÑтро открыть Ñцену.."
+msgid "Quick Open Scene..."
+msgstr "БыÑтро открыть Ñцену..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "БыÑтро открыть Ñкрипт.."
+msgid "Quick Open Script..."
+msgstr "БыÑтро открыть Ñкрипт..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1671,8 +1671,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Сохранить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² «%s» перед закрытием?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Сохранить Ñцену как.."
+msgid "Save Scene As..."
+msgstr "Сохранить Ñцену как..."
#: editor/editor_node.cpp
msgid "No"
@@ -1723,8 +1723,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Это дейÑтвие Ð½ÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ. ВоÑÑтановить в любом Ñлучае?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "БыÑтро запуÑтить Ñцену.."
+msgid "Quick Run Scene..."
+msgstr "БыÑтро запуÑтить Ñцену..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1880,8 +1880,8 @@ msgid "Previous tab"
msgstr "ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ð²ÐºÐ»Ð°Ð´ÐºÐ°"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "ОтÑортировать файлы.."
+msgid "Filter Files..."
+msgstr "ОтÑортировать файлы..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1892,12 +1892,12 @@ msgid "New Scene"
msgstr "ÐÐ¾Ð²Ð°Ñ Ñцена"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "ÐÐ¾Ð²Ð°Ñ ÑƒÐ½Ð°ÑÐ»ÐµÐ´Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð¡Ñ†ÐµÐ½Ð°.."
+msgid "New Inherited Scene..."
+msgstr "ÐÐ¾Ð²Ð°Ñ ÑƒÐ½Ð°ÑÐ»ÐµÐ´Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð¡Ñ†ÐµÐ½Ð°..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Открыть Ñцену.."
+msgid "Open Scene..."
+msgstr "Открыть Ñцену..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1916,16 +1916,16 @@ msgid "Open Recent"
msgstr "Открыть поÑледнее"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Конвертировать в.."
+msgid "Convert To..."
+msgstr "Конвертировать в..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "Библиотека полиÑеток.."
+msgid "MeshLibrary..."
+msgstr "Библиотека полиÑеток..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "Ðабор тайлов.."
+msgid "TileSet..."
+msgstr "Ðабор тайлов..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -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"
@@ -2188,8 +2188,8 @@ msgid "Save the currently edited resource."
msgstr "Сохранить текущий редактируемый реÑурÑ."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Сохранить как.."
+msgid "Save As..."
+msgstr "Сохранить как..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2297,8 +2297,8 @@ msgid "Creating Mesh Previews"
msgstr "Создание предпроÑмотра"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Миниатюра.."
+msgid "Thumbnail..."
+msgstr "Миниатюра..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2359,7 +2359,7 @@ msgstr "Включительно"
#: editor/editor_profiler.cpp
msgid "Self"
-msgstr "Self"
+msgstr ""
#: editor/editor_profiler.cpp
msgid "Frame #:"
@@ -2450,7 +2450,7 @@ msgid "(Current)"
msgstr "(Текущий)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr "Получение зеркал, пожалуйÑта подождите."
#: editor/export_template_manager.cpp
@@ -2528,8 +2528,8 @@ msgid "Error requesting url: "
msgstr "Ошибка запроÑа адреÑа ÑÑылки: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "Подключение к зеркалам.."
+msgid "Connecting to Mirror..."
+msgstr "Подключение к зеркалам..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2545,8 +2545,8 @@ msgstr "Ðе удаётÑÑ Ñ€Ð°Ð·Ñ€ÐµÑˆÐ¸Ñ‚ÑŒ"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Подключение.."
+msgid "Connecting..."
+msgstr "Подключение..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2558,8 +2558,8 @@ msgstr "Подключен"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Запрашиваю.."
+msgid "Requesting..."
+msgstr "Запрашиваю..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2695,12 +2695,12 @@ msgid "Collapse all"
msgstr "Свернуть вÑе"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Переименовать.."
+msgid "Rename..."
+msgstr "Переименовать..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "ПеремеÑтить в.."
+msgid "Move To..."
+msgstr "ПеремеÑтить в..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2711,16 +2711,16 @@ msgid "Instance"
msgstr "Добавить ÑкземплÑÑ€"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Редактировать завиÑимоÑти.."
+msgid "Edit Dependencies..."
+msgstr "Редактировать завиÑимоÑти..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "ПроÑмотреть владельцев.."
+msgid "View Owners..."
+msgstr "ПроÑмотреть владельцев..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Дублировать.."
+msgid "Duplicate..."
+msgstr "Дублировать..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2745,7 +2745,7 @@ msgstr "Добавить выбранную Ñцену(Ñ‹), в качеÑтве
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Сканирование файлов,\n"
"пожалуйÑта, подождите..."
@@ -2813,8 +2813,8 @@ msgid "Import Scene"
msgstr "Импортировать Ñцену"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Импортирование Ñцены.."
+msgid "Importing Scene..."
+msgstr "Импортирование Ñцены..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2825,8 +2825,8 @@ msgid "Generating for Mesh: "
msgstr "Создание Ð´Ð»Ñ Ð¿Ð¾Ð»Ð¸Ñетки: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "ЗапуÑк пользовательÑкого Ñкрипта.."
+msgid "Running Custom Script..."
+msgstr "ЗапуÑк пользовательÑкого Ñкрипта..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2841,8 +2841,8 @@ msgid "Error running post-import script:"
msgstr "Ошибка запуÑка поÑÑ‚-импорт Ñкрипта:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Сохранение.."
+msgid "Saving..."
+msgstr "Сохранение..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2861,8 +2861,8 @@ msgid "Import As:"
msgstr "Импортировать как:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "ПредуÑтановка.."
+msgid "Preset..."
+msgstr "ПредуÑтановка..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3282,16 +3282,16 @@ msgid "Transition Node"
msgstr "Transition узел"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Импортировать анимации.."
+msgid "Import Animations..."
+msgstr "Импортировать анимации..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "Редактировать фильтры узла"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "Фильтры.."
+msgid "Filters..."
+msgstr "Фильтры..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3358,7 +3358,7 @@ msgid "Fetching:"
msgstr "Извлечение:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr "ИнициализациÑ..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3425,8 +3425,8 @@ msgid "Site:"
msgstr "Сайт:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Поддержка.."
+msgid "Support..."
+msgstr "Поддержка..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3622,6 +3622,7 @@ msgid "Use Rotation Snap"
msgstr "ИÑпользовать привÑзку вращениÑ"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "ÐаÑтроить привÑзку..."
@@ -3718,18 +3719,16 @@ msgid "Show Guides"
msgstr "Показывать направлÑющие"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "Отображать начало координат"
+msgstr "Отображать центр"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 Окно"
+msgstr "Показать окно проÑмотра"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
-msgstr "Центрировать на выбранном"
+msgstr "Центрировать выбранное"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Frame Selection"
@@ -4018,7 +4017,7 @@ msgstr "Полиcетка не имеет поверхноÑти Ð´Ð»Ñ Ñозд
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "Тип полиÑетки не PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4049,8 +4048,8 @@ msgid "Create Convex Collision Sibling"
msgstr "Создать выпуклую облаÑÑ‚ÑŒ ÑтолкновениÑ"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "Создать полиÑетку обводки.."
+msgid "Create Outline Mesh..."
+msgstr "Создать полиÑетку обводки..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4254,8 +4253,8 @@ msgid "Error loading image:"
msgstr "Ошибка при загрузке изображениÑ:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Ðикаких пикÑелей Ñ Ð¿Ñ€Ð¾Ð·Ñ€Ð°Ñ‡Ð½Ð¾Ñтью > 128 в изображении.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Ðикаких пикÑелей Ñ Ð¿Ñ€Ð¾Ð·Ñ€Ð°Ñ‡Ð½Ð¾Ñтью > 128 в изображении..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4615,8 +4614,8 @@ msgid "Import Theme"
msgstr "Импортировать тему"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Сохранить тему как.."
+msgid "Save Theme As..."
+msgstr "Сохранить тему как..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4712,8 +4711,8 @@ msgstr "Переключить панель Ñкриптов"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Ðайти.."
+msgid "Find..."
+msgstr "Ðайти..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4922,16 +4921,16 @@ msgid "Find Previous"
msgstr "Ðайти предыдущее"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Заменить.."
+msgid "Replace..."
+msgstr "Заменить..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Перейти к функции.."
+msgid "Goto Function..."
+msgstr "Перейти к функции..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Перейти к Ñтроке.."
+msgid "Goto Line..."
+msgstr "Перейти к Ñтроке..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5384,12 +5383,8 @@ msgid "Transform"
msgstr "Преобразование"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "ÐаÑтроить привÑзку.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "Окно преобразованиÑ.."
+msgid "Transform Dialog..."
+msgstr "Окно преобразованиÑ..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5558,7 +5553,7 @@ msgstr "ПеремеÑтить (поÑле)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "SpriteFrames"
-msgstr "SpriteFrames"
+msgstr "Спрайт кадры"
#: editor/plugins/style_box_editor_plugin.cpp
msgid "StyleBox Preview:"
@@ -5566,7 +5561,7 @@ msgstr "ПредпроÑмотр StyleBox:"
#: editor/plugins/style_box_editor_plugin.cpp
msgid "StyleBox"
-msgstr "StyleBox"
+msgstr ""
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Set Region Rect"
@@ -5641,8 +5636,8 @@ msgid "Remove All"
msgstr "Удалить вÑе"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "Редактировать тему.."
+msgid "Edit theme..."
+msgstr "Редактировать тему..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5711,8 +5706,8 @@ msgid "Options"
msgstr "Параметры"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr "Имеет,Много,Разных,Опций!"
+msgid "Has,Many,Options"
+msgstr "ЕÑÑ‚ÑŒ,Много,Вариантов"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5903,8 +5898,8 @@ msgid "Presets"
msgstr "ПредуÑтановки"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "Добавить.."
+msgid "Add..."
+msgstr "Добавить..."
#: editor/project_export.cpp
msgid "Resources"
@@ -5995,6 +5990,10 @@ msgid "Imported Project"
msgstr "Импортированный проект"
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "ÐедопуÑтимое Ð¸Ð¼Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð°."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "Ðе удалоÑÑŒ Ñоздать папку."
@@ -6194,9 +6193,11 @@ msgstr "Кнопка мыши"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
+"ÐедопуÑтимое Ð¸Ð¼Ñ Ð´ÐµÐ¹ÑтвиÑ. Оно не может быть пуÑтым или Ñодержать '/', ':', "
+"'=', '\\' или '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6223,7 +6224,7 @@ msgid "Control+"
msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr "Ðажмите любую клавишу..."
#: editor/project_settings_editor.cpp
@@ -6407,7 +6408,7 @@ msgid "Property:"
msgstr "Параметр:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr "Переопределить длÑ..."
#: editor/project_settings_editor.cpp
@@ -6503,12 +6504,12 @@ msgid "Easing Out-In"
msgstr "Переход ИЗ-В"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "Файл.."
+msgid "File..."
+msgstr "Файл..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "Папка.."
+msgid "Dir..."
+msgstr "Папка..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6682,8 +6683,8 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Эта Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð½Ðµ может быть Ñделана на редактируемой Ñцене."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "Сохранить новую Сцену как.."
+msgid "Save New Scene As..."
+msgstr "Сохранить новую Сцену как..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
@@ -7232,7 +7233,7 @@ msgstr "Библиотеки: "
#: modules/gdnative/register_types.cpp
msgid "GDNative"
-msgstr "GDNative"
+msgstr ""
#: modules/gdscript/gdscript_functions.cpp
#: modules/visual_script/visual_script_builtin_funcs.cpp
@@ -7399,7 +7400,7 @@ msgstr "РаÑÑтоÑние выбора:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "Ð˜Ð¼Ñ ÐºÐ»Ð°ÑÑа не может быть зарезервированным ключевым Ñловом"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -8111,7 +8112,7 @@ msgstr "СвойÑтво Path должно указывать на дейÑтвÐ
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "WorldEnvironment необходим Environment реÑурÑ."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8125,6 +8126,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 ""
+"Этот WorldEnvironment игнорируетÑÑ. Либо добавьте Camera (Ð´Ð»Ñ 3D-Ñцен), либо "
+"уÑтановите в Environment реÑурÑе Background режим в Canvas (Ð´Ð»Ñ 2D Ñцен)."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8225,6 +8228,13 @@ msgstr "Ошибка загрузки шрифта."
msgid "Invalid font size."
msgstr "ÐедопуÑтимый размер шрифта."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ð²ÐºÐ»Ð°Ð´ÐºÐ°"
+
+#~ msgid "Next"
+#~ msgstr "Следующий"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "ÐедопуÑтимое название дейÑÑ‚Ð²Ð¸Ñ (подойдёт вÑÑ‘ кроме '/' или ':')."
@@ -8251,9 +8261,6 @@ msgstr "ÐедопуÑтимый размер шрифта."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "ОтÑутÑтвует project.godot в папке проекта."
-#~ msgid "Next"
-#~ msgstr "Следующий"
-
#~ msgid "Not found!"
#~ msgstr "Ðе найдено!"
@@ -8398,8 +8405,8 @@ msgstr "ÐедопуÑтимый размер шрифта."
#~ msgid "Exporting for %s"
#~ msgstr "ЭкÑпортирование Ð´Ð»Ñ %s"
-#~ msgid "Setting Up.."
-#~ msgstr "ÐаÑтройка.."
+#~ msgid "Setting Up..."
+#~ msgstr "ÐаÑтройка..."
#~ msgid "Error loading scene."
#~ msgstr "Ошибка загрузки Ñцены."
@@ -8459,8 +8466,8 @@ msgstr "ÐедопуÑтимый размер шрифта."
#~ msgid "Info"
#~ msgstr "ИнформациÑ"
-#~ msgid "Re-Import.."
-#~ msgstr "Переимпортировать.."
+#~ msgid "Re-Import..."
+#~ msgstr "Переимпортировать..."
#~ msgid "No bit masks to import!"
#~ msgstr "Ðет битовой маÑки Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð°!"
@@ -8856,14 +8863,14 @@ msgstr "ÐедопуÑтимый размер шрифта."
#~ msgid "Zoom (%):"
#~ msgstr "МаÑштаб (%):"
-#~ msgid "Skeleton.."
-#~ msgstr "Скелет.."
+#~ msgid "Skeleton..."
+#~ msgstr "Скелет..."
#~ msgid "Zoom Reset"
#~ msgstr "СброÑить маÑштаб"
-#~ msgid "Zoom Set.."
-#~ msgstr "УÑтановить маÑштаб.."
+#~ msgid "Zoom Set..."
+#~ msgstr "УÑтановить маÑштаб..."
#~ msgid "Set a Value"
#~ msgstr "УÑтановить значение"
@@ -9336,8 +9343,8 @@ msgstr "ÐедопуÑтимый размер шрифта."
#~ msgid "Export Project PCK"
#~ msgstr "ЭкÑпортировать PCK проекта"
-#~ msgid "Export.."
-#~ msgstr "ЭкÑпортировать.."
+#~ msgid "Export..."
+#~ msgstr "ЭкÑпортировать..."
#~ msgid "Project Export"
#~ msgstr "ЭкÑпортирование проекта"
@@ -9457,8 +9464,8 @@ msgstr "ÐедопуÑтимый размер шрифта."
#~ msgid "Reload Tool Script (Soft)"
#~ msgstr "Перезагрузить инÑтрум. Ñкрипт (мÑгко)"
-#~ msgid "Edit Connections.."
-#~ msgstr "Изменить ÑвÑзи.."
+#~ msgid "Edit Connections..."
+#~ msgstr "Изменить ÑвÑзи..."
#~ msgid "Set Params"
#~ msgstr "Ðазначить параметры"
diff --git a/editor/translations/sk.po b/editor/translations/sk.po
index 16f675df37..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"
@@ -495,7 +496,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr ""
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -910,11 +911,11 @@ msgid "Move Audio Bus"
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1051,11 +1052,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1126,7 +1127,7 @@ msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
#, fuzzy
-msgid "New Folder.."
+msgid "New Folder..."
msgstr "Vytvoriť adresár"
#: editor/editor_file_dialog.cpp
@@ -1394,12 +1395,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1604,11 +1605,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1621,7 +1622,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr ""
#: editor/editor_node.cpp
@@ -1673,7 +1674,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1820,7 +1821,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1832,11 +1833,11 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1857,15 +1858,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2110,7 +2111,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2222,7 +2223,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2373,7 +2374,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2449,7 +2450,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2466,7 +2467,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2479,7 +2480,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2613,11 +2614,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2630,15 +2631,15 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2664,7 +2665,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2730,7 +2731,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2742,7 +2743,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2758,7 +2759,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2779,7 +2780,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3197,7 +3198,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3205,7 +3206,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3275,7 +3276,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3342,7 +3343,7 @@ msgid "Site:"
msgstr "Stránka:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3532,6 +3533,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3958,7 +3960,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4165,7 +4167,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4530,7 +4532,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4628,7 +4630,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4834,15 +4836,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5297,11 +5299,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5558,7 +5556,7 @@ msgid "Remove All"
msgstr "Všetky vybrané"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5626,7 +5624,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5817,7 +5815,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5907,6 +5905,10 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr ""
+
+#: editor/project_manager.cpp
#, fuzzy
msgid "Couldn't create folder."
msgstr "Vytvoriť adresár"
@@ -6098,8 +6100,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6127,7 +6129,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6312,7 +6314,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6409,11 +6411,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6586,7 +6588,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -8049,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 74b469fc42..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-05-03 07:41+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-dev\n"
+"X-Generator: Weblate 3.0.1-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -191,11 +189,11 @@ msgstr "PoÄisti Animacijo"
#: editor/animation_editor.cpp
msgid "Create NEW track for %s and insert key?"
-msgstr "Ustvari NOVI trak za %s in vstavi kljuÄ?"
+msgstr "Ustvarim NOVO sled za %s in vstavim kljuÄ?"
#: editor/animation_editor.cpp
msgid "Create %d NEW tracks and insert keys?"
-msgstr ""
+msgstr "Ustvarim %d NOVO sled in vstavim kljuÄe?"
#: editor/animation_editor.cpp editor/create_dialog.cpp
#: editor/editor_audio_buses.cpp editor/plugins/abstract_polygon_2d_editor.cpp
@@ -207,43 +205,43 @@ msgstr "Ustvari"
#: editor/animation_editor.cpp
msgid "Anim Create & Insert"
-msgstr ""
+msgstr "Ustvari & Vstavi Animacijo"
#: editor/animation_editor.cpp
msgid "Anim Insert Track & Key"
-msgstr ""
+msgstr "V Animacijo Vstavi Sled & KljuÄ"
#: editor/animation_editor.cpp
msgid "Anim Insert Key"
-msgstr ""
+msgstr "V Animacijo Vstavi KljuÄ"
#: editor/animation_editor.cpp
msgid "Change Anim Len"
-msgstr ""
+msgstr "Spremeni Dolžino Animacije"
#: editor/animation_editor.cpp
msgid "Change Anim Loop"
-msgstr ""
+msgstr "Spremeni Zanko Animacije"
#: editor/animation_editor.cpp
msgid "Anim Create Typed Value Key"
-msgstr ""
+msgstr "V Animaciji Ustvari Vneseno Vrednost KljuÄa"
#: editor/animation_editor.cpp
msgid "Anim Insert"
-msgstr ""
+msgstr "Vstavi Animacijo"
#: editor/animation_editor.cpp
msgid "Anim Scale Keys"
-msgstr ""
+msgstr "Spremeni Obseg KljuÄev"
#: editor/animation_editor.cpp
msgid "Anim Add Call Track"
-msgstr ""
+msgstr "Dodaj KlicajoÄo Sled v Animacijo"
#: editor/animation_editor.cpp
msgid "Animation zoom."
-msgstr "Približaj animacijo"
+msgstr "Približaj animacijo."
#: editor/animation_editor.cpp
msgid "Length (s):"
@@ -259,39 +257,39 @@ msgstr "Korak (s):"
#: editor/animation_editor.cpp
msgid "Cursor step snap (in seconds)."
-msgstr ""
+msgstr "Korak postavitve kazalca (v sekundah)."
#: editor/animation_editor.cpp
msgid "Enable/Disable looping in animation."
-msgstr ""
+msgstr "OmogoÄi/OnemogoÄi zankanje v animaciji."
#: editor/animation_editor.cpp
msgid "Add new tracks."
-msgstr ""
+msgstr "Dodaj Novo Sled."
#: editor/animation_editor.cpp
msgid "Move current track up."
-msgstr ""
+msgstr "Trenutno sled premakni gor."
#: editor/animation_editor.cpp
msgid "Move current track down."
-msgstr ""
+msgstr "Trenutno sled premakni dol."
#: editor/animation_editor.cpp
msgid "Remove selected track."
-msgstr ""
+msgstr "Odstrani izbrano sled."
#: editor/animation_editor.cpp
msgid "Track tools"
-msgstr ""
+msgstr "Orodja sledi"
#: editor/animation_editor.cpp
msgid "Enable editing of individual keys by clicking them."
-msgstr ""
+msgstr "S klikom na posamezne kljuÄe omogoÄite njihovo urejanje."
#: editor/animation_editor.cpp
msgid "Anim. Optimizer"
-msgstr ""
+msgstr "Optimizacija Animacije"
#: editor/animation_editor.cpp
msgid "Max. Linear Error:"
@@ -307,11 +305,12 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Optimize"
-msgstr ""
+msgstr "Optimiziraj"
#: editor/animation_editor.cpp
msgid "Select an AnimationPlayer from the Scene Tree to edit animations."
msgstr ""
+"Če želite urediti animacije, izberite AnimationPlayer iz drevesa scene."
#: editor/animation_editor.cpp
msgid "Key"
@@ -319,15 +318,15 @@ msgstr "ÄŒrka"
#: editor/animation_editor.cpp
msgid "Transition"
-msgstr ""
+msgstr "Prehod"
#: editor/animation_editor.cpp
msgid "Scale Ratio:"
-msgstr ""
+msgstr "Razmerje Obsega:"
#: editor/animation_editor.cpp
msgid "Call Functions in Which Node?"
-msgstr ""
+msgstr "Klic funkcije v katerem gradniku?"
#: editor/animation_editor.cpp
msgid "Remove invalid keys"
@@ -335,7 +334,7 @@ msgstr "Odstrani nedovoljene ÄŒrke"
#: editor/animation_editor.cpp
msgid "Remove unresolved and empty tracks"
-msgstr ""
+msgstr "Odstrani nedoloÄene in prazne sledi"
#: editor/animation_editor.cpp
msgid "Clean-up all animations"
@@ -343,11 +342,11 @@ msgstr "Pobriši vse animacije"
#: editor/animation_editor.cpp
msgid "Clean-Up Animation(s) (NO UNDO!)"
-msgstr "Izbriši Animacij(o/e) (BREZ VRNITVE)"
+msgstr "Izbriši Animacijo/e (BREZ VRNITVE!)"
#: editor/animation_editor.cpp
msgid "Clean-Up"
-msgstr "Pobriši"
+msgstr "PoÄisti"
#: editor/array_property_edit.cpp
msgid "Resize Array"
@@ -355,35 +354,35 @@ msgstr "PoveÄaj Niz"
#: editor/array_property_edit.cpp
msgid "Change Array Value Type"
-msgstr ""
+msgstr "Spremeni Tip Vrednosti Niza"
#: editor/array_property_edit.cpp
msgid "Change Array Value"
-msgstr ""
+msgstr "Spremeni Vrednost Niza"
#: editor/code_editor.cpp
msgid "Go to Line"
-msgstr ""
+msgstr "Pojdi na Vrstico"
#: editor/code_editor.cpp
msgid "Line Number:"
-msgstr "Å tevilka vrste:"
+msgstr "Å tevilka Vrste:"
#: editor/code_editor.cpp
msgid "No Matches"
-msgstr ""
+msgstr "Ni Zadetkov"
#: editor/code_editor.cpp
msgid "Replaced %d occurrence(s)."
-msgstr ""
+msgstr "Zamenjana %d ponovitev/e."
#: editor/code_editor.cpp
msgid "Match Case"
-msgstr ""
+msgstr "Ujemanje Velikih ÄŒrk"
#: editor/code_editor.cpp
msgid "Whole Words"
-msgstr ""
+msgstr "Cele Besede"
#: editor/code_editor.cpp
msgid "Replace"
@@ -391,51 +390,53 @@ msgstr "Zamenjaj"
#: editor/code_editor.cpp
msgid "Replace All"
-msgstr ""
+msgstr "Zamenjaj Vse"
#: editor/code_editor.cpp
msgid "Selection Only"
-msgstr ""
+msgstr "Samo Izbira"
#: editor/code_editor.cpp
msgid "Zoom In"
-msgstr ""
+msgstr "Približaj"
#: editor/code_editor.cpp
msgid "Zoom Out"
-msgstr ""
+msgstr "Oddalji"
#: editor/code_editor.cpp
msgid "Reset Zoom"
-msgstr ""
+msgstr "Ponastavi PoveÄavo/PomanjÅ¡avo"
#: editor/code_editor.cpp editor/script_editor_debugger.cpp
msgid "Line:"
-msgstr ""
+msgstr "Vrstica:"
#: editor/code_editor.cpp
msgid "Col:"
-msgstr ""
+msgstr "Stolpec:"
#: editor/connections_dialog.cpp
msgid "Method in target Node must be specified!"
-msgstr ""
+msgstr "Metoda v ciljnem gradniku mora biti navedena!"
#: editor/connections_dialog.cpp
msgid ""
"Target method not found! Specify a valid method or attach a script to target "
"Node."
msgstr ""
+"Ciljna metoda ni bila najdena! Navedite veljavno metodo ali priložite "
+"skripto, ki cilja na Gradnik."
#: editor/connections_dialog.cpp
msgid "Connect To Node:"
-msgstr ""
+msgstr "Poveži se z Gradnikom:"
#: editor/connections_dialog.cpp editor/editor_autoload_settings.cpp
#: editor/groups_editor.cpp editor/plugins/item_list_editor_plugin.cpp
#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp
msgid "Add"
-msgstr ""
+msgstr "Dodaj"
#: editor/connections_dialog.cpp editor/dependency_editor.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -446,27 +447,27 @@ msgstr "Odstrani"
#: editor/connections_dialog.cpp
msgid "Add Extra Call Argument:"
-msgstr ""
+msgstr "Dodaj Dodaten Klicni Argument:"
#: editor/connections_dialog.cpp
msgid "Extra Call Arguments:"
-msgstr ""
+msgstr "Dodatni Klicni Argumenti:"
#: editor/connections_dialog.cpp
msgid "Path to Node:"
-msgstr ""
+msgstr "Pot do Gradnika:"
#: editor/connections_dialog.cpp
msgid "Make Function"
-msgstr ""
+msgstr "Naredi Funkcijo"
#: editor/connections_dialog.cpp
msgid "Deferred"
-msgstr ""
+msgstr "Odloženo"
#: editor/connections_dialog.cpp
msgid "Oneshot"
-msgstr ""
+msgstr "En Poizkus"
#: editor/connections_dialog.cpp editor/dependency_editor.cpp
#: editor/export_template_manager.cpp
@@ -484,37 +485,36 @@ msgstr "Zapri"
#: editor/connections_dialog.cpp
msgid "Connect"
-msgstr ""
+msgstr "Poveži"
#: editor/connections_dialog.cpp
msgid "Connect '%s' to '%s'"
-msgstr ""
+msgstr "Poveži '%s' v '%s'"
#: editor/connections_dialog.cpp
msgid "Connecting Signal:"
-msgstr ""
+msgstr "Povezovanje Signala:"
#: editor/connections_dialog.cpp
msgid "Disconnect '%s' from '%s'"
-msgstr ""
+msgstr "Odklopite '%s' iz '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr ""
+msgid "Connect..."
+msgstr "Poveži..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Disconnect"
-msgstr ""
+msgstr "Odklopi"
#: editor/connections_dialog.cpp editor/editor_help.cpp editor/node_dock.cpp
msgid "Signals"
-msgstr ""
+msgstr "Signali"
#: editor/create_dialog.cpp
-#, fuzzy
msgid "Change %s Type"
-msgstr "Osnovni Tip:"
+msgstr "Spremeni Tip %s"
#: editor/create_dialog.cpp editor/project_settings_editor.cpp
#: modules/visual_script/visual_script_editor.cpp
@@ -522,9 +522,8 @@ msgid "Change"
msgstr "Spremeni"
#: editor/create_dialog.cpp
-#, fuzzy
msgid "Create New %s"
-msgstr "Ustvari"
+msgstr "Ustvari Nov %s"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp
@@ -533,55 +532,59 @@ msgstr "Priljubljene:"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
msgid "Recent:"
-msgstr ""
+msgstr "Nedavni:"
#: editor/create_dialog.cpp editor/editor_node.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
#: editor/quick_open.cpp
msgid "Search:"
-msgstr ""
+msgstr "Iskanje:"
#: editor/create_dialog.cpp editor/editor_help.cpp
#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
#: editor/quick_open.cpp
msgid "Matches:"
-msgstr ""
+msgstr "Zadetki:"
#: editor/create_dialog.cpp editor/editor_help.cpp
#: editor/plugins/asset_library_editor_plugin.cpp editor/property_selector.cpp
#: editor/script_editor_debugger.cpp
msgid "Description:"
-msgstr ""
+msgstr "Opis:"
#: editor/dependency_editor.cpp
msgid "Search Replacement For:"
-msgstr ""
+msgstr "Iskanje Zamenjave Za:"
#: editor/dependency_editor.cpp
msgid "Dependencies For:"
-msgstr ""
+msgstr "Odvisnosti Za:"
#: editor/dependency_editor.cpp
msgid ""
"Scene '%s' is currently being edited.\n"
"Changes will not take effect unless reloaded."
msgstr ""
+"Scena '%s' je trenutno v urejanju.\n"
+"Spremembe bodo zaÄele veljati, ko bodo znova naložene."
#: editor/dependency_editor.cpp
msgid ""
"Resource '%s' is in use.\n"
"Changes will take effect when reloaded."
msgstr ""
+"Vir '%s' je v uporabi.\n"
+"Spremembe bodo zaÄele veljati, ko bodo znova naložene."
#: editor/dependency_editor.cpp
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Dependencies"
-msgstr ""
+msgstr "Odvisnosti"
#: editor/dependency_editor.cpp
msgid "Resource"
-msgstr ""
+msgstr "Viri"
#: editor/dependency_editor.cpp editor/editor_autoload_settings.cpp
#: editor/project_manager.cpp editor/project_settings_editor.cpp
@@ -591,34 +594,34 @@ msgstr "Pot"
#: editor/dependency_editor.cpp
msgid "Dependencies:"
-msgstr ""
+msgstr "Odvisnosti:"
#: editor/dependency_editor.cpp
msgid "Fix Broken"
-msgstr ""
+msgstr "Popravi Pokvarjeno"
#: editor/dependency_editor.cpp
msgid "Dependency Editor"
-msgstr ""
+msgstr "Urejevalnik Odvisnosti"
#: editor/dependency_editor.cpp
msgid "Search Replacement Resource:"
-msgstr ""
+msgstr "Iskanje Nadomestnih Virov:"
#: editor/dependency_editor.cpp editor/editor_file_dialog.cpp
#: editor/editor_help.cpp editor/editor_node.cpp editor/filesystem_dock.cpp
#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
#: editor/quick_open.cpp scene/gui/file_dialog.cpp
msgid "Open"
-msgstr ""
+msgstr "Odpri"
#: editor/dependency_editor.cpp
msgid "Owners Of:"
-msgstr ""
+msgstr "Lastniki:"
#: editor/dependency_editor.cpp
msgid "Remove selected files from the project? (no undo)"
-msgstr ""
+msgstr "Odstranim izbrane datoteke iz projekta? (brez vrnitve)"
#: editor/dependency_editor.cpp
msgid ""
@@ -626,46 +629,48 @@ msgid ""
"work.\n"
"Remove them anyway? (no undo)"
msgstr ""
+"Izbrisane datoteke so potrebne za delovanje drugih virov.\n"
+"Ali jih vseeno odstranim? (brez vrnitve)"
#: editor/dependency_editor.cpp
msgid "Cannot remove:"
-msgstr ""
+msgstr "Ni mogoÄe odstraniti:"
#: editor/dependency_editor.cpp
msgid "Error loading:"
-msgstr ""
+msgstr "Napaka pri nalaganju:"
#: editor/dependency_editor.cpp
msgid "Scene failed to load due to missing dependencies:"
-msgstr ""
+msgstr "Nalaganje scene je spodletelo zaradi manjkajoÄih odvisnosti:"
#: editor/dependency_editor.cpp editor/editor_node.cpp
msgid "Open Anyway"
-msgstr ""
+msgstr "Vseeno Odpri"
#: editor/dependency_editor.cpp
msgid "Which action should be taken?"
-msgstr ""
+msgstr "Katere ukrepe je treba sprejeti?"
#: editor/dependency_editor.cpp
msgid "Fix Dependencies"
-msgstr ""
+msgstr "Popravi Odvisnosti"
#: editor/dependency_editor.cpp
msgid "Errors loading!"
-msgstr ""
+msgstr "Napake pri nalaganju!"
#: editor/dependency_editor.cpp
msgid "Permanently delete %d item(s)? (No undo!)"
-msgstr ""
+msgstr "Trajno izbrišem %d predmet(e)? (Brez vrnitve!)"
#: editor/dependency_editor.cpp
msgid "Owns"
-msgstr ""
+msgstr "Lastnik"
#: editor/dependency_editor.cpp
msgid "Resources Without Explicit Ownership:"
-msgstr ""
+msgstr "Viri Brez Izrecnega Lastništva:"
#: editor/dependency_editor.cpp editor/editor_node.cpp
msgid "Orphan Resource Explorer"
@@ -673,7 +678,7 @@ msgstr "Raziskovalec Osamljenih Virov"
#: editor/dependency_editor.cpp
msgid "Delete selected files?"
-msgstr ""
+msgstr "Izbrišem izbrane datoteke?"
#: editor/dependency_editor.cpp editor/editor_audio_buses.cpp
#: editor/editor_file_dialog.cpp editor/editor_node.cpp
@@ -681,35 +686,35 @@ msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
#: editor/scene_tree_dock.cpp
msgid "Delete"
-msgstr ""
+msgstr "Izbriši"
#: editor/dictionary_property_edit.cpp
msgid "Change Dictionary Key"
-msgstr ""
+msgstr "Spremeni Slovarski KljuÄ"
#: editor/dictionary_property_edit.cpp
msgid "Change Dictionary Value"
-msgstr ""
+msgstr "Spremeni Slovarsko Vrednost"
#: editor/editor_about.cpp
msgid "Thanks from the Godot community!"
-msgstr ""
+msgstr "Zahvaljujemo se vam iz skupnosti Godota!"
#: editor/editor_about.cpp
msgid "Thanks!"
-msgstr ""
+msgstr "Hvala!"
#: editor/editor_about.cpp
msgid "Godot Engine contributors"
-msgstr ""
+msgstr "Godot Engine sodelovci"
#: editor/editor_about.cpp
msgid "Project Founders"
-msgstr ""
+msgstr "Ustanovitelji Projekta"
#: editor/editor_about.cpp
msgid "Lead Developer"
-msgstr ""
+msgstr "Vodilni Razvajalec"
#: editor/editor_about.cpp
msgid "Project Manager "
@@ -717,47 +722,47 @@ msgstr "Upravljalnik Projekta "
#: editor/editor_about.cpp
msgid "Developers"
-msgstr ""
+msgstr "Razvajalci"
#: editor/editor_about.cpp
msgid "Authors"
-msgstr ""
+msgstr "Avtorji"
#: editor/editor_about.cpp
msgid "Platinum Sponsors"
-msgstr ""
+msgstr "Platina Sponzorji"
#: editor/editor_about.cpp
msgid "Gold Sponsors"
-msgstr ""
+msgstr "Zlati Sponzorji"
#: editor/editor_about.cpp
msgid "Mini Sponsors"
-msgstr ""
+msgstr "Majhni Sponzorji"
#: editor/editor_about.cpp
msgid "Gold Donors"
-msgstr ""
+msgstr "Zlati Donatorji"
#: editor/editor_about.cpp
msgid "Silver Donors"
-msgstr ""
+msgstr "Srebrni Donatorji"
#: editor/editor_about.cpp
msgid "Bronze Donors"
-msgstr ""
+msgstr "Bronasti Donatorji"
#: editor/editor_about.cpp
msgid "Donors"
-msgstr ""
+msgstr "Donatorji"
#: editor/editor_about.cpp
msgid "License"
-msgstr ""
+msgstr "Licenca"
#: editor/editor_about.cpp
msgid "Thirdparty License"
-msgstr ""
+msgstr "Licenca Tretjih Oseb"
#: editor/editor_about.cpp
msgid ""
@@ -766,125 +771,125 @@ msgid ""
"is an exhaustive list of all such thirdparty components with their "
"respective copyright statements and license terms."
msgstr ""
+"Godot Engine se nanaÅ¡a na Å¡tevilne brezplaÄne in odprokodne knjižnice tretih "
+"oseb, ki so združljive z doloÄili MIT licence. SledeÄi obÅ¡iren seznam "
+"predstavi komponente tretjih oseb s pripadajoÄimi izjavami o avtorskih "
+"pravicah in licenÄnimi pogoji."
#: editor/editor_about.cpp
msgid "All Components"
-msgstr ""
+msgstr "Vse Komponente"
#: editor/editor_about.cpp
msgid "Components"
-msgstr ""
+msgstr "Komponente"
#: editor/editor_about.cpp
msgid "Licenses"
-msgstr ""
+msgstr "Licence"
#: editor/editor_asset_installer.cpp editor/project_manager.cpp
msgid "Error opening package file, not in zip format."
-msgstr ""
+msgstr "Napaka pri odpiranju datoteke paketa, ker ni v formatu zip."
#: editor/editor_asset_installer.cpp
msgid "Uncompressing Assets"
-msgstr ""
+msgstr "Razširjenje Dodatkov"
#: editor/editor_asset_installer.cpp editor/project_manager.cpp
msgid "Package Installed Successfully!"
-msgstr ""
+msgstr "Paket je UspeÅ¡no NameÅ¡Äen!"
#: editor/editor_asset_installer.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Success!"
-msgstr ""
+msgstr "Uspelo je!"
#: editor/editor_asset_installer.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Install"
-msgstr ""
+msgstr "Namesti"
#: editor/editor_asset_installer.cpp
msgid "Package Installer"
-msgstr ""
+msgstr "Namestnik Paketov"
#: editor/editor_audio_buses.cpp
msgid "Speakers"
-msgstr ""
+msgstr "ZvoÄniki"
#: editor/editor_audio_buses.cpp
msgid "Add Effect"
-msgstr ""
+msgstr "Dodaj UÄinek"
#: editor/editor_audio_buses.cpp
-#, fuzzy
msgid "Rename Audio Bus"
-msgstr "Preimenuj Funkcijo"
+msgstr "Preimenuj ZvoÄno Vodilo"
#: editor/editor_audio_buses.cpp
-#, fuzzy
msgid "Change Audio Bus Volume"
-msgstr "Preimenuj Funkcijo"
+msgstr "Spremeni Glasnost ZvoÄnega Vodila"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Solo"
-msgstr ""
+msgstr "Preklopi samo na ZvoÄno Vodilo"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Mute"
-msgstr ""
+msgstr "Preklopi na Tihi NaÄin ZvoÄnega Vodila"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Bypass Effects"
-msgstr ""
+msgstr "Preklopi na UÄinke Prehoda ZvoÄnega Vodila"
#: editor/editor_audio_buses.cpp
msgid "Select Audio Bus Send"
-msgstr ""
+msgstr "Izberi PoÅ¡lji možnost ZvoÄnega vodila"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus Effect"
-msgstr ""
+msgstr "Dodaj uÄinek ZvoÄnega Vodila"
#: editor/editor_audio_buses.cpp
msgid "Move Bus Effect"
-msgstr ""
+msgstr "Premakni uÄinek Vodila"
#: editor/editor_audio_buses.cpp
-#, fuzzy
msgid "Delete Bus Effect"
-msgstr "Izbriši Izbrano"
+msgstr "IzbriÅ¡i uÄinek Vodila"
#: editor/editor_audio_buses.cpp
msgid "Audio Bus, Drag and Drop to rearrange."
-msgstr ""
+msgstr "ZvoÄno Vodilo, Povelci in Izpusti za preureditev."
#: editor/editor_audio_buses.cpp
msgid "Solo"
-msgstr ""
+msgstr "Sam"
#: editor/editor_audio_buses.cpp
msgid "Mute"
-msgstr ""
+msgstr "Nem"
#: editor/editor_audio_buses.cpp
msgid "Bypass"
-msgstr ""
+msgstr "Prehod"
#: editor/editor_audio_buses.cpp
msgid "Bus options"
-msgstr ""
+msgstr "Možnosti Vodila"
#: editor/editor_audio_buses.cpp editor/filesystem_dock.cpp
#: editor/plugins/tile_map_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Duplicate"
-msgstr ""
+msgstr "Podvoji"
#: editor/editor_audio_buses.cpp
msgid "Reset Volume"
-msgstr ""
+msgstr "Ponastavi Glasnost"
#: editor/editor_audio_buses.cpp
-#, fuzzy
msgid "Delete Effect"
-msgstr "Izbriši Izbrano"
+msgstr "IzbriÅ¡i UÄinek"
#: editor/editor_audio_buses.cpp
msgid "Audio"
@@ -892,154 +897,156 @@ msgstr "Zvok"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus"
-msgstr ""
+msgstr "Dodaj ZvoÄno Vodilo"
#: editor/editor_audio_buses.cpp
msgid "Master bus can't be deleted!"
-msgstr ""
+msgstr "Glavno vodilo ni mogoÄe izbrisati!"
#: editor/editor_audio_buses.cpp
msgid "Delete Audio Bus"
-msgstr ""
+msgstr "IzbriÅ¡i ZvoÄno Vodilo"
#: editor/editor_audio_buses.cpp
msgid "Duplicate Audio Bus"
-msgstr ""
+msgstr "Podvoji ZvoÄno Vodilo"
#: editor/editor_audio_buses.cpp
msgid "Reset Bus Volume"
-msgstr ""
+msgstr "Ponastavi Glasnost Vodila"
#: editor/editor_audio_buses.cpp
msgid "Move Audio Bus"
-msgstr ""
+msgstr "Premakni ZvoÄno Vodilo"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr ""
+msgid "Save Audio Bus Layout As..."
+msgstr "Shrani Postavitev ZvoÄnega Vodila Kot..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr ""
+msgid "Location for New Layout..."
+msgstr "Lokacija za Novo Postavitev..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
-msgstr ""
+msgstr "Odpri ZvoÄno Vodilo"
#: editor/editor_audio_buses.cpp
msgid "There is no 'res://default_bus_layout.tres' file."
-msgstr ""
+msgstr "Datoteka 'res://default_bus_layout.tres' ne obstaja."
#: editor/editor_audio_buses.cpp
msgid "Invalid file, not an audio bus layout."
-msgstr ""
+msgstr "Neveljavna datoteka, ker ni postavitve zvoÄnega vodila."
#: editor/editor_audio_buses.cpp
msgid "Add Bus"
-msgstr ""
+msgstr "Dodaj Vodilo"
#: editor/editor_audio_buses.cpp
msgid "Create a new Bus Layout."
-msgstr ""
+msgstr "Ustvari novo Postavitev Vodila."
#: editor/editor_audio_buses.cpp editor/property_editor.cpp
#: editor/script_create_dialog.cpp
msgid "Load"
-msgstr ""
+msgstr "Naloži"
#: editor/editor_audio_buses.cpp
msgid "Load an existing Bus Layout."
-msgstr ""
+msgstr "Naloži obstojeÄo Postavitev Vodila."
#: editor/editor_audio_buses.cpp
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Save As"
-msgstr ""
+msgstr "Shrani Kot"
#: editor/editor_audio_buses.cpp
msgid "Save this Bus Layout to a file."
-msgstr ""
+msgstr "Shrani to Postavitev Vodila v datoteko."
#: editor/editor_audio_buses.cpp editor/import_dock.cpp
msgid "Load Default"
-msgstr ""
+msgstr "Naložite Prevzeto"
#: editor/editor_audio_buses.cpp
msgid "Load the default Bus Layout."
-msgstr ""
+msgstr "Naloži prevezeto Postavitev Vodila."
#: editor/editor_autoload_settings.cpp
msgid "Invalid name."
-msgstr ""
+msgstr "Neveljavno ime."
#: editor/editor_autoload_settings.cpp
msgid "Valid characters:"
-msgstr ""
+msgstr "Veljavni znaki:"
#: editor/editor_autoload_settings.cpp
msgid "Invalid name. Must not collide with an existing engine class name."
-msgstr ""
+msgstr "Neveljavno ime. Ne sme se prekrivati z obstojeÄim imenom razreda."
#: editor/editor_autoload_settings.cpp
msgid "Invalid name. Must not collide with an existing buit-in type name."
msgstr ""
+"Neveljavno ime. Ne sme se prekrivati z obstojeÄim vgrajenim imenom tipa."
#: editor/editor_autoload_settings.cpp
msgid "Invalid name. Must not collide with an existing global constant name."
msgstr ""
+"Neveljavno ime. Ne sme se prekrivati z obstojeÄim imenom globalne konstante."
#: editor/editor_autoload_settings.cpp
msgid "Invalid Path."
-msgstr ""
+msgstr "Neveljavna Pot."
#: editor/editor_autoload_settings.cpp
msgid "File does not exist."
-msgstr ""
+msgstr "Datoteka ne obstaja."
#: editor/editor_autoload_settings.cpp
msgid "Not in resource path."
-msgstr ""
+msgstr "Ni na poti virov."
#: editor/editor_autoload_settings.cpp
msgid "Add AutoLoad"
-msgstr ""
+msgstr "Dodaj SamodejnoNalaganje"
#: editor/editor_autoload_settings.cpp
msgid "Autoload '%s' already exists!"
-msgstr ""
+msgstr "SamodejnoNalaganje '%s' že obstaja!"
#: editor/editor_autoload_settings.cpp
msgid "Rename Autoload"
-msgstr ""
+msgstr "Preimenuj SamodejnoNalaganje"
#: editor/editor_autoload_settings.cpp
msgid "Toggle AutoLoad Globals"
-msgstr ""
+msgstr "Preklopi na Globalno SamodejnoNalaganje"
#: editor/editor_autoload_settings.cpp
msgid "Move Autoload"
-msgstr ""
+msgstr "Premakni SamodejnoNalaganje"
#: editor/editor_autoload_settings.cpp
msgid "Remove Autoload"
-msgstr ""
+msgstr "Odstrani SamodejnoNalaganje"
#: editor/editor_autoload_settings.cpp
msgid "Enable"
-msgstr ""
+msgstr "OmogoÄi"
#: editor/editor_autoload_settings.cpp
msgid "Rearrange Autoloads"
-msgstr ""
+msgstr "Preuredi SamodejnoNalaganje"
#: editor/editor_autoload_settings.cpp editor/editor_file_dialog.cpp
#: scene/gui/file_dialog.cpp
msgid "Path:"
-msgstr ""
+msgstr "Pot:"
#: editor/editor_autoload_settings.cpp
msgid "Node Name:"
-msgstr ""
+msgstr "Ime Gradnika:"
#: editor/editor_autoload_settings.cpp editor/editor_profiler.cpp
#: editor/project_manager.cpp editor/settings_config_dialog.cpp
@@ -1048,35 +1055,35 @@ msgstr "Ime"
#: editor/editor_autoload_settings.cpp
msgid "Singleton"
-msgstr ""
+msgstr "Posameznik"
#: editor/editor_data.cpp
msgid "Updating Scene"
msgstr "Posodabljanje Scene"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr ""
+msgid "Storing local changes..."
+msgstr "Shranjevanje lokalnih sprememb..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Posodabljanje scene.."
+msgid "Updating scene..."
+msgstr "Posodabljanje scene..."
#: editor/editor_data.cpp
msgid "[empty]"
-msgstr "[prazen]"
+msgstr "[prazno]"
#: editor/editor_data.cpp
msgid "[unsaved]"
-msgstr ""
+msgstr "[neshranjeno]"
#: editor/editor_dir_dialog.cpp
msgid "Please select a base directory first"
-msgstr ""
+msgstr "Najprej izberi osnovno mapo"
#: editor/editor_dir_dialog.cpp
msgid "Choose a Directory"
-msgstr ""
+msgstr "Izberi Mapo"
#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp
@@ -1088,32 +1095,32 @@ msgstr "Ustvarite Mapo"
#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
#: scene/gui/file_dialog.cpp
msgid "Name:"
-msgstr ""
+msgstr "Ime:"
#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp
msgid "Could not create folder."
-msgstr ""
+msgstr "Mape ni mogoÄe ustvariti."
#: editor/editor_dir_dialog.cpp
msgid "Choose"
-msgstr ""
+msgstr "Izberi"
#: editor/editor_export.cpp
msgid "Storing File:"
-msgstr ""
+msgstr "Shranjevanje Datoteke:"
#: editor/editor_export.cpp
msgid "Packing"
-msgstr ""
+msgstr "Pakiranje"
#: editor/editor_export.cpp platform/javascript/export/export.cpp
msgid "Template file not found:"
-msgstr ""
+msgstr "Predloge ni mogoÄe najti:"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "File Exists, Overwrite?"
-msgstr ""
+msgstr "Datoteka Obstaja, Prepišem?"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Select Current Folder"
@@ -1121,23 +1128,23 @@ msgstr "Izberite Trenutno Mapo"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
msgid "Copy Path"
-msgstr ""
+msgstr "Kopiraj Pot"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
msgid "Show In File Manager"
-msgstr ""
+msgstr "Pokaži V Upravitelju Datotek"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr ""
+msgid "New Folder..."
+msgstr "Nova Mapa..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
-msgstr ""
+msgstr "Osveži"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "All Recognized"
-msgstr ""
+msgstr "Vse Prepoznano"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "All Files (*)"
@@ -1145,69 +1152,69 @@ msgstr "Vse Datoteke (*)"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open a File"
-msgstr ""
+msgstr "Odpri v Datoteki"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open File(s)"
-msgstr ""
+msgstr "Odpri Datotek(o/e)"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open a Directory"
-msgstr ""
+msgstr "Odpri v Mapi"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open a File or Directory"
-msgstr ""
+msgstr "Odpri Datoteko ali Mapo"
#: editor/editor_file_dialog.cpp editor/editor_node.cpp
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/script_editor_plugin.cpp scene/gui/file_dialog.cpp
msgid "Save"
-msgstr ""
+msgstr "Shrani"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Save a File"
-msgstr ""
+msgstr "Shrani Datoteko"
#: editor/editor_file_dialog.cpp
msgid "Go Back"
-msgstr ""
+msgstr "Pojdi Nazaj"
#: editor/editor_file_dialog.cpp
msgid "Go Forward"
-msgstr ""
+msgstr "Pojdi Naprej"
#: editor/editor_file_dialog.cpp
msgid "Go Up"
-msgstr ""
+msgstr "Pojdi Navzgor"
#: editor/editor_file_dialog.cpp
msgid "Toggle Hidden Files"
-msgstr ""
+msgstr "Preklopi na Skrite Datoteke"
#: editor/editor_file_dialog.cpp
msgid "Toggle Favorite"
-msgstr ""
+msgstr "Preklopi na Najljubše"
#: editor/editor_file_dialog.cpp
msgid "Toggle Mode"
-msgstr ""
+msgstr "Preklopi NaÄin"
#: editor/editor_file_dialog.cpp
msgid "Focus Path"
-msgstr ""
+msgstr "Poudari Pot"
#: editor/editor_file_dialog.cpp
msgid "Move Favorite Up"
-msgstr ""
+msgstr "Premakni Priljubljeno Navzgor"
#: editor/editor_file_dialog.cpp
msgid "Move Favorite Down"
-msgstr ""
+msgstr "Premakni Priljubljeno Navzdol"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Go to parent folder"
-msgstr ""
+msgstr "Pojdi v nadrejeno mapo"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Directories & Files:"
@@ -1215,7 +1222,7 @@ msgstr "Mape & Datoteke:"
#: editor/editor_file_dialog.cpp
msgid "Preview:"
-msgstr ""
+msgstr "Predogled:"
#: editor/editor_file_dialog.cpp editor/script_editor_debugger.cpp
#: scene/gui/file_dialog.cpp
@@ -1224,53 +1231,52 @@ msgstr "Datoteka:"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Must use a valid extension."
-msgstr ""
+msgstr "Uporabiti moraš valjavno razširitev."
#: editor/editor_file_system.cpp
msgid "ScanSources"
-msgstr ""
+msgstr "BranjeVirov"
#: editor/editor_file_system.cpp
msgid "(Re)Importing Assets"
-msgstr ""
+msgstr "Uvoz Dodatkov"
#: editor/editor_help.cpp editor/editor_node.cpp
#: editor/plugins/script_editor_plugin.cpp
msgid "Search Help"
-msgstr ""
+msgstr "IÅ¡Äi PomoÄ"
#: editor/editor_help.cpp
msgid "Class List:"
-msgstr ""
+msgstr "Seznam Razredov:"
#: editor/editor_help.cpp
msgid "Search Classes"
-msgstr ""
+msgstr "IÅ¡Äi Razrede"
#: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp
msgid "Top"
-msgstr ""
+msgstr "Vrh"
#: editor/editor_help.cpp editor/property_editor.cpp
msgid "Class:"
-msgstr ""
+msgstr "Razred:"
#: editor/editor_help.cpp editor/scene_tree_editor.cpp
msgid "Inherits:"
-msgstr ""
+msgstr "Dedovanja:"
#: editor/editor_help.cpp
msgid "Inherited by:"
-msgstr ""
+msgstr "Podedovano od:"
#: editor/editor_help.cpp
msgid "Brief Description:"
-msgstr ""
+msgstr "Kratek Opis:"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Members"
-msgstr "ÄŒlani:"
+msgstr "ÄŒlani"
#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
msgid "Members:"
@@ -1278,53 +1284,51 @@ msgstr "ÄŒlani:"
#: editor/editor_help.cpp
msgid "Public Methods"
-msgstr ""
+msgstr "Javne Metode"
#: editor/editor_help.cpp
msgid "Public Methods:"
-msgstr ""
+msgstr "Javne Metode:"
#: editor/editor_help.cpp
msgid "GUI Theme Items"
-msgstr ""
+msgstr "Elementi GUI Teme"
#: editor/editor_help.cpp
msgid "GUI Theme Items:"
-msgstr ""
+msgstr "Elementi GUI Teme:"
#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
msgid "Signals:"
msgstr "Signali:"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Enumerations"
-msgstr "Funkcije:"
+msgstr "OÅ¡tevilÄenja"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Enumerations:"
-msgstr "Funkcije:"
+msgstr "OÅ¡tevilÄenja:"
#: editor/editor_help.cpp
msgid "enum "
-msgstr ""
+msgstr "oštevil "
#: editor/editor_help.cpp
msgid "Constants"
-msgstr ""
+msgstr "Konstante"
#: editor/editor_help.cpp
msgid "Constants:"
-msgstr ""
+msgstr "Konstante:"
#: editor/editor_help.cpp
msgid "Description"
-msgstr ""
+msgstr "Opis"
#: editor/editor_help.cpp
msgid "Online Tutorials:"
-msgstr ""
+msgstr "Spletne Vaje:"
#: editor/editor_help.cpp
msgid ""
@@ -1332,42 +1336,48 @@ msgid ""
"$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/"
"url][/color]."
msgstr ""
+"Trenutno ni vaj za ta razred, lahko ga [color=$color][url=$url]prispevate[/"
+"url][/color] ali [color=$color][url=$url2]zahtevate enega[/url][/color]."
#: editor/editor_help.cpp
msgid "Properties"
-msgstr ""
+msgstr "Lastnosti"
#: editor/editor_help.cpp
msgid "Property Description:"
-msgstr ""
+msgstr "Opis lastnosti:"
#: editor/editor_help.cpp
msgid ""
"There is currently no description for this property. Please help us by "
"[color=$color][url=$url]contributing one[/url][/color]!"
msgstr ""
+"Trenutno ni opisa za to lastnost. Pomagajte nam s [color=$color][url="
+"$url]prispevkom[/url][/color]!"
#: editor/editor_help.cpp
msgid "Methods"
-msgstr ""
+msgstr "Metode"
#: editor/editor_help.cpp
msgid "Method Description:"
-msgstr ""
+msgstr "Opis Metode:"
#: editor/editor_help.cpp
msgid ""
"There is currently no description for this method. Please help us by [color="
"$color][url=$url]contributing one[/url][/color]!"
msgstr ""
+"Trenutno ni opisa za to metodo. Pomagajte nam s [color=$color][url="
+"$url]prispevkom[/url][/color]!"
#: editor/editor_help.cpp
msgid "Search Text"
-msgstr ""
+msgstr "IÅ¡Äi Besedilo"
#: editor/editor_help.cpp
msgid "Find"
-msgstr ""
+msgstr "Najdi"
#: editor/editor_log.cpp
msgid "Output:"
@@ -1378,60 +1388,60 @@ msgstr "Izhod:"
#: modules/gdnative/gdnative_library_editor_plugin.cpp scene/gui/line_edit.cpp
#: scene/gui/text_edit.cpp
msgid "Clear"
-msgstr ""
+msgstr "PoÄisti"
#: editor/editor_log.cpp
msgid "Clear Output"
-msgstr ""
+msgstr "PoÄisti Izhod"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Izvoz projekta ni uspelo s kodno napako %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
-msgstr ""
+msgstr "Napaka pri shranjevanju virov!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr ""
+msgid "Save Resource As..."
+msgstr "Shrani Vire Kot..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr ""
+msgid "I see..."
+msgstr "Vidim..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
-msgstr ""
+msgstr "Datoteke ni mogoÄe odpreti za pisanje:"
#: editor/editor_node.cpp
msgid "Requested file format unknown:"
-msgstr ""
+msgstr "Zahtevan format datoteke ni znan:"
#: editor/editor_node.cpp
msgid "Error while saving."
-msgstr ""
+msgstr "Napaka med shranjevanjem."
#: editor/editor_node.cpp
msgid "Can't open '%s'."
-msgstr ""
+msgstr "Ni mogoÄe odpreti '%s'."
#: editor/editor_node.cpp
msgid "Error while parsing '%s'."
-msgstr ""
+msgstr "Napaka pri razÄlenjevanju '%s'."
#: editor/editor_node.cpp
msgid "Unexpected end of file '%s'."
-msgstr ""
+msgstr "NepriÄakovan konec datoteke '%s'."
#: editor/editor_node.cpp
msgid "Missing '%s' or its dependencies."
-msgstr ""
+msgstr "Manjka '%s' ali njegove odvisnosti."
#: editor/editor_node.cpp
msgid "Error while loading '%s'."
-msgstr ""
+msgstr "Napaka pri nalaganju '%s'."
#: editor/editor_node.cpp
msgid "Saving Scene"
@@ -1439,57 +1449,59 @@ msgstr "Shranjevanje Scene"
#: editor/editor_node.cpp
msgid "Analyzing"
-msgstr ""
+msgstr "Analiziranje"
#: editor/editor_node.cpp
msgid "Creating Thumbnail"
-msgstr ""
+msgstr "Ustvarjanje SliÄic"
#: editor/editor_node.cpp
msgid "This operation can't be done without a tree root."
-msgstr ""
+msgstr "Te operacije ne moremo storiti brez osnovnega drevesa."
#: editor/editor_node.cpp
msgid ""
"Couldn't save scene. Likely dependencies (instances or inheritance) couldn't "
"be satisfied."
msgstr ""
+"Ni mogoÄe shraniti scene. Najverjetneje odvisnosti (primeri ali dedovanja) "
+"ne morejo biti izpolnjene."
#: editor/editor_node.cpp
msgid "Failed to load resource."
-msgstr ""
+msgstr "Napaka pri nalaganju vira."
#: editor/editor_node.cpp
msgid "Can't load MeshLibrary for merging!"
-msgstr ""
+msgstr "Knjižnice Modelov ni mogoÄe naložiti za združitev!"
#: editor/editor_node.cpp
msgid "Error saving MeshLibrary!"
-msgstr ""
+msgstr "Napaka pri shranjevanju Knjižnice Modelov!"
#: editor/editor_node.cpp
msgid "Can't load TileSet for merging!"
-msgstr ""
+msgstr "PloÅ¡ÄniNiz ni mogoÄe naložiti za združitev!"
#: editor/editor_node.cpp
msgid "Error saving TileSet!"
-msgstr ""
+msgstr "Napaka pri shranjevanju PloÅ¡ÄnegaNiza!"
#: editor/editor_node.cpp
msgid "Error trying to save layout!"
-msgstr ""
+msgstr "Napaka pri shranjevanju postavitev!"
#: editor/editor_node.cpp
msgid "Default editor layout overridden."
-msgstr ""
+msgstr "Privzeti urejevalnik postavitev je bil prepisan."
#: editor/editor_node.cpp
msgid "Layout name not found!"
-msgstr ""
+msgstr "Ime postavitve ni mogoÄe najti!"
#: editor/editor_node.cpp
msgid "Restored default layout to base settings."
-msgstr ""
+msgstr "Privzeta postavitev je bila ponastavljena na osnovne nastaviteve."
#: editor/editor_node.cpp
msgid ""
@@ -1497,18 +1509,24 @@ msgid ""
"Please read the documentation relevant to importing scenes to better "
"understand this workflow."
msgstr ""
+"Ta vir pripada uvoženi sceni, zato ga ne moremo spreminjati.\n"
+"Za boljše razumevanje preberi dokumentacijo namenjeno za uvažanje scen."
#: editor/editor_node.cpp
msgid ""
"This resource belongs to a scene that was instanced or inherited.\n"
"Changes to it will not be kept when saving the current scene."
msgstr ""
+"Ta vir pripada sceni, ki je dedovana ali je primer druge.\n"
+"Pri shranjevanju trenutne scene se spremembe ne bodo ohranile."
#: editor/editor_node.cpp
msgid ""
"This resource was imported, so it's not editable. Change its settings in the "
"import panel and then re-import."
msgstr ""
+"Ta vir je bil uvožen tako, da ga ne morete spreminjati. Spremenite svoje "
+"nastavitve na ploÅ¡Äi za uvoz in nato znova uvozite."
#: editor/editor_node.cpp
msgid ""
@@ -1517,6 +1535,9 @@ msgid ""
"Please read the documentation relevant to importing scenes to better "
"understand this workflow."
msgstr ""
+"Ta scena je bila uvožena tako, da spremembe ne bodo shranjene.\n"
+"Primer druge ali dedovanje bo omogoÄilo spremembe v njem.\n"
+"Za boljše razumevanje preberi dokumentacijo namenjeno za uvažanje scen."
#: editor/editor_node.cpp
msgid ""
@@ -1524,46 +1545,48 @@ msgid ""
"Please read the documentation relevant to debugging to better understand "
"this workflow."
msgstr ""
+"To je objekt odprt na daljavo, zato spremembe v njem ne bodo shranjene.\n"
+"Za boljÅ¡e razumevanje preberi dokumentacijo namenjeno razhroÅ¡Äevanju."
#: editor/editor_node.cpp
msgid "Expand all properties"
-msgstr ""
+msgstr "Razširi vse lastnosti"
#: editor/editor_node.cpp
msgid "Collapse all properties"
-msgstr ""
+msgstr "SkrÄi vse lastnosti"
#: editor/editor_node.cpp
msgid "Copy Params"
-msgstr ""
+msgstr "Kopiraj Parametre"
#: editor/editor_node.cpp
msgid "Paste Params"
-msgstr ""
+msgstr "Prilepi Parametre"
#: editor/editor_node.cpp editor/plugins/resource_preloader_editor_plugin.cpp
msgid "Paste Resource"
-msgstr ""
+msgstr "Prilepi Vir"
#: editor/editor_node.cpp
msgid "Copy Resource"
-msgstr ""
+msgstr "Kopiraj Vir"
#: editor/editor_node.cpp
msgid "Make Built-In"
-msgstr ""
+msgstr "Naredi Vgrajeno"
#: editor/editor_node.cpp
msgid "Make Sub-Resources Unique"
-msgstr ""
+msgstr "Naredi Pod-Vire Samostojne"
#: editor/editor_node.cpp
msgid "Open in Help"
-msgstr ""
+msgstr "Odpri v PomoÄi"
#: editor/editor_node.cpp
msgid "There is no defined scene to run."
-msgstr ""
+msgstr "Ni doloÄene scene za zagon."
#: editor/editor_node.cpp
msgid ""
@@ -1571,6 +1594,9 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
+"Glavna scena ni bila doloÄena, izberem eno?\n"
+"Kasneje jo lahko spremeniš v \"Nastavitve Projekta\" pod kategorijo "
+"'aplikacija'."
#: editor/editor_node.cpp
msgid ""
@@ -1578,6 +1604,9 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
+"Izbrana scena '%s' ne obstaja, izberem veljavno?\n"
+"Kasneje jo lahko spremeniš v \"Nastavitve Projekta\" pod kategorijo "
+"'aplikacija'."
#: editor/editor_node.cpp
msgid ""
@@ -1585,14 +1614,17 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
+"Izbrana scena '%s' ni datoteka scene, izberem veljavno?\n"
+"Kasneje jo lahko spremeniš v \"Nastavitve Projekta\" pod kategorijo "
+"'aplikacija'."
#: editor/editor_node.cpp
msgid "Current scene was never saved, please save it prior to running."
-msgstr ""
+msgstr "Trenutna scena ni bila shranjena, shranite jo pred zagonom."
#: editor/editor_node.cpp
msgid "Could not start subprocess!"
-msgstr ""
+msgstr "Nemorem zaÄeti podprocesa!"
#: editor/editor_node.cpp
msgid "Open Scene"
@@ -1603,76 +1635,76 @@ msgid "Open Base Scene"
msgstr "Odpri Osnovno Sceno"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Hitro Odpri Sceno.."
+msgid "Quick Open Scene..."
+msgstr "Hitro Odpri Sceno..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Hitro Odpri Skripto.."
+msgid "Quick Open Script..."
+msgstr "Hitro Odpri Skripto..."
#: editor/editor_node.cpp
msgid "Save & Close"
-msgstr ""
+msgstr "Shrani & Zapri"
#: editor/editor_node.cpp
msgid "Save changes to '%s' before closing?"
-msgstr ""
+msgstr "Shranim spremembe v '%s' pred zapiranjem?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Shrani Sceno Kot.."
+msgid "Save Scene As..."
+msgstr "Shrani Sceno Kot..."
#: editor/editor_node.cpp
msgid "No"
-msgstr ""
+msgstr "Ne"
#: editor/editor_node.cpp
msgid "Yes"
-msgstr ""
+msgstr "Da"
#: editor/editor_node.cpp
msgid "This scene has never been saved. Save before running?"
-msgstr ""
+msgstr "Ta scena ni bila nikoli shranjena. Shranim pred zagonom?"
#: editor/editor_node.cpp editor/scene_tree_dock.cpp
msgid "This operation can't be done without a scene."
-msgstr ""
+msgstr "Ta operacija ni mogoÄa brez scene."
#: editor/editor_node.cpp
msgid "Export Mesh Library"
-msgstr ""
+msgstr "Izvozi Knjižnico Modelov"
#: editor/editor_node.cpp
msgid "This operation can't be done without a root node."
-msgstr ""
+msgstr "Ta operacija ni mogoÄa brez osnovnega gradnika."
#: editor/editor_node.cpp
msgid "Export Tile Set"
-msgstr ""
+msgstr "Izvozi PloÅ¡Äno Zbirko"
#: editor/editor_node.cpp
msgid "This operation can't be done without a selected node."
-msgstr ""
+msgstr "Te operacije ne moremo storiti brez izbranega gradnika."
#: editor/editor_node.cpp
msgid "Current scene not saved. Open anyway?"
-msgstr ""
+msgstr "Trenutna scena ni shranjena. Vseeno odprem?"
#: editor/editor_node.cpp
msgid "Can't reload a scene that was never saved."
-msgstr ""
+msgstr "Ni mogoÄe osvežiti scene, ki ni bila shranjena."
#: editor/editor_node.cpp
msgid "Revert"
-msgstr ""
+msgstr "Povrni"
#: editor/editor_node.cpp
msgid "This action cannot be undone. Revert anyway?"
-msgstr ""
+msgstr "Tega dejanja ni mogoÄe razveljaviti. Vseeno povrni?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Hitro Zaženi Sceno.."
+msgid "Quick Run Scene..."
+msgstr "Hitro Zaženi Sceno..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1680,187 +1712,200 @@ msgstr "Zapri"
#: editor/editor_node.cpp
msgid "Exit the editor?"
-msgstr ""
+msgstr "Zaprem urejevalnik?"
#: editor/editor_node.cpp
msgid "Open Project Manager?"
-msgstr ""
+msgstr "Odprem Upravljalnik Projekta?"
#: editor/editor_node.cpp
msgid "Save & Quit"
-msgstr ""
+msgstr "Shrani & Zapri"
#: editor/editor_node.cpp
msgid "Save changes to the following scene(s) before quitting?"
-msgstr ""
+msgstr "Shranim spremembe na sledeÄih scenah pred zaprtjem?"
#: editor/editor_node.cpp
msgid "Save changes the following scene(s) before opening Project Manager?"
msgstr ""
+"Shranim spremembe na sledeÄih scenah pred odpiranjem Upravljalnika Projekta?"
#: editor/editor_node.cpp
msgid ""
"This option is deprecated. Situations where refresh must be forced are now "
"considered a bug. Please report."
msgstr ""
+"Ta možnost je zastarela. Situacije, kjer je treba osvežitev prisiliti, se "
+"zdaj Å¡tejejo za napako. Prosimo, prijavite."
#: editor/editor_node.cpp
msgid "Pick a Main Scene"
-msgstr ""
+msgstr "Izberi Glavno Sceno"
#: editor/editor_node.cpp
msgid "Unable to enable addon plugin at: '%s' parsing of config failed."
msgstr ""
+"Ni mogoÄe omogoÄiti dodatnega vtiÄnika na: '%s'. RazÄlenjevanje "
+"konfiguracije ni uspelo."
#: editor/editor_node.cpp
msgid "Unable to find script field for addon plugin at: 'res://addons/%s'."
msgstr ""
+"Ni mogoÄe najti polja skripte za dodatni vtiÄnik na: 'res://addons/%s'."
#: editor/editor_node.cpp
msgid "Unable to load addon script from path: '%s'."
-msgstr ""
+msgstr "Ni mogoÄe naložiti dodatno skripto iz poti: '%s'."
#: editor/editor_node.cpp
msgid ""
"Unable to load addon script from path: '%s' Base type is not EditorPlugin."
msgstr ""
+"Ni mogoÄe naložiti dodatno skripto iz poti: '%s' Osnovni tip ni "
+"UrejevalniVtiÄnik."
#: editor/editor_node.cpp
msgid "Unable to load addon script from path: '%s' Script is not in tool mode."
msgstr ""
+"Ni mogoÄe naložiti dodatno skripto iz poti: '%s' Skripta ni v naÄinu orodje."
#: 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' je bila samodejno uvožena, zato je ni mogoÄe spremeniti.\n"
+"Če želite narediti spremembe, lahko ustvarite novo podedovano sceno."
#: 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 ""
+"Napaka pri nalaganju prizora, zato ker ni znotraj poti projekta. Uporabite "
+"'Uvoz', da odprete prizor in ga nato shranite znotraj poti projekta."
#: editor/editor_node.cpp
msgid "Scene '%s' has broken dependencies:"
-msgstr ""
+msgstr "Prizor '%s' ima pretrgane odvisnosti:"
#: editor/editor_node.cpp
msgid "Clear Recent Scenes"
-msgstr "PoÄisti Nedavne Scene"
+msgstr "PoÄisti Nedavne Prizore"
#: editor/editor_node.cpp
msgid "Save Layout"
-msgstr ""
+msgstr "Shrani Postavitev"
#: editor/editor_node.cpp
msgid "Delete Layout"
-msgstr ""
+msgstr "Izbriši Postavitev"
#: editor/editor_node.cpp editor/import_dock.cpp
#: editor/script_create_dialog.cpp
msgid "Default"
-msgstr ""
+msgstr "Prevzeto"
#: editor/editor_node.cpp
msgid "Switch Scene Tab"
-msgstr ""
+msgstr "Preklopi na zavihek Prizor"
#: editor/editor_node.cpp
msgid "%d more files or folders"
-msgstr ""
+msgstr "%d veÄ datotek ali map"
#: editor/editor_node.cpp
msgid "%d more folders"
-msgstr ""
+msgstr "%d veÄ map"
#: editor/editor_node.cpp
msgid "%d more files"
-msgstr ""
+msgstr "%d veÄ datotek"
#: editor/editor_node.cpp
msgid "Dock Position"
-msgstr ""
+msgstr "Položaj Sidranja"
#: editor/editor_node.cpp
msgid "Distraction Free Mode"
-msgstr ""
+msgstr "NaÄin Brez Motenj"
#: editor/editor_node.cpp
msgid "Toggle distraction-free mode."
-msgstr ""
+msgstr "Preklop naÄin pisanja brez motenj."
#: editor/editor_node.cpp
msgid "Add a new scene."
-msgstr "Dodaj novo Sceno."
+msgstr "Dodaj nov Prizor."
#: editor/editor_node.cpp
msgid "Scene"
-msgstr "Scena"
+msgstr "Prizor"
#: editor/editor_node.cpp
msgid "Go to previously opened scene."
-msgstr "Pojdite na predhodno odprte scene."
+msgstr "Pojdi na predhodno odprti prizor."
#: editor/editor_node.cpp
msgid "Next tab"
-msgstr ""
+msgstr "Naslednji zavihek"
#: editor/editor_node.cpp
msgid "Previous tab"
-msgstr ""
+msgstr "Prejšnji zavihek"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr ""
+msgid "Filter Files..."
+msgstr "Filtriraj datoteke..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
-msgstr ""
+msgstr "Operacije z datotekami prizora."
#: editor/editor_node.cpp
msgid "New Scene"
-msgstr "Nova Scena"
+msgstr "Nov Prizor"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Nova Podedovana Scena.."
+msgid "New Inherited Scene..."
+msgstr "Nov Podedovan Prizor..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Odpri Sceno.."
+msgid "Open Scene..."
+msgstr "Odpri Prizor..."
#: editor/editor_node.cpp
msgid "Save Scene"
-msgstr "Shrani Sceno"
+msgstr "Shrani Prizor"
#: editor/editor_node.cpp
msgid "Save all Scenes"
-msgstr "Shrani vse Scene"
+msgstr "Shrani vse Prizore"
#: editor/editor_node.cpp
msgid "Close Scene"
-msgstr "Zapri Sceno"
+msgstr "Zapri Prizor"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Open Recent"
-msgstr "Odpri Nedavno"
+msgstr "Odpri Nedavne"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Pretvori V.."
+msgid "Convert To..."
+msgstr "Pretvori V..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr ""
+msgid "MeshLibrary..."
+msgstr "Knjižnica Modelov..."
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -1875,11 +1920,11 @@ msgstr "Ponovi"
#: editor/editor_node.cpp
msgid "Revert Scene"
-msgstr "Povrni Sceno"
+msgstr "Povrni Prizor"
#: editor/editor_node.cpp
msgid "Miscellaneous project or scene-wide tools."
-msgstr ""
+msgstr "RazliÄna projektna ali prizorska orodja."
#: editor/editor_node.cpp
msgid "Project"
@@ -1891,7 +1936,7 @@ msgstr "Nastavitve Projekta"
#: editor/editor_node.cpp
msgid "Run Script"
-msgstr ""
+msgstr "Zaženi Skripto"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Export"
@@ -1918,6 +1963,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 ""
+"Pri izvažanju ali uvajanju se bo konÄna izvrÅ¡ljiva datoteka razhroÅ¡Äevala, "
+"tako da se bo skuÅ¡ala povezati z IP-jem tega raÄunalnika."
#: editor/editor_node.cpp
msgid "Small Deploy with Network FS"
@@ -1932,30 +1979,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 ""
+"Ko je ta možnost omogoÄena, se bo pri izvozu ali uvajanju ustvarila "
+"minimalna izvršljiva datoteka.\n"
+"DatoteÄni sistem bo iz projekta zagotovljen z urejevalnikom preko omrežja.\n"
+"Na Androidu bo uvajanje zaradi hitrejšega delovanja potekalo preko kabla "
+"USB. Ta možnost pospeši testiranje iger z velikim odtisom."
#: editor/editor_node.cpp
msgid "Visible Collision Shapes"
-msgstr ""
+msgstr "Vidne Oblike Trka"
#: 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 ""
+"Gradniki oblike trka in prikaz žarka (za 2D in 3D) bodo vidni pri poteku "
+"igre, Äe je ta možnost vklopljena."
#: editor/editor_node.cpp
msgid "Visible Navigation"
-msgstr ""
+msgstr "Vidna Navigacija"
#: editor/editor_node.cpp
msgid ""
"Navigation meshes and polygons will be visible on the running game if this "
"option is turned on."
msgstr ""
+"ÄŒe je ta možnost vkljuÄena, bodo navigacijske oblike in poligoni vidni pri "
+"poteku igre."
#: editor/editor_node.cpp
msgid "Sync Scene Changes"
-msgstr ""
+msgstr "Usklajuj Spremembe Prizora"
#: editor/editor_node.cpp
msgid ""
@@ -1964,10 +2020,12 @@ msgid ""
"When used remotely on a device, this is more efficient with network "
"filesystem."
msgstr ""
+"Ko je ta možnost vkljuÄena, bodo vse spremebe v prizoru ali urejevalniku "
+"ponovljene med potekom igre."
#: editor/editor_node.cpp
msgid "Sync Script Changes"
-msgstr ""
+msgstr "Usklajuj Spremembe Skript"
#: editor/editor_node.cpp
msgid ""
@@ -1976,6 +2034,10 @@ msgid ""
"When used remotely on a device, this is more efficient with network "
"filesystem."
msgstr ""
+"ÄŒe je ta možnost vkljuÄena, bo vsaka shranjena skripta ponovno naložena v "
+"igro, ki se izvaja.\n"
+"ÄŒe se uporablja napravo na daljavo, je to bolj uÄinkovito pri omrežnem "
+"datoteÄnem sistemu."
#: editor/editor_node.cpp
msgid "Editor"
@@ -1983,19 +2045,19 @@ msgstr "Urejevalnik"
#: editor/editor_node.cpp editor/settings_config_dialog.cpp
msgid "Editor Settings"
-msgstr ""
+msgstr "Nastavitve Urejevalnika"
#: editor/editor_node.cpp
msgid "Editor Layout"
-msgstr ""
+msgstr "Postavitev Urejevalnika"
#: editor/editor_node.cpp
msgid "Toggle Fullscreen"
-msgstr ""
+msgstr "Preklopi na Celozaslonski NaÄin"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Manage Export Templates"
-msgstr ""
+msgstr "Upravljaj Izvozne Predloge"
#: editor/editor_node.cpp
msgid "Help"
@@ -2003,7 +2065,7 @@ msgstr "PomoÄ"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Classes"
-msgstr ""
+msgstr "Razredi"
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
#: editor/plugins/script_editor_plugin.cpp
@@ -2014,119 +2076,119 @@ msgstr "Iskanje"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Online Docs"
-msgstr ""
+msgstr "Spletna Dokumentacija"
#: editor/editor_node.cpp
msgid "Q&A"
-msgstr ""
+msgstr "V&O"
#: editor/editor_node.cpp
msgid "Issue Tracker"
-msgstr ""
+msgstr "Sledilnik Napak"
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
msgid "Community"
-msgstr ""
+msgstr "Skupnost"
#: editor/editor_node.cpp
msgid "About"
-msgstr ""
+msgstr "O Programu"
#: editor/editor_node.cpp
msgid "Play the project."
-msgstr ""
+msgstr "Zaženi projekt."
#: editor/editor_node.cpp
msgid "Play"
-msgstr ""
+msgstr "Zaženi"
#: editor/editor_node.cpp
msgid "Pause the scene"
-msgstr ""
+msgstr "Zaustavi prizor"
#: editor/editor_node.cpp
msgid "Pause Scene"
-msgstr ""
+msgstr "Zaustavi prizor"
#: editor/editor_node.cpp
msgid "Stop the scene."
-msgstr ""
+msgstr "Ustavi Prizor."
#: editor/editor_node.cpp
msgid "Stop"
-msgstr ""
+msgstr "Ustavi"
#: editor/editor_node.cpp
msgid "Play the edited scene."
-msgstr ""
+msgstr "Zaženi prizor u urejanju."
#: editor/editor_node.cpp
msgid "Play Scene"
-msgstr ""
+msgstr "Zaženi Prizor"
#: editor/editor_node.cpp
msgid "Play custom scene"
-msgstr ""
+msgstr "Zaženi prizor po meri"
#: editor/editor_node.cpp
msgid "Play Custom Scene"
-msgstr ""
+msgstr "Zaženi Prizor po Meri"
#: editor/editor_node.cpp
msgid "Spins when the editor window repaints!"
-msgstr ""
+msgstr "Vrti se ob spremembi okna urejevalnika!"
#: editor/editor_node.cpp
msgid "Update Always"
-msgstr ""
+msgstr "Posodobi Vedno"
#: editor/editor_node.cpp
msgid "Update Changes"
-msgstr ""
+msgstr "Posodobi Spremembe"
#: editor/editor_node.cpp
msgid "Disable Update Spinner"
-msgstr ""
+msgstr "OnemogoÄi Posodobitve Kolesca"
#: editor/editor_node.cpp
msgid "Inspector"
-msgstr ""
+msgstr "Nadzornik"
#: editor/editor_node.cpp
msgid "Create a new resource in memory and edit it."
-msgstr ""
+msgstr "Ustvari nov vir v pomnilniku in ga uredi."
#: editor/editor_node.cpp
msgid "Load an existing resource from disk and edit it."
-msgstr ""
+msgstr "Naloži obstojeÄi vir iz spomina in ga uredi."
#: editor/editor_node.cpp
msgid "Save the currently edited resource."
-msgstr ""
+msgstr "Shrani trenutno urejani vir."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr ""
+msgid "Save As..."
+msgstr "Shrani Kot..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
-msgstr ""
+msgstr "Pojdi na prejšnji urejani objekt v zgodovini."
#: editor/editor_node.cpp
msgid "Go to the next edited object in history."
-msgstr ""
+msgstr "Pojdi na naslednji urejani objekt v zgodovini."
#: editor/editor_node.cpp
msgid "History of recently edited objects."
-msgstr ""
+msgstr "Zgodovina nedavno urejanih objektov."
#: editor/editor_node.cpp
msgid "Object properties."
-msgstr ""
+msgstr "Lastnosti objekta."
#: editor/editor_node.cpp
msgid "Changes may be lost!"
-msgstr ""
+msgstr "Spremembe se lahko izgubijo!"
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
#: editor/project_manager.cpp
@@ -2135,7 +2197,7 @@ msgstr "Uvozi"
#: editor/editor_node.cpp
msgid "Node"
-msgstr ""
+msgstr "Gradnik"
#: editor/editor_node.cpp
msgid "FileSystem"
@@ -2147,521 +2209,526 @@ msgstr "Izhod"
#: editor/editor_node.cpp
msgid "Don't Save"
-msgstr ""
+msgstr "Ne Shrani"
#: editor/editor_node.cpp
msgid "Import Templates From ZIP File"
-msgstr ""
+msgstr "Uvozi Predloge iz ZIP Datoteke"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Export Project"
-msgstr ""
+msgstr "Izvozi Projekt"
#: editor/editor_node.cpp
msgid "Export Library"
-msgstr ""
+msgstr "Izvozi Knjižnico"
#: editor/editor_node.cpp
msgid "Merge With Existing"
-msgstr ""
+msgstr "Spoji z ObstojeÄim"
#: editor/editor_node.cpp
msgid "Password:"
-msgstr ""
+msgstr "Geslo:"
#: editor/editor_node.cpp
msgid "Open & Run a Script"
-msgstr ""
+msgstr "Odpri & Zaženi Skripto"
#: editor/editor_node.cpp
msgid "New Inherited"
-msgstr ""
+msgstr "Novo Podedovano"
#: editor/editor_node.cpp
msgid "Load Errors"
-msgstr ""
+msgstr "Napake pri Nalaganju"
#: editor/editor_node.cpp editor/plugins/tile_map_editor_plugin.cpp
msgid "Select"
-msgstr ""
+msgstr "Izberi"
#: editor/editor_node.cpp
msgid "Open 2D Editor"
-msgstr ""
+msgstr "Odpri 2D Urejevalnik"
#: editor/editor_node.cpp
msgid "Open 3D Editor"
-msgstr ""
+msgstr "Odpri 3D Urejevalnik"
#: editor/editor_node.cpp
msgid "Open Script Editor"
-msgstr ""
+msgstr "Odpri Urejevalnik Skript"
#: editor/editor_node.cpp editor/project_manager.cpp
msgid "Open Asset Library"
-msgstr "Odprite Asset Library"
+msgstr "Odpri Knjižnico Dodatkov"
#: editor/editor_node.cpp
msgid "Open the next Editor"
-msgstr ""
+msgstr "Odpri naslednji Urejevalnik"
#: editor/editor_node.cpp
msgid "Open the previous Editor"
-msgstr ""
+msgstr "Odpri prejšnji Urejevalnik"
#: editor/editor_plugin.cpp
msgid "Creating Mesh Previews"
-msgstr ""
+msgstr "Ustvari Predogled Modela"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr ""
+msgid "Thumbnail..."
+msgstr "SliÄica..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
-msgstr ""
+msgstr "NameÅ¡Äeni VtiÄniki:"
#: editor/editor_plugin_settings.cpp
msgid "Update"
-msgstr ""
+msgstr "Posodobi"
#: editor/editor_plugin_settings.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Version:"
-msgstr ""
+msgstr "RazliÄica:"
#: editor/editor_plugin_settings.cpp
msgid "Author:"
-msgstr ""
+msgstr "Avtor:"
#: editor/editor_plugin_settings.cpp
msgid "Status:"
-msgstr ""
+msgstr "Stanje:"
#: editor/editor_profiler.cpp
msgid "Stop Profiling"
-msgstr ""
+msgstr "Ustavi Modeliranje"
#: editor/editor_profiler.cpp
msgid "Start Profiling"
-msgstr ""
+msgstr "ZaÄni Modeliranje"
#: editor/editor_profiler.cpp
msgid "Measure:"
-msgstr ""
+msgstr "Mera:"
#: editor/editor_profiler.cpp
msgid "Frame Time (sec)"
-msgstr ""
+msgstr "Okvirni ÄŒas (sek)"
#: editor/editor_profiler.cpp
msgid "Average Time (sec)"
-msgstr ""
+msgstr "PovpreÄni ÄŒas (sek)"
#: editor/editor_profiler.cpp
msgid "Frame %"
-msgstr ""
+msgstr "Okvir %"
#: editor/editor_profiler.cpp
msgid "Physics Frame %"
-msgstr ""
+msgstr "Fizikalni Okvir %"
#: editor/editor_profiler.cpp editor/script_editor_debugger.cpp
msgid "Time:"
-msgstr ""
+msgstr "ÄŒas:"
#: editor/editor_profiler.cpp
msgid "Inclusive"
-msgstr ""
+msgstr "VkljuÄno"
#: editor/editor_profiler.cpp
msgid "Self"
-msgstr ""
+msgstr "Samo"
#: editor/editor_profiler.cpp
msgid "Frame #:"
-msgstr ""
+msgstr "Okvir #:"
#: editor/editor_profiler.cpp
msgid "Time"
-msgstr ""
+msgstr "ÄŒas"
#: editor/editor_profiler.cpp
msgid "Calls"
-msgstr ""
+msgstr "Klici"
#: editor/editor_run_native.cpp
msgid "Select device from the list"
-msgstr ""
+msgstr "Izberite napravo s seznama"
#: 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 ""
+"Za to platformo ni mogoÄe najti obstojeÄih izvoznih nastavitev.\n"
+"V izvoznem meniju dodajte svoje nastavitve."
#: editor/editor_run_script.cpp
msgid "Write your logic in the _run() method."
-msgstr ""
+msgstr "Napišite svojo logiko v metodi _run() ."
#: editor/editor_run_script.cpp
msgid "There is an edited scene already."
-msgstr ""
+msgstr "Tu že obstaja prizor v urejanju."
#: editor/editor_run_script.cpp
msgid "Couldn't instance script:"
-msgstr ""
+msgstr "Ni mogoÄe ustvariti primera skripte:"
#: editor/editor_run_script.cpp
msgid "Did you forget the 'tool' keyword?"
-msgstr ""
+msgstr "Ali si pozabil kljuÄno besedo 'orodje'?"
#: editor/editor_run_script.cpp
msgid "Couldn't run script:"
-msgstr ""
+msgstr "Ni mogoÄe zagnati skripte:"
#: editor/editor_run_script.cpp
msgid "Did you forget the '_run' method?"
-msgstr ""
+msgstr "Ali si pozabil metodo '_run' ?"
#: editor/editor_settings.cpp
msgid "Default (Same as Editor)"
-msgstr ""
+msgstr "Privzeto (Enako kot Urejevalnik)"
#: editor/editor_sub_scene.cpp
msgid "Select Node(s) to Import"
-msgstr ""
+msgstr "Izberi Gradnik(e) za Uvoz"
#: editor/editor_sub_scene.cpp
msgid "Scene Path:"
-msgstr ""
+msgstr "Pot Prizora:"
#: editor/editor_sub_scene.cpp
msgid "Import From Node:"
-msgstr ""
+msgstr "Uvozi iz Gradnika:"
#: editor/export_template_manager.cpp
msgid "Re-Download"
-msgstr ""
+msgstr "Ponovno Prenesi"
#: editor/export_template_manager.cpp
msgid "Uninstall"
-msgstr ""
+msgstr "Odstrani"
#: editor/export_template_manager.cpp
msgid "(Installed)"
-msgstr ""
+msgstr "(NameÅ¡Äeno)"
#: editor/export_template_manager.cpp
msgid "Download"
-msgstr ""
+msgstr "Prenesi"
#: editor/export_template_manager.cpp
msgid "(Missing)"
-msgstr ""
+msgstr "(ManjkajoÄe)"
#: editor/export_template_manager.cpp
msgid "(Current)"
-msgstr ""
+msgstr "(Trenutno)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr ""
+msgid "Retrieving mirrors, please wait..."
+msgstr "Pridobivanje virov, poÄakajte..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
-msgstr ""
+msgstr "ŽeliÅ¡ odstraniti predlogo razliÄice '%s'?"
#: editor/export_template_manager.cpp
msgid "Can't open export templates zip."
-msgstr ""
+msgstr "Ne morem odpreti zip izvozne predloge."
#: editor/export_template_manager.cpp
msgid "Invalid version.txt format inside templates."
-msgstr ""
+msgstr "Neveljaven format version.txt znotraj predloge."
#: editor/export_template_manager.cpp
msgid "No version.txt found inside templates."
-msgstr ""
+msgstr "Datoteke version.txt ni v predlogi."
#: editor/export_template_manager.cpp
msgid "Error creating path for templates:"
-msgstr ""
+msgstr "Napaka pri ustvarjanju poti za predloge:"
#: editor/export_template_manager.cpp
msgid "Extracting Export Templates"
-msgstr ""
+msgstr "Razširjanje Izvoznih Predlog"
#: editor/export_template_manager.cpp
msgid "Importing:"
-msgstr ""
+msgstr "Uvažanje:"
#: editor/export_template_manager.cpp
msgid ""
"No download links found for this version. Direct download is only available "
"for official releases."
msgstr ""
+"Za to razliÄico ni mogoÄe najti linkov za prenos. Neposredni prenos je na "
+"voljo samo za uradne izdaje."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Can't resolve."
-msgstr ""
+msgstr "Ni mogoÄe razreÅ¡iti."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Can't connect."
-msgstr ""
+msgstr "NemogoÄe se je povezati."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "No response."
-msgstr ""
+msgstr "Ni odgovora."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Request Failed."
-msgstr ""
+msgstr "Zahteva Ni Uspela."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Redirect Loop."
-msgstr ""
+msgstr "Preusmeritev Zanke."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Failed:"
-msgstr ""
+msgstr "Spodletelo:"
#: editor/export_template_manager.cpp
msgid "Download Complete."
-msgstr ""
+msgstr "Prenos je DokonÄan."
#: editor/export_template_manager.cpp
msgid "Error requesting url: "
-msgstr ""
+msgstr "Napaka pri zahtevi URL-ja: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr ""
+msgid "Connecting to Mirror..."
+msgstr "Povezovanje z Virom..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
-msgstr ""
+msgstr "Nepovezano"
#: editor/export_template_manager.cpp
msgid "Resolving"
-msgstr ""
+msgstr "Razreševanje"
#: editor/export_template_manager.cpp
msgid "Can't Resolve"
-msgstr ""
+msgstr "Ni MogoÄe RazreÅ¡iti"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr ""
+msgid "Connecting..."
+msgstr "Povezovanje..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
-msgstr ""
+msgstr "NemogoÄe se je Povezati"
#: editor/export_template_manager.cpp
msgid "Connected"
-msgstr ""
+msgstr "Povezano"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr ""
+msgid "Requesting..."
+msgstr "Zahtevam..."
#: editor/export_template_manager.cpp
msgid "Downloading"
-msgstr ""
+msgstr "Prenašanje"
#: editor/export_template_manager.cpp
msgid "Connection Error"
-msgstr ""
+msgstr "Napaka Pri Povezavi"
#: editor/export_template_manager.cpp
msgid "SSL Handshake Error"
-msgstr ""
+msgstr "Napaka Pri Usklanjevanju SSH"
#: editor/export_template_manager.cpp
msgid "Current Version:"
-msgstr ""
+msgstr "Trenutna RazliÄica:"
#: editor/export_template_manager.cpp
msgid "Installed Versions:"
-msgstr ""
+msgstr "NameÅ¡Äene RazliÄice:"
#: editor/export_template_manager.cpp
msgid "Install From File"
-msgstr ""
+msgstr "Namesti Iz Datoteke"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Remove Template"
-msgstr "Odstrani Spremenljivko"
+msgstr "Odstrani Predlogo"
#: editor/export_template_manager.cpp
msgid "Select template file"
-msgstr ""
+msgstr "Izberi datoteko predloge"
#: editor/export_template_manager.cpp
msgid "Export Template Manager"
-msgstr ""
+msgstr "Izvozni Upravitelj Predlog"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Download Templates"
-msgstr "Odstrani Spremenljivko"
+msgstr "Prenesi Predloge"
#: editor/export_template_manager.cpp
msgid "Select mirror from list: "
-msgstr ""
+msgstr "Izberi vire s seznama: "
#: editor/file_type_cache.cpp
msgid "Can't open file_type_cache.cch for writing, not saving file type cache!"
msgstr ""
+"Za pisanje ni mogoÄe odpreti file_type_cache.cch, ne da bi shranili "
+"predpomnilnik tipa datoteke!"
#: editor/filesystem_dock.cpp
msgid "Cannot navigate to '%s' as it has not been found in the file system!"
msgstr ""
+"Ne morem se postaviti na mesto '%s', ker ni bilo najdeno v datoteÄnem "
+"sistemu!"
#: editor/filesystem_dock.cpp
msgid "View items as a grid of thumbnails"
-msgstr ""
+msgstr "Oglejte si elemente, kot mrežo sliÄic"
#: editor/filesystem_dock.cpp
msgid "View items as a list"
-msgstr ""
+msgstr "Oglejte si elemente v seznamu"
#: editor/filesystem_dock.cpp
msgid "Status: Import of file failed. Please fix file and reimport manually."
msgstr ""
+"Stanje: Uvoz datoteke ni uspel. Popravi datoteko in ponovno roÄno uvozi."
#: editor/filesystem_dock.cpp
msgid "Cannot move/rename resources root."
-msgstr ""
+msgstr "Ni mogoÄe premakniti/preimenovati osnovne vire."
#: editor/filesystem_dock.cpp
msgid "Cannot move a folder into itself."
-msgstr ""
+msgstr "Mape ni mogoÄe premakniti vase."
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Error moving:"
-msgstr "Napaka naložitve pisave."
+msgstr "Napaka pri premikanju:"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Error duplicating:"
-msgstr "Preimenuj Spremenljivko"
+msgstr "Napaka pri podvajanju:"
#: editor/filesystem_dock.cpp
msgid "Unable to update dependencies:"
-msgstr ""
+msgstr "Odvisnosti ni mogoÄe posodobiti:"
#: editor/filesystem_dock.cpp
msgid "No name provided"
-msgstr ""
+msgstr "Ime ni na voljo"
#: editor/filesystem_dock.cpp
msgid "Provided name contains invalid characters"
-msgstr ""
+msgstr "Vnešeno ime vsebuje neveljavne znake"
#: editor/filesystem_dock.cpp
msgid "No name provided."
-msgstr ""
+msgstr "Ime ni doloÄeno."
#: editor/filesystem_dock.cpp
msgid "Name contains invalid characters."
-msgstr ""
+msgstr "Ime vsebuje neveljavne znake."
#: editor/filesystem_dock.cpp
msgid "A file or folder with this name already exists."
-msgstr ""
+msgstr "Datoteka ali mapa s tem imenom že obstaja."
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Renaming file:"
-msgstr "Preimenuj Spremenljivko"
+msgstr "Preimenovanje Datoteke:"
#: editor/filesystem_dock.cpp
msgid "Renaming folder:"
-msgstr ""
+msgstr "Preimenovanje mape:"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Duplicating file:"
-msgstr "Preimenuj Spremenljivko"
+msgstr "Podvajanje datoteke:"
#: editor/filesystem_dock.cpp
msgid "Duplicating folder:"
-msgstr ""
+msgstr "Podvajanje mape:"
#: editor/filesystem_dock.cpp
msgid "Expand all"
-msgstr ""
+msgstr "Razširi vse"
#: editor/filesystem_dock.cpp
msgid "Collapse all"
-msgstr ""
+msgstr "SkrÄi vse"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr ""
+msgid "Rename..."
+msgstr "Preimenuj..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr ""
+msgid "Move To..."
+msgstr "Premakni V..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
-msgstr ""
+msgstr "Odpri Prizor(e)"
#: editor/filesystem_dock.cpp
msgid "Instance"
-msgstr ""
+msgstr "Primer"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr ""
+msgid "Edit Dependencies..."
+msgstr "Uredi Odvisnosti..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr ""
+msgid "View Owners..."
+msgstr "Poglej Lastnike..."
#: editor/filesystem_dock.cpp
-#, fuzzy
-msgid "Duplicate.."
-msgstr "Podvoji Izbrano"
+msgid "Duplicate..."
+msgstr "Podvoji..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
-msgstr ""
+msgstr "Prejšna Mapa"
#: editor/filesystem_dock.cpp
msgid "Next Directory"
-msgstr ""
+msgstr "Naslednja Mapa"
#: editor/filesystem_dock.cpp
msgid "Re-Scan Filesystem"
-msgstr ""
+msgstr "Ponovno Preglej DatoteÄni Sistem"
#: editor/filesystem_dock.cpp
msgid "Toggle folder status as Favorite"
-msgstr ""
+msgstr "Nastavi mapo status kot Priljubljeno"
#: editor/filesystem_dock.cpp
msgid "Instance the selected scene(s) as child of the selected node."
msgstr ""
+"Naredi primer iz izbranih prizorov, ki bo naslednik izbranega gradnika."
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
+"Pregledovanje Datotek,\n"
+"Prosimo, PoÄakajte..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2674,153 +2741,153 @@ msgstr "Preimenuj"
#: editor/groups_editor.cpp
msgid "Add to Group"
-msgstr ""
+msgstr "Dodaj v Skupino"
#: editor/groups_editor.cpp
msgid "Remove from Group"
-msgstr ""
+msgstr "Odstrani iz Skupine"
#: editor/import/resource_importer_scene.cpp
msgid "Import as Single Scene"
-msgstr ""
+msgstr "Uvozi kot En Prizor"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Animations"
-msgstr ""
+msgstr "Uvozi z LoÄenimi Animacijami"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Materials"
-msgstr ""
+msgstr "Uvozi z LoÄenimi Materiali"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Objects"
-msgstr ""
+msgstr "Uvozi z LoÄenimi Objekti"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Objects+Materials"
-msgstr ""
+msgstr "Uvozi z LoÄenimi Objekti+Materiali"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Objects+Animations"
-msgstr ""
+msgstr "Uvozi z LoÄenimi Objekti+Animacijami"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Materials+Animations"
-msgstr ""
+msgstr "Uvozi z LoÄenimi Materiali+Animacijami"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Objects+Materials+Animations"
-msgstr ""
+msgstr "Uvozi z LoÄenimi Objekti+Materiali+Animacijami"
#: editor/import/resource_importer_scene.cpp
msgid "Import as Multiple Scenes"
-msgstr ""
+msgstr "Uvozi kot VeÄ Prizorov"
#: editor/import/resource_importer_scene.cpp
msgid "Import as Multiple Scenes+Materials"
-msgstr ""
+msgstr "Uvozi kot VeÄkratnik Prizorov+Materialov"
#: editor/import/resource_importer_scene.cpp
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Import Scene"
-msgstr ""
+msgstr "Uvozi Prizor"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr ""
+msgid "Importing Scene..."
+msgstr "Uvažanje Prizora..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
-msgstr ""
+msgstr "Ustvarjanje Svetlobnih Kart"
#: editor/import/resource_importer_scene.cpp
msgid "Generating for Mesh: "
-msgstr ""
+msgstr "Ustvarjanje za Model: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr ""
+msgid "Running Custom Script..."
+msgstr "Izvajanje Skripte Po Meri..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
-msgstr ""
+msgstr "Skripte po uvozu ni bilo mogoÄe naložiti:"
#: editor/import/resource_importer_scene.cpp
msgid "Invalid/broken script for post-import (check console):"
-msgstr ""
+msgstr "Neveljavna/pokvarjena skripta za naknadno uvažanje (Glej konzolo):"
#: editor/import/resource_importer_scene.cpp
msgid "Error running post-import script:"
-msgstr ""
+msgstr "Napaka pri zagonu skripte po uvozu:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr ""
+msgid "Saving..."
+msgstr "Shranjevanje..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
-msgstr ""
+msgstr "Nastavi kot Privzeto za '%s'"
#: editor/import_dock.cpp
msgid "Clear Default for '%s'"
-msgstr ""
+msgstr "PoÄisti privzeto za '%s'"
#: editor/import_dock.cpp
msgid " Files"
-msgstr ""
+msgstr " Datoteke"
#: editor/import_dock.cpp
msgid "Import As:"
-msgstr ""
+msgstr "Uvozi Kot:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr ""
+msgid "Preset..."
+msgstr "Prednastavitev..."
#: editor/import_dock.cpp
msgid "Reimport"
-msgstr ""
+msgstr "Ponovno Uvozi"
#: editor/multi_node_edit.cpp
msgid "MultiNode Set"
-msgstr ""
+msgstr "Niz VeÄkratnih Gradnikov"
#: editor/node_dock.cpp
msgid "Groups"
-msgstr ""
+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 ""
@@ -2829,199 +2896,201 @@ 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
-#, fuzzy
msgid "Delete points"
-msgstr "Izbriši Izbrano"
+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
-#, fuzzy
msgid "Directions"
-msgstr "Funkcije:"
+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)"
@@ -3029,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
@@ -3061,78 +3130,77 @@ msgstr "Animacija"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "New name:"
-msgstr ""
+msgstr "Novo ime:"
#: editor/plugins/animation_tree_editor_plugin.cpp
-#, fuzzy
msgid "Edit Filters"
-msgstr "Uredi Spremenljivko:"
+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"
@@ -3140,136 +3208,135 @@ 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 ""
+msgid "Import Animations..."
+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 ""
+msgid "Filters..."
+msgstr "Filtri..."
#: editor/plugins/animation_tree_editor_plugin.cpp
-#, fuzzy
msgid "AnimationTree"
-msgstr "Približaj Animacijo"
+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 ""
+msgid "Resolving..."
+msgstr "Razreševanje..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3335,8 +3402,8 @@ msgid "Site:"
msgstr "Stran:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "Podpora.."
+msgid "Support..."
+msgstr "Podpora..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3362,6 +3429,8 @@ msgid ""
"No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake "
"Light' flag is on."
msgstr ""
+"Brez modelov za peko. Poskrbi, da vsebujejo kanal UV2 in da je vkljuÄena "
+"oznaka 'ZapeÄi Svetlobo'."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid "Failed creating lightmap images, make sure path is writable."
@@ -3415,9 +3484,8 @@ msgid "Create new vertical guide"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Remove vertical guide"
-msgstr "Odstrani Spremenljivko"
+msgstr "Odstranite navpiÄni vodnik"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move horizontal guide"
@@ -3428,9 +3496,8 @@ msgid "Create new horizontal guide"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Remove horizontal guide"
-msgstr "Odstrani Spremenljivko"
+msgstr "Odstrani vodoravno vodilo"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Create new horizontal and vertical guides"
@@ -3506,9 +3573,8 @@ msgid "Pan Mode"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Toggles snapping"
-msgstr "Preklopi na Zaustavitev"
+msgstr "Preklopi pripenjanje"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Use Snap"
@@ -3527,8 +3593,9 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
-msgstr ""
+msgstr "Preoblikuj Zaskok..."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap Relative"
@@ -3667,9 +3734,8 @@ msgid "Drag pivot from mouse position"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Set pivot at mouse position"
-msgstr "Odstrani Signal"
+msgstr "Nastavite toÄko na položaj miÅ¡ke"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Multiply grid step by 2"
@@ -3778,14 +3844,12 @@ msgid "Load Curve Preset"
msgstr ""
#: editor/plugins/curve_editor_plugin.cpp
-#, fuzzy
msgid "Add point"
-msgstr "Dodaj Signal"
+msgstr "Dodaj toÄko"
#: editor/plugins/curve_editor_plugin.cpp
-#, fuzzy
msgid "Remove point"
-msgstr "Odstrani Signal"
+msgstr "Odstrani toÄko"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Left linear"
@@ -3800,9 +3864,8 @@ msgid "Load preset"
msgstr ""
#: editor/plugins/curve_editor_plugin.cpp
-#, fuzzy
msgid "Remove Curve Point"
-msgstr "Odstrani Signal"
+msgstr "Odstrani Krivuljno ToÄko"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Toggle Curve Linear Tangent"
@@ -3869,11 +3932,11 @@ msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh is empty!"
-msgstr ""
+msgstr "Model je prazen!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Static Trimesh Body"
-msgstr ""
+msgstr "Ustvari StatiÄno Telo TriModel"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Static Convex Body"
@@ -3952,7 +4015,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4157,7 +4220,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4336,19 +4399,16 @@ msgid "Curve Point #"
msgstr ""
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Set Curve Point Position"
-msgstr "Odstrani Signal"
+msgstr "Nastavi Položaj Krivuljne ToÄke"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Set Curve In Position"
-msgstr "Odstrani Signal"
+msgstr "Nastavi Krivuljo na Položaj"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Set Curve Out Position"
-msgstr "Odstrani Signal"
+msgstr "Nastavi Krivuljo iz Položaja"
#: editor/plugins/path_editor_plugin.cpp
msgid "Split Path"
@@ -4359,9 +4419,8 @@ msgid "Remove Path Point"
msgstr ""
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Remove Out-Control Point"
-msgstr "Odstrani Funkcijo"
+msgstr "Odstrani ToÄko Izven Nadzora"
#: editor/plugins/path_editor_plugin.cpp
msgid "Remove In-Control Point"
@@ -4522,7 +4581,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4602,9 +4661,8 @@ msgid "Close Docs"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Close All"
-msgstr "Zapri"
+msgstr "Zapri Vse"
#: editor/plugins/script_editor_plugin.cpp
msgid "Close Other Tabs"
@@ -4620,7 +4678,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4745,9 +4803,8 @@ msgid "Select All"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Delete Line"
-msgstr "Izbriši Izbrano"
+msgstr "Izbriši Vrstico"
#: editor/plugins/script_text_editor.cpp
msgid "Indent Left"
@@ -4762,9 +4819,8 @@ msgid "Toggle Comment"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Fold/Unfold Line"
-msgstr "Izbriši Izbrano"
+msgstr "Pregibna/Nepregibna ÄŒrta"
#: editor/plugins/script_text_editor.cpp
msgid "Fold All Lines"
@@ -4828,15 +4884,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5028,9 +5084,8 @@ msgid "Material Changes"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Shader Changes"
-msgstr "Spremeni"
+msgstr "Spremebe v Shader"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Surface Changes"
@@ -5267,9 +5322,8 @@ msgid "Align Selection With View"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Tool Select"
-msgstr "Izbriši Izbrano"
+msgstr "Izbira Orodja"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Tool Move"
@@ -5284,21 +5338,16 @@ msgid "Tool Scale"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Toggle Freelook"
-msgstr "Preklopi na Zaustavitev"
+msgstr "Preklopi Svobodni Pregled"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform"
msgstr "Preoblikovanje"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Preoblikuj Zaskok.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "Preoblikovanje Dialoga.."
+msgid "Transform Dialog..."
+msgstr "Preoblikovanje Dialoga..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5542,17 +5591,15 @@ msgid "Remove Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Remove All Items"
-msgstr "Odstrani Spremenljivko"
+msgstr "Odstrani Vse Stvari"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Remove All"
-msgstr "Odstrani Signal"
+msgstr "Odstrani Vse"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5620,7 +5667,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5776,9 +5823,8 @@ msgid ""
msgstr ""
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Select current edited sub-tile."
-msgstr "Dodaj Setter Lastnost"
+msgstr "Izberi trenutno pod-ploÅ¡Äo v urejanju."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Select sub-tile to change its priority."
@@ -5809,7 +5855,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5899,6 +5945,11 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Ime Projekta:"
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -5933,9 +5984,8 @@ msgid "The following files failed extraction from package:"
msgstr ""
#: editor/project_manager.cpp
-#, fuzzy
msgid "Rename Project"
-msgstr "Preimenuj Funkcijo"
+msgstr "Preimenuj Projekt"
#: editor/project_manager.cpp
msgid "New Game Project"
@@ -6088,8 +6138,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6117,7 +6167,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6213,9 +6263,8 @@ msgid "Wheel Down."
msgstr ""
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Add Global Property"
-msgstr "Dodaj Getter Lastnost"
+msgstr "Dodaj Globalno Lastnost"
#: editor/project_settings_editor.cpp
msgid "Select a setting item first!"
@@ -6230,9 +6279,8 @@ msgid "Setting '%s' is internal, and it can't be deleted."
msgstr ""
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Delete Item"
-msgstr "Izbriši Izbrano"
+msgstr "Izbriši Predmet"
#: editor/project_settings_editor.cpp
msgid "Already existing"
@@ -6303,7 +6351,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6399,11 +6447,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6411,9 +6459,8 @@ msgid "Assign"
msgstr ""
#: editor/property_editor.cpp
-#, fuzzy
msgid "Select Node"
-msgstr "Dodaj Setter Lastnost"
+msgstr "Izberi Gradnik"
#: editor/property_editor.cpp
msgid "New Script"
@@ -6468,9 +6515,8 @@ msgid "Properties:"
msgstr ""
#: editor/property_selector.cpp
-#, fuzzy
msgid "Select Property"
-msgstr "Dodaj Setter Lastnost"
+msgstr "Izberi Lastnost"
#: editor/property_selector.cpp
msgid "Select Virtual Method"
@@ -6576,7 +6622,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -6692,9 +6738,8 @@ msgid "Clear a script for the selected node."
msgstr ""
#: editor/scene_tree_dock.cpp
-#, fuzzy
msgid "Remote"
-msgstr "Odstrani Signal"
+msgstr "Upravljalnik"
#: editor/scene_tree_dock.cpp
msgid "Local"
@@ -6823,18 +6868,16 @@ msgid "Wrong extension chosen"
msgstr ""
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Invalid Path"
-msgstr ": Neveljavni argumenti: "
+msgstr "Neveljavna Pot"
#: editor/script_create_dialog.cpp
msgid "Invalid class name"
msgstr ""
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Invalid inherited parent name or path"
-msgstr "Neveljaven indeks lastnosti imena."
+msgstr "Neveljaveno prevzeto ime ali pot nadrejenega"
#: editor/script_create_dialog.cpp
msgid "Script valid"
@@ -6869,9 +6912,8 @@ msgid "Class Name"
msgstr ""
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Template"
-msgstr "Odstrani Spremenljivko"
+msgstr "Predloga"
#: editor/script_create_dialog.cpp
msgid "Built-in Script"
@@ -6882,9 +6924,8 @@ msgid "Attach Node Script"
msgstr ""
#: editor/script_editor_debugger.cpp
-#, fuzzy
msgid "Remote "
-msgstr "Odstrani Signal"
+msgstr "Upravljalnik "
#: editor/script_editor_debugger.cpp
msgid "Bytes:"
@@ -7075,9 +7116,8 @@ msgid "Select dependencies of the library for this entry"
msgstr ""
#: modules/gdnative/gdnative_library_editor_plugin.cpp
-#, fuzzy
msgid "Remove current entry"
-msgstr "Odstrani Signal"
+msgstr "Odstrani trenutni vnos"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Double click to create a new entry"
@@ -7191,9 +7231,8 @@ msgid "Floor:"
msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "GridMap Delete Selection"
-msgstr "Izbriši Izbrano"
+msgstr "GridMap Izbriši Izbor"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "GridMap Duplicate Selection"
@@ -7272,9 +7311,8 @@ msgid "Erase Area"
msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Clear Selection"
-msgstr "Izbriši Izbrano"
+msgstr "PoÄisti izbrano"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "GridMap Settings"
@@ -7345,8 +7383,8 @@ msgid ""
"A node yielded without working memory, please read the docs on how to yield "
"properly!"
msgstr ""
-"VozliÅ¡Äe se je ustavilo brez delovnega spomina! Prosimo preberite si v "
-"dokumentaciji, kako pravilno ustaviti vozliÅ¡Äe."
+"Gradnik je bil ustavljen brez delovnega spomina, v dokumentaciji si "
+"preberite kako ga pravilno ustaviti!"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7378,9 +7416,8 @@ msgid "Stack overflow with stack depth: "
msgstr "Sklad prepoln z stack depth: "
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Change Signal Arguments"
-msgstr "Uredi Argumente Signala:"
+msgstr "Spremeni Argumente Signala"
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Argument Type"
@@ -7395,9 +7432,8 @@ msgid "Set Variable Default Value"
msgstr ""
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Set Variable Type"
-msgstr "Uredi Spremenljivko:"
+msgstr "Nastavite Tip Spremenljivke"
#: modules/visual_script/visual_script_editor.cpp
msgid "Functions:"
@@ -7448,9 +7484,8 @@ msgid "Add Node"
msgstr "Dodaj vozliÅ¡Äe"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Remove VisualScript Nodes"
-msgstr "Odstrani Spremenljivko"
+msgstr "Odstrani Gradnike VizualnaSkripta"
#: modules/visual_script/visual_script_editor.cpp
msgid "Duplicate VisualScript Nodes"
@@ -7485,9 +7520,8 @@ msgid "Add Preload Node"
msgstr "Dodaj prednaloženo vozliÅ¡Äe"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Add Node(s) From Tree"
-msgstr "Dodaj vozliÅ¡Äe(a) iz drevesa"
+msgstr "Dodaj Gradnik(e) iz Drevesa"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Getter Property"
@@ -7498,18 +7532,16 @@ msgid "Add Setter Property"
msgstr "Dodaj Setter Lastnost"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Change Base Type"
-msgstr "Osnovni Tip:"
+msgstr "Spremeni Osnovni Tip"
#: modules/visual_script/visual_script_editor.cpp
msgid "Move Node(s)"
msgstr ""
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Remove VisualScript Node"
-msgstr "Odstrani Spremenljivko"
+msgstr "Odstrani Gradnik VizualnaSkripta"
#: modules/visual_script/visual_script_editor.cpp
msgid "Connect Nodes"
@@ -7572,18 +7604,16 @@ msgid "Remove Function"
msgstr "Odstrani Funkcijo"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Edit Variable"
-msgstr "Uredi Spremenljivko:"
+msgstr "Uredi Spremenljivko"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove Variable"
msgstr "Odstrani Spremenljivko"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Edit Signal"
-msgstr "Urejanje Signala:"
+msgstr "Uredi Signal"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove Signal"
@@ -7710,9 +7740,8 @@ msgid "Could not open template for export:"
msgstr ""
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Invalid export template:"
-msgstr "Neveljaven indeks lastnosti imena."
+msgstr "Neveljavna izvozna predloga:"
#: platform/javascript/export/export.cpp
msgid "Could not read custom HTML shell:"
@@ -8051,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 2c2b1eb001..c838174131 100644
--- a/editor/translations/sr_Cyrl.po
+++ b/editor/translations/sr_Cyrl.po
@@ -500,7 +500,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Повежи '%s' Ñа '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "Повежи..."
#: editor/connections_dialog.cpp
@@ -925,11 +925,11 @@ msgid "Move Audio Bus"
msgstr "Помери звучни баÑ"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr "Сачувај раÑпоред звучног баÑа као..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr "Локација за нови раÑпоред..."
#: editor/editor_audio_buses.cpp
@@ -1065,11 +1065,11 @@ msgid "Updating Scene"
msgstr "Ðжурирање Ñцене"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr "Чувам локалне промене..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr "Ðжурирам Ñцену..."
#: editor/editor_data.cpp
@@ -1140,7 +1140,7 @@ msgid "Show In File Manager"
msgstr "Покажи у менаџеру датотека"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr "Ðови директоријум..."
#: editor/editor_file_dialog.cpp
@@ -1411,12 +1411,12 @@ msgid "Error saving resource!"
msgstr "Грешка при чувању реÑурÑа!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "Сачувај реÑÑƒÑ€Ñ ÐºÐ°Ð¾..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr "Разумем..."
#: editor/editor_node.cpp
@@ -1645,11 +1645,11 @@ msgid "Open Base Scene"
msgstr "Отвори базну Ñцену"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr "Брзо отварање Ñцене..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr "Брзо отварање Ñкриптице..."
#: editor/editor_node.cpp
@@ -1661,7 +1661,7 @@ msgid "Save changes to '%s' before closing?"
msgstr "Сачувај промене '%s' пре излаÑка?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr "Сачувај Ñцену као..."
#: editor/editor_node.cpp
@@ -1713,7 +1713,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Ова акција Ñе не може опозвати. ÐаÑтави?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr "Брзо покретање Ñцене..."
#: editor/editor_node.cpp
@@ -1869,7 +1869,7 @@ msgid "Previous tab"
msgstr "Претходни таб"
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr "Филтрирај датотеке..."
#: editor/editor_node.cpp
@@ -1881,11 +1881,11 @@ msgid "New Scene"
msgstr "Ðова Ñцена"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr "Ðова наÑлеђена Ñцена..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr "Отвори Ñцену..."
#: editor/editor_node.cpp
@@ -1905,15 +1905,15 @@ msgid "Open Recent"
msgstr "Отвори недавно коришћено"
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr "Конвертуј у..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr "MeshLibrary..."
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2177,7 +2177,7 @@ msgid "Save the currently edited resource."
msgstr "Сачувај тренутно измењени реÑурÑ."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr "Сачувај као..."
#: editor/editor_node.cpp
@@ -2286,7 +2286,7 @@ msgid "Creating Mesh Previews"
msgstr "Ðаправи приказ мрежа"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr "Сличица..."
#: editor/editor_plugin_settings.cpp
@@ -2441,7 +2441,7 @@ msgid "(Current)"
msgstr "(Тренутно)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr "Прихватам одредишта, молим Ñачекајте..."
#: editor/export_template_manager.cpp
@@ -2521,7 +2521,7 @@ msgid "Error requesting url: "
msgstr "Грешка при захтеву url: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr "Повезивање Ñа одредиштем..."
#: editor/export_template_manager.cpp
@@ -2538,7 +2538,7 @@ msgstr "Ðе могу решити"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr "Повезивање..."
#: editor/export_template_manager.cpp
@@ -2552,7 +2552,7 @@ msgstr "Повезан"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "Захтевање..."
#: editor/export_template_manager.cpp
@@ -2696,11 +2696,11 @@ msgid "Collapse all"
msgstr "Умањи Ñве"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr "Преименуј..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr "Помери у..."
#: editor/filesystem_dock.cpp
@@ -2713,16 +2713,16 @@ msgid "Instance"
msgstr "Додај инÑтанцу"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr "Измени завиÑноÑти..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr "Погледај влаÑнике..."
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Дуплирај"
#: editor/filesystem_dock.cpp
@@ -2748,7 +2748,7 @@ msgstr "Ðаправи Ñледећу Ñцену/е као дете одабра
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Скенирање датотека,\n"
"Молим Ñачекајте..."
@@ -2816,7 +2816,7 @@ msgid "Import Scene"
msgstr "Увези Ñцену"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr "Увожење Ñцеене..."
#: editor/import/resource_importer_scene.cpp
@@ -2830,7 +2830,7 @@ msgid "Generating for Mesh: "
msgstr "ГенериÑање оÑног поравнаног граничниог оквира (AABB)"
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr "Обрађивање Ñкриптице..."
#: editor/import/resource_importer_scene.cpp
@@ -2846,7 +2846,7 @@ msgid "Error running post-import script:"
msgstr "Грешка при обрађивању поÑÑ‚-увозне Ñкриптице:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr "Чување..."
#: editor/import_dock.cpp
@@ -2866,7 +2866,7 @@ msgid "Import As:"
msgstr "Увези као:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr "ПоÑтавке..."
#: editor/import_dock.cpp
@@ -3284,7 +3284,7 @@ msgid "Transition Node"
msgstr "Transition чвор"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr "Увези анимације..."
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3292,7 +3292,7 @@ msgid "Edit Node Filters"
msgstr "Измени филтере чвора"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr "Филтери..."
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3361,7 +3361,7 @@ msgid "Fetching:"
msgstr "Преузимање:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr "Решавање..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3428,7 +3428,7 @@ msgid "Site:"
msgstr "Веб Ñтраница:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr "Подршка..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3618,6 +3618,7 @@ msgid "Use Rotation Snap"
msgstr "КориÑти лепљење ротације"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "ПоÑтавке лепљења..."
@@ -4045,7 +4046,7 @@ msgid "Create Convex Collision Sibling"
msgstr "Ðаправи конвекÑног Ñударног брата"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr "Ðаправи ивичну мрежу..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4253,7 +4254,7 @@ msgid "Error loading image:"
msgstr "Грешка при учитавању Ñлике:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr "У Ñлици нема пикÑела Ñа транÑпарентношћу већом од 128..."
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4618,7 +4619,7 @@ msgid "Import Theme"
msgstr "Увези тему"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr "Сачувај тему као..."
#: editor/plugins/script_editor_plugin.cpp
@@ -4717,7 +4718,7 @@ msgstr "Прикажи панел Ñкриптица"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr "Тражи..."
#: editor/plugins/script_editor_plugin.cpp
@@ -4928,15 +4929,15 @@ msgid "Find Previous"
msgstr "Ðађи претходни"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr "Замени..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr "Иди на функцију..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr "Иди на линију..."
#: editor/plugins/script_text_editor.cpp
@@ -5393,11 +5394,7 @@ msgid "Transform"
msgstr "ТранÑформација"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Конфигуриши лепљење..."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr "Прозор транÑформације..."
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5653,7 +5650,7 @@ msgid "Remove All"
msgstr "Обриши Ñве"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr "Измени тему..."
#: editor/plugins/theme_editor_plugin.cpp
@@ -5725,7 +5722,8 @@ msgid "Options"
msgstr "Опција"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+#, fuzzy
+msgid "Has,Many,Options"
msgstr "Има,много,неколико,опција!"
#: editor/plugins/theme_editor_plugin.cpp
@@ -5924,7 +5922,7 @@ msgid "Presets"
msgstr "ПоÑтавке"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr "Додај..."
#: editor/project_export.cpp
@@ -6020,6 +6018,11 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Ðеважеће име."
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "ÐеуÑпех при прављењу директоријума."
@@ -6209,8 +6212,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6238,7 +6241,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6423,7 +6426,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6519,11 +6522,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6695,7 +6698,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -8146,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."
@@ -8156,9 +8166,6 @@ msgstr "Ðеважећа величина фонта."
#~ msgid "Can't write file."
#~ msgstr "ÐеуÑпех при запиÑивању датотеке."
-#~ msgid "Next"
-#~ msgstr "Следеће"
-
#~ msgid "Not found!"
#~ msgstr "Ðије пронађено!"
diff --git a/editor/translations/sr_Latn.po b/editor/translations/sr_Latn.po
index d7cb85af1b..975418d4fb 100644
--- a/editor/translations/sr_Latn.po
+++ b/editor/translations/sr_Latn.po
@@ -8,7 +8,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-04-25 14:41+0000\n"
+"PO-Revision-Date: 2018-05-15 08:41+0000\n"
"Last-Translator: Milos Ponjavusic <brane@branegames.com>\n"
"Language-Team: Serbian (latin) <https://hosted.weblate.org/projects/godot-"
"engine/godot/sr_Latn/>\n"
@@ -215,7 +215,7 @@ msgstr "Animacija dodaj kljuÄ"
#: editor/animation_editor.cpp
msgid "Change Anim Len"
-msgstr ""
+msgstr "Promijeni Dužinu Animacije"
#: editor/animation_editor.cpp
msgid "Change Anim Loop"
@@ -223,15 +223,15 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Anim Create Typed Value Key"
-msgstr ""
+msgstr "Animacija Napravit Tip Vrijednosni KljuÄ"
#: editor/animation_editor.cpp
msgid "Anim Insert"
-msgstr ""
+msgstr "Animacija Umetni"
#: editor/animation_editor.cpp
msgid "Anim Scale Keys"
-msgstr ""
+msgstr "Animacija Skaliraj KljuÄeve"
#: editor/animation_editor.cpp
msgid "Anim Add Call Track"
@@ -495,7 +495,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr ""
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -905,11 +905,11 @@ msgid "Move Audio Bus"
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1045,11 +1045,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1118,7 +1118,7 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr ""
#: editor/editor_file_dialog.cpp
@@ -1380,12 +1380,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1590,11 +1590,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1606,7 +1606,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr ""
#: editor/editor_node.cpp
@@ -1658,7 +1658,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1803,7 +1803,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1815,11 +1815,11 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1839,15 +1839,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2092,7 +2092,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2201,7 +2201,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2352,7 +2352,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2428,7 +2428,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2445,7 +2445,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2458,7 +2458,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2590,11 +2590,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2606,15 +2606,15 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2640,7 +2640,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2706,7 +2706,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2718,7 +2718,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2734,7 +2734,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2754,7 +2754,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3168,7 +3168,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3176,7 +3176,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3244,7 +3244,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3311,7 +3311,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3498,6 +3498,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3919,7 +3920,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4124,7 +4125,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4485,7 +4486,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4582,7 +4583,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4788,15 +4789,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5247,11 +5248,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5504,7 +5501,7 @@ msgid "Remove All"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5572,7 +5569,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5760,7 +5757,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5850,6 +5847,10 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr ""
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -6036,8 +6037,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6065,7 +6066,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6249,7 +6250,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6345,11 +6346,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6520,7 +6521,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
diff --git a/editor/translations/sv.po b/editor/translations/sv.po
index 4a861d1b76..9ec654128a 100644
--- a/editor/translations/sv.po
+++ b/editor/translations/sv.po
@@ -3,21 +3,23 @@
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
#
-# bergmarklund <davemcgroin@gmail.com>, 2017.
+# bergmarklund <davemcgroin@gmail.com>, 2017, 2018.
# Christoffer Sundbom <christoffer_karlsson@live.se>, 2017.
+# Jakob Sinclair <sinclair.jakob@mailbox.org>, 2018.
+# . <grenoscar@gmail.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2017-12-01 23:50+0000\n"
-"Last-Translator: bergmarklund <davemcgroin@gmail.com>\n"
+"PO-Revision-Date: 2018-05-07 11:42+0000\n"
+"Last-Translator: anonymous <>\n"
"Language-Team: Swedish <https://hosted.weblate.org/projects/godot-engine/"
"godot/sv/>\n"
"Language: sv\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.18-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -41,17 +43,16 @@ msgid "Anim Change Transform"
msgstr "Anim Ändra Transformation"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Keyframe Value"
-msgstr "Anim Ändra Värde"
+msgstr "Anim Ändra Värde På Tidsnyckeln"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
-msgstr "Anim Ändra Samtal"
+msgstr "Anim Ändra Anrop"
#: editor/animation_editor.cpp
msgid "Anim Add Track"
-msgstr "Lägg till spår"
+msgstr "Anim Lägg till spår"
#: editor/animation_editor.cpp
msgid "Anim Duplicate Keys"
@@ -59,11 +60,11 @@ msgstr "Anim Duplicera Nycklar"
#: editor/animation_editor.cpp
msgid "Move Anim Track Up"
-msgstr "Flytta Anim Spåra Upp"
+msgstr "Flytta Anim Spåra Uppåt"
#: editor/animation_editor.cpp
msgid "Move Anim Track Down"
-msgstr "Flytta Anim Spår Ner"
+msgstr "Flytta Anim Spår Neråt"
#: editor/animation_editor.cpp
msgid "Remove Anim Track"
@@ -83,16 +84,15 @@ msgstr "Anim Ändra Spårets Interpolation"
#: editor/animation_editor.cpp
msgid "Anim Track Change Value Mode"
-msgstr ""
+msgstr "Ändra Anim Spårets Värde Läge"
#: editor/animation_editor.cpp
msgid "Anim Track Change Wrap Mode"
msgstr ""
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Edit Node Curve"
-msgstr "Redigera Node-Kurva"
+msgstr "Redigera Nodkurva"
#: editor/animation_editor.cpp
#, fuzzy
@@ -491,7 +491,7 @@ msgstr "Skapa Funktion"
#: editor/connections_dialog.cpp
msgid "Deferred"
-msgstr ""
+msgstr "Uppskjuten"
#: editor/connections_dialog.cpp
#, fuzzy
@@ -531,8 +531,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Anslut '%s' till '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "Anslut.."
+msgid "Connect..."
+msgstr "Anslut..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -733,7 +733,7 @@ msgstr "Resurser Utan Explicit Ägande:"
#: editor/dependency_editor.cpp editor/editor_node.cpp
msgid "Orphan Resource Explorer"
-msgstr ""
+msgstr "Föräldralös Resursutforskare"
#: editor/dependency_editor.cpp
msgid "Delete selected files?"
@@ -988,7 +988,7 @@ msgstr "Ta bort Effekt"
#: editor/editor_audio_buses.cpp
msgid "Audio"
-msgstr ""
+msgstr "Ljud"
#: editor/editor_audio_buses.cpp
#, fuzzy
@@ -1022,13 +1022,13 @@ msgstr "Flytta Ljud-Buss"
#: editor/editor_audio_buses.cpp
#, fuzzy
-msgid "Save Audio Bus Layout As.."
-msgstr "Spara Ljud-Buss Layout Som.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Spara Ljud-Buss Layout Som..."
#: editor/editor_audio_buses.cpp
#, fuzzy
-msgid "Location for New Layout.."
-msgstr "Plats för Ny Layout.."
+msgid "Location for New Layout..."
+msgstr "Plats för Ny Layout..."
#: editor/editor_audio_buses.cpp
#, fuzzy
@@ -1193,12 +1193,12 @@ msgstr "Uppdaterar Scen"
#: editor/editor_data.cpp
#, fuzzy
-msgid "Storing local changes.."
-msgstr "Lagrar lokala ändringar.."
+msgid "Storing local changes..."
+msgstr "Lagrar lokala ändringar..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Uppdaterar scen.."
+msgid "Updating scene..."
+msgstr "Uppdaterar scen..."
#: editor/editor_data.cpp
#, fuzzy
@@ -1276,8 +1276,8 @@ msgid "Show In File Manager"
msgstr "Visa I Filhanteraren"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Ny Mapp.."
+msgid "New Folder..."
+msgstr "Ny Mapp..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1285,7 +1285,7 @@ msgstr "Uppdatera"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "All Recognized"
-msgstr ""
+msgstr "Alla Erkända"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "All Files (*)"
@@ -1348,7 +1348,7 @@ msgstr "Växla Läge"
#: editor/editor_file_dialog.cpp
msgid "Focus Path"
-msgstr ""
+msgstr "Fokusera på Sökväg"
#: editor/editor_file_dialog.cpp
msgid "Move Favorite Up"
@@ -1360,7 +1360,7 @@ msgstr "Flytta Favorit Ner"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Go to parent folder"
-msgstr ""
+msgstr "Gå till överordnad mapp"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
#, fuzzy
@@ -1464,11 +1464,11 @@ msgstr "Signaler:"
#: editor/editor_help.cpp
msgid "Enumerations"
-msgstr ""
+msgstr "Enumerations"
#: editor/editor_help.cpp
msgid "Enumerations:"
-msgstr ""
+msgstr "Enumerations:"
#: editor/editor_help.cpp
#, fuzzy
@@ -1570,7 +1570,7 @@ msgstr "Output:"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Projekt exporten misslyckades med följande felmeddelande %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
#, fuzzy
@@ -1578,13 +1578,13 @@ msgid "Error saving resource!"
msgstr "Fel vid sparande av resurs!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Spara Resurs Som.."
+msgid "Save Resource As..."
+msgstr "Spara Resurs Som..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "Jag förstår.."
+msgid "I see..."
+msgstr "Jag förstår..."
#: editor/editor_node.cpp
#, fuzzy
@@ -1845,13 +1845,13 @@ msgstr "Öppna Bas-Scen"
#: editor/editor_node.cpp
#, fuzzy
-msgid "Quick Open Scene.."
-msgstr "Snabböppna Scen.."
+msgid "Quick Open Scene..."
+msgstr "Snabböppna Scen..."
#: editor/editor_node.cpp
#, fuzzy
-msgid "Quick Open Script.."
-msgstr "Snabböppna Skript.."
+msgid "Quick Open Script..."
+msgstr "Snabböppna Skript..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1863,8 +1863,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Spara ändringar i '%s' innan stängning?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Spara Scen Som.."
+msgid "Save Scene As..."
+msgstr "Spara Scen Som..."
#: editor/editor_node.cpp
msgid "No"
@@ -1925,8 +1925,8 @@ msgstr "Åtgärden kan inte ångras. Återställ ändå?"
#: editor/editor_node.cpp
#, fuzzy
-msgid "Quick Run Scene.."
-msgstr "Snabbkör Scen.."
+msgid "Quick Run Scene..."
+msgstr "Snabbkör Scen..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -2102,8 +2102,8 @@ msgid "Previous tab"
msgstr "Föregående flik"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Filtrera Filer.."
+msgid "Filter Files..."
+msgstr "Filtrera Filer..."
#: editor/editor_node.cpp
#, fuzzy
@@ -2116,12 +2116,12 @@ msgstr "Ny Scen"
#: editor/editor_node.cpp
#, fuzzy
-msgid "New Inherited Scene.."
-msgstr "Ny Ärvd Scen.."
+msgid "New Inherited Scene..."
+msgstr "Ny Ärvd Scen..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Öppna Scen.."
+msgid "Open Scene..."
+msgstr "Öppna Scen..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -2141,18 +2141,18 @@ msgid "Open Recent"
msgstr "Öppna Senaste"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Konvertera Till.."
+msgid "Convert To..."
+msgstr "Konvertera Till..."
#: editor/editor_node.cpp
#, fuzzy
-msgid "MeshLibrary.."
-msgstr "MeshLibrary.."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary..."
#: editor/editor_node.cpp
#, fuzzy
-msgid "TileSet.."
-msgstr "TileSet.."
+msgid "TileSet..."
+msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2340,7 +2340,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Play"
-msgstr ""
+msgstr "Spela"
#: editor/editor_node.cpp
msgid "Pause the scene"
@@ -2366,7 +2366,7 @@ msgstr "Spela den redigerade scenen."
#: editor/editor_node.cpp
msgid "Play Scene"
-msgstr ""
+msgstr "Spela Scen"
#: editor/editor_node.cpp
msgid "Play custom scene"
@@ -2412,8 +2412,8 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Spara Som.."
+msgid "Save As..."
+msgstr "Spara Som..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2526,8 +2526,8 @@ msgstr ""
#: editor/editor_plugin.cpp
#, fuzzy
-msgid "Thumbnail.."
-msgstr "Miniatyr.."
+msgid "Thumbnail..."
+msgstr "Miniatyr..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2564,7 +2564,7 @@ msgstr ""
#: editor/editor_profiler.cpp
msgid "Frame Time (sec)"
-msgstr ""
+msgstr "Bildrutetid (sek)"
#: editor/editor_profiler.cpp
msgid "Average Time (sec)"
@@ -2686,7 +2686,7 @@ msgid "(Current)"
msgstr "(Nuvarande)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2767,7 +2767,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2786,8 +2786,8 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
-msgid "Connecting.."
-msgstr "Ansluter.."
+msgid "Connecting..."
+msgstr "Ansluter..."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -2801,7 +2801,7 @@ msgstr "Ansluten"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2951,13 +2951,13 @@ msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Rename.."
-msgstr "Byt namn.."
+msgid "Rename..."
+msgstr "Byt namn..."
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Move To.."
-msgstr "Flytta Till.."
+msgid "Move To..."
+msgstr "Flytta Till..."
#: editor/filesystem_dock.cpp
#, fuzzy
@@ -2970,17 +2970,17 @@ msgid "Instance"
msgstr "Instans"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "View Owners.."
-msgstr "Visa Ägare.."
+msgid "View Owners..."
+msgstr "Visa Ägare..."
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Duplicera"
#: editor/filesystem_dock.cpp
@@ -3007,7 +3007,7 @@ msgstr "Instansiera valda scen(er) som barn till vald Node."
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -3077,8 +3077,8 @@ msgstr "Importera Scen"
#: editor/import/resource_importer_scene.cpp
#, fuzzy
-msgid "Importing Scene.."
-msgstr "Importerar Scen.."
+msgid "Importing Scene..."
+msgstr "Importerar Scen..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -3089,7 +3089,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -3105,8 +3105,8 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "Sparar.."
+msgid "Saving..."
+msgstr "Sparar..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -3126,7 +3126,7 @@ msgid "Import As:"
msgstr "Importera Som:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3156,25 +3156,26 @@ msgstr ""
#: editor/plugins/collision_polygon_editor_plugin.cpp
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Edit Poly"
-msgstr ""
+msgstr "Redigera Polygon"
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Insert Point"
-msgstr ""
+msgstr "Infoga Punkt"
#: 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 "Redigera Polygon (ta bort punkt)"
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Remove Poly And Point"
-msgstr ""
+msgstr "Ta bort Polygon och Punkt"
#: editor/plugins/abstract_polygon_2d_editor.cpp
+#, fuzzy
msgid "Create a new polygon from scratch"
-msgstr ""
+msgstr "Skapa ny polygon från grunden"
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid ""
@@ -3186,7 +3187,7 @@ msgstr ""
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Delete points"
-msgstr ""
+msgstr "Radera punkter"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Toggle Autoplay"
@@ -3194,7 +3195,7 @@ msgstr ""
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "New Animation Name:"
-msgstr ""
+msgstr "Nytt Animationsnamn:"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "New Anim"
@@ -3202,7 +3203,7 @@ msgstr "Ny Anim"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Change Animation Name:"
-msgstr ""
+msgstr "Ändra Animationsnamn:"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Delete Animation?"
@@ -3215,11 +3216,11 @@ msgstr "Ta bort Animation"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: Invalid animation name!"
-msgstr ""
+msgstr "ERROR: Ogiltigt animationsnamn!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: Animation name already exists!"
-msgstr ""
+msgstr "ERROR: Animationsnamn finns redan!"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
@@ -3558,8 +3559,8 @@ msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
-msgid "Import Animations.."
-msgstr "Importera Animationer.."
+msgid "Import Animations..."
+msgstr "Importera Animationer..."
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
@@ -3568,8 +3569,8 @@ msgstr "Redigera Node-Filter"
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
-msgid "Filters.."
-msgstr "Filter.."
+msgid "Filters..."
+msgstr "Filter..."
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
@@ -3638,7 +3639,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3667,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"
@@ -3707,7 +3709,7 @@ msgid "Site:"
msgstr "Webbplats:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3895,6 +3897,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -4328,7 +4331,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4497,7 +4500,7 @@ msgstr ""
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
msgid "Partitioning..."
-msgstr "Partitionerar.."
+msgstr "Partitionerar..."
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
@@ -4542,7 +4545,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4914,8 +4917,8 @@ msgstr "Importera Tema"
#: editor/plugins/script_editor_plugin.cpp
#, fuzzy
-msgid "Save Theme As.."
-msgstr "Spara Tema Som.."
+msgid "Save Theme As..."
+msgstr "Spara Tema Som..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -5023,8 +5026,8 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
#, fuzzy
-msgid "Find.."
-msgstr "Hitta.."
+msgid "Find..."
+msgstr "Hitta..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -5244,15 +5247,15 @@ msgstr ""
#: editor/plugins/script_text_editor.cpp
#, fuzzy
-msgid "Replace.."
-msgstr "Ersätt.."
+msgid "Replace..."
+msgstr "Ersätt..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5733,11 +5736,7 @@ msgid "Transform"
msgstr "Transformera"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5999,8 +5998,8 @@ msgstr "Ta bort Alla"
#: editor/plugins/theme_editor_plugin.cpp
#, fuzzy
-msgid "Edit theme.."
-msgstr "Redigera tema.."
+msgid "Edit theme..."
+msgstr "Redigera tema..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -6069,8 +6068,9 @@ msgid "Options"
msgstr "Alternativ"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr ""
+#, fuzzy
+msgid "Has,Many,Options"
+msgstr "Alternativ"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -6201,7 +6201,7 @@ msgstr ""
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
msgid "Tile Set"
-msgstr "TileSet.."
+msgstr "TileSet..."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -6267,8 +6267,8 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "Lägg till.."
+msgid "Add..."
+msgstr "Lägg till..."
#: editor/project_export.cpp
msgid "Resources"
@@ -6362,6 +6362,11 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Projektnamn:"
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "Kunde inte skapa mapp."
@@ -6572,8 +6577,8 @@ msgstr "Musknapp"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6602,8 +6607,8 @@ msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
#, fuzzy
-msgid "Press a Key.."
-msgstr "Tryck på en Knapp.."
+msgid "Press a Key..."
+msgstr "Tryck på en Knapp..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6791,7 +6796,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6892,11 +6897,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "Fil.."
+msgid "File..."
+msgstr "Fil..."
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -7082,8 +7087,8 @@ msgstr ""
#: editor/scene_tree_dock.cpp
#, fuzzy
-msgid "Save New Scene As.."
-msgstr "Spara Ny Scen Som.."
+msgid "Save New Scene As..."
+msgstr "Spara Ny Scen Som..."
#: editor/scene_tree_dock.cpp
#, fuzzy
@@ -8646,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"
@@ -8681,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/ta.po b/editor/translations/ta.po
index e7269ffa0e..d7910c2c87 100644
--- a/editor/translations/ta.po
+++ b/editor/translations/ta.po
@@ -496,7 +496,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr ""
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -906,11 +906,11 @@ msgid "Move Audio Bus"
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1046,11 +1046,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1119,7 +1119,7 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr ""
#: editor/editor_file_dialog.cpp
@@ -1381,12 +1381,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1591,11 +1591,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1607,7 +1607,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr ""
#: editor/editor_node.cpp
@@ -1659,7 +1659,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1804,7 +1804,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1816,11 +1816,11 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1840,15 +1840,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2093,7 +2093,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2202,7 +2202,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2353,7 +2353,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2429,7 +2429,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2446,7 +2446,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2459,7 +2459,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2591,11 +2591,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2607,16 +2607,16 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "அசைவூடà¯à®Ÿà¯ போலிபசà¯à®šà®¾à®µà®¿à®•à®³à¯"
#: editor/filesystem_dock.cpp
@@ -2642,7 +2642,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2708,7 +2708,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2720,7 +2720,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2736,7 +2736,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2756,7 +2756,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3170,7 +3170,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3178,7 +3178,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3246,7 +3246,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3313,7 +3313,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3500,6 +3500,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3921,7 +3922,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4126,7 +4127,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4487,7 +4488,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4584,7 +4585,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4790,15 +4791,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5249,11 +5250,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5506,7 +5503,7 @@ msgid "Remove All"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5574,7 +5571,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5762,7 +5759,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5852,6 +5849,10 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr ""
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -6038,8 +6039,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6067,7 +6068,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6251,7 +6252,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6347,11 +6348,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6522,7 +6523,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
diff --git a/editor/translations/th.po b/editor/translations/th.po
index 74e2270f2c..4db8459f1b 100644
--- a/editor/translations/th.po
+++ b/editor/translations/th.po
@@ -495,8 +495,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "ลบà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¹‚ยง '%s' à¸à¸±à¸š '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "เชื่อมโยง.."
+msgid "Connect..."
+msgstr "เชื่อมโยง..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -914,12 +914,12 @@ msgid "Move Audio Bus"
msgstr "ย้าย Audio Bus"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "บันทึà¸à¹€à¸¥à¸¢à¹Œà¹€à¸­à¸²à¸•à¹Œà¸‚อง Audio Bus เป็น.."
+msgid "Save Audio Bus Layout As..."
+msgstr "บันทึà¸à¹€à¸¥à¸¢à¹Œà¹€à¸­à¸²à¸•à¹Œà¸‚อง Audio Bus เป็น..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "ตำà¹à¸«à¸™à¹ˆà¸‡à¸‚องเลย์เอาต์ใหม่.."
+msgid "Location for New Layout..."
+msgstr "ตำà¹à¸«à¸™à¹ˆà¸‡à¸‚องเลย์เอาต์ใหม่..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1054,12 +1054,12 @@ msgid "Updating Scene"
msgstr "อัพเดทฉาà¸"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "เà¸à¹‡à¸šà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸ à¸²à¸¢à¹ƒà¸™.."
+msgid "Storing local changes..."
+msgstr "เà¸à¹‡à¸šà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸ à¸²à¸¢à¹ƒà¸™..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "อัพเดทฉาà¸.."
+msgid "Updating scene..."
+msgstr "อัพเดทฉาà¸..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1127,8 +1127,8 @@ msgid "Show In File Manager"
msgstr "à¹à¸ªà¸”งในตัวจัดà¸à¸²à¸£à¹„ฟล์"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "สร้างโฟลเดอร์.."
+msgid "New Folder..."
+msgstr "สร้างโฟลเดอร์..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1391,13 +1391,13 @@ msgid "Error saving resource!"
msgstr "บันทึà¸à¸£à¸µà¸‹à¸­à¸£à¹Œà¸ªà¸œà¸´à¸”พลาด!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "บันทึà¸à¸£à¸µà¸‹à¸­à¸£à¹Œà¸ªà¹€à¸›à¹‡à¸™.."
+msgid "Save Resource As..."
+msgstr "บันทึà¸à¸£à¸µà¸‹à¸­à¸£à¹Œà¸ªà¹€à¸›à¹‡à¸™..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "ตà¸à¸¥à¸‡.."
+msgid "I see..."
+msgstr "ตà¸à¸¥à¸‡..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1616,12 +1616,12 @@ msgid "Open Base Scene"
msgstr "เปิดไฟล์ฉาà¸à¸—ี่ใช้สืบทอด"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "เปิดไฟล์ฉาà¸à¸”่วน.."
+msgid "Quick Open Scene..."
+msgstr "เปิดไฟล์ฉาà¸à¸”่วน..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "เปิดไฟล์สคริปต์ด่วน.."
+msgid "Quick Open Script..."
+msgstr "เปิดไฟล์สคริปต์ด่วน..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1632,8 +1632,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "บันทึภ'%s' à¸à¹ˆà¸­à¸™à¸›à¸´à¸”โปรà¹à¸à¸£à¸¡à¸«à¸£à¸·à¸­à¹„ม่?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "บันทึà¸à¸‰à¸²à¸à¹€à¸›à¹‡à¸™.."
+msgid "Save Scene As..."
+msgstr "บันทึà¸à¸‰à¸²à¸à¹€à¸›à¹‡à¸™..."
#: editor/editor_node.cpp
msgid "No"
@@ -1684,8 +1684,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "à¸à¸²à¸£à¸„ืนà¸à¸¥à¸±à¸šà¹„ม่สามารถยà¸à¹€à¸¥à¸´à¸à¹„ด้ คืนà¸à¸¥à¸±à¸š?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "เริ่มฉาà¸à¸”่วน.."
+msgid "Quick Run Scene..."
+msgstr "เริ่มฉาà¸à¸”่วน..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1834,8 +1834,8 @@ msgid "Previous tab"
msgstr "à¹à¸—็บà¸à¹ˆà¸­à¸™à¸«à¸™à¹‰à¸²"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "คัดà¸à¸£à¸­à¸‡à¹„ฟล์.."
+msgid "Filter Files..."
+msgstr "คัดà¸à¸£à¸­à¸‡à¹„ฟล์..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1846,12 +1846,12 @@ msgid "New Scene"
msgstr "ฉาà¸à¹ƒà¸«à¸¡à¹ˆ"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "สืบทอดฉาà¸à¹ƒà¸«à¸¡à¹ˆ.."
+msgid "New Inherited Scene..."
+msgstr "สืบทอดฉาà¸à¹ƒà¸«à¸¡à¹ˆ..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "เปิดไฟล์ฉาà¸.."
+msgid "Open Scene..."
+msgstr "เปิดไฟล์ฉาà¸..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1870,16 +1870,16 @@ msgid "Open Recent"
msgstr "เปิดไฟล์ล่าสุด"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "à¹à¸›à¸¥à¸‡à¹€à¸›à¹‡à¸™.."
+msgid "Convert To..."
+msgstr "à¹à¸›à¸¥à¸‡à¹€à¸›à¹‡à¸™..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshLibrary.."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet.."
+msgid "TileSet..."
+msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2130,8 +2130,8 @@ msgid "Save the currently edited resource."
msgstr "บันทึà¸à¸£à¸µà¸‹à¸­à¸£à¹Œà¸ªà¸—ี่à¸à¸³à¸¥à¸±à¸‡à¸›à¸£à¸±à¸šà¹à¸•à¹ˆà¸‡"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "บันทึà¸à¹€à¸›à¹‡à¸™.."
+msgid "Save As..."
+msgstr "บันทึà¸à¹€à¸›à¹‡à¸™..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2239,8 +2239,8 @@ msgid "Creating Mesh Previews"
msgstr "à¸à¸³à¸¥à¸±à¸‡à¸ªà¸£à¹‰à¸²à¸‡à¸ à¸²à¸žà¸•à¸±à¸§à¸­à¸¢à¹ˆà¸²à¸‡ Mesh"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "รูปตัวอย่าง.."
+msgid "Thumbnail..."
+msgstr "รูปตัวอย่าง..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2392,8 +2392,8 @@ msgid "(Current)"
msgstr "(ปัจจุบัน)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "à¸à¸³à¸¥à¸±à¸‡à¹€à¸£à¸µà¸¢à¸à¸‚้อมูล โปรดรอ.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "à¸à¸³à¸¥à¸±à¸‡à¹€à¸£à¸µà¸¢à¸à¸‚้อมูล โปรดรอ..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2468,8 +2468,8 @@ msgid "Error requesting url: "
msgstr "ผิดพลาดขณะร้องขอที่อยู่: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "à¸à¸³à¸¥à¸±à¸‡à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­.."
+msgid "Connecting to Mirror..."
+msgstr "à¸à¸³à¸¥à¸±à¸‡à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2477,7 +2477,7 @@ msgstr "à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸ªà¸´à¹‰à¸™à¸ªà¸¸à¸”"
#: editor/export_template_manager.cpp
msgid "Resolving"
-msgstr "à¸à¸³à¸¥à¸±à¸‡à¸„้นหา.."
+msgstr "à¸à¸³à¸¥à¸±à¸‡à¸„้นหา..."
#: editor/export_template_manager.cpp
msgid "Can't Resolve"
@@ -2485,8 +2485,8 @@ msgstr "ค้นหาไม่สำเร็จ"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "à¸à¸³à¸¥à¸±à¸‡à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­.."
+msgid "Connecting..."
+msgstr "à¸à¸³à¸¥à¸±à¸‡à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2498,8 +2498,8 @@ msgstr "เชื่อมต่อà¹à¸¥à¹‰à¸§"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "à¸à¸³à¸¥à¸±à¸‡à¸£à¹‰à¸­à¸‡à¸‚อ.."
+msgid "Requesting..."
+msgstr "à¸à¸³à¸¥à¸±à¸‡à¸£à¹‰à¸­à¸‡à¸‚อ..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2630,12 +2630,12 @@ msgid "Collapse all"
msgstr "ยุบโฟลเดอร์"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "เปลี่ยนชื่อ.."
+msgid "Rename..."
+msgstr "เปลี่ยนชื่อ..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "ย้ายไป.."
+msgid "Move To..."
+msgstr "ย้ายไป..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2646,16 +2646,16 @@ msgid "Instance"
msgstr "อินสà¹à¸•à¸™à¸‹à¹Œ"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "à¹à¸à¹‰à¹„ขà¸à¸²à¸£à¸­à¹‰à¸²à¸‡à¸­à¸´à¸‡.."
+msgid "Edit Dependencies..."
+msgstr "à¹à¸à¹‰à¹„ขà¸à¸²à¸£à¸­à¹‰à¸²à¸‡à¸­à¸´à¸‡..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "ดูเจ้าของ.."
+msgid "View Owners..."
+msgstr "ดูเจ้าของ..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "ทำซ้ำ.."
+msgid "Duplicate..."
+msgstr "ทำซ้ำ..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2680,10 +2680,10 @@ msgstr "อินสà¹à¸•à¸™à¸‹à¹Œà¸‰à¸²à¸à¸—ี่เลือà¸à¹ƒà¸«à¹‰à¹€
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"à¸à¸³à¸¥à¸±à¸‡à¸ªà¹à¸à¸™à¹„ฟล์,\n"
-"à¸à¸£à¸¸à¸“ารอ.."
+"à¸à¸£à¸¸à¸“ารอ..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2748,8 +2748,8 @@ msgid "Import Scene"
msgstr "นำเข้าฉาà¸"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "à¸à¸³à¸¥à¸±à¸‡à¸™à¸³à¹€à¸‚้าฉาà¸.."
+msgid "Importing Scene..."
+msgstr "à¸à¸³à¸¥à¸±à¸‡à¸™à¸³à¹€à¸‚้าฉาà¸..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2760,8 +2760,8 @@ msgid "Generating for Mesh: "
msgstr "สร้างสำหรับพื้นผิว: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "à¸à¸³à¸¥à¸±à¸‡à¸£à¸±à¸™à¸ªà¸„ริปต์.."
+msgid "Running Custom Script..."
+msgstr "à¸à¸³à¸¥à¸±à¸‡à¸£à¸±à¸™à¸ªà¸„ริปต์..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2776,8 +2776,8 @@ msgid "Error running post-import script:"
msgstr "ผิดพลาดขณะรันสคริปต์หลังนำเข้า:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "à¸à¸³à¸¥à¸±à¸‡à¸šà¸±à¸™à¸—ึà¸.."
+msgid "Saving..."
+msgstr "à¸à¸³à¸¥à¸±à¸‡à¸šà¸±à¸™à¸—ึà¸..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2796,8 +2796,8 @@ msgid "Import As:"
msgstr "นำเข้าเป็น:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "à¹à¸šà¸š.."
+msgid "Preset..."
+msgstr "à¹à¸šà¸š..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3214,16 +3214,16 @@ msgid "Transition Node"
msgstr "โหนดทรานสิชัน"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "นำเข้าà¹à¸­à¸™à¸´à¹€à¸¡à¸Šà¸±à¸™.."
+msgid "Import Animations..."
+msgstr "นำเข้าà¹à¸­à¸™à¸´à¹€à¸¡à¸Šà¸±à¸™..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "à¹à¸à¹‰à¹„ขตัวà¸à¸£à¸­à¸‡à¹‚หนด"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "ตัวà¸à¸£à¸­à¸‡.."
+msgid "Filters..."
+msgstr "ตัวà¸à¸£à¸­à¸‡..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3290,8 +3290,8 @@ msgid "Fetching:"
msgstr "à¸à¸³à¸¥à¸±à¸‡à¸£à¸±à¸šà¸‚้อมูล:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "à¸à¸³à¸¥à¸±à¸‡à¸„้นหา.."
+msgid "Resolving..."
+msgstr "à¸à¸³à¸¥à¸±à¸‡à¸„้นหา..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3357,8 +3357,8 @@ msgid "Site:"
msgstr "ไซต์:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "à¸à¸²à¸£à¸ªà¸™à¸±à¸šà¸ªà¸™à¸¸à¸™.."
+msgid "Support..."
+msgstr "à¸à¸²à¸£à¸ªà¸™à¸±à¸šà¸ªà¸™à¸¸à¸™..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3549,6 +3549,7 @@ msgid "Use Rotation Snap"
msgstr "จำà¸à¸±à¸”à¸à¸²à¸£à¸«à¸¡à¸¸à¸™"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "ตั้งค่าà¸à¸²à¸£à¸ˆà¸³à¸à¸±à¸”..."
@@ -3976,8 +3977,8 @@ msgid "Create Convex Collision Sibling"
msgstr "สร้างรูปทรงตันà¸à¸²à¸¢à¸ à¸²à¸žà¹€à¸›à¹‡à¸™à¹‚หนดà¸à¸²à¸•à¸´"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "สร้างเส้นขอบ Mesh.."
+msgid "Create Outline Mesh..."
+msgstr "สร้างเส้นขอบ Mesh..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4181,8 +4182,8 @@ msgid "Error loading image:"
msgstr "ผิดพลาดขณะโหลดรูป:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "รูปไม่มีพิà¸à¹€à¸‹à¸¥à¹ƒà¸”ที่ความโปร่งà¹à¸ªà¸‡ > 128 .."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "รูปไม่มีพิà¸à¹€à¸‹à¸¥à¹ƒà¸”ที่ความโปร่งà¹à¸ªà¸‡ > 128 ..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4542,7 +4543,7 @@ msgid "Import Theme"
msgstr "นำเข้าธีม"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr "บันทึà¸à¸˜à¸µà¸¡à¹€à¸›à¹‡à¸™"
#: editor/plugins/script_editor_plugin.cpp
@@ -4639,8 +4640,8 @@ msgstr "เปิด/ปิดà¹à¸œà¸‡à¸ªà¸„ริปต์"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "ค้นหา.."
+msgid "Find..."
+msgstr "ค้นหา..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4847,16 +4848,16 @@ msgid "Find Previous"
msgstr "ค้นหาà¸à¹ˆà¸­à¸™à¸«à¸™à¹‰à¸²"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "à¹à¸—นที่.."
+msgid "Replace..."
+msgstr "à¹à¸—นที่..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "ไปยังฟังà¸à¹Œà¸Šà¸±à¸™.."
+msgid "Goto Function..."
+msgstr "ไปยังฟังà¸à¹Œà¸Šà¸±à¸™..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "ไปยังบรรทัด.."
+msgid "Goto Line..."
+msgstr "ไปยังบรรทัด..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5309,12 +5310,8 @@ msgid "Transform"
msgstr "เคลื่อนย้าย"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "ตั้งค่าà¸à¸²à¸£à¸ˆà¸³à¸à¸±à¸”.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "เครื่องมือเคลื่อนย้าย.."
+msgid "Transform Dialog..."
+msgstr "เครื่องมือเคลื่อนย้าย..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5566,8 +5563,8 @@ msgid "Remove All"
msgstr "ลบทั้งหมด"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "à¹à¸à¹‰à¹„ขธีม.."
+msgid "Edit theme..."
+msgstr "à¹à¸à¹‰à¹„ขธีม..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5636,7 +5633,8 @@ msgid "Options"
msgstr "ตัวเลือà¸"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+#, fuzzy
+msgid "Has,Many,Options"
msgstr "มี,มาà¸à¸¡à¸²à¸¢,หลาย,ตัวเลือà¸!"
#: editor/plugins/theme_editor_plugin.cpp
@@ -5826,8 +5824,8 @@ msgid "Presets"
msgstr "à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸­à¸­à¸"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "เพิ่ม.."
+msgid "Add..."
+msgstr "เพิ่ม..."
#: editor/project_export.cpp
msgid "Resources"
@@ -5916,6 +5914,11 @@ msgid "Imported Project"
msgstr "นำเข้าโปรเจà¸à¸•à¹Œà¹à¸¥à¹‰à¸§"
#: editor/project_manager.cpp
+#, fuzzy
+msgid "Invalid Project Name."
+msgstr "ชื่อโปรเจà¸à¸•à¹Œ:"
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "ไม่สามารถสร้างโฟลเดอร์"
@@ -6111,8 +6114,8 @@ msgstr "ปุ่มเมาส์"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6140,8 +6143,8 @@ msgid "Control+"
msgstr "Control+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "à¸à¸”ปุ่ม.."
+msgid "Press a Key..."
+msgstr "à¸à¸”ปุ่ม..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6324,8 +6327,8 @@ msgid "Property:"
msgstr "คุณสมบัติ:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
-msgstr "à¸à¸³à¸«à¸™à¸”เฉพาะ.."
+msgid "Override For..."
+msgstr "à¸à¸³à¸«à¸™à¸”เฉพาะ..."
#: editor/project_settings_editor.cpp
msgid "Input Map"
@@ -6420,12 +6423,12 @@ msgid "Easing Out-In"
msgstr "ออà¸-เข้านุ่มนวล"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "ไฟล์.."
+msgid "File..."
+msgstr "ไฟล์..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "โฟลเดอร์.."
+msgid "Dir..."
+msgstr "โฟลเดอร์..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6595,8 +6598,8 @@ msgid "This operation can't be done on instanced scenes."
msgstr "ทำà¸à¸±à¸šà¸‰à¸²à¸à¸—ี่เป็นอินสà¹à¸•à¸™à¸‹à¹Œà¹„ม่ได้"
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "บันทึà¸à¸‰à¸²à¸à¹ƒà¸«à¸¡à¹ˆà¹€à¸›à¹‡à¸™.."
+msgid "Save New Scene As..."
+msgstr "บันทึà¸à¸‰à¸²à¸à¹ƒà¸«à¸¡à¹ˆà¹€à¸›à¹‡à¸™..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
@@ -8074,6 +8077,13 @@ msgstr "ผิดพลาดขณะโหลดฟอนต์"
msgid "Invalid font size."
msgstr "ขนาดฟอนต์ผิดพลาด"
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "à¹à¸—็บà¸à¹ˆà¸­à¸™à¸«à¸™à¹‰à¸²"
+
+#~ msgid "Next"
+#~ msgstr "ต่อไป"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "ใช้ชื่อนี้ไม่ได้ (มี '/' หรือ ':')"
@@ -8097,9 +8107,6 @@ msgstr "ขนาดฟอนต์ผิดพลาด"
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "ไม่พบไฟล์ project.godot"
-#~ msgid "Next"
-#~ msgstr "ต่อไป"
-
#~ msgid "Not found!"
#~ msgstr "ไม่พบ!"
@@ -8242,8 +8249,8 @@ msgstr "ขนาดฟอนต์ผิดพลาด"
#~ msgid "Exporting for %s"
#~ msgstr "ส่งออà¸à¸ªà¸³à¸«à¸£à¸±à¸š %s"
-#~ msgid "Setting Up.."
-#~ msgstr "à¸à¸³à¸¥à¸±à¸‡à¸•à¸±à¹‰à¸‡à¸„่า.."
+#~ msgid "Setting Up..."
+#~ msgstr "à¸à¸³à¸¥à¸±à¸‡à¸•à¸±à¹‰à¸‡à¸„่า..."
#~ msgid "Error loading scene."
#~ msgstr "ผิดพลาดขณะโหลดฉาà¸"
@@ -8303,8 +8310,8 @@ msgstr "ขนาดฟอนต์ผิดพลาด"
#~ msgid "Info"
#~ msgstr "ข้อมูล"
-#~ msgid "Re-Import.."
-#~ msgstr "นำเข้าอีà¸à¸„รั้ง.."
+#~ msgid "Re-Import..."
+#~ msgstr "นำเข้าอีà¸à¸„รั้ง..."
#~ msgid "No bit masks to import!"
#~ msgstr "ไม่มีบิตà¹à¸¡à¸ªà¸à¹Œà¹ƒà¸«à¹‰à¸™à¸³à¹€à¸‚้า!"
@@ -8684,14 +8691,14 @@ msgstr "ขนาดฟอนต์ผิดพลาด"
#~ msgid "Zoom (%):"
#~ msgstr "ซูม (%):"
-#~ msgid "Skeleton.."
-#~ msgstr "โครงà¸à¸£à¸°à¸”ูà¸.."
+#~ msgid "Skeleton..."
+#~ msgstr "โครงà¸à¸£à¸°à¸”ูà¸..."
#~ msgid "Zoom Reset"
#~ msgstr "รีเซ็ตà¸à¸²à¸£à¸‹à¸¹à¸¡"
-#~ msgid "Zoom Set.."
-#~ msgstr "ตั้งค่าà¸à¸²à¸£à¸‹à¸¹à¸¡.."
+#~ msgid "Zoom Set..."
+#~ msgstr "ตั้งค่าà¸à¸²à¸£à¸‹à¸¹à¸¡..."
#~ msgid "Set a Value"
#~ msgstr "เซ็ตค่า"
@@ -9105,8 +9112,8 @@ msgstr "ขนาดฟอนต์ผิดพลาด"
#~ msgid "Export Project PCK"
#~ msgstr "ส่งออภPCK โปรเจà¸à¸•à¹Œ"
-#~ msgid "Export.."
-#~ msgstr "ส่งออà¸.."
+#~ msgid "Export..."
+#~ msgstr "ส่งออà¸..."
#~ msgid "Project Export"
#~ msgstr "ส่งออà¸à¹‚ปรเจà¸à¸•à¹Œ"
diff --git a/editor/translations/tr.po b/editor/translations/tr.po
index 5e4a18ce28..292cec4063 100644
--- a/editor/translations/tr.po
+++ b/editor/translations/tr.po
@@ -2,32 +2,33 @@
# 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.
# Fatih Mert DoÄŸancan <fatihmertdogancan@hotmail.com>, 2017.
# hubbyist <hub@legrud.net>, 2017.
# H.Hüseyin CİHANGİR <hashusfb@gmail.com>, 2018.
+# Kaan Gül <qaantum@hotmail.com>, 2018.
# M. Yavuz Uzun <myavuzuzun@yandex.com>, 2016.
+# monolifed <monolifed@gmail.com>, 2018.
# Orkun Turan <holygatestudio@yandex.com>, 2016-2017.
# 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-04-19 12:41+0000\n"
-"Last-Translator: H.Hüseyin CİHANGİR <hashusfb@gmail.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-dev\n"
+"X-Generator: Weblate 3.0.1-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -509,7 +510,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Şunun: '%s' şununla: '%s' bağlantısını kes"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "BaÄŸlan..."
#: editor/connections_dialog.cpp
@@ -929,12 +930,12 @@ msgid "Move Audio Bus"
msgstr "Audio Bus'ı Taşı"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Audio Bus Yerleşim Düzenini Farklı Kaydet.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Audio Bus Yerleşim Düzenini Farklı Kaydet..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Yeni Yerleşim Düzeni için Konum.."
+msgid "Location for New Layout..."
+msgstr "Yeni Yerleşim Düzeni için Konum..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -1069,12 +1070,12 @@ msgid "Updating Scene"
msgstr "Sahne Güncelleniyor"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Yerel değişiklikler kayıt ediliyor.."
+msgid "Storing local changes..."
+msgstr "Yerel değişiklikler kayıt ediliyor..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "Sahne güncelleniyor.."
+msgid "Updating scene..."
+msgstr "Sahne güncelleniyor..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1142,8 +1143,8 @@ msgid "Show In File Manager"
msgstr "Dosya Yöneticisinde Göster"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Yeni Klasör.."
+msgid "New Folder..."
+msgstr "Yeni Klasör..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1404,20 +1405,20 @@ msgstr "Çıktıyı Temizle"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Proje dışa aktarımı %d hata koduyla başarısız."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Kaynak kaydedilirken hata!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Kaynağı Farklı Kaydet.."
+msgid "Save Resource As..."
+msgstr "Kaynağı Farklı Kaydet..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "Anlıyorum.."
+msgid "I see..."
+msgstr "Anlıyorum..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1648,12 +1649,12 @@ msgid "Open Base Scene"
msgstr "Ana Sahneyi Aç"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Sahneyi Hızlı Aç.."
+msgid "Quick Open Scene..."
+msgstr "Sahneyi Hızlı Aç..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Betiği Hızlı Aç.."
+msgid "Quick Open Script..."
+msgstr "Betiği Hızlı Aç..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1664,8 +1665,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "Kapatmadan önce değişklikler buraya '%s' kaydedilsin mi?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Sahneyi Farklı Kaydet.."
+msgid "Save Scene As..."
+msgstr "Sahneyi Farklı Kaydet..."
#: editor/editor_node.cpp
msgid "No"
@@ -1716,8 +1717,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Bu eylem geri alınamaz. Yine de geri dönsün mü?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Sahneyi Hızlı Çalıştır.."
+msgid "Quick Run Scene..."
+msgstr "Sahneyi Hızlı Çalıştır..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1872,8 +1873,8 @@ msgid "Previous tab"
msgstr "Önceki sekme"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "Dosyaları Süz.."
+msgid "Filter Files..."
+msgstr "Dosyaları Süz..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1884,12 +1885,12 @@ msgid "New Scene"
msgstr "Yeni Sahne"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Yeni Miras Alınmış Sahne .."
+msgid "New Inherited Scene..."
+msgstr "Yeni Miras Alınmış Sahne ..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Sahne Aç.."
+msgid "Open Scene..."
+msgstr "Sahne Aç..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1908,16 +1909,16 @@ msgid "Open Recent"
msgstr "En Sonuncuyu Aç"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Şuna Dönüştür.."
+msgid "Convert To..."
+msgstr "Şuna Dönüştür..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshLibrary .."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary ..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet .."
+msgid "TileSet..."
+msgstr "TileSet ..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -1966,9 +1967,8 @@ msgid "Debug"
msgstr "Hata Ayıklama"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Deploy with Remote Debug"
-msgstr "Uzaktan Hata Ayıklama ile Aç"
+msgstr "Uzaktan Hata Ayıklama ile Dağıt"
#: editor/editor_node.cpp
msgid ""
@@ -2181,8 +2181,8 @@ msgid "Save the currently edited resource."
msgstr "Düzenlenen kaynağı kaydedin."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "Farklı Kaydet.."
+msgid "Save As..."
+msgstr "Farklı Kaydet..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2290,8 +2290,8 @@ msgid "Creating Mesh Previews"
msgstr "Mesh Önizlemeleri Oluşturuluyor"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Küçük Resim.."
+msgid "Thumbnail..."
+msgstr "Küçük Resim..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2443,8 +2443,8 @@ msgid "(Current)"
msgstr "(Åžuanki)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "Aynalar alınıyor, lütfen bekleyin.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "Aynalar alınıyor, lütfen bekleyin..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2521,8 +2521,8 @@ msgid "Error requesting url: "
msgstr "Url isteği hatası: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "Aynaya bağlanılıyor.."
+msgid "Connecting to Mirror..."
+msgstr "Aynaya bağlanılıyor..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2538,8 +2538,8 @@ msgstr "Çözümlenemedi"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "Bağlanılıyor.."
+msgid "Connecting..."
+msgstr "Bağlanılıyor..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2551,8 +2551,8 @@ msgstr "Bağlı"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
-msgstr "Ä°steniyor.."
+msgid "Requesting..."
+msgstr "Ä°steniyor..."
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2687,12 +2687,12 @@ msgid "Collapse all"
msgstr "Hepsini daralt"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
-msgstr "Yeniden Adlandır.."
+msgid "Rename..."
+msgstr "Yeniden Adlandır..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "Şuraya Taşı.."
+msgid "Move To..."
+msgstr "Şuraya Taşı..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2703,16 +2703,16 @@ msgid "Instance"
msgstr "Örnek"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Bağımlılıkları Düzenle.."
+msgid "Edit Dependencies..."
+msgstr "Bağımlılıkları Düzenle..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Sahipleri Görüntüle.."
+msgid "View Owners..."
+msgstr "Sahipleri Görüntüle..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Çoğalt.."
+msgid "Duplicate..."
+msgstr "Çoğalt..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2737,10 +2737,10 @@ msgstr "Seçilen sahneyi/sahneleri seçilen düğüme çocuk olarak örneklendir
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Dosyalar Taranıyor,\n"
-"Lütfen Bekleyiniz.."
+"Lütfen Bekleyiniz..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2805,7 +2805,7 @@ msgid "Import Scene"
msgstr "Sahneyi İçe Aktar"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr "Sahneyi İçe Aktarıyor..."
#: editor/import/resource_importer_scene.cpp
@@ -2817,8 +2817,8 @@ msgid "Generating for Mesh: "
msgstr "Örüntü için Üretiliyor: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "Çalışan Özel Betik.."
+msgid "Running Custom Script..."
+msgstr "Çalışan Özel Betik..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2835,7 +2835,7 @@ msgid "Error running post-import script:"
msgstr "sonradan-içe aktarılmış betik çalıştırılırken hata:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr "Kaydediliyor..."
#: editor/import_dock.cpp
@@ -2855,8 +2855,8 @@ msgid "Import As:"
msgstr "Şu Şekilde İçe Aktar:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Ön ayar.."
+msgid "Preset..."
+msgstr "Ön ayar..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3273,15 +3273,15 @@ msgid "Transition Node"
msgstr "Geçiş Düğümü"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Animasyonları İçe Aktar.."
+msgid "Import Animations..."
+msgstr "Animasyonları İçe Aktar..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "Düğüm Süzgeçlerini Düzenle"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr "Süzgeçler..."
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3349,7 +3349,7 @@ msgid "Fetching:"
msgstr "Alınıyor:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr "Çözümleniyor..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3416,7 +3416,7 @@ msgid "Site:"
msgstr "Yer:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr "Destek..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3614,6 +3614,7 @@ msgid "Use Rotation Snap"
msgstr "Döndürme Yapışması Kullan"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "Yapışmayı Yapılandır..."
@@ -3710,14 +3711,12 @@ msgid "Show Guides"
msgstr "Kılavuzları göster"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "Başlatım Görünümü"
+msgstr "Başlatımı Göster"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 Görüntükapısı"
+msgstr "Görüntükapısını Göster"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -3773,7 +3772,7 @@ msgstr "Ekle %s"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Adding %s..."
-msgstr "Ekliyor %s.."
+msgstr "Ekliyor %s..."
#: editor/plugins/canvas_item_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Ok"
@@ -4010,7 +4009,7 @@ msgstr "Örüntü anahat oluşturmak için bir yüzeye sahip değil!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "Örüntü ilkel türü PRIMITIVE_TRIANGLES değil!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4041,8 +4040,8 @@ msgid "Create Convex Collision Sibling"
msgstr "Dışbükey Çarpışma Kardeşi Oluştur"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "Anahat Örüntüsü Oluştur.."
+msgid "Create Outline Mesh..."
+msgstr "Anahat Örüntüsü Oluştur..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4246,8 +4245,8 @@ msgid "Error loading image:"
msgstr "Resim yüklenirken hata:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Saydamlığı olan nokta yok > 128 bedizde.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Saydamlığı olan nokta yok > 128 bedizde..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4607,8 +4606,8 @@ msgid "Import Theme"
msgstr "Kalıbı İçe Aktar"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "Temayı Farklı Kaydet.."
+msgid "Save Theme As..."
+msgstr "Temayı Farklı Kaydet..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4704,8 +4703,8 @@ msgstr "Betikler Panelini Aç/Kapa"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Bul.."
+msgid "Find..."
+msgstr "Bul..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4914,16 +4913,16 @@ msgid "Find Previous"
msgstr "Öncekini Bul"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "DeÄŸiÅŸtir.."
+msgid "Replace..."
+msgstr "DeÄŸiÅŸtir..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Ä°ÅŸleve Git.."
+msgid "Goto Function..."
+msgstr "Ä°ÅŸleve Git..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Dizeye Git.."
+msgid "Goto Line..."
+msgstr "Dizeye Git..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5376,12 +5375,8 @@ msgid "Transform"
msgstr "Dönüşüm"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Yapışmayı Yapılandır.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "Dönüştürme İletişim Kutusu.."
+msgid "Transform Dialog..."
+msgstr "Dönüştürme İletişim Kutusu..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5633,8 +5628,8 @@ msgid "Remove All"
msgstr "Tümünü Kaldır"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "Tema düzenle.."
+msgid "Edit theme..."
+msgstr "Tema düzenle..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5681,14 +5676,12 @@ msgid "Checked Item"
msgstr "Denetlenen Öğe"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "Öğe Ekle"
+msgstr "Radyo Ögesi"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "Denetlenen Öğe"
+msgstr "Seçili Radyo Ögesi"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5703,7 +5696,8 @@ msgid "Options"
msgstr "Seçenekler"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+#, fuzzy
+msgid "Has,Many,Options"
msgstr "Bir Çok,Seçenek,Var!"
#: editor/plugins/theme_editor_plugin.cpp
@@ -5895,8 +5889,8 @@ msgid "Presets"
msgstr "Önayarlar"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "Ekle.."
+msgid "Add..."
+msgstr "Ekle..."
#: editor/project_export.cpp
msgid "Resources"
@@ -5989,6 +5983,10 @@ msgid "Imported Project"
msgstr "İçe Aktarılan Proje"
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "Geçersiz Proje Adı."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "Klasör oluşturulamadı."
@@ -6188,9 +6186,10 @@ msgstr "Fare Düğmesi"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"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!"
@@ -6217,8 +6216,8 @@ msgid "Control+"
msgstr "Denetim+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "Bir Dokunaca Basın.."
+msgid "Press a Key..."
+msgstr "Bir Dokunaca Basın..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6401,8 +6400,8 @@ msgid "Property:"
msgstr "Özellik:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
-msgstr "Åžunun Ãœzerine Yaz.."
+msgid "Override For..."
+msgstr "Åžunun Ãœzerine Yaz..."
#: editor/project_settings_editor.cpp
msgid "Input Map"
@@ -6497,12 +6496,12 @@ msgid "Easing Out-In"
msgstr "Kararma Açılma"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "Dosya.."
+msgid "File..."
+msgstr "Dosya..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "Diz.."
+msgid "Dir..."
+msgstr "Diz..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6674,8 +6673,8 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Bu işlem örneklenmiş sahnelerde yapılamaz."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "Yeni Sahneyi Farklı Kaydet .."
+msgid "Save New Scene As..."
+msgstr "Yeni Sahneyi Farklı Kaydet ..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
@@ -7389,11 +7388,11 @@ msgstr "Uzaklık Seç:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "Sınıf ismi ayrılmış anahtar kelime olamaz"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
-msgstr "solü oluşturuluyor..."
+msgstr "Çözüm oluşturuluyor..."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating C# project..."
@@ -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"
@@ -7442,7 +7440,7 @@ msgstr "Uyarılar"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "İç özel durum yığını izlemesinin sonu"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -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ı!"
@@ -8380,7 +8383,7 @@ msgstr "Geçersiz yazıtipi boyutu."
#~ msgid "Exporting for %s"
#~ msgstr "%s için Dışa Aktarım"
-#~ msgid "Setting Up.."
+#~ msgid "Setting Up..."
#~ msgstr "Kurulum..."
#~ msgid "Error loading scene."
@@ -8435,8 +8438,8 @@ msgstr "Geçersiz yazıtipi boyutu."
#~ msgid "Info"
#~ msgstr "Bilgi"
-#~ msgid "Re-Import.."
-#~ msgstr "Yeniden İçe Aktar.."
+#~ msgid "Re-Import..."
+#~ msgstr "Yeniden İçe Aktar..."
#~ msgid "No bit masks to import!"
#~ msgstr "Alınacak hiç bit örteci yok!"
@@ -8832,14 +8835,14 @@ msgstr "Geçersiz yazıtipi boyutu."
#~ msgid "Zoom (%):"
#~ msgstr "YaklaÅŸ (%):"
-#~ msgid "Skeleton.."
-#~ msgstr "Ä°skelet.."
+#~ msgid "Skeleton..."
+#~ msgstr "Ä°skelet..."
#~ msgid "Zoom Reset"
#~ msgstr "Yakınlaşmayı Sıfırla"
-#~ msgid "Zoom Set.."
-#~ msgstr "Yakınlaşmayı Ayarla.."
+#~ msgid "Zoom Set..."
+#~ msgstr "Yakınlaşmayı Ayarla..."
#~ msgid "Set a Value"
#~ msgstr "Bir DeÄŸer Ata"
@@ -8980,7 +8983,7 @@ msgstr "Geçersiz yazıtipi boyutu."
#~ "Download and install export templates."
#~ msgstr ""
#~ "Hiçbir dışa aktarım kalıbı bulunamadı.\n"
-#~ "Dışa aktarım kalıplarını indirin ve yükleyin.."
+#~ "Dışa aktarım kalıplarını indirin ve yükleyin..."
#~ msgid "Custom debug package not found."
#~ msgstr "Özel kusur ayıklama çıkını bulunmadı."
@@ -9303,8 +9306,8 @@ msgstr "Geçersiz yazıtipi boyutu."
#~ msgid "Export Project PCK"
#~ msgstr "Tasarı PCK Dışa Aktar"
-#~ msgid "Export.."
-#~ msgstr "Dışa Aktar.."
+#~ msgid "Export..."
+#~ msgstr "Dışa Aktar..."
#~ msgid "Project Export"
#~ msgstr "Tasarı Dışa Aktar"
diff --git a/editor/translations/uk.po b/editor/translations/uk.po
index 45138cd5de..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,11 +9,10 @@
# МакÑим Якимчук <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-04-20 18:42+0000\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"
@@ -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"
@@ -345,7 +343,7 @@ msgstr "ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð²ÑÑ–Ñ… анімації"
#: editor/animation_editor.cpp
msgid "Clean-Up Animation(s) (NO UNDO!)"
-msgstr "ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð°Ð½Ñ–Ð¼Ð°Ñ†Ñ–Ñ—(Ñ–) (не ÑкаÑувати!)"
+msgstr "ОчиÑтити анімацію(Ñ—) (ÐЕ СКÐСУВÐТИ!)"
#: editor/animation_editor.cpp
msgid "Clean-Up"
@@ -492,7 +490,7 @@ msgstr "З'єднати"
#: editor/connections_dialog.cpp
msgid "Connect '%s' to '%s'"
-msgstr "З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ '%s' Ð´Ð»Ñ %s'"
+msgstr "Приєднати '%s' до %s'"
#: editor/connections_dialog.cpp
msgid "Connecting Signal:"
@@ -503,8 +501,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "Від'єднати '%s' від '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "Приєднати.."
+msgid "Connect..."
+msgstr "Приєднати..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -666,7 +664,7 @@ msgstr "Помилки завантаженнÑ!"
#: editor/dependency_editor.cpp
msgid "Permanently delete %d item(s)? (No undo!)"
-msgstr "ОÑтаточно вилучити %d об'єкти (неможливо ÑкаÑувати)"
+msgstr "ОÑтаточно вилучити %d об'єкт(и)? (Ðеможливо ÑкаÑувати)"
#: editor/dependency_editor.cpp
msgid "Owns"
@@ -706,7 +704,7 @@ msgstr "СпаÑибі від Ñпільноти Godot!"
#: editor/editor_about.cpp
msgid "Thanks!"
-msgstr "ДÑкую!"
+msgstr "ПодÑка!"
#: editor/editor_about.cpp
msgid "Godot Engine contributors"
@@ -776,7 +774,7 @@ msgid ""
"respective copyright statements and license terms."
msgstr ""
"Рушій Godot ÑпираєтьÑÑ Ð½Ð° Ñ€Ñд Ñторонніх безкоштовних Ñ– відкритих бібліотек, "
-"ÑуміÑних з умовами ліцензії mit. Ðижче наводитьÑÑ Ð²Ð¸Ñ‡ÐµÑ€Ð¿Ð½Ð¸Ð¹ ÑпиÑок вÑÑ–Ñ… "
+"ÑуміÑних з умовами ліцензії MIT. Ðижче наводитьÑÑ Ð²Ð¸Ñ‡ÐµÑ€Ð¿Ð½Ð¸Ð¹ ÑпиÑок вÑÑ–Ñ… "
"таких Ñторонніх компонентів з відповідними заÑвами авторÑьких прав Ñ– умов "
"ліцензійної угоди."
@@ -824,7 +822,7 @@ msgstr "Динаміки"
#: editor/editor_audio_buses.cpp
msgid "Add Effect"
-msgstr "Додати ефект"
+msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ ÐµÑ„ÐµÐºÑ‚Ñƒ"
#: editor/editor_audio_buses.cpp
msgid "Rename Audio Bus"
@@ -852,7 +850,7 @@ msgstr "Вибір передачі аудіо шини"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus Effect"
-msgstr "Додати ефект аудіо шини"
+msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ ÐµÑ„ÐµÐºÑ‚Ñƒ аудіо шини"
#: editor/editor_audio_buses.cpp
msgid "Move Bus Effect"
@@ -897,11 +895,11 @@ msgstr "Видалити ефект"
#: editor/editor_audio_buses.cpp
msgid "Audio"
-msgstr "Звук"
+msgstr "Ðудіо"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus"
-msgstr "Додати аудіо шину"
+msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð°ÑƒÐ´Ñ–Ð¾ шини"
#: editor/editor_audio_buses.cpp
msgid "Master bus can't be deleted!"
@@ -924,16 +922,16 @@ msgid "Move Audio Bus"
msgstr "ПереміÑтити аудіо шину"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "Зберегти макет аудіо шини Ñк.."
+msgid "Save Audio Bus Layout As..."
+msgstr "Зберегти ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð°ÑƒÐ´Ñ–Ð¾ шини Ñк..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "Ð Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ макета..."
+msgid "Location for New Layout..."
+msgstr "Ð Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ компонуваннÑ..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
-msgstr "Відкрити макет аудіо шини"
+msgstr "Відкрити ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð°ÑƒÐ´Ñ–Ð¾ шини"
#: editor/editor_audio_buses.cpp
msgid "There is no 'res://default_bus_layout.tres' file."
@@ -941,7 +939,7 @@ msgstr "Файл 'res: //default_bus_layout.tres' не знайдено."
#: editor/editor_audio_buses.cpp
msgid "Invalid file, not an audio bus layout."
-msgstr "ÐеприпуÑтимий файл, це не макет аудіо-шини."
+msgstr "ÐеприпуÑтимий файл, це не ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð°ÑƒÐ´Ñ–Ð¾-шини."
#: editor/editor_audio_buses.cpp
msgid "Add Bus"
@@ -949,7 +947,7 @@ msgstr "Додати шину"
#: editor/editor_audio_buses.cpp
msgid "Create a new Bus Layout."
-msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ макету шини."
+msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ¸Ð½Ð¸."
#: editor/editor_audio_buses.cpp editor/property_editor.cpp
#: editor/script_create_dialog.cpp
@@ -958,16 +956,16 @@ msgstr "Завантажити"
#: editor/editor_audio_buses.cpp
msgid "Load an existing Bus Layout."
-msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ–Ñнуючого макета шини."
+msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ–Ñнуючого ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ¸Ð½Ð¸."
#: editor/editor_audio_buses.cpp
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Save As"
-msgstr "Зберегти Як"
+msgstr "Зберегти Ñк"
#: editor/editor_audio_buses.cpp
msgid "Save this Bus Layout to a file."
-msgstr "Зберегти цей макет шини у файлі."
+msgstr "Зберегти це ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ¸Ð½Ð¸ у файлі."
#: editor/editor_audio_buses.cpp editor/import_dock.cpp
msgid "Load Default"
@@ -975,7 +973,7 @@ msgstr "Завантажити типовий"
#: editor/editor_audio_buses.cpp
msgid "Load the default Bus Layout."
-msgstr "Завантажити типовий макет шини."
+msgstr "Завантажити типове ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ¸Ð½Ð¸."
#: editor/editor_autoload_settings.cpp
msgid "Invalid name."
@@ -998,7 +996,7 @@ msgstr ""
#: editor/editor_autoload_settings.cpp
msgid "Invalid name. Must not collide with an existing global constant name."
msgstr ""
-"Ðеправильне ім'Ñ. Ðе повинно збігатиÑÑŒ з іменем Ñ–Ñнуючої глобальної "
+"ÐеприпуÑтиме ім'Ñ. Ðе повинно збігатиÑÑŒ з іменем Ñ–Ñнуючої глобальної "
"конÑтанти."
#: editor/editor_autoload_settings.cpp
@@ -1068,12 +1066,12 @@ msgid "Updating Scene"
msgstr "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñцени"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¸Ñ… змін.."
+msgid "Storing local changes..."
+msgstr "Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¸Ñ… змін..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñцени.."
+msgid "Updating scene..."
+msgstr "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñцени..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1141,8 +1139,8 @@ msgid "Show In File Manager"
msgstr "Показати в файловому менеджері"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "Створити теку.."
+msgid "New Folder..."
+msgstr "Створити теку..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1154,7 +1152,7 @@ msgstr "УÑе розпізнано"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "All Files (*)"
-msgstr "УÑÑ– фали (*)"
+msgstr "УÑÑ– файли (*)"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open a File"
@@ -1180,11 +1178,11 @@ msgstr "Зберегти"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Save a File"
-msgstr "Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ"
+msgstr "Зберегти файл"
#: editor/editor_file_dialog.cpp
msgid "Go Back"
-msgstr "ПовертатиÑÑ"
+msgstr "ПовернутиÑÑ Ð½Ð°Ð·Ð°Ð´"
#: editor/editor_file_dialog.cpp
msgid "Go Forward"
@@ -1212,11 +1210,11 @@ msgstr "ФокуÑувати шлÑÑ…"
#: editor/editor_file_dialog.cpp
msgid "Move Favorite Up"
-msgstr "ПереміÑтити обране вгору"
+msgstr "ПереміÑтити вибране вгору"
#: editor/editor_file_dialog.cpp
msgid "Move Favorite Down"
-msgstr "ПереміÑтити обране вниз"
+msgstr "ПереміÑтити вибране вниз"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Go to parent folder"
@@ -1245,7 +1243,7 @@ msgstr "Сканувати Ñирці"
#: editor/editor_file_system.cpp
msgid "(Re)Importing Assets"
-msgstr "(Re)Імпорт активів"
+msgstr "Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ñ–Ð²"
#: editor/editor_help.cpp editor/editor_node.cpp
#: editor/plugins/script_editor_plugin.cpp
@@ -1258,7 +1256,7 @@ msgstr "СпиÑок клаÑів:"
#: editor/editor_help.cpp
msgid "Search Classes"
-msgstr "Пошук клаÑу"
+msgstr "Пошук клаÑів"
#: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp
msgid "Top"
@@ -1274,19 +1272,19 @@ msgstr "УÑпадковує:"
#: editor/editor_help.cpp
msgid "Inherited by:"
-msgstr "УÑпадкована:"
+msgstr "УÑпадковано:"
#: editor/editor_help.cpp
msgid "Brief Description:"
-msgstr "Короткий опиÑ:"
+msgstr "СтиÑлий опиÑ:"
#: editor/editor_help.cpp
msgid "Members"
-msgstr "УчаÑники"
+msgstr "Члени"
#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
msgid "Members:"
-msgstr "УчаÑники:"
+msgstr "Члени:"
#: editor/editor_help.cpp
msgid "Public Methods"
@@ -1298,11 +1296,11 @@ msgstr "Публічні методи:"
#: editor/editor_help.cpp
msgid "GUI Theme Items"
-msgstr "Елементи графічного інтерфейÑу теми"
+msgstr "Тема елементів ГІК"
#: editor/editor_help.cpp
msgid "GUI Theme Items:"
-msgstr "Елементи графічного інтерфейÑу теми:"
+msgstr "Тема елементів ГІК:"
#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
msgid "Signals:"
@@ -1310,15 +1308,15 @@ msgstr "Сигнали:"
#: editor/editor_help.cpp
msgid "Enumerations"
-msgstr "Перелік"
+msgstr "Перелічуваний"
#: editor/editor_help.cpp
msgid "Enumerations:"
-msgstr "Перелік:"
+msgstr "Перелічуваний:"
#: editor/editor_help.cpp
msgid "enum "
-msgstr "перелік "
+msgstr "перелічуваний "
#: editor/editor_help.cpp
msgid "Constants"
@@ -1368,7 +1366,7 @@ msgstr "Методи"
#: editor/editor_help.cpp
msgid "Method Description:"
-msgstr "ÐžÐ¿Ð¸Ñ Ð¼ÐµÑ‚Ð¾Ð´Ñƒ:"
+msgstr "ÐžÐ¿Ð¸Ñ Ð¼ÐµÑ‚Ð¾Ð´Ñ–Ð²:"
#: editor/editor_help.cpp
msgid ""
@@ -1403,20 +1401,20 @@ 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!"
msgstr "Помилка Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñ€ÐµÑурÑу!"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "Зберегти реÑÑƒÑ€Ñ Ñк.."
+msgid "Save Resource As..."
+msgstr "Зберегти реÑÑƒÑ€Ñ Ñк..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "Бачу.."
+msgid "I see..."
+msgstr "Бачу..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1496,7 +1494,7 @@ msgstr "Помилка Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð½Ð°Ð±Ð¾Ñ€Ñƒ тайлів!"
#: editor/editor_node.cpp
msgid "Error trying to save layout!"
-msgstr "Помилка при Ñпробі зберегти макет!"
+msgstr "Помилка при Ñпробі зберегти компонуваннÑ!"
#: editor/editor_node.cpp
msgid "Default editor layout overridden."
@@ -1504,7 +1502,7 @@ msgstr "Типове ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€Ð° перевизÐ
#: editor/editor_node.cpp
msgid "Layout name not found!"
-msgstr "Ðазву макета не знайдено!"
+msgstr "Ðазву ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ðµ знайдено!"
#: editor/editor_node.cpp
msgid "Restored default layout to base settings."
@@ -1516,7 +1514,7 @@ msgid ""
"Please read the documentation relevant to importing scenes to better "
"understand this workflow."
msgstr ""
-"Цей реÑÑƒÑ€Ñ Ð½Ð°Ð»ÐµÐ¶Ð¸Ñ‚ÑŒ до Ñцени, Ñкий було імпортовано, тому не можна "
+"Цей реÑÑƒÑ€Ñ Ð½Ð°Ð»ÐµÐ¶Ð¸Ñ‚ÑŒ до Ñцени, Ñкий було імпортовано, тому його не можна "
"редагувати.\n"
"Будь лаÑка, прочитайте документацію, що ÑтоÑуютьÑÑ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñцен, щоб "
"краще зрозуміти цей робочий процеÑ."
@@ -1597,7 +1595,7 @@ msgstr "Відкрити у довідці"
#: editor/editor_node.cpp
msgid "There is no defined scene to run."
-msgstr "Ðе Ñ–Ñнує визначеної Ñцени Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑку."
+msgstr "Ðемає визначеної Ñцени Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ."
#: editor/editor_node.cpp
msgid ""
@@ -1647,12 +1645,12 @@ msgid "Open Base Scene"
msgstr "Відкрити оÑновну Ñцену"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "Швидке Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñцени.."
+msgid "Quick Open Scene..."
+msgstr "Швидке Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñцени..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "Швидке Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñкрипту.."
+msgid "Quick Open Script..."
+msgstr "Швидке Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñкрипту..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1660,11 +1658,11 @@ msgstr "Зберегти та закрити"
#: editor/editor_node.cpp
msgid "Save changes to '%s' before closing?"
-msgstr "Зберегти зміни, внеÑені до '%s ' перед закриттÑм?"
+msgstr "Зберегти зміни, внеÑені до '%s' перед закриттÑм?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Зберегти Ñцени, Ñк..."
+msgid "Save Scene As..."
+msgstr "Зберегти Ñцену Ñк..."
#: editor/editor_node.cpp
msgid "No"
@@ -1715,8 +1713,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "Цю дію не можна ÑкаÑувати. ПовернутиÑÑ Ð² будь-Ñкому випадку?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "Швидкий запуÑк Ñцени.."
+msgid "Quick Run Scene..."
+msgstr "Швидкий запуÑк Ñцени..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1815,11 +1813,11 @@ msgstr "ОчиÑтити недавні Ñцени"
#: editor/editor_node.cpp
msgid "Save Layout"
-msgstr "Зберегти макет"
+msgstr "Зберегти компонуваннÑ"
#: editor/editor_node.cpp
msgid "Delete Layout"
-msgstr "Видалити макет"
+msgstr "Видалити компонуваннÑ"
#: editor/editor_node.cpp editor/import_dock.cpp
#: editor/script_create_dialog.cpp
@@ -1828,7 +1826,7 @@ msgstr "Типовий"
#: editor/editor_node.cpp
msgid "Switch Scene Tab"
-msgstr "Перемкнути вкладку \"Сцена\""
+msgstr "ÐŸÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ð½Ð½Ñ Ð²ÐºÐ»Ð°Ð´ÐºÐ¸ \"Сцена\""
#: editor/editor_node.cpp
msgid "%d more files or folders"
@@ -1875,7 +1873,7 @@ msgid "Previous tab"
msgstr "ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð½Ñ Ð²ÐºÐ»Ð°Ð´ÐºÐ°"
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr "Фільтрувати файли..."
#: editor/editor_node.cpp
@@ -1887,12 +1885,12 @@ msgid "New Scene"
msgstr "Ðова Ñцена"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Ðова уÑпадкована Ñцена.."
+msgid "New Inherited Scene..."
+msgstr "Ðова уÑпадкована Ñцена..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "Відкрити Ñцену.."
+msgid "Open Scene..."
+msgstr "Відкрити Ñцену..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1911,16 +1909,16 @@ msgid "Open Recent"
msgstr "Відкрити оÑтанні"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "Перетворити на.."
+msgid "Convert To..."
+msgstr "Перетворити на..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "Бібліотека Ñітки.."
+msgid "MeshLibrary..."
+msgstr "Бібліотека Ñітки..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "Ðабір тайлів.."
+msgid "TileSet..."
+msgstr "Ðабір тайлів..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -1930,7 +1928,7 @@ msgstr "СкаÑувати"
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp
msgid "Redo"
-msgstr "Повторити"
+msgstr "Повернути"
#: editor/editor_node.cpp
msgid "Revert Scene"
@@ -1954,7 +1952,7 @@ msgstr "ЗапуÑтити Ñкрипт"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Export"
-msgstr "ЕкÑпортувати"
+msgstr "ЕкÑпортуваннÑ"
#: editor/editor_node.cpp
msgid "Tools"
@@ -2001,14 +1999,14 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Visible Collision Shapes"
-msgstr "Видимі форми зіткнень"
+msgstr "Видимі контури зіткнень"
#: 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 ""
-"Форми Ð·Ñ–Ñ‚ÐºÐ½ÐµÐ½Ð½Ñ Ñ‚Ð° вузли raycast (Ð´Ð»Ñ 2D та 3D) будуть видно в роботі гри, "
+"Контури Ð·Ñ–Ñ‚ÐºÐ½ÐµÐ½Ð½Ñ Ñ‚Ð° вузли raycast (Ð´Ð»Ñ 2D та 3D) буде видно в роботі гри, "
"Ñкщо Ñ†Ñ Ð¾Ð¿Ñ†Ñ–Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð°."
#: editor/editor_node.cpp
@@ -2036,8 +2034,8 @@ msgid ""
msgstr ""
"Якщо цей параметр увімкнено, будь-Ñкі зміни, внеÑені в Ñцену в редакторі, "
"будуть відтворені в роботі гри.\n"
-"Коли він викориÑтовуєтьÑÑ Ð²Ñ–Ð´Ð´Ð°Ð»ÐµÐ½Ð¾ на приÑтрої, це більш ефективно з "
-"мережевою файловою ÑиÑтемою."
+"При віддаленому викориÑтанні на приÑтрої, це більш ефективно з мережевою "
+"файловою ÑиÑтемою."
#: editor/editor_node.cpp
msgid "Sync Script Changes"
@@ -2052,8 +2050,8 @@ msgid ""
msgstr ""
"Якщо цей параметр увімкнено, будь-Ñкий Ñкрипт, Ñкий буде збережений, буде "
"перезавантажений у поточній грі.\n"
-"Коли він викориÑтовуєтьÑÑ Ð²Ñ–Ð´Ð´Ð°Ð»ÐµÐ½Ð¾ на приÑтрої, це більш ефективно з "
-"мережевою файловою ÑиÑтемою."
+"При віддаленому викориÑтанні на приÑтрої, це більш ефективно з мережевою "
+"файловою ÑиÑтемою."
#: editor/editor_node.cpp
msgid "Editor"
@@ -2065,7 +2063,7 @@ msgstr "Параметри редактора"
#: editor/editor_node.cpp
msgid "Editor Layout"
-msgstr "Редактор макетів"
+msgstr "Редактор компонуваннÑ"
#: editor/editor_node.cpp
msgid "Toggle Fullscreen"
@@ -2108,7 +2106,7 @@ msgstr "Спільнота"
#: editor/editor_node.cpp
msgid "About"
-msgstr "Про програму"
+msgstr "Про"
#: editor/editor_node.cpp
msgid "Play the project."
@@ -2160,7 +2158,7 @@ msgstr "Завжди оновлювати"
#: editor/editor_node.cpp
msgid "Update Changes"
-msgstr "Оновити зміни"
+msgstr "Оновлювати зміни"
#: editor/editor_node.cpp
msgid "Disable Update Spinner"
@@ -2183,7 +2181,7 @@ msgid "Save the currently edited resource."
msgstr "Зберегти поточний редагований реÑурÑ."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr "Зберегти Ñк..."
#: editor/editor_node.cpp
@@ -2257,7 +2255,7 @@ msgstr "Ðовий уÑпадкований"
#: editor/editor_node.cpp
msgid "Load Errors"
-msgstr "Завантажити помилки"
+msgstr "Помилки завантаженнÑ"
#: editor/editor_node.cpp editor/plugins/tile_map_editor_plugin.cpp
msgid "Select"
@@ -2292,12 +2290,12 @@ msgid "Creating Mesh Previews"
msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð½ÑŒÐ¾Ð³Ð¾ переглÑду Ñітки"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "Мініатюра.."
+msgid "Thumbnail..."
+msgstr "Мініатюра..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
-msgstr "Ð’Ñтановлені плагіни:"
+msgstr "Ð’Ñтановлені плаґіни:"
#: editor/editor_plugin_settings.cpp
msgid "Update"
@@ -2342,7 +2340,7 @@ msgstr "Кадр %"
#: editor/editor_profiler.cpp
msgid "Physics Frame %"
-msgstr "Фізика кадрів %"
+msgstr "Фізичний кадр %"
#: editor/editor_profiler.cpp editor/script_editor_debugger.cpp
msgid "Time:"
@@ -2386,7 +2384,7 @@ msgstr "Ðапишіть Ñвою логіку в методі _run ()."
#: editor/editor_run_script.cpp
msgid "There is an edited scene already."
-msgstr "Є вже редагована Ñцена."
+msgstr "Редагована Ñцена вже Ñ–Ñнує."
#: editor/editor_run_script.cpp
msgid "Couldn't instance script:"
@@ -2394,7 +2392,7 @@ msgstr "Ðеможливо Ñтворити екземплÑÑ€ Ñкрипту:"
#: editor/editor_run_script.cpp
msgid "Did you forget the 'tool' keyword?"
-msgstr "Ви забули ключове Ñлово \"інÑтрумент\"?"
+msgstr "Ви забули ключове Ñлово 'tool'?"
#: editor/editor_run_script.cpp
msgid "Couldn't run script:"
@@ -2402,7 +2400,7 @@ msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити Ñкрипт:"
#: editor/editor_run_script.cpp
msgid "Did you forget the '_run' method?"
-msgstr "Ви забули метод \"_run\"?"
+msgstr "Ви забули метод '_run'?"
#: editor/editor_settings.cpp
msgid "Default (Same as Editor)"
@@ -2445,12 +2443,12 @@ msgid "(Current)"
msgstr "(Поточний)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
-msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð·ÐµÑ€ÐºÐ°Ð», будь лаÑка, зачекайте.."
+msgid "Retrieving mirrors, please wait..."
+msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð·ÐµÑ€ÐºÐ°Ð», будь лаÑка, зачекайте..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
-msgstr "Видалити верÑÑ–ÑŽ шаблону '%s'?"
+msgstr "Видалити верÑÑ–ÑŽ шаблону '%s'?"
#: editor/export_template_manager.cpp
msgid "Can't open export templates zip."
@@ -2470,7 +2468,7 @@ msgstr "Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÑˆÐ»Ñху Ð´Ð»Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ–Ð²:"
#: editor/export_template_manager.cpp
msgid "Extracting Export Templates"
-msgstr "ВитÑг шаблонів екÑпорту"
+msgstr "Ð Ð¾Ð·Ð¿Ð°ÐºÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ–Ð² екÑпорту"
#: editor/export_template_manager.cpp
msgid "Importing:"
@@ -2481,8 +2479,8 @@ msgid ""
"No download links found for this version. Direct download is only available "
"for official releases."
msgstr ""
-"Ðемає поÑилань на Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— верÑÑ–Ñ—. ПрÑме Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð¾Ñтупне "
-"лише Ð´Ð»Ñ Ð¾Ñ„Ñ–Ñ†Ñ–Ð¹Ð½Ð¸Ñ… випуÑків."
+"Ðе знайдено поÑилань Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— верÑÑ–Ñ—. ПрÑме Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ "
+"доÑтупне лише Ð´Ð»Ñ Ð¾Ñ„Ñ–Ñ†Ñ–Ð¹Ð½Ð¸Ñ… випуÑків."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -2523,8 +2521,8 @@ msgid "Error requesting url: "
msgstr "Помилка запиту url: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
-msgstr "ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ дзеркала.."
+msgid "Connecting to Mirror..."
+msgstr "ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ дзеркала..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2540,8 +2538,8 @@ msgstr "Ðе вдаєтьÑÑ Ð²Ð¸Ñ€Ñ–ÑˆÐ¸Ñ‚Ð¸"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "З’єднаннÑ.."
+msgid "Connecting..."
+msgstr "З’єднаннÑ..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2553,7 +2551,7 @@ msgstr "З’єднано"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "Запит..."
#: editor/export_template_manager.cpp
@@ -2566,7 +2564,7 @@ msgstr "Помилка з'єднаннÑ"
#: editor/export_template_manager.cpp
msgid "SSL Handshake Error"
-msgstr "Помилка SSL Handshake"
+msgstr "Помилка SSL РукоÑтиÑканнÑ"
#: editor/export_template_manager.cpp
msgid "Current Version:"
@@ -2586,7 +2584,7 @@ msgstr "Видалити шаблон"
#: editor/export_template_manager.cpp
msgid "Select template file"
-msgstr "Виберіть файл шаблону"
+msgstr "Вибрати файл шаблону"
#: editor/export_template_manager.cpp
msgid "Export Template Manager"
@@ -2603,18 +2601,17 @@ msgstr "Виберіть дзеркало зі ÑпиÑку: "
#: editor/file_type_cache.cpp
msgid "Can't open file_type_cache.cch for writing, not saving file type cache!"
msgstr ""
-"Ðе вдаєтьÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл file_type_cache.cch Ð´Ð»Ñ Ð½Ð°Ð¿Ð¸ÑаннÑ, кеш тип файлу "
-"не буде збережений!"
+"Ðе вдаєтьÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ file_type_cache.cch Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñу, не буде збережений файл "
+"тип кешу!"
#: editor/filesystem_dock.cpp
msgid "Cannot navigate to '%s' as it has not been found in the file system!"
msgstr ""
-"Ðеможливо перейти до \"'%s' , оÑкільки він не був знайдений в файлової "
-"ÑиÑтемі!"
+"Ðеможливо перейти до '%s' , оÑкільки він не був знайдений в файловій ÑиÑтемі!"
#: editor/filesystem_dock.cpp
msgid "View items as a grid of thumbnails"
-msgstr "ПереглÑд елементів у виглÑді Ñітки еÑкізів"
+msgstr "ПереглÑд елементів у виглÑді Ñітки мініатюр"
#: editor/filesystem_dock.cpp
msgid "View items as a list"
@@ -2623,8 +2620,8 @@ msgstr "ПереглÑд елементів Ñк ÑпиÑок"
#: editor/filesystem_dock.cpp
msgid "Status: Import of file failed. Please fix file and reimport manually."
msgstr ""
-"СтатуÑ: не вдалоÑÑ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ñ‚Ð¸ файл. Виправте файл та повторно імпортуйте "
-"вручну."
+"СтатуÑ: не вдалоÑÑ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ñ‚Ð¸ файл. Будь лаÑка, виправте файл та повторно "
+"імпортуйте вручну."
#: editor/filesystem_dock.cpp
msgid "Cannot move/rename resources root."
@@ -2684,18 +2681,18 @@ msgstr "Ð”ÑƒÐ±Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ñ‚ÐµÐºÐ¸:"
#: editor/filesystem_dock.cpp
msgid "Expand all"
-msgstr "Розгорнути вÑÑ–"
+msgstr "Розгорнути вÑе"
#: editor/filesystem_dock.cpp
msgid "Collapse all"
-msgstr "Згорнути вÑÑ–"
+msgstr "Згорнути вÑе"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr "Перейменувати..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr "ПереміÑтити до..."
#: editor/filesystem_dock.cpp
@@ -2707,16 +2704,16 @@ msgid "Instance"
msgstr "ЕкземплÑÑ€"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Редагувати залежноÑÑ‚Ñ–.."
+msgid "Edit Dependencies..."
+msgstr "Редагувати залежноÑÑ‚Ñ–..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "ПереглÑнути влаÑників.."
+msgid "View Owners..."
+msgstr "ПереглÑнути влаÑників..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "Дублювати.."
+msgid "Duplicate..."
+msgstr "Дублювати..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2728,20 +2725,20 @@ msgstr "ÐаÑтупний каталог"
#: editor/filesystem_dock.cpp
msgid "Re-Scan Filesystem"
-msgstr "Повторне ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ñ— ÑиÑтеми"
+msgstr "ПереÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ñ— ÑиÑтеми"
#: editor/filesystem_dock.cpp
msgid "Toggle folder status as Favorite"
-msgstr "Переключити ÑÑ‚Ð°Ñ‚ÑƒÑ Ñ‚ÐµÐºÐ¸ у вибране"
+msgstr "Переключити ÑÑ‚Ð°Ñ‚ÑƒÑ Ñ‚ÐµÐºÐ¸ Ñк обране"
#: editor/filesystem_dock.cpp
msgid "Instance the selected scene(s) as child of the selected node."
-msgstr "Додати обрану Ñцену(и), Ñк нащадка обраного вузла."
+msgstr "Додати вибрану Ñцену(и), Ñк нащадка вибраного вузла."
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð²,\n"
"будь лаÑка, зачекайте..."
@@ -2809,8 +2806,8 @@ msgid "Import Scene"
msgstr "Імпортувати Ñцену"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñцени.."
+msgid "Importing Scene..."
+msgstr "Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñцени..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2821,8 +2818,8 @@ msgid "Generating for Mesh: "
msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñітки: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "ЗапуÑк кориÑтувацького Ñкрипту.."
+msgid "Running Custom Script..."
+msgstr "ЗапуÑк кориÑтувацького Ñкрипту..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2837,16 +2834,16 @@ msgid "Error running post-import script:"
msgstr "Помилка запуÑку піÑÐ»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ Ñкрипту:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr "ЗбереженнÑ..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
-msgstr "Ð’Ñтановити за замовчуваннÑм Ð´Ð»Ñ \"%s\""
+msgstr "Ð’Ñтановити Ñк типове Ð´Ð»Ñ '%s'"
#: editor/import_dock.cpp
msgid "Clear Default for '%s'"
-msgstr "ОчиÑтити за замовчуваннÑм Ð´Ð»Ñ '%s'"
+msgstr "ОчиÑтити типове Ð´Ð»Ñ '%s'"
#: editor/import_dock.cpp
msgid " Files"
@@ -2857,12 +2854,12 @@ msgid "Import As:"
msgstr "Імпортувати Ñк:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "Заздалегідь уÑтановлений.."
+msgid "Preset..."
+msgstr "Заздалегідь уÑтановлений..."
#: editor/import_dock.cpp
msgid "Reimport"
-msgstr "Переімпортивути"
+msgstr "Переімпортувати"
#: editor/multi_node_edit.cpp
msgid "MultiNode Set"
@@ -2935,7 +2932,7 @@ msgstr "Ðова анімаціÑ"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Change Animation Name:"
-msgstr "Змініть ім'Ñ Ð°Ð½Ñ–Ð¼Ð°Ñ†Ñ–Ñ—:"
+msgstr "Змінити ім'Ñ Ð°Ð½Ñ–Ð¼Ð°Ñ†Ñ–Ñ—:"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Delete Animation?"
@@ -2982,7 +2979,7 @@ msgstr "Дублювати анімацію"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: No animation to copy!"
-msgstr "ПОМИЛКÐ: Ðемає копії анімації!"
+msgstr "ПОМИЛКÐ: Ðемає анімації Ð´Ð»Ñ ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: No animation resource on clipboard!"
@@ -3031,7 +3028,7 @@ msgstr "Шкала Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ð¾ анімації д
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Create new animation in player."
-msgstr "Створювати нові анімації у програвачі."
+msgstr "Створити нову анімацію у програвачі."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Load animation from disk."
@@ -3276,15 +3273,15 @@ msgid "Transition Node"
msgstr "Вузол переходу"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "Імпортувати анімації.."
+msgid "Import Animations..."
+msgstr "Імпортувати анімації..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "Редагувати фільтри вузла"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr "Фільтри..."
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3352,7 +3349,7 @@ msgid "Fetching:"
msgstr "ВидобуваннÑ:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr "ВирішеннÑ..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3419,7 +3416,7 @@ msgid "Site:"
msgstr "Сайт:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr "Підтримка..."
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3617,6 +3614,7 @@ msgid "Use Rotation Snap"
msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ð¾Ð±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð²'Ñзки"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð²'Ñзки..."
@@ -3713,14 +3711,12 @@ msgid "Show Guides"
msgstr "Показати напрÑмні"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "ПереглÑд центра"
+msgstr "Показати центр"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1 панель переглÑду"
+msgstr "Показати панель переглÑду"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -4014,7 +4010,7 @@ msgstr "Сітка не має поверхні, щоб Ñтворити конÑ
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "Типом Ñітки примітива не Ñ” PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4045,8 +4041,8 @@ msgid "Create Convex Collision Sibling"
msgstr "Створити опуклу облаÑÑ‚ÑŒ зіткненнÑ"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "Створити контурну Ñітку .."
+msgid "Create Outline Mesh..."
+msgstr "Створити контурну Ñітку ..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4251,8 +4247,8 @@ msgid "Error loading image:"
msgstr "Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "Ð’ зображенні немає пікÑелів з прозоріÑÑ‚ÑŽ > 128.."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "Ð’ зображенні немає пікÑелів з прозоріÑÑ‚ÑŽ > 128..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4612,7 +4608,7 @@ msgid "Import Theme"
msgstr "Імпортувати тему"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr "Зберегти тему Ñк..."
#: editor/plugins/script_editor_plugin.cpp
@@ -4709,8 +4705,8 @@ msgstr "Перемкнути панель Ñценаріїв"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "Знайти.."
+msgid "Find..."
+msgstr "Знайти..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4835,7 +4831,7 @@ msgstr "Копіювати"
#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp
#: scene/gui/text_edit.cpp
msgid "Select All"
-msgstr "Вибрати вÑе"
+msgstr "Виділити вÑе"
#: editor/plugins/script_text_editor.cpp
msgid "Delete Line"
@@ -4919,16 +4915,16 @@ msgid "Find Previous"
msgstr "Знайти попереднє"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "Замінити.."
+msgid "Replace..."
+msgstr "Замінити..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "Перейти до функції.."
+msgid "Goto Function..."
+msgstr "Перейти до функції..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "Перейти до Ñ€Ñдка.."
+msgid "Goto Line..."
+msgstr "Перейти до Ñ€Ñдка..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5381,11 +5377,7 @@ msgid "Transform"
msgstr "ПеретвореннÑ"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "Ðалаштувати прилипаннÑ..."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr "Вікно перетвореннÑ..."
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5638,7 +5630,7 @@ msgid "Remove All"
msgstr "Вилучити уÑÑ–"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr "Редагувати тему..."
#: editor/plugins/theme_editor_plugin.cpp
@@ -5686,14 +5678,12 @@ msgid "Checked Item"
msgstr "Позначений елемент"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "Додати елемент"
+msgstr "Пункт варіанта"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "Позначений елемент"
+msgstr "Позначений пункт варіанта"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5705,11 +5695,11 @@ msgstr "Багато"
#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
msgid "Options"
-msgstr "Параметрів"
+msgstr "Параметри"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr "Має,Багато,Декілька,Параметрів!"
+msgid "Has,Many,Options"
+msgstr "Має,Багато,Параметрів"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5901,7 +5891,7 @@ msgid "Presets"
msgstr "Ðабори"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr "Додати..."
#: editor/project_export.cpp
@@ -5996,6 +5986,10 @@ msgid "Imported Project"
msgstr "Імпортований проект"
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "Ðекоректна назва проекту."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "Ðеможливо Ñтворити теку."
@@ -6196,9 +6190,11 @@ msgstr "Кнопка миші"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
+"Ðекоректна назва дії. Ðазва не може бути порожньою Ñ– не може міÑтити "
+"Ñимволів «/», «:», «=», «\\» та «\"»."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6225,7 +6221,7 @@ msgid "Control+"
msgstr "Ctrl+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr "ÐатиÑніть клавішу,..."
#: editor/project_settings_editor.cpp
@@ -6402,14 +6398,14 @@ msgstr "Параметри проекту (project.godot)"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "General"
-msgstr "Загальне"
+msgstr "\"Загальне\""
#: editor/project_settings_editor.cpp editor/property_editor.cpp
msgid "Property:"
msgstr "ВлаÑтивіÑÑ‚ÑŒ:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr "Перевизначити на..."
#: editor/project_settings_editor.cpp
@@ -6454,19 +6450,19 @@ msgstr "ПереÑпрÑÐ¼ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð° локаллю:"
#: editor/project_settings_editor.cpp
msgid "Locale"
-msgstr "Локаль"
+msgstr "Мова"
#: editor/project_settings_editor.cpp
msgid "Locales Filter"
-msgstr "Фільтр локалей"
+msgstr "Фільтр локалізацій"
#: editor/project_settings_editor.cpp
msgid "Show all locales"
-msgstr "Показати уÑÑ– локалі"
+msgstr "Показати уÑÑ– локалізації"
#: editor/project_settings_editor.cpp
msgid "Show only selected locales"
-msgstr "Показати лише позначені локалі"
+msgstr "Показати лише позначені локалізації"
#: editor/project_settings_editor.cpp
msgid "Filter mode:"
@@ -6474,7 +6470,7 @@ msgstr "Режим фільтруваннÑ:"
#: editor/project_settings_editor.cpp
msgid "Locales:"
-msgstr "Локалі:"
+msgstr "Мови:"
#: editor/project_settings_editor.cpp
msgid "AutoLoad"
@@ -6505,11 +6501,11 @@ msgid "Easing Out-In"
msgstr "Перейти з-у"
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr "Файл..."
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr "Каталог..."
#: editor/property_editor.cpp
@@ -6683,7 +6679,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr "Цю дію не можна виконувати над Ñценами з екземплÑрами."
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr "Зберегти нову Ñцену Ñк..."
#: editor/scene_tree_dock.cpp
@@ -7401,7 +7397,7 @@ msgstr "ВідÑтань вибору:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "Ðазвою клаÑу не може бути зарезервоване ключове Ñлово"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -8114,7 +8110,7 @@ msgstr ""
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "WorldEnvironment потребує реÑурÑу Environment."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8128,6 +8124,9 @@ 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 ""
+"Цей Ð·Ð°Ð¿Ð¸Ñ WorldEnvironment проігноровано. Ðбо додайте Ð·Ð°Ð¿Ð¸Ñ Camera (Ð´Ð»Ñ "
+"проÑторових Ñцен) або вÑтановіть Ð´Ð»Ñ Background Mode цього Ñередовища "
+"Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Canvas (Ð´Ð»Ñ Ð´Ð²Ð¾Ð²Ð¸Ð¼Ñ–Ñ€Ð½Ð¸Ñ… Ñцен)."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8226,6 +8225,13 @@ msgstr "Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ ÑˆÑ€Ð¸Ñ„Ñ‚Ñƒ."
msgid "Invalid font size."
msgstr "Ðекоректний розмір шрифту."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð½Ñ Ð²ÐºÐ»Ð°Ð´ÐºÐ°"
+
+#~ msgid "Next"
+#~ msgstr "Далі"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Ðекоректна Ð´Ñ–Ñ (можна уÑе, окрім «/» або «:»)."
@@ -8252,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/ur_PK.po b/editor/translations/ur_PK.po
index 4f03e8a387..0162eb0788 100644
--- a/editor/translations/ur_PK.po
+++ b/editor/translations/ur_PK.po
@@ -495,7 +495,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr ""
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -909,11 +909,11 @@ msgid "Move Audio Bus"
msgstr "ایکشن منتقل کریں"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1051,11 +1051,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1124,7 +1124,7 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr ""
#: editor/editor_file_dialog.cpp
@@ -1391,12 +1391,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1601,11 +1601,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1617,7 +1617,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr ""
#: editor/editor_node.cpp
@@ -1669,7 +1669,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1815,7 +1815,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1827,11 +1827,11 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1851,15 +1851,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2104,7 +2104,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2214,7 +2214,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2365,7 +2365,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2441,7 +2441,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2458,7 +2458,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2471,7 +2471,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2605,11 +2605,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2621,15 +2621,15 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2655,7 +2655,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2721,7 +2721,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2733,7 +2733,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2749,7 +2749,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2769,7 +2769,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3185,7 +3185,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3193,7 +3193,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3261,7 +3261,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3328,7 +3328,7 @@ msgid "Site:"
msgstr "سائٹ:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ".سپورٹ"
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3519,6 +3519,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3944,7 +3945,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4149,7 +4150,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4514,7 +4515,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4612,7 +4613,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4818,15 +4819,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5280,11 +5281,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5541,7 +5538,7 @@ msgid "Remove All"
msgstr ".تمام کا انتخاب"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5609,7 +5606,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5798,7 +5795,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5888,6 +5885,10 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr ""
+
+#: editor/project_manager.cpp
#, fuzzy
msgid "Couldn't create folder."
msgstr "سب سکریپشن بنائیں"
@@ -6078,8 +6079,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6107,7 +6108,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6292,7 +6293,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6388,11 +6389,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6564,7 +6565,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
diff --git a/editor/translations/vi.po b/editor/translations/vi.po
index d6284d640e..6651bd170c 100644
--- a/editor/translations/vi.po
+++ b/editor/translations/vi.po
@@ -498,7 +498,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr ""
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -908,11 +908,11 @@ msgid "Move Audio Bus"
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1048,11 +1048,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1121,7 +1121,7 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr ""
#: editor/editor_file_dialog.cpp
@@ -1383,12 +1383,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1593,11 +1593,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1609,8 +1609,8 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "Lưu Scene với tên.."
+msgid "Save Scene As..."
+msgstr "Lưu Scene với tên..."
#: editor/editor_node.cpp
msgid "No"
@@ -1661,7 +1661,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1808,7 +1808,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1820,11 +1820,11 @@ msgid "New Scene"
msgstr "Tạo Scene Mới"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "Tạo Scene Con.."
+msgid "New Inherited Scene..."
+msgstr "Tạo Scene Con..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1844,15 +1844,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2097,7 +2097,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2206,7 +2206,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2357,7 +2357,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2433,7 +2433,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2450,7 +2450,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2463,7 +2463,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2595,11 +2595,11 @@ msgid "Collapse all"
msgstr "Thu gá»n tất cả"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr "Äổi tên..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr "Di chuyển đến..."
#: editor/filesystem_dock.cpp
@@ -2611,15 +2611,15 @@ msgid "Instance"
msgstr "Thêm vào scene"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "Chỉnh sửa các File phụ thuộc.."
+msgid "Edit Dependencies..."
+msgstr "Chỉnh sửa các File phụ thuộc..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "Xem các scene sở hữu.."
+msgid "View Owners..."
+msgstr "Xem các scene sở hữu..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "Nhân đôi..."
#: editor/filesystem_dock.cpp
@@ -2645,10 +2645,10 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"Äang quét file,\n"
-"ChỠmôt chút.."
+"ChỠmôt chút..."
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2713,7 +2713,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2725,7 +2725,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2741,7 +2741,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2761,7 +2761,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3175,7 +3175,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3183,7 +3183,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3251,7 +3251,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3318,7 +3318,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3505,6 +3505,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3926,7 +3927,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4131,7 +4132,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4492,7 +4493,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4589,7 +4590,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4795,15 +4796,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5254,11 +5255,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5511,7 +5508,7 @@ msgid "Remove All"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5579,7 +5576,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5767,7 +5764,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5857,6 +5854,11 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
+#, fuzzy
+msgid "Invalid Project Name."
+msgstr "Kích thước font không hợp lệ."
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr ""
@@ -6045,8 +6047,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6074,7 +6076,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6258,7 +6260,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6354,11 +6356,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6529,7 +6531,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -7965,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 45d2d81505..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.
@@ -12,9 +11,12 @@
# Bruce Guo <guoboism@hotmail.com>, 2016.
# 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.
# seanfy <everxiao@qq.com>, 2018.
# sersoong <seraphim945@qq.com>, 2017-2018.
@@ -23,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-03 08:59+0000\n"
-"Last-Translator: Geequlim <geequlim@gmail.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"
@@ -37,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"
@@ -515,8 +517,8 @@ msgid "Disconnect '%s' from '%s'"
msgstr "å–消'%s'的连接'%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
-msgstr "连接信å·.."
+msgid "Connect..."
+msgstr "连接信å·..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -927,12 +929,12 @@ msgid "Move Audio Bus"
msgstr "移动音频总线"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
-msgstr "将音频Bus布局ä¿å­˜ä¸º.."
+msgid "Save Audio Bus Layout As..."
+msgstr "将音频Bus布局ä¿å­˜ä¸º..."
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
-msgstr "新布局的ä½ç½®.."
+msgid "Location for New Layout..."
+msgstr "新布局的ä½ç½®..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
@@ -970,7 +972,7 @@ msgstr "å¦å­˜ä¸º"
#: editor/editor_audio_buses.cpp
msgid "Save this Bus Layout to a file."
-msgstr "将音频Bus布局ä¿å­˜ä¸º.."
+msgstr "将音频Bus布局ä¿å­˜ä¸º..."
#: editor/editor_audio_buses.cpp editor/import_dock.cpp
msgid "Load Default"
@@ -1067,12 +1069,12 @@ msgid "Updating Scene"
msgstr "更新场景"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "ä¿å­˜ä¿®æ”¹ä¸­.."
+msgid "Storing local changes..."
+msgstr "ä¿å­˜ä¿®æ”¹ä¸­..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "更新场景中.."
+msgid "Updating scene..."
+msgstr "更新场景中..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1140,8 +1142,8 @@ msgid "Show In File Manager"
msgstr "在资æºç®¡ç†å™¨ä¸­æ‰“å¼€"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
-msgstr "新建文件夹 .."
+msgid "New Folder..."
+msgstr "新建文件夹 ..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1401,20 +1403,20 @@ 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!"
msgstr "ä¿å­˜èµ„æºå‡ºé”™ï¼"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
-msgstr "资æºå¦å­˜ä¸º.."
+msgid "Save Resource As..."
+msgstr "资æºå¦å­˜ä¸º..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
-msgstr "好å§.."
+msgid "I see..."
+msgstr "好å§..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1634,12 +1636,12 @@ msgid "Open Base Scene"
msgstr "打开父场景"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
-msgstr "快速打开场景.."
+msgid "Quick Open Scene..."
+msgstr "快速打开场景..."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
-msgstr "快速打开脚本.."
+msgid "Quick Open Script..."
+msgstr "快速打开脚本..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1650,8 +1652,8 @@ msgid "Save changes to '%s' before closing?"
msgstr "在关闭å‰ä¿å­˜æ›´æ”¹åˆ° %s å—?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "场景å¦å­˜ä¸º.."
+msgid "Save Scene As..."
+msgstr "场景å¦å­˜ä¸º..."
#: editor/editor_node.cpp
msgid "No"
@@ -1702,8 +1704,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "æ­¤æ“作无法撤销,确定è¦ç»§ç»­å—?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "快速è¿è¡Œåœºæ™¯.."
+msgid "Quick Run Scene..."
+msgstr "快速è¿è¡Œåœºæ™¯..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1850,8 +1852,8 @@ msgid "Previous tab"
msgstr "上一个目录"
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "筛选文件.."
+msgid "Filter Files..."
+msgstr "筛选文件..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1862,12 +1864,12 @@ msgid "New Scene"
msgstr "新建场景"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
-msgstr "从现有场景中创建.."
+msgid "New Inherited Scene..."
+msgstr "从现有场景中创建..."
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "打开场景.."
+msgid "Open Scene..."
+msgstr "打开场景..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1886,16 +1888,16 @@ msgid "Open Recent"
msgstr "最近打开"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "转æ¢ä¸º.."
+msgid "Convert To..."
+msgstr "转æ¢ä¸º..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshLibrary(网格库).."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary(网格库)..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "ç –å—集.."
+msgid "TileSet..."
+msgstr "ç –å—集..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2148,8 +2150,8 @@ msgid "Save the currently edited resource."
msgstr "ä¿å­˜å½“å‰ç¼–辑的资æºã€‚"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "å¦å­˜ä¸º.."
+msgid "Save As..."
+msgstr "å¦å­˜ä¸º..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2257,8 +2259,8 @@ msgid "Creating Mesh Previews"
msgstr "创建网格预览"
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
-msgstr "缩略图.."
+msgid "Thumbnail..."
+msgstr "缩略图..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
@@ -2410,7 +2412,7 @@ msgid "(Current)"
msgstr "(当å‰)"
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr "检索镜åƒï¼Œè¯·ç­‰å¾…..."
#: editor/export_template_manager.cpp
@@ -2486,7 +2488,7 @@ msgid "Error requesting url: "
msgstr "请求链接错误: "
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr "正在连接镜åƒç½‘站。。"
#: editor/export_template_manager.cpp
@@ -2503,8 +2505,8 @@ msgstr "无法解æž"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
-msgstr "连接中.."
+msgid "Connecting..."
+msgstr "连接中..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
@@ -2516,7 +2518,7 @@ msgstr "已连接"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "正在请求。。"
#: editor/export_template_manager.cpp
@@ -2648,12 +2650,12 @@ msgid "Collapse all"
msgstr "收起所有"
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr "é‡å‘½å为..."
#: editor/filesystem_dock.cpp
-msgid "Move To.."
-msgstr "移动.."
+msgid "Move To..."
+msgstr "移动..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
@@ -2664,16 +2666,16 @@ msgid "Instance"
msgstr "创建实例节点"
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
-msgstr "编辑ä¾èµ–.."
+msgid "Edit Dependencies..."
+msgstr "编辑ä¾èµ–..."
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
-msgstr "查看所有者.."
+msgid "View Owners..."
+msgstr "查看所有者..."
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
-msgstr "æ‹·è´.."
+msgid "Duplicate..."
+msgstr "æ‹·è´..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2698,7 +2700,7 @@ msgstr "将选中的场景实例为选中节点的å­èŠ‚点。"
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
"扫æ文件,\n"
"请ç¨å€™ã€‚"
@@ -2746,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"
@@ -2766,8 +2768,8 @@ msgid "Import Scene"
msgstr "导入场景"
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
-msgstr "导入场景.."
+msgid "Importing Scene..."
+msgstr "导入场景..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -2778,8 +2780,8 @@ msgid "Generating for Mesh: "
msgstr "正在生æˆMesh: "
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
-msgstr "执行自定义脚本.."
+msgid "Running Custom Script..."
+msgstr "执行自定义脚本..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
@@ -2794,7 +2796,7 @@ msgid "Error running post-import script:"
msgstr "åŽå¤„ç†è„šæœ¬è¿è¡Œå‘生错误:"
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr "ä¿å­˜ä¸­..."
#: editor/import_dock.cpp
@@ -2814,8 +2816,8 @@ msgid "Import As:"
msgstr "导入为:"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
-msgstr "预设.."
+msgid "Preset..."
+msgstr "预设..."
#: editor/import_dock.cpp
msgid "Reimport"
@@ -3232,16 +3234,16 @@ msgid "Transition Node"
msgstr "过渡节点"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
-msgstr "导入动画.."
+msgid "Import Animations..."
+msgstr "导入动画..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
msgstr "编辑节点筛选"
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
-msgstr "筛选.."
+msgid "Filters..."
+msgstr "筛选..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3308,8 +3310,8 @@ msgid "Fetching:"
msgstr "获å–:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
-msgstr "解æžä¸­.."
+msgid "Resolving..."
+msgstr "解æžä¸­..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -3375,8 +3377,8 @@ msgid "Site:"
msgstr "站点:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
-msgstr "支æŒ.."
+msgid "Support..."
+msgstr "支æŒ..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3565,8 +3567,9 @@ msgid "Use Rotation Snap"
msgstr "使用旋转å¸é™„"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
-msgstr "设置å¸é™„.."
+msgstr "设置å¸é™„..."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap Relative"
@@ -3661,14 +3664,12 @@ msgid "Show Guides"
msgstr "显示引导"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
msgstr "显示原点"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "1个视å£"
+msgstr "显示视图窗å£"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -3852,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"
@@ -3961,7 +3962,7 @@ msgstr "Mesh(网格)没有表é¢æ¥åˆ›å»ºè½®å»“(outlines)ï¼"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "网格原始类型ä¸æ˜¯ PRIMITIVE_TRIANGLES(三角形网格)ï¼"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -3992,8 +3993,8 @@ msgid "Create Convex Collision Sibling"
msgstr "创建凸(Convex)碰撞åŒçº§"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
-msgstr "创建轮廓网格(Outline Mesh).."
+msgid "Create Outline Mesh..."
+msgstr "创建轮廓网格(Outline Mesh)..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4197,8 +4198,8 @@ msgid "Error loading image:"
msgstr "加载图片出错:"
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
-msgstr "图片中没有é€æ˜Žåº¦> 128çš„åƒç´ .."
+msgid "No pixels with transparency > 128 in image..."
+msgstr "图片中没有é€æ˜Žåº¦> 128çš„åƒç´ ..."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4558,8 +4559,8 @@ msgid "Import Theme"
msgstr "导入主题"
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
-msgstr "主题å¦å­˜ä¸º.."
+msgid "Save Theme As..."
+msgstr "主题å¦å­˜ä¸º..."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
@@ -4655,8 +4656,8 @@ msgstr "切æ¢è„šæœ¬é¢æ¿"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
-msgstr "查找.."
+msgid "Find..."
+msgstr "查找..."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4863,16 +4864,16 @@ msgid "Find Previous"
msgstr "查找上一项"
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
-msgstr "替æ¢.."
+msgid "Replace..."
+msgstr "替æ¢..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
-msgstr "å‰å¾€å‡½æ•°.."
+msgid "Goto Function..."
+msgstr "å‰å¾€å‡½æ•°..."
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
-msgstr "å‰å¾€è¡Œ.."
+msgid "Goto Line..."
+msgstr "å‰å¾€è¡Œ..."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
@@ -5325,12 +5326,8 @@ msgid "Transform"
msgstr "å˜æ¢"
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr "设置å¸é™„.."
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
-msgstr "å˜æ¢å¯¹è¯æ¡†.."
+msgid "Transform Dialog..."
+msgstr "å˜æ¢å¯¹è¯æ¡†..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5582,8 +5579,8 @@ msgid "Remove All"
msgstr "移除全部"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
-msgstr "编辑主题.."
+msgid "Edit theme..."
+msgstr "编辑主题..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
@@ -5630,14 +5627,12 @@ msgid "Checked Item"
msgstr "已选项目(Checked Item)"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "添加项目"
+msgstr "å•é€‰é¡¹ç›®"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "已选项目(Checked Item)"
+msgstr "已选å•é€‰é¡¹ç›®"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
@@ -5652,8 +5647,8 @@ msgid "Options"
msgstr "选项"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr "有,很多,几个,选项(Have,Many,Several,Options)ï¼"
+msgid "Has,Many,Options"
+msgstr "有,很多,选项"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5843,8 +5838,8 @@ msgid "Presets"
msgstr "预设"
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
-msgstr "添加.."
+msgid "Add..."
+msgstr "添加..."
#: editor/project_export.cpp
msgid "Resources"
@@ -5933,6 +5928,10 @@ msgid "Imported Project"
msgstr "已导入的项目"
#: editor/project_manager.cpp
+msgid "Invalid Project Name."
+msgstr "无效项目å称。"
+
+#: editor/project_manager.cpp
msgid "Couldn't create folder."
msgstr "无法创建文件夹。"
@@ -6128,9 +6127,9 @@ msgstr "鼠标按键"
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
-msgstr ""
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
+msgstr "无效的æ“作å称。它ä¸èƒ½æ˜¯ç©ºçš„也ä¸èƒ½åŒ…å« '/', ':', '=', '\\' 或者 '\"'。"
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6157,8 +6156,8 @@ msgid "Control+"
msgstr "Ctrl+"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
-msgstr "按下一个键.."
+msgid "Press a Key..."
+msgstr "按下一个键..."
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
@@ -6341,7 +6340,7 @@ msgid "Property:"
msgstr "属性:"
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr "é‡å†™çš„......"
#: editor/project_settings_editor.cpp
@@ -6386,7 +6385,7 @@ msgstr "地区é‡å®šå‘:"
#: editor/project_settings_editor.cpp
msgid "Locale"
-msgstr "地区"
+msgstr "区域"
#: editor/project_settings_editor.cpp
msgid "Locales Filter"
@@ -6437,12 +6436,12 @@ msgid "Easing Out-In"
msgstr "å缓入缓出"
#: editor/property_editor.cpp
-msgid "File.."
-msgstr "文件.."
+msgid "File..."
+msgstr "文件..."
#: editor/property_editor.cpp
-msgid "Dir.."
-msgstr "目录.."
+msgid "Dir..."
+msgstr "目录..."
#: editor/property_editor.cpp
msgid "Assign"
@@ -6612,8 +6611,8 @@ msgid "This operation can't be done on instanced scenes."
msgstr "æ­¤æ“作ä¸èƒ½åº”用于实例化的场景。"
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
-msgstr "将新场景å¦å­˜ä¸º.."
+msgid "Save New Scene As..."
+msgstr "将新场景å¦å­˜ä¸º..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
@@ -7323,7 +7322,7 @@ msgstr "拾å–è·ç¦»:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
-msgstr ""
+msgstr "ç±»åä¸èƒ½æ˜¯ä¿ç•™å…³é”®å­—"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
@@ -7347,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"
@@ -7992,7 +7991,7 @@ msgstr "path属性必须指å‘一个åˆæ³•çš„Spatial节点æ‰èƒ½æ­£å¸¸å·¥ä½œã€‚"
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "WorldEnvironment需è¦ä¸€ä¸ªçŽ¯å¢ƒèµ„æºã€‚"
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8004,6 +8003,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 ""
+"这个WorldEnvironment被忽略。添加摄åƒå¤´ï¼ˆç”¨äºŽ3D场景)或将此环境的背景模å¼è®¾ç½®"
+"为画布(用于2D场景)。"
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8096,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åéžæ³•(ä¸å¾—包å«'/'或':')。"
@@ -8119,9 +8127,6 @@ msgstr "字体大å°éžæ³•ã€‚"
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "无法在项目目录下找到project.godot文件。"
-#~ msgid "Next"
-#~ msgstr "下一项"
-
#~ msgid "Not found!"
#~ msgstr "未找到ï¼"
@@ -8268,8 +8273,8 @@ msgstr "字体大å°éžæ³•ã€‚"
#~ msgid "Exporting for %s"
#~ msgstr "正在导出 %s"
-#~ msgid "Setting Up.."
-#~ msgstr "é…ç½®.."
+#~ msgid "Setting Up..."
+#~ msgstr "é…ç½®..."
#~ msgid "Error loading scene."
#~ msgstr "加载场景出错。"
@@ -8329,8 +8334,8 @@ msgstr "字体大å°éžæ³•ã€‚"
#~ msgid "Info"
#~ msgstr "ä¿¡æ¯"
-#~ msgid "Re-Import.."
-#~ msgstr "é‡æ–°å¯¼å…¥.."
+#~ msgid "Re-Import..."
+#~ msgstr "é‡æ–°å¯¼å…¥..."
#~ msgid "No bit masks to import!"
#~ msgstr "没有è¦å¯¼å…¥çš„bit masksï¼"
@@ -8721,14 +8726,14 @@ msgstr "字体大å°éžæ³•ã€‚"
#~ msgid "Zoom (%):"
#~ msgstr "缩放(%):"
-#~ msgid "Skeleton.."
-#~ msgstr "骨骼.."
+#~ msgid "Skeleton..."
+#~ msgstr "骨骼..."
#~ msgid "Zoom Reset"
#~ msgstr "é‡ç½®ç¼©æ”¾"
-#~ msgid "Zoom Set.."
-#~ msgstr "设置缩放.."
+#~ msgid "Zoom Set..."
+#~ msgstr "设置缩放..."
#~ msgid "Set a Value"
#~ msgstr "设置值"
@@ -9196,8 +9201,8 @@ msgstr "字体大å°éžæ³•ã€‚"
#~ msgid "Export Project PCK"
#~ msgstr "导出项目PCK文件"
-#~ msgid "Export.."
-#~ msgstr "导出.."
+#~ msgid "Export..."
+#~ msgstr "导出..."
#~ msgid "Project Export"
#~ msgstr "项目导出"
@@ -9286,7 +9291,7 @@ msgstr "字体大å°éžæ³•ã€‚"
#~ msgid "Reload Tool Script (Soft)"
#~ msgstr "é‡æ–°åŠ è½½Tool脚本(Soft)"
-#~ msgid "Edit Connections.."
+#~ msgid "Edit Connections..."
#~ msgstr "编辑事件连接"
#~ msgid "Set Params"
diff --git a/editor/translations/zh_HK.po b/editor/translations/zh_HK.po
index f4c6a39788..568390a7a8 100644
--- a/editor/translations/zh_HK.po
+++ b/editor/translations/zh_HK.po
@@ -534,7 +534,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "由 '%s' 連到 '%s'"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "連到..."
#: editor/connections_dialog.cpp
@@ -972,11 +972,11 @@ msgid "Move Audio Bus"
msgstr "移動"
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1126,11 +1126,11 @@ msgid "Updating Scene"
msgstr "更新場景"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "儲存本地更改.."
+msgid "Storing local changes..."
+msgstr "儲存本地更改..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr "正在更新場景..."
#: editor/editor_data.cpp
@@ -1203,7 +1203,7 @@ msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
#, fuzzy
-msgid "New Folder.."
+msgid "New Folder..."
msgstr "新增資料夾"
#: editor/editor_file_dialog.cpp
@@ -1481,12 +1481,12 @@ msgid "Error saving resource!"
msgstr "儲存資æºæ™‚出ç¾éŒ¯èª¤ï¼"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr "把資æºå¦å­˜ç‚º..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr "如來如此"
#: editor/editor_node.cpp
@@ -1713,11 +1713,11 @@ msgid "Open Base Scene"
msgstr "開啟基礎場景"
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr "快速開啟場景.."
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr "快速開啟腳本.."
#: editor/editor_node.cpp
@@ -1731,7 +1731,7 @@ msgid "Save changes to '%s' before closing?"
msgstr "關閉å‰è¦å…ˆå„²å­˜å° '%s' 任何更改嗎?"
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr "把場景å¦å­˜ç‚º"
#: editor/editor_node.cpp
@@ -1783,8 +1783,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "快速é‹è¡Œå ´æ™¯.."
+msgid "Quick Run Scene..."
+msgstr "快速é‹è¡Œå ´æ™¯..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1943,8 +1943,8 @@ msgstr "上一個tab"
#: editor/editor_node.cpp
#, fuzzy
-msgid "Filter Files.."
-msgstr "篩é¸æª”案.."
+msgid "Filter Files..."
+msgstr "篩é¸æª”案..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1955,11 +1955,11 @@ msgid "New Scene"
msgstr "新增場景"
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr "開啓場景"
#: editor/editor_node.cpp
@@ -1979,16 +1979,16 @@ msgid "Open Recent"
msgstr "開啓最近的"
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr "轉為..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
-msgstr "MeshLibrary.."
+msgid "MeshLibrary..."
+msgstr "MeshLibrary..."
#: editor/editor_node.cpp
-msgid "TileSet.."
-msgstr "TileSet.."
+msgid "TileSet..."
+msgstr "TileSet..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -2236,8 +2236,8 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
-msgstr "å¦å­˜ç‚º.."
+msgid "Save As..."
+msgstr "å¦å­˜ç‚º..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
@@ -2351,7 +2351,7 @@ msgstr ""
#: editor/editor_plugin.cpp
#, fuzzy
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr "縮圖"
#: editor/editor_plugin_settings.cpp
@@ -2507,7 +2507,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2591,7 +2591,7 @@ msgstr "請求時出ç¾éŒ¯èª¤"
#: editor/export_template_manager.cpp
#, fuzzy
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr "連到..."
#: editor/export_template_manager.cpp
@@ -2610,7 +2610,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
-msgid "Connecting.."
+msgid "Connecting..."
msgstr "連到..."
#: editor/export_template_manager.cpp
@@ -2625,7 +2625,7 @@ msgstr "連到"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr "請求中..."
#: editor/export_template_manager.cpp
@@ -2771,13 +2771,13 @@ msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Rename.."
-msgstr "é‡æ–°å‘½å.."
+msgid "Rename..."
+msgstr "é‡æ–°å‘½å..."
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Move To.."
-msgstr "æ¬åˆ°.."
+msgid "Move To..."
+msgstr "æ¬åˆ°..."
#: editor/filesystem_dock.cpp
#, fuzzy
@@ -2789,16 +2789,16 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "複製"
#: editor/filesystem_dock.cpp
@@ -2824,7 +2824,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2834,7 +2834,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
msgid "Add to Group"
@@ -2891,7 +2891,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2903,7 +2903,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2919,8 +2919,8 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
-msgstr "儲存中.."
+msgid "Saving..."
+msgstr "儲存中..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
@@ -2941,7 +2941,7 @@ msgid "Import As:"
msgstr "å°Žå…¥"
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3364,7 +3364,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3372,7 +3372,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3444,7 +3444,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3511,7 +3511,7 @@ msgid "Site:"
msgstr "地å€:"
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3701,6 +3701,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -4129,7 +4130,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4281,7 +4282,7 @@ msgstr ""
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
msgid "Marking walkable triangles..."
-msgstr "儲存本地更改.."
+msgstr "儲存本地更改..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Constructing compact heightfield..."
@@ -4337,7 +4338,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4704,7 +4705,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4806,7 +4807,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -5019,15 +5020,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5491,11 +5492,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5751,7 +5748,7 @@ msgid "Remove All"
msgstr "移除"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5819,8 +5816,9 @@ msgid "Options"
msgstr "é¸é …"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
-msgstr ""
+#, fuzzy
+msgid "Has,Many,Options"
+msgstr "é¸é …"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5947,7 +5945,7 @@ msgstr ""
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
msgid "Tile Set"
-msgstr "TileSet.."
+msgstr "TileSet..."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -6014,7 +6012,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr "添加..."
#: editor/project_export.cpp
@@ -6109,6 +6107,11 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "無效å稱"
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "無法新增資料夾"
@@ -6303,8 +6306,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6332,7 +6335,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6519,7 +6522,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6616,11 +6619,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6798,7 +6801,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -7331,7 +7334,7 @@ msgstr ""
#: modules/gdnative/gdnative_library_editor_plugin.cpp
#, fuzzy
msgid "Dynamic Library"
-msgstr "MeshLibrary.."
+msgstr "MeshLibrary..."
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Add an architecture entry"
@@ -7340,12 +7343,12 @@ msgstr ""
#: modules/gdnative/gdnative_library_editor_plugin.cpp
#, fuzzy
msgid "GDNativeLibrary"
-msgstr "MeshLibrary.."
+msgstr "MeshLibrary..."
#: modules/gdnative/gdnative_library_singleton_editor.cpp
#, fuzzy
msgid "Library"
-msgstr "MeshLibrary.."
+msgstr "MeshLibrary..."
#: modules/gdnative/gdnative_library_singleton_editor.cpp
msgid "Status"
@@ -8292,6 +8295,13 @@ msgid "Invalid font size."
msgstr "無效字型"
#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "上一個tab"
+
+#~ msgid "Next"
+#~ msgstr "下一個"
+
+#, fuzzy
#~ msgid "Can't contain '/' or ':'"
#~ msgstr "ä¸èƒ½é€£åˆ°ä¸»æ©Ÿï¼š"
@@ -8299,9 +8309,6 @@ msgstr "無效字型"
#~ msgid "Can't write file."
#~ msgstr "無法新增資料夾"
-#~ msgid "Next"
-#~ msgstr "下一個"
-
#~ msgid "Not found!"
#~ msgstr "找ä¸åˆ°!"
@@ -8452,7 +8459,7 @@ msgstr "無效字型"
#~ msgid "Cannot go into subdir:"
#~ msgstr "無法進入次è¦è³‡æ–™å¤¾"
-#~ msgid "Edit Connections.."
+#~ msgid "Edit Connections..."
#~ msgstr "編輯連接"
#~ msgid "Live Editing"
diff --git a/editor/translations/zh_TW.po b/editor/translations/zh_TW.po
index ccbd45bf31..38b565a37f 100644
--- a/editor/translations/zh_TW.po
+++ b/editor/translations/zh_TW.po
@@ -510,7 +510,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr "將 '%s' 從 '%s' 中斷連接"
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr "連接..."
#: editor/connections_dialog.cpp
@@ -936,11 +936,11 @@ msgid "Move Audio Bus"
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1076,12 +1076,12 @@ msgid "Updating Scene"
msgstr "更新場景"
#: editor/editor_data.cpp
-msgid "Storing local changes.."
-msgstr "正在儲存變更.."
+msgid "Storing local changes..."
+msgstr "正在儲存變更..."
#: editor/editor_data.cpp
-msgid "Updating scene.."
-msgstr "更新場景中.."
+msgid "Updating scene..."
+msgstr "更新場景中..."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1151,7 +1151,7 @@ msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
#, fuzzy
-msgid "New Folder.."
+msgid "New Folder..."
msgstr "新增資料夾"
#: editor/editor_file_dialog.cpp
@@ -1415,12 +1415,12 @@ msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr "我知é“了"
#: editor/editor_node.cpp
@@ -1626,11 +1626,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr "快速開啟場景"
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1643,8 +1643,8 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
-msgstr "å¦å­˜å ´æ™¯ç‚º.."
+msgid "Save Scene As..."
+msgstr "å¦å­˜å ´æ™¯ç‚º..."
#: editor/editor_node.cpp
msgid "No"
@@ -1696,8 +1696,8 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr "æ­¤æ“作無法復原, 確定è¦é‚„原嗎?"
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
-msgstr "快速執行場景.."
+msgid "Quick Run Scene..."
+msgstr "快速執行場景..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1827,7 +1827,7 @@ msgstr ""
#: editor/editor_node.cpp
#, fuzzy
msgid "Add a new scene."
-msgstr "更新場景中.."
+msgstr "更新場景中..."
#: editor/editor_node.cpp
#, fuzzy
@@ -1847,8 +1847,8 @@ msgid "Previous tab"
msgstr "上個分é "
#: editor/editor_node.cpp
-msgid "Filter Files.."
-msgstr "éŽæ¿¾æª”案.."
+msgid "Filter Files..."
+msgstr "éŽæ¿¾æª”案..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
@@ -1859,12 +1859,12 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
-msgstr "開啟場景.."
+msgid "Open Scene..."
+msgstr "開啟場景..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1883,15 +1883,15 @@ msgid "Open Recent"
msgstr "開啟最近存å–"
#: editor/editor_node.cpp
-msgid "Convert To.."
-msgstr "轉æ›æˆ.."
+msgid "Convert To..."
+msgstr "轉æ›æˆ..."
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2137,7 +2137,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2248,7 +2248,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2399,7 +2399,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2479,7 +2479,7 @@ msgstr "載入場景時發生錯誤"
#: editor/export_template_manager.cpp
#, fuzzy
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr "連接..."
#: editor/export_template_manager.cpp
@@ -2498,7 +2498,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
-msgid "Connecting.."
+msgid "Connecting..."
msgstr "連接..."
#: editor/export_template_manager.cpp
@@ -2513,7 +2513,7 @@ msgstr "連接..."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2653,11 +2653,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2670,16 +2670,16 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
#, fuzzy
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr "複製動畫關éµç•«æ ¼"
#: editor/filesystem_dock.cpp
@@ -2705,7 +2705,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2772,7 +2772,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2784,7 +2784,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2800,7 +2800,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2820,7 +2820,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3112,7 +3112,7 @@ msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
msgid "Edit Filters"
-msgstr "éŽæ¿¾æª”案.."
+msgstr "éŽæ¿¾æª”案..."
#: editor/plugins/animation_tree_editor_plugin.cpp
#: editor/plugins/multimesh_editor_plugin.cpp
@@ -3237,7 +3237,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3245,7 +3245,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3263,7 +3263,7 @@ msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
#, fuzzy
msgid "View Files"
-msgstr "éŽæ¿¾æª”案.."
+msgstr "éŽæ¿¾æª”案..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Can't resolve hostname:"
@@ -3314,7 +3314,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -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"
@@ -3382,7 +3383,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3571,6 +3572,7 @@ msgid "Use Rotation Snap"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
msgstr ""
@@ -3997,18 +3999,18 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
#, fuzzy
msgid "View UV1"
-msgstr "éŽæ¿¾æª”案.."
+msgstr "éŽæ¿¾æª”案..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
#, fuzzy
msgid "View UV2"
-msgstr "éŽæ¿¾æª”案.."
+msgstr "éŽæ¿¾æª”案..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Unwrap UV2 for Lightmap/AO"
@@ -4149,7 +4151,7 @@ msgstr ""
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
msgid "Marking walkable triangles..."
-msgstr "正在儲存變更.."
+msgstr "正在儲存變更..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Constructing compact heightfield..."
@@ -4205,7 +4207,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4571,7 +4573,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4669,7 +4671,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4765,7 +4767,7 @@ msgstr ""
#: editor/plugins/script_text_editor.cpp
#, fuzzy
msgid "Convert Case"
-msgstr "轉æ›æˆ.."
+msgstr "轉æ›æˆ..."
#: editor/plugins/script_text_editor.cpp
msgid "Uppercase"
@@ -4869,27 +4871,27 @@ msgstr ""
#: editor/plugins/script_text_editor.cpp
#, fuzzy
msgid "Convert To Uppercase"
-msgstr "轉æ›æˆ.."
+msgstr "轉æ›æˆ..."
#: editor/plugins/script_text_editor.cpp
#, fuzzy
msgid "Convert To Lowercase"
-msgstr "轉æ›æˆ.."
+msgstr "轉æ›æˆ..."
#: editor/plugins/script_text_editor.cpp
msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5079,7 +5081,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
#, fuzzy
msgid "Material Changes"
-msgstr "正在儲存變更.."
+msgstr "正在儲存變更..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Shader Changes"
@@ -5192,7 +5194,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
#, fuzzy
msgid "View FPS"
-msgstr "éŽæ¿¾æª”案.."
+msgstr "éŽæ¿¾æª”案..."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Half Resolution"
@@ -5346,11 +5348,7 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
-msgstr ""
-
-#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5604,7 +5602,7 @@ msgid "Remove All"
msgstr "移除"
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5672,7 +5670,7 @@ msgid "Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Have,Many,Several,Options!"
+msgid "Has,Many,Options"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5863,7 +5861,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -5957,6 +5955,11 @@ msgstr ""
#: editor/project_manager.cpp
#, fuzzy
+msgid "Invalid Project Name."
+msgstr "ä¸èƒ½ä½¿ç”¨çš„å稱。"
+
+#: editor/project_manager.cpp
+#, fuzzy
msgid "Couldn't create folder."
msgstr "無法新增資料夾"
@@ -6148,8 +6151,8 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid ""
-"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
-"'\"'"
+"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6177,7 +6180,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6365,7 +6368,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6427,7 +6430,7 @@ msgstr ""
#: editor/project_settings_editor.cpp
#, fuzzy
msgid "Filter mode:"
-msgstr "éŽæ¿¾æª”案.."
+msgstr "éŽæ¿¾æª”案..."
#: editor/project_settings_editor.cpp
msgid "Locales:"
@@ -6462,11 +6465,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6496,7 +6499,7 @@ msgstr ""
#: editor/property_editor.cpp
#, fuzzy
msgid "Convert To %s"
-msgstr "轉æ›æˆ.."
+msgstr "轉æ›æˆ..."
#: editor/property_editor.cpp
msgid "Error loading file: Not a resource!"
@@ -6638,7 +6641,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -6745,7 +6748,7 @@ msgstr ""
#: editor/scene_tree_dock.cpp
#, fuzzy
msgid "Filter nodes"
-msgstr "éŽæ¿¾æª”案.."
+msgstr "éŽæ¿¾æª”案..."
#: editor/scene_tree_dock.cpp
msgid "Attach a new or existing script for the selected node."
@@ -8120,6 +8123,10 @@ msgstr "讀å–字體錯誤。"
msgid "Invalid font size."
msgstr "無效的字體大å°ã€‚"
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "上個分é "
+
#~ msgid "Next"
#~ msgstr "下一個"
@@ -8138,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 5959deb495..23acb60c89 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -82,6 +82,8 @@
#include "version.h"
#include "version_hash.gen.h"
+#include "main/timer_sync.h"
+
static ProjectSettings *globals = NULL;
static Engine *engine = NULL;
static InputMap *input_map = NULL;
@@ -261,9 +263,10 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print("Standalone tools:\n");
OS::get_singleton()->print(" -s, --script <script> Run a script.\n");
+ OS::get_singleton()->print(" --check-only Only parse for errors and quit (use with --script).\n");
#ifdef TOOLS_ENABLED
- OS::get_singleton()->print(" --export <target> Export the project using the given export target.\n");
- OS::get_singleton()->print(" --export-debug Use together with --export, enables debug mode for the template.\n");
+ OS::get_singleton()->print(" --export <target> Export the project using the given export target. Export only main pack if path ends with .pck or .zip'.\n");
+ OS::get_singleton()->print(" --export-debug <target> Like --export, but use debug template.\n");
OS::get_singleton()->print(" --doctool <path> Dump the engine API reference to the given <path> in XML format, merging if existing files are found.\n");
OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n");
OS::get_singleton()->print(" --build-solutions Build the scripting solutions (e.g. for C# projects).\n");
@@ -337,7 +340,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
String video_driver = "";
String audio_driver = "";
- String game_path = ".";
+ String project_path = ".";
bool upwards = false;
String debug_mode;
String debug_host;
@@ -536,6 +539,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (I->get() == "--build-solutions") { // Build the scripting solution such C#
auto_build_solutions = true;
+ editor = true;
#endif
} else if (I->get() == "--no-window") { // disable window creation, Windows only
@@ -553,7 +557,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (OS::get_singleton()->set_cwd(p) == OK) {
//nothing
} else {
- game_path = I->next()->get(); //use game_path instead
+ project_path = I->next()->get(); //use project_path instead
}
N = I->next()->next();
} else {
@@ -576,7 +580,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (OS::get_singleton()->set_cwd(path) == OK) {
// path already specified, don't override
} else {
- game_path = path;
+ project_path = path;
}
#ifdef TOOLS_ENABLED
editor = true;
@@ -672,35 +676,20 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (I->get() == "--disable-crash-handler") {
OS::get_singleton()->disable_crash_handler();
} else {
-
- //test for game path
- bool gpfound = false;
-
- if (!I->get().begins_with("-") && game_path == "") {
- DirAccess *da = DirAccess::open(I->get());
- if (da != NULL) {
- game_path = I->get();
- gpfound = true;
- memdelete(da);
- }
- }
-
- if (!gpfound) {
- main_args.push_back(I->get());
- }
+ main_args.push_back(I->get());
}
I = N;
}
- if (globals->setup(game_path, main_pack, upwards) == OK) {
+ if (globals->setup(project_path, main_pack, upwards) == OK) {
found_project = true;
} else {
#ifdef TOOLS_ENABLED
editor = false;
#else
- OS::get_singleton()->print("Error: Could not load game path '%s'.\n", game_path.ascii().get_data());
+ OS::get_singleton()->print("Error: Could not load game path '%s'.\n", project_path.ascii().get_data());
goto error;
#endif
@@ -726,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") {
@@ -875,7 +865,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->_allow_hidpi = GLOBAL_DEF("display/window/dpi/allow_hidpi", false);
}
+ 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);
GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_allocation", 2);
GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_allocation.mobile", 3);
@@ -883,6 +879,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (editor || project_manager) {
// The editor and project manager always detect and use hiDPI if needed
OS::get_singleton()->_allow_hidpi = true;
+ OS::get_singleton()->_allow_layered = false;
}
Engine::get_singleton()->_pixel_snap = GLOBAL_DEF("rendering/quality/2d/use_pixel_snap", false);
@@ -986,7 +983,7 @@ error:
video_driver = "";
audio_driver = "";
- game_path = "";
+ project_path = "";
args.clear();
main_args.clear();
@@ -1145,14 +1142,14 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton());
if (id) {
- if (bool(GLOBAL_DEF("input/pointing_devices/emulate_touch_from_mouse", false)) && !(editor || project_manager)) {
+ if (bool(GLOBAL_DEF("input_devices/pointing/emulate_touch_from_mouse", false)) && !(editor || project_manager)) {
if (!OS::get_singleton()->has_touchscreen_ui_hint()) {
//only if no touchscreen ui hint, set emulation
id->set_emulate_touch_from_mouse(true);
}
}
- id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF("input/pointing_devices/emulate_mouse_from_touch", true)));
+ id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF("input_devices/pointing/emulate_mouse_from_touch", true)));
}
MAIN_PRINT("Main: Load Remaps");
@@ -1183,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();
@@ -1231,227 +1224,8 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
}
// everything the main loop needs to know about frame timings
-struct _FrameTime {
- float animation_step; // time to advance animations for (argument to process())
- int physics_steps; // number of times to iterate the physics engine
-
- void clamp_animation(float min_animation_step, float max_animation_step) {
- if (animation_step < min_animation_step) {
- animation_step = min_animation_step;
- } else if (animation_step > max_animation_step) {
- animation_step = max_animation_step;
- }
- }
-};
-
-class _TimerSync {
- // wall clock time measured on the main thread
- uint64_t last_cpu_ticks_usec;
- uint64_t current_cpu_ticks_usec;
-
- // logical game time since last physics timestep
- float time_accum;
-
- // current difference between wall clock time and reported sum of animation_steps
- float time_deficit;
-
- // number of frames back for keeping accumulated physics steps roughly constant.
- // value of 12 chosen because that is what is required to make 144 Hz monitors
- // behave well with 60 Hz physics updates. The only worse commonly available refresh
- // would be 85, requiring CONTROL_STEPS = 17.
- static const int CONTROL_STEPS = 12;
-
- // sum of physics steps done over the last (i+1) frames
- int accumulated_physics_steps[CONTROL_STEPS];
-
- // typical value for accumulated_physics_steps[i] is either this or this plus one
- int typical_physics_steps[CONTROL_STEPS];
-
-protected:
- // returns the fraction of p_frame_slice required for the timer to overshoot
- // before advance_core considers changing the physics_steps return from
- // the typical values as defined by typical_physics_steps
- float get_physics_jitter_fix() {
- return Engine::get_singleton()->get_physics_jitter_fix();
- }
-
- // gets our best bet for the average number of physics steps per render frame
- // return value: number of frames back this data is consistent
- int get_average_physics_steps(float &p_min, float &p_max) {
- p_min = typical_physics_steps[0];
- p_max = p_min + 1;
-
- for (int i = 1; i < CONTROL_STEPS; ++i) {
- const float typical_lower = typical_physics_steps[i];
- const float current_min = typical_lower / (i + 1);
- if (current_min > p_max)
- return i; // bail out of further restrictions would void the interval
- else if (current_min > p_min)
- p_min = current_min;
- const float current_max = (typical_lower + 1) / (i + 1);
- if (current_max < p_min)
- return i;
- else if (current_max < p_max)
- p_max = current_max;
- }
-
- return CONTROL_STEPS;
- }
-
- // advance physics clock by p_animation_step, return appropriate number of steps to simulate
- _FrameTime advance_core(float p_frame_slice, int p_iterations_per_second, float p_animation_step) {
- _FrameTime ret;
-
- ret.animation_step = p_animation_step;
-
- // simple determination of number of physics iteration
- time_accum += ret.animation_step;
- ret.physics_steps = floor(time_accum * p_iterations_per_second);
-
- int min_typical_steps = typical_physics_steps[0];
- int max_typical_steps = min_typical_steps + 1;
-
- // given the past recorded steps and typcial steps to match, calculate bounds for this
- // step to be typical
- bool update_typical = false;
-
- for (int i = 0; i < CONTROL_STEPS - 1; ++i) {
- int steps_left_to_match_typical = typical_physics_steps[i + 1] - accumulated_physics_steps[i];
- if (steps_left_to_match_typical > max_typical_steps ||
- steps_left_to_match_typical + 1 < min_typical_steps) {
- update_typical = true;
- break;
- }
-
- if (steps_left_to_match_typical > min_typical_steps)
- min_typical_steps = steps_left_to_match_typical;
- if (steps_left_to_match_typical + 1 < max_typical_steps)
- max_typical_steps = steps_left_to_match_typical + 1;
- }
-
- // try to keep it consistent with previous iterations
- if (ret.physics_steps < min_typical_steps) {
- const int max_possible_steps = floor((time_accum)*p_iterations_per_second + get_physics_jitter_fix());
- if (max_possible_steps < min_typical_steps) {
- ret.physics_steps = max_possible_steps;
- update_typical = true;
- } else {
- ret.physics_steps = min_typical_steps;
- }
- } else if (ret.physics_steps > max_typical_steps) {
- const int min_possible_steps = floor((time_accum)*p_iterations_per_second - get_physics_jitter_fix());
- if (min_possible_steps > max_typical_steps) {
- ret.physics_steps = min_possible_steps;
- update_typical = true;
- } else {
- ret.physics_steps = max_typical_steps;
- }
- }
-
- time_accum -= ret.physics_steps * p_frame_slice;
-
- // keep track of accumulated step counts
- for (int i = CONTROL_STEPS - 2; i >= 0; --i) {
- accumulated_physics_steps[i + 1] = accumulated_physics_steps[i] + ret.physics_steps;
- }
- accumulated_physics_steps[0] = ret.physics_steps;
-
- if (update_typical) {
- for (int i = CONTROL_STEPS - 1; i >= 0; --i) {
- if (typical_physics_steps[i] > accumulated_physics_steps[i]) {
- typical_physics_steps[i] = accumulated_physics_steps[i];
- } else if (typical_physics_steps[i] < accumulated_physics_steps[i] - 1) {
- typical_physics_steps[i] = accumulated_physics_steps[i] - 1;
- }
- }
- }
-
- return ret;
- }
-
- // calls advance_core, keeps track of deficit it adds to animaption_step, make sure the deficit sum stays close to zero
- _FrameTime advance_checked(float p_frame_slice, int p_iterations_per_second, float p_animation_step) {
- if (fixed_fps != -1)
- p_animation_step = 1.0 / fixed_fps;
-
- // compensate for last deficit
- p_animation_step += time_deficit;
-
- _FrameTime ret = advance_core(p_frame_slice, p_iterations_per_second, p_animation_step);
-
- // we will do some clamping on ret.animation_step and need to sync those changes to time_accum,
- // that's easiest if we just remember their fixed difference now
- const double animation_minus_accum = ret.animation_step - time_accum;
-
- // first, least important clamping: keep ret.animation_step consistent with typical_physics_steps.
- // this smoothes out the animation steps and culls small but quick variations.
- {
- float min_average_physics_steps, max_average_physics_steps;
- int consistent_steps = get_average_physics_steps(min_average_physics_steps, max_average_physics_steps);
- if (consistent_steps > 3) {
- ret.clamp_animation(min_average_physics_steps * p_frame_slice, max_average_physics_steps * p_frame_slice);
- }
- }
-
- // second clamping: keep abs(time_deficit) < jitter_fix * frame_slise
- float max_clock_deviation = get_physics_jitter_fix() * p_frame_slice;
- ret.clamp_animation(p_animation_step - max_clock_deviation, p_animation_step + max_clock_deviation);
-
- // last clamping: make sure time_accum is between 0 and p_frame_slice for consistency between physics and animation
- ret.clamp_animation(animation_minus_accum, animation_minus_accum + p_frame_slice);
-
- // restore time_accum
- time_accum = ret.animation_step - animation_minus_accum;
-
- // track deficit
- time_deficit = p_animation_step - ret.animation_step;
-
- return ret;
- }
-
- // determine wall clock step since last iteration
- float get_cpu_animation_step() {
- uint64_t cpu_ticks_elapsed = current_cpu_ticks_usec - last_cpu_ticks_usec;
- last_cpu_ticks_usec = current_cpu_ticks_usec;
-
- return cpu_ticks_elapsed / 1000000.0;
- }
-
-public:
- explicit _TimerSync() :
- last_cpu_ticks_usec(0),
- current_cpu_ticks_usec(0),
- time_accum(0),
- time_deficit(0) {
- for (int i = CONTROL_STEPS - 1; i >= 0; --i) {
- typical_physics_steps[i] = i;
- accumulated_physics_steps[i] = i;
- }
- }
- // start the clock
- void init(uint64_t p_cpu_ticks_usec) {
- current_cpu_ticks_usec = last_cpu_ticks_usec = p_cpu_ticks_usec;
- }
-
- // set measured wall clock time
- void set_cpu_ticks_usec(uint64_t p_cpu_ticks_usec) {
- current_cpu_ticks_usec = p_cpu_ticks_usec;
- }
-
- // advance one frame, return timesteps to take
- _FrameTime advance(float p_frame_slice, int p_iterations_per_second) {
- float cpu_animation_step = get_cpu_animation_step();
-
- return advance_checked(p_frame_slice, p_iterations_per_second, cpu_animation_step);
- }
-
- void before_start_render() {
- VisualServer::get_singleton()->sync();
- }
-};
-
-static _TimerSync _timer_sync;
+static MainTimerSync main_timer_sync;
bool Main::start() {
@@ -1466,8 +1240,9 @@ bool Main::start() {
String test;
String _export_preset;
bool export_debug = false;
+ bool check_only = false;
- _timer_sync.init(OS::get_singleton()->get_ticks_usec());
+ main_timer_sync.init(OS::get_singleton()->get_ticks_usec());
List<String> args = OS::get_singleton()->get_cmdline_args();
for (int i = 0; i < args.size(); i++) {
@@ -1488,6 +1263,8 @@ bool Main::start() {
bool parsed_pair = true;
if (args[i] == "-s" || args[i] == "--script") {
script = args[i + 1];
+ } else if (args[i] == "--check-only") {
+ check_only = true;
} else if (args[i] == "--test") {
test = args[i + 1];
#ifdef TOOLS_ENABLED
@@ -1610,6 +1387,10 @@ bool Main::start() {
ERR_EXPLAIN("Can't load script: " + script);
ERR_FAIL_COND_V(script_res.is_null(), false);
+ if (check_only) {
+ return false;
+ }
+
if (script_res->can_instance() /*&& script_res->inherits_from("SceneTreeScripted")*/) {
StringName instance_type = script_res->get_instance_base_type();
@@ -1671,6 +1452,91 @@ bool Main::start() {
}
#endif
+ if (!project_manager && !editor) { // game
+ if (game_path != "" || script != "") {
+ //autoload
+ List<PropertyInfo> props;
+ ProjectSettings::get_singleton()->get_property_list(&props);
+
+ //first pass, add the constants so they exist before any script is loaded
+ 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_slicec('/', 1);
+ String path = ProjectSettings::get_singleton()->get(s);
+ bool global_var = false;
+ if (path.begins_with("*")) {
+ global_var = true;
+ }
+
+ if (global_var) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_global_constant(name, Variant());
+ }
+ }
+ }
+
+ //second pass, load into global constants
+ List<Node *> to_add;
+ 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_slicec('/', 1);
+ String path = ProjectSettings::get_singleton()->get(s);
+ bool global_var = false;
+ if (path.begins_with("*")) {
+ global_var = true;
+ path = path.substr(1, path.length() - 1);
+ }
+
+ RES res = ResourceLoader::load(path);
+ ERR_EXPLAIN("Can't autoload: " + path);
+ ERR_CONTINUE(res.is_null());
+ Node *n = NULL;
+ if (res->is_class("PackedScene")) {
+ Ref<PackedScene> ps = res;
+ n = ps->instance();
+ } else if (res->is_class("Script")) {
+ Ref<Script> s = res;
+ StringName ibt = s->get_instance_base_type();
+ bool valid_type = ClassDB::is_parent_class(ibt, "Node");
+ ERR_EXPLAIN("Script does not inherit a Node: " + path);
+ ERR_CONTINUE(!valid_type);
+
+ Object *obj = ClassDB::instance(ibt);
+
+ ERR_EXPLAIN("Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt));
+ ERR_CONTINUE(obj == NULL);
+
+ n = Object::cast_to<Node>(obj);
+ n->set_script(s.get_ref_ptr());
+ }
+
+ ERR_EXPLAIN("Path in autoload not a node or script: " + path);
+ ERR_CONTINUE(!n);
+ n->set_name(name);
+
+ //defer so references are all valid on _ready()
+ to_add.push_back(n);
+
+ if (global_var) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_global_constant(name, n);
+ }
+ }
+ }
+
+ for (List<Node *>::Element *E = to_add.front(); E; E = E->next()) {
+
+ sml->get_root()->add_child(E->get());
+ }
+ }
+ }
+
#ifdef TOOLS_ENABLED
EditorNode *editor_node = NULL;
@@ -1690,9 +1556,6 @@ bool Main::start() {
}
#endif
- {
- }
-
if (!editor && !project_manager) {
//standard helpers that can be changed from main config
@@ -1805,89 +1668,6 @@ bool Main::start() {
}
if (!project_manager && !editor) { // game
- if (game_path != "" || script != "") {
- //autoload
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
-
- //first pass, add the constants so they exist before any script is loaded
- 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_slicec('/', 1);
- String path = ProjectSettings::get_singleton()->get(s);
- bool global_var = false;
- if (path.begins_with("*")) {
- global_var = true;
- }
-
- if (global_var) {
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->add_global_constant(name, Variant());
- }
- }
- }
-
- //second pass, load into global constants
- List<Node *> to_add;
- 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_slicec('/', 1);
- String path = ProjectSettings::get_singleton()->get(s);
- bool global_var = false;
- if (path.begins_with("*")) {
- global_var = true;
- path = path.substr(1, path.length() - 1);
- }
-
- RES res = ResourceLoader::load(path);
- ERR_EXPLAIN("Can't autoload: " + path);
- ERR_CONTINUE(res.is_null());
- Node *n = NULL;
- if (res->is_class("PackedScene")) {
- Ref<PackedScene> ps = res;
- n = ps->instance();
- } else if (res->is_class("Script")) {
- Ref<Script> s = res;
- StringName ibt = s->get_instance_base_type();
- bool valid_type = ClassDB::is_parent_class(ibt, "Node");
- ERR_EXPLAIN("Script does not inherit a Node: " + path);
- ERR_CONTINUE(!valid_type);
-
- Object *obj = ClassDB::instance(ibt);
-
- ERR_EXPLAIN("Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt));
- ERR_CONTINUE(obj == NULL);
-
- n = Object::cast_to<Node>(obj);
- n->set_script(s.get_ref_ptr());
- }
-
- ERR_EXPLAIN("Path in autoload not a node or script: " + path);
- ERR_CONTINUE(!n);
- n->set_name(name);
-
- //defer so references are all valid on _ready()
- to_add.push_back(n);
-
- if (global_var) {
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->add_global_constant(name, n);
- }
- }
- }
-
- for (List<Node *>::Element *E = to_add.front(); E; E = E->next()) {
-
- sml->get_root()->add_child(E->get());
- }
- }
-
if (game_path != "") {
Node *scene = NULL;
Ref<PackedScene> scenedata = ResourceLoader::load(local_game_path);
@@ -1946,15 +1726,16 @@ bool Main::iteration() {
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
Engine::get_singleton()->_frame_ticks = ticks;
- _timer_sync.set_cpu_ticks_usec(ticks);
+ main_timer_sync.set_cpu_ticks_usec(ticks);
+ main_timer_sync.set_fixed_fps(fixed_fps);
uint64_t ticks_elapsed = ticks - last_ticks;
int physics_fps = Engine::get_singleton()->get_iterations_per_second();
float frame_slice = 1.0 / physics_fps;
- _FrameTime advance = _timer_sync.advance(frame_slice, physics_fps);
- double step = advance.animation_step;
+ MainFrameTime advance = main_timer_sync.advance(frame_slice, physics_fps);
+ double step = advance.idle_step;
Engine::get_singleton()->_frame_step = step;
@@ -2018,7 +1799,7 @@ bool Main::iteration() {
OS::get_singleton()->get_main_loop()->idle(step * time_scale);
message_queue->flush();
- _timer_sync.before_start_render(); //sync if still drawing from previous frames.
+ VisualServer::get_singleton()->sync(); //sync if still drawing from previous frames.
if (OS::get_singleton()->can_draw() && !disable_render_loop) {
@@ -2034,9 +1815,6 @@ bool Main::iteration() {
}
}
- if (AudioServer::get_singleton())
- AudioServer::get_singleton()->update();
-
idle_process_ticks = OS::get_singleton()->get_ticks_usec() - idle_begin;
idle_process_max = MAX(idle_process_ticks, idle_process_max);
uint64_t frame_time = OS::get_singleton()->get_ticks_usec() - ticks;
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_main.cpp b/main/tests/test_main.cpp
index c9431a1a09..cbc1107acb 100644
--- a/main/tests/test_main.cpp
+++ b/main/tests/test_main.cpp
@@ -50,15 +50,20 @@ const char **tests_get_names() {
static const char *test_names[] = {
"string",
- "containers",
"math",
+ "physics",
+ "physics_2d",
"render",
- "multimesh",
+ "oa_hash_map",
"gui",
"io",
"shaderlang",
- "physics",
- "oa_hash_map",
+ "gd_tokenizer",
+ "gd_parser",
+ "gd_compiler",
+ "gd_bytecode",
+ "image",
+ "ordered_hash_map",
NULL
};
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/main/timer_sync.cpp b/main/timer_sync.cpp
new file mode 100644
index 0000000000..9f4f6ed271
--- /dev/null
+++ b/main/timer_sync.cpp
@@ -0,0 +1,223 @@
+/*************************************************************************/
+/* timer_sync.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 "timer_sync.h"
+
+void MainFrameTime::clamp_idle(float min_idle_step, float max_idle_step) {
+ if (idle_step < min_idle_step) {
+ idle_step = min_idle_step;
+ } else if (idle_step > max_idle_step) {
+ idle_step = max_idle_step;
+ }
+}
+
+/////////////////////////////////
+
+// returns the fraction of p_frame_slice required for the timer to overshoot
+// before advance_core considers changing the physics_steps return from
+// the typical values as defined by typical_physics_steps
+float MainTimerSync::get_physics_jitter_fix() {
+ return Engine::get_singleton()->get_physics_jitter_fix();
+}
+
+// gets our best bet for the average number of physics steps per render frame
+// return value: number of frames back this data is consistent
+int MainTimerSync::get_average_physics_steps(float &p_min, float &p_max) {
+ p_min = typical_physics_steps[0];
+ p_max = p_min + 1;
+
+ for (int i = 1; i < CONTROL_STEPS; ++i) {
+ const float typical_lower = typical_physics_steps[i];
+ const float current_min = typical_lower / (i + 1);
+ if (current_min > p_max)
+ return i; // bail out of further restrictions would void the interval
+ else if (current_min > p_min)
+ p_min = current_min;
+ const float current_max = (typical_lower + 1) / (i + 1);
+ if (current_max < p_min)
+ return i;
+ else if (current_max < p_max)
+ p_max = current_max;
+ }
+
+ return CONTROL_STEPS;
+}
+
+// advance physics clock by p_idle_step, return appropriate number of steps to simulate
+MainFrameTime MainTimerSync::advance_core(float p_frame_slice, int p_iterations_per_second, float p_idle_step) {
+ MainFrameTime ret;
+
+ ret.idle_step = p_idle_step;
+
+ // simple determination of number of physics iteration
+ time_accum += ret.idle_step;
+ ret.physics_steps = floor(time_accum * p_iterations_per_second);
+
+ int min_typical_steps = typical_physics_steps[0];
+ int max_typical_steps = min_typical_steps + 1;
+
+ // given the past recorded steps and typcial steps to match, calculate bounds for this
+ // step to be typical
+ bool update_typical = false;
+
+ for (int i = 0; i < CONTROL_STEPS - 1; ++i) {
+ int steps_left_to_match_typical = typical_physics_steps[i + 1] - accumulated_physics_steps[i];
+ if (steps_left_to_match_typical > max_typical_steps ||
+ steps_left_to_match_typical + 1 < min_typical_steps) {
+ update_typical = true;
+ break;
+ }
+
+ if (steps_left_to_match_typical > min_typical_steps)
+ min_typical_steps = steps_left_to_match_typical;
+ if (steps_left_to_match_typical + 1 < max_typical_steps)
+ max_typical_steps = steps_left_to_match_typical + 1;
+ }
+
+ // try to keep it consistent with previous iterations
+ if (ret.physics_steps < min_typical_steps) {
+ const int max_possible_steps = floor((time_accum)*p_iterations_per_second + get_physics_jitter_fix());
+ if (max_possible_steps < min_typical_steps) {
+ ret.physics_steps = max_possible_steps;
+ update_typical = true;
+ } else {
+ ret.physics_steps = min_typical_steps;
+ }
+ } else if (ret.physics_steps > max_typical_steps) {
+ const int min_possible_steps = floor((time_accum)*p_iterations_per_second - get_physics_jitter_fix());
+ if (min_possible_steps > max_typical_steps) {
+ ret.physics_steps = min_possible_steps;
+ update_typical = true;
+ } else {
+ ret.physics_steps = max_typical_steps;
+ }
+ }
+
+ time_accum -= ret.physics_steps * p_frame_slice;
+
+ // keep track of accumulated step counts
+ for (int i = CONTROL_STEPS - 2; i >= 0; --i) {
+ accumulated_physics_steps[i + 1] = accumulated_physics_steps[i] + ret.physics_steps;
+ }
+ accumulated_physics_steps[0] = ret.physics_steps;
+
+ if (update_typical) {
+ for (int i = CONTROL_STEPS - 1; i >= 0; --i) {
+ if (typical_physics_steps[i] > accumulated_physics_steps[i]) {
+ typical_physics_steps[i] = accumulated_physics_steps[i];
+ } else if (typical_physics_steps[i] < accumulated_physics_steps[i] - 1) {
+ typical_physics_steps[i] = accumulated_physics_steps[i] - 1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+// calls advance_core, keeps track of deficit it adds to animaption_step, make sure the deficit sum stays close to zero
+MainFrameTime MainTimerSync::advance_checked(float p_frame_slice, int p_iterations_per_second, float p_idle_step) {
+ if (fixed_fps != -1)
+ p_idle_step = 1.0 / fixed_fps;
+
+ // compensate for last deficit
+ p_idle_step += time_deficit;
+
+ MainFrameTime ret = advance_core(p_frame_slice, p_iterations_per_second, p_idle_step);
+
+ // we will do some clamping on ret.idle_step and need to sync those changes to time_accum,
+ // that's easiest if we just remember their fixed difference now
+ const double idle_minus_accum = ret.idle_step - time_accum;
+
+ // first, least important clamping: keep ret.idle_step consistent with typical_physics_steps.
+ // this smoothes out the idle steps and culls small but quick variations.
+ {
+ float min_average_physics_steps, max_average_physics_steps;
+ int consistent_steps = get_average_physics_steps(min_average_physics_steps, max_average_physics_steps);
+ if (consistent_steps > 3) {
+ ret.clamp_idle(min_average_physics_steps * p_frame_slice, max_average_physics_steps * p_frame_slice);
+ }
+ }
+
+ // second clamping: keep abs(time_deficit) < jitter_fix * frame_slise
+ float max_clock_deviation = get_physics_jitter_fix() * p_frame_slice;
+ ret.clamp_idle(p_idle_step - max_clock_deviation, p_idle_step + max_clock_deviation);
+
+ // last clamping: make sure time_accum is between 0 and p_frame_slice for consistency between physics and idle
+ ret.clamp_idle(idle_minus_accum, idle_minus_accum + p_frame_slice);
+
+ // restore time_accum
+ time_accum = ret.idle_step - idle_minus_accum;
+
+ // track deficit
+ time_deficit = p_idle_step - ret.idle_step;
+
+ return ret;
+}
+
+// determine wall clock step since last iteration
+float MainTimerSync::get_cpu_idle_step() {
+ uint64_t cpu_ticks_elapsed = current_cpu_ticks_usec - last_cpu_ticks_usec;
+ last_cpu_ticks_usec = current_cpu_ticks_usec;
+
+ return cpu_ticks_elapsed / 1000000.0;
+}
+
+MainTimerSync::MainTimerSync() :
+ last_cpu_ticks_usec(0),
+ current_cpu_ticks_usec(0),
+ time_accum(0),
+ time_deficit(0),
+ fixed_fps(0) {
+ for (int i = CONTROL_STEPS - 1; i >= 0; --i) {
+ typical_physics_steps[i] = i;
+ accumulated_physics_steps[i] = i;
+ }
+}
+
+// start the clock
+void MainTimerSync::init(uint64_t p_cpu_ticks_usec) {
+ current_cpu_ticks_usec = last_cpu_ticks_usec = p_cpu_ticks_usec;
+}
+
+// set measured wall clock time
+void MainTimerSync::set_cpu_ticks_usec(uint64_t p_cpu_ticks_usec) {
+ current_cpu_ticks_usec = p_cpu_ticks_usec;
+}
+
+void MainTimerSync::set_fixed_fps(int p_fixed_fps) {
+ fixed_fps = p_fixed_fps;
+}
+
+// advance one frame, return timesteps to take
+MainFrameTime MainTimerSync::advance(float p_frame_slice, int p_iterations_per_second) {
+ float cpu_idle_step = get_cpu_idle_step();
+
+ return advance_checked(p_frame_slice, p_iterations_per_second, cpu_idle_step);
+}
diff --git a/main/timer_sync.h b/main/timer_sync.h
new file mode 100644
index 0000000000..75fbe6a9dc
--- /dev/null
+++ b/main/timer_sync.h
@@ -0,0 +1,101 @@
+/*************************************************************************/
+/* timer_sync.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 TIMER_SYNC_H
+#define TIMER_SYNC_H
+
+#include "core/engine.h"
+
+struct MainFrameTime {
+ float idle_step; // time to advance idles for (argument to process())
+ int physics_steps; // number of times to iterate the physics engine
+
+ void clamp_idle(float min_idle_step, float max_idle_step);
+};
+
+class MainTimerSync {
+ // wall clock time measured on the main thread
+ uint64_t last_cpu_ticks_usec;
+ uint64_t current_cpu_ticks_usec;
+
+ // logical game time since last physics timestep
+ float time_accum;
+
+ // current difference between wall clock time and reported sum of idle_steps
+ float time_deficit;
+
+ // number of frames back for keeping accumulated physics steps roughly constant.
+ // value of 12 chosen because that is what is required to make 144 Hz monitors
+ // behave well with 60 Hz physics updates. The only worse commonly available refresh
+ // would be 85, requiring CONTROL_STEPS = 17.
+ static const int CONTROL_STEPS = 12;
+
+ // sum of physics steps done over the last (i+1) frames
+ int accumulated_physics_steps[CONTROL_STEPS];
+
+ // typical value for accumulated_physics_steps[i] is either this or this plus one
+ int typical_physics_steps[CONTROL_STEPS];
+
+ int fixed_fps;
+
+protected:
+ // returns the fraction of p_frame_slice required for the timer to overshoot
+ // before advance_core considers changing the physics_steps return from
+ // the typical values as defined by typical_physics_steps
+ float get_physics_jitter_fix();
+
+ // gets our best bet for the average number of physics steps per render frame
+ // return value: number of frames back this data is consistent
+ int get_average_physics_steps(float &p_min, float &p_max);
+
+ // advance physics clock by p_idle_step, return appropriate number of steps to simulate
+ MainFrameTime advance_core(float p_frame_slice, int p_iterations_per_second, float p_idle_step);
+
+ // calls advance_core, keeps track of deficit it adds to animaption_step, make sure the deficit sum stays close to zero
+ MainFrameTime advance_checked(float p_frame_slice, int p_iterations_per_second, float p_idle_step);
+
+ // determine wall clock step since last iteration
+ float get_cpu_idle_step();
+
+public:
+ MainTimerSync();
+
+ // start the clock
+ void init(uint64_t p_cpu_ticks_usec);
+ // set measured wall clock time
+ void set_cpu_ticks_usec(uint64_t p_cpu_ticks_usec);
+ //set fixed fps
+ void set_fixed_fps(int p_fixed_fps);
+
+ // advance one frame, return timesteps to take
+ MainFrameTime advance(float p_frame_slice, int p_iterations_per_second);
+};
+
+#endif // TIMER_SYNC_H
diff --git a/methods.py b/methods.py
index 7cdc160075..33d503a41a 100644
--- a/methods.py
+++ b/methods.py
@@ -1,5 +1,5 @@
import os
-from compat import iteritems
+from compat import iteritems, itervalues, open_utf8, escape_string
def add_source_files(self, sources, filetype, lib_env=None, shared=False):
@@ -515,6 +515,232 @@ def build_gles2_headers(target, source, env):
for x in source:
build_legacygl_header(str(x), include="drivers/gles2/shader_gles2.h", class_suffix="GLES2", output_attribs=True, gles2=True)
+def make_authors_header(target, source, env):
+
+ sections = ["Project Founders", "Lead Developer", "Project Manager", "Developers"]
+ sections_id = ["AUTHORS_FOUNDERS", "AUTHORS_LEAD_DEVELOPERS", "AUTHORS_PROJECT_MANAGERS", "AUTHORS_DEVELOPERS"]
+
+ src = source[0].srcnode().abspath
+ dst = target[0].srcnode().abspath
+ f = open_utf8(src, "r")
+ g = open_utf8(dst, "w")
+
+ g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
+ g.write("#ifndef _EDITOR_AUTHORS_H\n")
+ g.write("#define _EDITOR_AUTHORS_H\n")
+
+ current_section = ""
+ reading = False
+
+ def close_section():
+ g.write("\t0\n")
+ g.write("};\n")
+
+ for line in f:
+ if reading:
+ if line.startswith(" "):
+ g.write("\t\"" + escape_string(line.strip()) + "\",\n")
+ continue
+ if line.startswith("## "):
+ if reading:
+ close_section()
+ reading = False
+ for i in range(len(sections)):
+ if line.strip().endswith(sections[i]):
+ current_section = escape_string(sections_id[i])
+ reading = True
+ g.write("const char *const " + current_section + "[] = {\n")
+ break
+
+ if reading:
+ close_section()
+
+ g.write("#endif\n")
+
+ g.close()
+ f.close()
+
+def make_donors_header(target, source, env):
+
+ sections = ["Platinum sponsors", "Gold sponsors", "Mini sponsors",
+ "Gold donors", "Silver donors", "Bronze donors"]
+ sections_id = ["DONORS_SPONSOR_PLAT", "DONORS_SPONSOR_GOLD", "DONORS_SPONSOR_MINI",
+ "DONORS_GOLD", "DONORS_SILVER", "DONORS_BRONZE"]
+
+ src = source[0].srcnode().abspath
+ dst = target[0].srcnode().abspath
+ f = open_utf8(src, "r")
+ g = open_utf8(dst, "w")
+
+ g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
+ g.write("#ifndef _EDITOR_DONORS_H\n")
+ g.write("#define _EDITOR_DONORS_H\n")
+
+ current_section = ""
+ reading = False
+
+ def close_section():
+ g.write("\t0\n")
+ g.write("};\n")
+
+ for line in f:
+ if reading >= 0:
+ if line.startswith(" "):
+ g.write("\t\"" + escape_string(line.strip()) + "\",\n")
+ continue
+ if line.startswith("## "):
+ if reading:
+ close_section()
+ reading = False
+ for i in range(len(sections)):
+ if line.strip().endswith(sections[i]):
+ current_section = escape_string(sections_id[i])
+ reading = True
+ g.write("const char *const " + current_section + "[] = {\n")
+ break
+
+ if reading:
+ close_section()
+
+ g.write("#endif\n")
+
+ g.close()
+ f.close()
+
+
+def make_license_header(target, source, env):
+ src_copyright = source[0].srcnode().abspath
+ src_license = source[1].srcnode().abspath
+ dst = target[0].srcnode().abspath
+
+ class LicenseReader:
+ def __init__(self, license_file):
+ self._license_file = license_file
+ self.line_num = 0
+ self.current = self.next_line()
+
+ def next_line(self):
+ line = self._license_file.readline()
+ self.line_num += 1
+ while line.startswith("#"):
+ line = self._license_file.readline()
+ self.line_num += 1
+ self.current = line
+ return line
+
+ def next_tag(self):
+ if not ':' in self.current:
+ return ('',[])
+ tag, line = self.current.split(":", 1)
+ lines = [line.strip()]
+ while self.next_line() and self.current.startswith(" "):
+ lines.append(self.current.strip())
+ return (tag, lines)
+
+ from collections import OrderedDict
+ projects = OrderedDict()
+ license_list = []
+
+ with open_utf8(src_copyright, "r") as copyright_file:
+ reader = LicenseReader(copyright_file)
+ part = {}
+ while reader.current:
+ tag, content = reader.next_tag()
+ if tag in ("Files", "Copyright", "License"):
+ part[tag] = content[:]
+ elif tag == "Comment":
+ # attach part to named project
+ projects[content[0]] = projects.get(content[0], []) + [part]
+
+ if not tag or not reader.current:
+ # end of a paragraph start a new part
+ if "License" in part and not "Files" in part:
+ # no Files tag in this one, so assume standalone license
+ license_list.append(part["License"])
+ part = {}
+ reader.next_line()
+
+ data_list = []
+ for project in itervalues(projects):
+ for part in project:
+ part["file_index"] = len(data_list)
+ data_list += part["Files"]
+ part["copyright_index"] = len(data_list)
+ data_list += part["Copyright"]
+
+ with open_utf8(dst, "w") as f:
+
+ f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
+ f.write("#ifndef _EDITOR_LICENSE_H\n")
+ f.write("#define _EDITOR_LICENSE_H\n")
+ f.write("const char *const GODOT_LICENSE_TEXT =")
+
+ with open_utf8(src_license, "r") as license_file:
+ for line in license_file:
+ escaped_string = escape_string(line.strip())
+ f.write("\n\t\t\"" + escaped_string + "\\n\"")
+ f.write(";\n\n")
+
+ f.write("struct ComponentCopyrightPart {\n"
+ "\tconst char *license;\n"
+ "\tconst char *const *files;\n"
+ "\tconst char *const *copyright_statements;\n"
+ "\tint file_count;\n"
+ "\tint copyright_count;\n"
+ "};\n\n")
+
+ f.write("struct ComponentCopyright {\n"
+ "\tconst char *name;\n"
+ "\tconst ComponentCopyrightPart *parts;\n"
+ "\tint part_count;\n"
+ "};\n\n")
+
+ f.write("const char *const COPYRIGHT_INFO_DATA[] = {\n")
+ for line in data_list:
+ f.write("\t\"" + escape_string(line) + "\",\n")
+ f.write("};\n\n")
+
+ f.write("const ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n")
+ part_index = 0
+ part_indexes = {}
+ for project_name, project in iteritems(projects):
+ part_indexes[project_name] = part_index
+ for part in project:
+ f.write("\t{ \"" + escape_string(part["License"][0]) + "\", "
+ + "&COPYRIGHT_INFO_DATA[" + str(part["file_index"]) + "], "
+ + "&COPYRIGHT_INFO_DATA[" + str(part["copyright_index"]) + "], "
+ + str(len(part["Files"])) + ", "
+ + str(len(part["Copyright"])) + " },\n")
+ part_index += 1
+ f.write("};\n\n")
+
+ f.write("const int COPYRIGHT_INFO_COUNT = " + str(len(projects)) + ";\n")
+
+ f.write("const ComponentCopyright COPYRIGHT_INFO[] = {\n")
+ for project_name, project in iteritems(projects):
+ f.write("\t{ \"" + escape_string(project_name) + "\", "
+ + "&COPYRIGHT_PROJECT_PARTS[" + str(part_indexes[project_name]) + "], "
+ + str(len(project)) + " },\n")
+ f.write("};\n\n")
+
+ f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n")
+
+ f.write("const char *const LICENSE_NAMES[] = {\n")
+ for l in license_list:
+ f.write("\t\"" + escape_string(l[0]) + "\",\n")
+ f.write("};\n\n")
+
+ f.write("const char *const LICENSE_BODIES[] = {\n\n")
+ for l in license_list:
+ for line in l[1:]:
+ if line == ".":
+ f.write("\t\"\\n\"\n")
+ else:
+ f.write("\t\"" + escape_string(line) + "\\n\"\n")
+ f.write("\t\"\",\n\n")
+ f.write("};\n\n")
+
+ f.write("#endif\n")
def add_module_version_string(self,s):
self.module_version_string += "." + s
@@ -711,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):
@@ -1103,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/html/default.html b/misc/dist/html/default.html
index 4e3515a7b6..14766e8239 100644
--- a/misc/dist/html/default.html
+++ b/misc/dist/html/default.html
@@ -195,7 +195,7 @@ $GODOT_HEAD_INCLUDE
</head>
<body>
<div id="container">
- <canvas id="canvas" oncontextmenu="event.preventDefault();" width="640" height="480">
+ <canvas id="canvas" width="640" height="480">
HTML5 canvas appears to be unsupported in the current browser.<br />
Please try updating or use a different browser.
</canvas>
@@ -229,7 +229,7 @@ $GODOT_HEAD_INCLUDE
(function() {
- const BASENAME = '$GODOT_BASENAME';
+ const MAIN_PACK = '$GODOT_BASENAME.pck';
const DEBUG_ENABLED = $GODOT_DEBUG_ENABLED;
const INDETERMINATE_STATUS_STEP_MS = 100;
@@ -380,7 +380,7 @@ $GODOT_HEAD_INCLUDE
} else {
setStatusMode('indeterminate');
engine.setCanvas(canvas);
- engine.startGame(BASENAME + '.pck').then(() => {
+ engine.startGame(MAIN_PACK).then(() => {
setStatusMode('hidden');
initializing = false;
}, displayFailureNotice);
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/linux/godot.6 b/misc/dist/linux/godot.6
index 26ebe01af7..64495d817e 100644
--- a/misc/dist/linux/godot.6
+++ b/misc/dist/linux/godot.6
@@ -128,10 +128,10 @@ Print the frames per second to the stdout.
Run a script.
.TP
\fB\-\-export\fR <target>
-Export the project using the given export target.
+Export the project using the given export target. Export only main pack if path ends with .pck or .zip.
.TP
\fB\-\-export\-debug\fR
-Use together with \fB\-\-export\fR, enables debug mode for the template.
+Like \-\-export, but use debug template.
.TP
\fB\-\-doctool\fR <path>
Dump the engine API reference to the given <path> in XML format, merging if existing files are found.
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/scripts/make_icons.sh b/misc/scripts/make_icons.sh
index 71037cd1c3..5f3ea40d6a 100644
--- a/misc/scripts/make_icons.sh
+++ b/misc/scripts/make_icons.sh
@@ -1,5 +1,24 @@
-convert -resize 32x32 ../../icon.svg icon32.ico
-convert -resize 32x32 ../../icon.svg icon32.icns
-for s in 16 24 32 64 96 128 256; do convert -resize ${s}x$s ../../icon.svg icon$s.png; done
-zip icons.zip icon*.png
-rm icon*.png
+# Generate .ico, .icns and .zip set of icons for Steam
+
+# Make icons with transparent backgrounds and all sizes
+for s in 16 24 32 48 64 128 256 512 1024; do
+ convert -resize ${s}x$s -antialias \
+ -background transparent \
+ ../../icon.svg icon$s.png
+done
+
+# 16px tga file for library
+convert icon16.png icon16.tga
+
+# zip for Linux
+zip godot-icons.zip icon*.png
+
+# ico for Windows
+# Not including biggest ones or it blows up in size
+icotool -c -o godot-icon.ico icon{16,24,32,48,64,128,256}.png
+
+# icns for macOS
+# Only some sizes: http://iconhandbook.co.uk/reference/chart/osx/
+png2icns godot-icon.icns icon{16,32,128,256,512,1024}.png
+
+rm -f icon*.png
diff --git a/modules/bmp/config.py b/modules/bmp/config.py
index fb920482f5..1c8cd12a2d 100644
--- a/modules/bmp/config.py
+++ b/modules/bmp/config.py
@@ -1,7 +1,5 @@
-
-def can_build(platform):
+def can_build(env, platform):
return True
-
def configure(env):
pass
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..54431f93f1 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);
diff --git a/modules/bullet/config.py b/modules/bullet/config.py
index 0a31c2e503..92dbcf5cb0 100644
--- a/modules/bullet/config.py
+++ b/modules/bullet/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 2494063c22..2fc96a77b5 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));
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/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 3a1f5d78dd..971fd39509 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -841,20 +841,19 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
}
#endif
- btTransform body_safe_position;
- G_TO_B(p_from, body_safe_position);
- UNSCALE_BT_BASIS(body_safe_position);
+ btTransform body_transform;
+ G_TO_B(p_from, body_transform);
+ UNSCALE_BT_BASIS(body_transform);
- btVector3 recover_initial_position(0, 0, 0);
+ btVector3 initial_recover_motion(0, 0, 0);
{ /// Phase one - multi shapes depenetration using margin
for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) {
- if (!recover_from_penetration(p_body, body_safe_position, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, recover_initial_position)) {
+ if (!recover_from_penetration(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, initial_recover_motion)) {
break;
}
}
-
// Add recover movement in order to make it safe
- body_safe_position.getOrigin() += recover_initial_position;
+ body_transform.getOrigin() += initial_recover_motion;
}
btVector3 motion;
@@ -885,7 +884,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
}
btConvexShape *convex_shape_test(static_cast<btConvexShape *>(p_body->get_bt_shape(shIndex)));
- btTransform shape_world_from = body_safe_position * p_body->get_kinematic_utilities()->shapes[shIndex].transform;
+ btTransform shape_world_from = body_transform * p_body->get_kinematic_utilities()->shapes[shIndex].transform;
btTransform shape_world_to(shape_world_from);
shape_world_to.getOrigin() += motion;
@@ -903,62 +902,49 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
}
}
- body_safe_position.getOrigin() += motion;
+ body_transform.getOrigin() += motion;
}
bool has_penetration = false;
- { /// Phase three - Recover + contact test with margin
+ { /// Phase three - contact test with margin
- btVector3 delta_recover_movement(0, 0, 0);
+ btVector3 __rec(0, 0, 0);
RecoverResult r_recover_result;
- bool l_has_penetration;
- real_t l_penetration_distance = 1e20;
- for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) {
- l_has_penetration = recover_from_penetration(p_body, body_safe_position, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, delta_recover_movement, &r_recover_result);
+ has_penetration = recover_from_penetration(p_body, body_transform, 0, p_infinite_inertia, __rec, &r_recover_result);
- if (r_result) {
- B_TO_G(motion + delta_recover_movement + recover_initial_position, r_result->motion);
+ // Parse results
+ if (r_result) {
+ B_TO_G(motion + initial_recover_motion, r_result->motion);
- if (l_has_penetration) {
- has_penetration = true;
- if (l_penetration_distance <= r_recover_result.penetration_distance) {
- continue;
- }
+ if (has_penetration) {
- l_penetration_distance = r_recover_result.penetration_distance;
+ const btRigidBody *btRigid = static_cast<const btRigidBody *>(r_recover_result.other_collision_object);
+ CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(btRigid->getUserPointer());
- const btRigidBody *btRigid = static_cast<const btRigidBody *>(r_recover_result.other_collision_object);
- CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(btRigid->getUserPointer());
+ B_TO_G(motion, r_result->remainder); // is the remaining movements
+ r_result->remainder = p_motion - r_result->remainder;
- B_TO_G(motion, r_result->remainder); // is the remaining movements
- r_result->remainder = p_motion - r_result->remainder;
- B_TO_G(r_recover_result.pointWorld, r_result->collision_point);
- B_TO_G(r_recover_result.normal, r_result->collision_normal);
- B_TO_G(btRigid->getVelocityInLocalPoint(r_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_result->collider_velocity); // It calculates velocity at point and assign it using special function Bullet_to_Godot
- r_result->collider = collisionObject->get_self();
- r_result->collider_id = collisionObject->get_instance_id();
- r_result->collider_shape = r_recover_result.other_compound_shape_index;
- r_result->collision_local_shape = r_recover_result.local_shape_most_recovered;
+ B_TO_G(r_recover_result.pointWorld, r_result->collision_point);
+ B_TO_G(r_recover_result.normal, r_result->collision_normal);
+ B_TO_G(btRigid->getVelocityInLocalPoint(r_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_result->collider_velocity); // It calculates velocity at point and assign it using special function Bullet_to_Godot
+ r_result->collider = collisionObject->get_self();
+ r_result->collider_id = collisionObject->get_instance_id();
+ r_result->collider_shape = r_recover_result.other_compound_shape_index;
+ r_result->collision_local_shape = r_recover_result.local_shape_most_recovered;
#if debug_test_motion
- Vector3 sup_line2;
- B_TO_G(motion, sup_line2);
- normalLine->clear();
- normalLine->begin(Mesh::PRIMITIVE_LINES, NULL);
- normalLine->add_vertex(r_result->collision_point);
- normalLine->add_vertex(r_result->collision_point + r_result->collision_normal * 10);
- normalLine->end();
+ Vector3 sup_line2;
+ B_TO_G(motion, sup_line2);
+ normalLine->clear();
+ normalLine->begin(Mesh::PRIMITIVE_LINES, NULL);
+ normalLine->add_vertex(r_result->collision_point);
+ normalLine->add_vertex(r_result->collision_point + r_result->collision_normal * 10);
+ normalLine->end();
#endif
- } else {
- r_result->remainder = Vector3();
- }
} else {
- if (!l_has_penetration)
- break;
- else
- has_penetration = true;
+ r_result->remainder = Vector3();
}
}
}
diff --git a/modules/csg/config.py b/modules/csg/config.py
index 5f133eba90..38ccc66d91 100644
--- a/modules/csg/config.py
+++ b/modules/csg/config.py
@@ -1,5 +1,21 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
pass
+
+def get_doc_classes():
+ return [
+ "CSGBox",
+ "CSGCombiner",
+ "CSGCylinder",
+ "CSGMesh",
+ "CSGPolygon",
+ "CSGPrimitive",
+ "CSGShape",
+ "CSGSphere",
+ "CSGTorus",
+ ]
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index c1fe11d6aa..4e6e701bfd 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* csg.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 "csg.h"
#include "face3.h"
#include "geometry.h"
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
index bb67e1fb36..53303a6533 100644
--- a/modules/csg/csg.h
+++ b/modules/csg/csg.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* csg.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 CSG_H
#define CSG_H
diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp
index 06cbaab3b0..2150320c4a 100644
--- a/modules/csg/csg_gizmos.cpp
+++ b/modules/csg/csg_gizmos.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* csg_gizmos.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 "csg_gizmos.h"
///////////
diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h
index b5e394ecad..68e916823b 100644
--- a/modules/csg/csg_gizmos.h
+++ b/modules/csg/csg_gizmos.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* csg_gizmos.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 CSG_GIZMOS_H
#define CSG_GIZMOS_H
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index 1f2f12f54d..5f13474d2c 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* csg_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 "csg_shape.h"
#include "scene/3d/path.h"
@@ -132,6 +162,10 @@ CSGBrush *CSGShape::_get_brush() {
void CSGShape::_update_shape() {
//print_line("updating shape for " + String(get_path()));
+
+ if (parent)
+ return;
+
set_base(RID());
root_mesh.unref(); //byebye root mesh
@@ -319,6 +353,10 @@ void CSGShape::_notification(int p_what) {
Node *parentn = get_parent();
if (parentn) {
parent = Object::cast_to<CSGShape>(parentn);
+ if (parent) {
+ set_base(RID());
+ root_mesh.unref();
+ }
}
if (use_collision && is_root_shape()) {
@@ -341,6 +379,7 @@ void CSGShape::_notification(int p_what) {
}
if (p_what == NOTIFICATION_EXIT_TREE) {
+
if (parent)
parent->_make_dirty();
parent = NULL;
@@ -389,9 +428,9 @@ void CSGShape::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap");
- BIND_CONSTANT(OPERATION_UNION);
- BIND_CONSTANT(OPERATION_INTERSECTION);
- BIND_CONSTANT(OPERATION_SUBTRACTION);
+ BIND_ENUM_CONSTANT(OPERATION_UNION);
+ BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
+ BIND_ENUM_CONSTANT(OPERATION_SUBTRACTION);
}
CSGShape::CSGShape() {
@@ -966,9 +1005,9 @@ void CSGBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CSGBox::get_material);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "width", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_height", "get_height");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_depth", "get_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "width", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_height", "get_height");
+ 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::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
}
@@ -1178,8 +1217,8 @@ void CSGCylinder::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder::set_smooth_faces);
ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder::get_smooth_faces);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cone"), "set_cone", "is_cone");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
@@ -1403,8 +1442,8 @@ void CSGTorus::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus::set_smooth_faces);
ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus::get_smooth_faces);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_inner_radius", "get_inner_radius");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_outer_radius", "get_outer_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "inner_radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_inner_radius", "get_inner_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "outer_radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_outer_radius", "get_outer_radius");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_sides", "get_ring_sides");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
@@ -1978,11 +2017,11 @@ void CSGPolygon::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_depth", "get_depth");
+ 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::REAL, "path_interval", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_path_interval", "get_path_interval");
+ 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, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index c1d2cce606..cbb5c7e041 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* csg_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 CSG_SHAPE_H
#define CSG_SHAPE_H
diff --git a/modules/csg/doc_classes/CSGBox.xml b/modules/csg/doc_classes/CSGBox.xml
new file mode 100644
index 0000000000..80455fda80
--- /dev/null
+++ b/modules/csg/doc_classes/CSGBox.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGBox" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="depth" type="float" setter="set_depth" getter="get_depth">
+ </member>
+ <member name="height" type="float" setter="set_height" getter="get_height">
+ </member>
+ <member name="material" type="Material" setter="set_material" getter="get_material">
+ </member>
+ <member name="width" type="float" setter="set_width" getter="get_width">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/theora/doc_classes/ResourceImporterTheora.xml b/modules/csg/doc_classes/CSGCombiner.xml
index 5fc40a6eba..b2265d7703 100644
--- a/modules/theora/doc_classes/ResourceImporterTheora.xml
+++ b/modules/csg/doc_classes/CSGCombiner.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.1">
+<class name="CSGCombiner" inherits="CSGShape" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/csg/doc_classes/CSGCylinder.xml b/modules/csg/doc_classes/CSGCylinder.xml
new file mode 100644
index 0000000000..0cab26ad3d
--- /dev/null
+++ b/modules/csg/doc_classes/CSGCylinder.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGCylinder" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="cone" type="bool" setter="set_cone" getter="is_cone">
+ </member>
+ <member name="height" type="float" setter="set_height" getter="get_height">
+ </member>
+ <member name="material" type="Material" setter="set_material" getter="get_material">
+ </member>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius">
+ </member>
+ <member name="sides" type="int" setter="set_sides" getter="get_sides">
+ </member>
+ <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGMesh.xml b/modules/csg/doc_classes/CSGMesh.xml
new file mode 100644
index 0000000000..e5c3e5ccf3
--- /dev/null
+++ b/modules/csg/doc_classes/CSGMesh.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGMesh" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGPolygon.xml b/modules/csg/doc_classes/CSGPolygon.xml
new file mode 100644
index 0000000000..379c512d6a
--- /dev/null
+++ b/modules/csg/doc_classes/CSGPolygon.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGPolygon" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="depth" type="float" setter="set_depth" getter="get_depth">
+ </member>
+ <member name="material" type="Material" setter="set_material" getter="get_material">
+ </member>
+ <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="CSGPolygon.Mode">
+ </member>
+ <member name="path_interval" type="float" setter="set_path_interval" getter="get_path_interval">
+ </member>
+ <member name="path_node" type="NodePath" setter="set_path_node" getter="get_path_node">
+ </member>
+ <member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon.PathRotation">
+ </member>
+ <member name="polygon" type="PoolVector2Array" setter="set_polygon" getter="get_polygon">
+ </member>
+ <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ </member>
+ <member name="spin_degrees" type="float" setter="set_spin_degrees" getter="get_spin_degrees">
+ </member>
+ <member name="spin_sides" type="int" setter="set_spin_sides" getter="get_spin_sides">
+ </member>
+ </members>
+ <constants>
+ <constant name="MODE_DEPTH" value="0" enum="Mode">
+ </constant>
+ <constant name="MODE_SPIN" value="1" enum="Mode">
+ </constant>
+ <constant name="MODE_PATH" value="2" enum="Mode">
+ </constant>
+ <constant name="PATH_ROTATION_POLYGON" value="0" enum="PathRotation">
+ </constant>
+ <constant name="PATH_ROTATION_PATH" value="1" enum="PathRotation">
+ </constant>
+ <constant name="PATH_ROTATION_PATH_FOLLOW" value="2" enum="PathRotation">
+ </constant>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGPrimitive.xml b/modules/csg/doc_classes/CSGPrimitive.xml
new file mode 100644
index 0000000000..bf41c40f22
--- /dev/null
+++ b/modules/csg/doc_classes/CSGPrimitive.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGPrimitive" inherits="CSGShape" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="invert_faces" type="bool" setter="set_invert_faces" getter="is_inverting_faces">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGShape.xml b/modules/csg/doc_classes/CSGShape.xml
new file mode 100644
index 0000000000..cf236a4207
--- /dev/null
+++ b/modules/csg/doc_classes/CSGShape.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGShape" inherits="VisualInstance" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="is_root_shape" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape.Operation">
+ </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">
+ </member>
+ </members>
+ <constants>
+ <constant name="OPERATION_UNION" value="0" enum="Operation">
+ </constant>
+ <constant name="OPERATION_INTERSECTION" value="1" enum="Operation">
+ </constant>
+ <constant name="OPERATION_SUBTRACTION" value="2" enum="Operation">
+ </constant>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGSphere.xml b/modules/csg/doc_classes/CSGSphere.xml
new file mode 100644
index 0000000000..520368506e
--- /dev/null
+++ b/modules/csg/doc_classes/CSGSphere.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGSphere" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="material" type="Material" setter="set_material" getter="get_material">
+ </member>
+ <member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments">
+ </member>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius">
+ </member>
+ <member name="rings" type="int" setter="set_rings" getter="get_rings">
+ </member>
+ <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGTorus.xml b/modules/csg/doc_classes/CSGTorus.xml
new file mode 100644
index 0000000000..58bbef2600
--- /dev/null
+++ b/modules/csg/doc_classes/CSGTorus.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGTorus" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius">
+ </member>
+ <member name="material" type="Material" setter="set_material" getter="get_material">
+ </member>
+ <member name="outer_radius" type="float" setter="set_outer_radius" getter="get_outer_radius">
+ </member>
+ <member name="ring_sides" type="int" setter="set_ring_sides" getter="get_ring_sides">
+ </member>
+ <member name="sides" type="int" setter="set_sides" getter="get_sides">
+ </member>
+ <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp
index 020724ee59..0dea09808a 100644
--- a/modules/csg/register_types.cpp
+++ b/modules/csg/register_types.cpp
@@ -30,8 +30,8 @@
#include "register_types.h"
-#include "csg_shape.h"
#include "csg_gizmos.h"
+#include "csg_shape.h"
void register_csg_types() {
@@ -51,9 +51,7 @@ void register_csg_types() {
EditorPlugins::add_by_type<EditorPluginCSG>();
#endif
#endif
-
}
void unregister_csg_types() {
-
}
diff --git a/modules/dds/config.py b/modules/dds/config.py
index 5f133eba90..1c8cd12a2d 100644
--- a/modules/dds/config.py
+++ b/modules/dds/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/enet/config.py b/modules/enet/config.py
index 8031fbb4b6..3e30bbe778 100644
--- a/modules/enet/config.py
+++ b/modules/enet/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
index 7bee63019b..fab4b05da9 100644
--- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
+++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
@@ -7,8 +7,8 @@
A PacketPeer implementation that should be passed to [method SceneTree.set_network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html
- http://enet.bespin.org/usergroup0.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html</link>
+ <link>http://enet.bespin.org/usergroup0.html</link>
</tutorials>
<demos>
</demos>
@@ -16,6 +16,8 @@
<method name="close_connection">
<return type="void">
</return>
+ <argument index="0" name="wait_usec" type="int" default="100">
+ </argument>
<description>
Closes the connection. Ignored if no connection is currently established. If this is a server it tries to notify all clients before forcibly disconnecting them. If this is a client it simply closes the connection to the server.
</description>
@@ -23,7 +25,7 @@
<method name="create_client">
<return type="int" enum="Error">
</return>
- <argument index="0" name="ip" type="String">
+ <argument index="0" name="address" type="String">
</argument>
<argument index="1" name="port" type="int">
</argument>
@@ -31,8 +33,10 @@
</argument>
<argument index="3" name="out_bandwidth" type="int" default="0">
</argument>
+ <argument index="4" name="client_port" type="int" default="0">
+ </argument>
<description>
- Create client that connects to a server at address [code]ip[/code] using specified [code]port[/code]. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]192.168.1.1[/code]. The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [code]OK[/code] if a client was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the client could not be created.
+ Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain nome (e.g. [code]www.example.com[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]192.168.1.1[/code]). The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [code]OK[/code] if a client was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the client could not be created. If [code]client_port[/code] is specified, the client will also listen to the given port, this is useful in some NAT traveral technique.
</description>
</method>
<method name="create_server">
@@ -50,6 +54,49 @@
Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]*[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4096 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [code]OK[/code] if a server was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the server could not be created.
</description>
</method>
+ <method name="disconnect_peer">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="now" type="bool" default="false">
+ </argument>
+ <description>
+ Disconnect the given peer. If "now" is set to true, the connection will be closed immediately without flushing queued messages.
+ </description>
+ </method>
+ <method name="get_last_packet_channel" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the channel of the last packet fetched via [method PacketPeer.get_packet]
+ </description>
+ </method>
+ <method name="get_packet_channel" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the channel of the next packet that will be retrieved via [method PacketPeer.get_packet_peer]
+ </description>
+ </method>
+ <method name="get_peer_address" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Returns the IP address of the given peer.
+ </description>
+ </method>
+ <method name="get_peer_port" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Returns the remote port of the given peer.
+ </description>
+ </method>
<method name="set_bind_ip">
<return type="void">
</return>
@@ -61,9 +108,18 @@
</method>
</methods>
<members>
+ <member name="always_ordered" type="bool" setter="set_always_ordered" getter="is_always_ordered">
+ Always use [code]TRANSFER_MODE_ORDERED[/code] in place of [code]TRANSFER_MODE_UNRELIABLE[/code]. This is the only way to use ordering with the RPC system.
+ </member>
+ <member name="channel_count" type="int" setter="set_channel_count" getter="get_channel_count">
+ The number of channels to be used by ENet. Default: [code]3[/code]. Channels are used to separate different kinds of data. In realiable or ordered mode, for example, the packet delivery order is ensured on a per channel basis.
+ </member>
<member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode">
The compression method used for network packets. Default is no compression. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all.
</member>
+ <member name="transfer_channel" type="int" setter="set_transfer_channel" getter="get_transfer_channel">
+ Set the default channel to be used to transfer data. By default this value is [code]-1[/code] which means that ENet will only use 2 channels, one for reliable and one for unreliable packets. Channel [code]0[/code] is reserved, and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data.
+ </member>
</members>
<constants>
<constant name="COMPRESS_NONE" value="0" enum="CompressionMode">
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index bd76c766a0..88768829d7 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -55,6 +55,22 @@ int NetworkedMultiplayerENet::get_packet_peer() const {
return incoming_packets.front()->get().from;
}
+int NetworkedMultiplayerENet::get_packet_channel() const {
+
+ ERR_FAIL_COND_V(!active, -1);
+ ERR_FAIL_COND_V(incoming_packets.size() == 0, -1);
+
+ return incoming_packets.front()->get().channel;
+}
+
+int NetworkedMultiplayerENet::get_last_packet_channel() const {
+
+ ERR_FAIL_COND_V(!active, -1);
+ ERR_FAIL_COND_V(!current_packet.packet, -1);
+
+ return current_packet.channel;
+}
+
Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int p_in_bandwidth, int p_out_bandwidth) {
ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE);
@@ -83,7 +99,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
host = enet_host_create(&address /* the address to bind the server host to */,
p_max_clients /* allow up to 32 clients and/or outgoing connections */,
- SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
+ channel_count /* allow up to channel_count to be used */,
p_in_bandwidth /* limit incoming bandwith if > 0 */,
p_out_bandwidth /* limit outgoing bandwith if > 0 */);
@@ -127,13 +143,13 @@ Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_por
host = enet_host_create(&c_client /* create a client host */,
1 /* only allow 1 outgoing connection */,
- SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
+ channel_count /* allow up to channel_count to be used */,
p_in_bandwidth /* limit incoming bandwith if > 0 */,
p_out_bandwidth /* limit outgoing bandwith if > 0 */);
} else {
host = enet_host_create(NULL /* create a client host */,
1 /* only allow 1 outgoing connection */,
- SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
+ channel_count /* allow up to channel_count to be used */,
p_in_bandwidth /* limit incoming bandwith if > 0 */,
p_out_bandwidth /* limit outgoing bandwith if > 0 */);
}
@@ -167,7 +183,7 @@ Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_por
unique_id = _gen_unique_id();
// Initiate connection, allocating enough channels
- ENetPeer *peer = enet_host_connect(host, &address, SYSCH_MAX, unique_id);
+ ENetPeer *peer = enet_host_connect(host, &address, channel_count, unique_id);
if (peer == NULL) {
enet_host_destroy(host);
@@ -316,7 +332,7 @@ void NetworkedMultiplayerENet::poll() {
}
enet_packet_destroy(event.packet);
- } else if (event.channelID < SYSCH_MAX) {
+ } else if (event.channelID < channel_count) {
Packet packet;
packet.packet = event.packet;
@@ -330,6 +346,7 @@ void NetworkedMultiplayerENet::poll() {
uint32_t flags = decode_uint32(&event.packet->data[8]);
packet.from = source;
+ packet.channel = event.channelID;
if (server) {
// Someone is cheating and trying to fake the source!
@@ -496,7 +513,10 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
switch (transfer_mode) {
case TRANSFER_MODE_UNRELIABLE: {
- packet_flags = ENET_PACKET_FLAG_UNSEQUENCED;
+ if (always_ordered)
+ packet_flags = 0;
+ else
+ packet_flags = ENET_PACKET_FLAG_UNSEQUENCED;
channel = SYSCH_UNRELIABLE;
} break;
case TRANSFER_MODE_UNRELIABLE_ORDERED: {
@@ -509,6 +529,9 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
} break;
}
+ if (transfer_channel > SYSCH_CONFIG)
+ channel = transfer_channel;
+
Map<int, ENetPeer *>::Element *E = NULL;
if (target_peer != 0) {
@@ -572,6 +595,7 @@ void NetworkedMultiplayerENet::_pop_current_packet() {
enet_packet_destroy(current_packet.packet);
current_packet.packet = NULL;
current_packet.from = 0;
+ current_packet.channel = -1;
}
}
@@ -759,6 +783,40 @@ int NetworkedMultiplayerENet::get_peer_port(int p_peer_id) const {
#endif
}
+void NetworkedMultiplayerENet::set_transfer_channel(int p_channel) {
+
+ ERR_FAIL_COND(p_channel < -1 || p_channel >= channel_count);
+
+ if (p_channel == SYSCH_CONFIG) {
+ ERR_EXPLAIN("Channel " + itos(SYSCH_CONFIG) + " is reserved");
+ ERR_FAIL();
+ }
+ transfer_channel = p_channel;
+}
+
+int NetworkedMultiplayerENet::get_transfer_channel() const {
+ return transfer_channel;
+}
+
+void NetworkedMultiplayerENet::set_channel_count(int p_channel) {
+
+ ERR_FAIL_COND(active);
+ ERR_FAIL_COND(p_channel < SYSCH_MAX);
+ channel_count = p_channel;
+}
+
+int NetworkedMultiplayerENet::get_channel_count() const {
+ return channel_count;
+}
+
+void NetworkedMultiplayerENet::set_always_ordered(bool p_ordered) {
+ always_ordered = p_ordered;
+}
+
+bool NetworkedMultiplayerENet::is_always_ordered() const {
+ return always_ordered;
+}
+
void NetworkedMultiplayerENet::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0));
@@ -768,10 +826,22 @@ void NetworkedMultiplayerENet::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_compression_mode", "mode"), &NetworkedMultiplayerENet::set_compression_mode);
ClassDB::bind_method(D_METHOD("get_compression_mode"), &NetworkedMultiplayerENet::get_compression_mode);
ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &NetworkedMultiplayerENet::set_bind_ip);
- ClassDB::bind_method(D_METHOD("get_peer_address"), &NetworkedMultiplayerENet::get_peer_address);
- ClassDB::bind_method(D_METHOD("get_peer_port"), &NetworkedMultiplayerENet::get_peer_port);
+ ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &NetworkedMultiplayerENet::get_peer_address);
+ ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &NetworkedMultiplayerENet::get_peer_port);
+
+ ClassDB::bind_method(D_METHOD("get_packet_channel"), &NetworkedMultiplayerENet::get_packet_channel);
+ ClassDB::bind_method(D_METHOD("get_last_packet_channel"), &NetworkedMultiplayerENet::get_last_packet_channel);
+ ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &NetworkedMultiplayerENet::set_transfer_channel);
+ ClassDB::bind_method(D_METHOD("get_transfer_channel"), &NetworkedMultiplayerENet::get_transfer_channel);
+ ClassDB::bind_method(D_METHOD("set_channel_count", "channels"), &NetworkedMultiplayerENet::set_channel_count);
+ ClassDB::bind_method(D_METHOD("get_channel_count"), &NetworkedMultiplayerENet::get_channel_count);
+ ClassDB::bind_method(D_METHOD("set_always_ordered", "ordered"), &NetworkedMultiplayerENet::set_always_ordered);
+ ClassDB::bind_method(D_METHOD("is_always_ordered"), &NetworkedMultiplayerENet::is_always_ordered);
ADD_PROPERTY(PropertyInfo(Variant::INT, "compression_mode", PROPERTY_HINT_ENUM, "None,Range Coder,FastLZ,ZLib,ZStd"), "set_compression_mode", "get_compression_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel"), "set_transfer_channel", "get_transfer_channel");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "channel_count"), "set_channel_count", "get_channel_count");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "always_ordered"), "set_always_ordered", "is_always_ordered");
BIND_ENUM_CONSTANT(COMPRESS_NONE);
BIND_ENUM_CONSTANT(COMPRESS_RANGE_CODER);
@@ -789,6 +859,9 @@ NetworkedMultiplayerENet::NetworkedMultiplayerENet() {
target_peer = 0;
current_packet.packet = NULL;
transfer_mode = TRANSFER_MODE_RELIABLE;
+ channel_count = SYSCH_MAX;
+ transfer_channel = -1;
+ always_ordered = false;
connection_status = CONNECTION_DISCONNECTED;
compression_mode = COMPRESS_NONE;
enet_compressor.context = this;
diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h
index d481f5d496..705807d429 100644
--- a/modules/enet/networked_multiplayer_enet.h
+++ b/modules/enet/networked_multiplayer_enet.h
@@ -68,6 +68,9 @@ private:
int target_peer;
TransferMode transfer_mode;
+ int transfer_channel;
+ int channel_count;
+ bool always_ordered;
ENetEvent event;
ENetPeer *peer;
@@ -83,6 +86,7 @@ private:
ENetPacket *packet;
int from;
+ int channel;
};
CompressionMode compression_mode;
@@ -145,6 +149,15 @@ public:
void set_compression_mode(CompressionMode p_mode);
CompressionMode get_compression_mode() const;
+ int get_packet_channel() const;
+ int get_last_packet_channel() const;
+ void set_transfer_channel(int p_channel);
+ int get_transfer_channel() const;
+ void set_channel_count(int p_channel);
+ int get_channel_count() const;
+ void set_always_ordered(bool p_ordered);
+ bool is_always_ordered() const;
+
NetworkedMultiplayerENet();
~NetworkedMultiplayerENet();
diff --git a/modules/etc/config.py b/modules/etc/config.py
index 395fc1bb02..098f1eafa9 100644
--- a/modules/etc/config.py
+++ b/modules/etc/config.py
@@ -1,9 +1,5 @@
-def can_build(platform):
- return True
+def can_build(env, platform):
+ return env['tools']
def configure(env):
- # Tools only, disabled for non-tools
- # TODO: Find a cleaner way to achieve that
- if not env['tools']:
- env['module_etc_enabled'] = False
- env.disabled_modules.append("etc")
+ pass
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index 8a7c2a773a..301f218361 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -17,18 +17,15 @@ if env['builtin_freetype']:
"src/base/ftbitmap.c",
"src/base/ftcid.c",
"src/base/ftdebug.c",
- "src/base/ftfntfmt.c",
"src/base/ftfstype.c",
"src/base/ftgasp.c",
"src/base/ftglyph.c",
"src/base/ftgxval.c",
"src/base/ftinit.c",
- "src/base/ftlcdfil.c",
"src/base/ftmm.c",
"src/base/ftotval.c",
"src/base/ftpatent.c",
"src/base/ftpfr.c",
- "src/base/ftpic.c",
"src/base/ftstroke.c",
"src/base/ftsynth.c",
"src/base/ftsystem.c",
diff --git a/modules/freetype/config.py b/modules/freetype/config.py
index 5f133eba90..1c8cd12a2d 100644
--- a/modules/freetype/config.py
+++ b/modules/freetype/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub
index 8654ef3d82..116a86b27b 100644
--- a/modules/gdnative/SCsub
+++ b/modules/gdnative/SCsub
@@ -5,6 +5,7 @@ Import('env')
gdn_env = env.Clone()
gdn_env.add_source_files(env.modules_sources, "gdnative.cpp")
gdn_env.add_source_files(env.modules_sources, "register_types.cpp")
+gdn_env.add_source_files(env.modules_sources, "android/*.cpp")
gdn_env.add_source_files(env.modules_sources, "gdnative/*.cpp")
gdn_env.add_source_files(env.modules_sources, "nativescript/*.cpp")
gdn_env.add_source_files(env.modules_sources, "gdnative_library_singleton_editor.cpp")
@@ -12,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")
@@ -23,7 +25,8 @@ def _build_gdnative_api_struct_header(api):
'\textern const godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct;'
]
- for name in api['extensions']:
+ for ext in api['extensions']:
+ name = ext['name']
gdnative_api_init_macro.append(
'\textern const godot_gdnative_ext_{0}_api_struct *_gdnative_wrapper_{0}_api_struct;'.format(name))
@@ -31,9 +34,10 @@ def _build_gdnative_api_struct_header(api):
gdnative_api_init_macro.append('\tfor (unsigned int i = 0; i < _gdnative_wrapper_api_struct->num_extensions; i++) { ')
gdnative_api_init_macro.append('\t\tswitch (_gdnative_wrapper_api_struct->extensions[i]->type) {')
- for name in api['extensions']:
+ for ext in api['extensions']:
+ name = ext['name']
gdnative_api_init_macro.append(
- '\t\t\tcase GDNATIVE_EXT_%s:' % api['extensions'][name]['type'])
+ '\t\t\tcase GDNATIVE_EXT_%s:' % ext['type'])
gdnative_api_init_macro.append(
'\t\t\t\t_gdnative_wrapper_{0}_api_struct = (godot_gdnative_ext_{0}_api_struct *)'
' _gdnative_wrapper_api_struct->extensions[i];'.format(name))
@@ -47,6 +51,7 @@ def _build_gdnative_api_struct_header(api):
'#define GODOT_GDNATIVE_API_STRUCT_H',
'',
'#include <gdnative/gdnative.h>',
+ '#include <android/godot_android.h>',
'#include <arvr/godot_arvr.h>',
'#include <nativescript/godot_nativescript.h>',
'#include <pluginscript/godot_pluginscript.h>',
@@ -61,8 +66,8 @@ def _build_gdnative_api_struct_header(api):
'\tGDNATIVE_' + api['core']['type'] + ','
]
- for name in api['extensions']:
- out += ['\tGDNATIVE_EXT_' + api['extensions'][name]['type'] + ',']
+ for ext in api['extensions']:
+ out += ['\tGDNATIVE_EXT_' + ext['type'] + ',']
out += ['};', '']
@@ -88,8 +93,9 @@ def _build_gdnative_api_struct_header(api):
return ret_val
- for name in api['extensions']:
- out += generate_extension_struct(name, api['extensions'][name], False)
+ for ext in api['extensions']:
+ name = ext['name']
+ out += generate_extension_struct(name, ext, False)
out += [
'typedef struct godot_gdnative_core_api_struct {',
@@ -151,12 +157,14 @@ def _build_gdnative_api_struct_source(api):
return ret_val
- for name in api['extensions']:
- out += get_extension_struct_definition(name, api['extensions'][name], False)
+ for ext in api['extensions']:
+ name = ext['name']
+ out += get_extension_struct_definition(name, ext, False)
out += ['', 'const godot_gdnative_api_struct *gdnative_extensions_pointers[] = {']
- for name in api['extensions']:
+ for ext in api['extensions']:
+ name = ext['name']
out += ['\t(godot_gdnative_api_struct *)&api_extension_' + name + '_struct,']
out += ['};\n']
@@ -189,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])
@@ -214,7 +222,8 @@ def _build_gdnative_wrapper_code(api):
'godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct = 0;',
]
- for name in api['extensions']:
+ for ext in api['extensions']:
+ name = ext['name']
out.append('godot_gdnative_ext_' + name + '_api_struct *_gdnative_wrapper_' + name + '_api_struct = 0;')
out += ['']
@@ -232,8 +241,9 @@ def _build_gdnative_wrapper_code(api):
out.append('}')
out.append('')
- for name in api['extensions']:
- for funcdef in api['extensions'][name]['api']:
+ for ext in api['extensions']:
+ name = ext['name']
+ for funcdef in ext['api']:
args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args))
@@ -267,8 +277,8 @@ 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)
+#build 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/android/android_gdn.cpp b/modules/gdnative/android/android_gdn.cpp
new file mode 100644
index 0000000000..edc948e086
--- /dev/null
+++ b/modules/gdnative/android/android_gdn.cpp
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* android_gdn.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 "modules/gdnative/gdnative.h"
+
+// Code by Paritosh97 with minor tweaks by Mux213
+// These entry points are only for the android platform and are simple stubs in all others.
+
+#ifdef __ANDROID__
+#include "platform/android/thread_jandroid.h"
+#else
+#define JNIEnv void
+#define jobject void *
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEnv *GDAPI godot_android_get_env() {
+#ifdef __ANDROID__
+ return ThreadAndroid::get_env();
+#else
+ return NULL;
+#endif
+}
+
+jobject GDAPI godot_android_get_activity() {
+#ifdef __ANDROID__
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ jclass activityThread = env->FindClass("android/app/ActivityThread");
+ jmethodID currentActivityThread = env->GetStaticMethodID(activityThread, "currentActivityThread", "()Landroid/app/ActivityThread;");
+ jobject at = env->CallStaticObjectMethod(activityThread, currentActivityThread);
+ jmethodID getApplication = env->GetMethodID(activityThread, "getApplication", "()Landroid/app/Application;");
+ jobject context = env->CallObjectMethod(at, getApplication);
+
+ return env->NewGlobalRef(context);
+#else
+ return NULL;
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.cpp b/modules/gdnative/arvr/arvr_interface_gdnative.cpp
index 49e0a19d9e..385c020a78 100644
--- a/modules/gdnative/arvr/arvr_interface_gdnative.cpp
+++ b/modules/gdnative/arvr/arvr_interface_gdnative.cpp
@@ -217,6 +217,10 @@ void ARVRInterfaceGDNative::process() {
extern "C" {
void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_interface) {
+ // If our major version is 0 or bigger then 10, we're likely looking at our constructor pointer from an older plugin
+ ERR_EXPLAINC("GDNative ARVR interfaces build for Godot 3.0 are not supported");
+ ERR_FAIL_COND((p_interface->version.major == 0) || (p_interface->version.major > 10));
+
Ref<ARVRInterfaceGDNative> new_interface;
new_interface.instance();
new_interface->set_interface((godot_arvr_interface_gdnative *const)p_interface);
diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py
index 68148c4d87..c5b37d35b4 100644
--- a/modules/gdnative/config.py
+++ b/modules/gdnative/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
@@ -10,6 +10,7 @@ def get_doc_classes():
"GDNative",
"GDNativeLibrary",
"NativeScript",
+ "PacketPeerGDNative",
"PluginScript",
]
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index 897588385a..e7ebcc73af 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -469,7 +469,9 @@ Variant GDNative::call_native(StringName p_native_call_type, StringName p_proced
godot_variant result = E->get()(procedure_handle, (godot_array *)&p_arguments);
- return *(Variant *)&result;
+ Variant res = *(Variant *)&result;
+ godot_variant_destroy(&result);
+ return res;
}
Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional) {
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index f41c3859bd..217fd87c3e 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -5756,8 +5756,9 @@
}
]
},
- "extensions": {
- "nativescript": {
+ "extensions": [
+ {
+ "name": "nativescript",
"type": "NATIVESCRIPT",
"version": {
"major": 1,
@@ -5942,7 +5943,8 @@
}
]
},
- "pluginscript": {
+ {
+ "name": "pluginscript",
"type": "PLUGINSCRIPT",
"version": {
"major": 1,
@@ -5959,8 +5961,9 @@
}
]
},
- "arvr": {
- "type": "ARVR",
+ {
+ "name": "android",
+ "type": "ANDROID",
"version": {
"major": 1,
"minor": 0
@@ -5968,6 +5971,29 @@
"next": null,
"api": [
{
+ "name": "godot_android_get_env",
+ "return_type": "JNIEnv*",
+ "arguments": [
+ ]
+ },
+ {
+ "name": "godot_android_get_activity",
+ "return_type": "jobject",
+ "arguments": [
+ ]
+ }
+ ]
+ },
+ {
+ "name": "arvr",
+ "type": "ARVR",
+ "version": {
+ "major": 1,
+ "minor": 1
+ },
+ "next": null,
+ "api": [
+ {
"name": "godot_arvr_register_interface",
"return_type": "void",
"arguments": [
@@ -6055,5 +6081,5 @@
}
]
}
- }
+ ]
}
diff --git a/modules/gdnative/include/android/godot_android.h b/modules/gdnative/include/android/godot_android.h
new file mode 100644
index 0000000000..832dac9ac3
--- /dev/null
+++ b/modules/gdnative/include/android/godot_android.h
@@ -0,0 +1,54 @@
+/*************************************************************************/
+/* godot_android.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_ANDROID_GDN_H
+#define GODOT_ANDROID_GDN_H
+
+#include <gdnative/gdnative.h>
+
+#ifdef __ANDROID__
+#include <jni.h>
+#else
+#define JNIEnv void
+#define jobject void *
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEnv *GDAPI godot_android_get_env();
+jobject GDAPI godot_android_get_activity();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !GODOT_ANDROID_GDN_H */
diff --git a/modules/gdnative/include/arvr/godot_arvr.h b/modules/gdnative/include/arvr/godot_arvr.h
index b9aedc0bef..63de62b507 100644
--- a/modules/gdnative/include/arvr/godot_arvr.h
+++ b/modules/gdnative/include/arvr/godot_arvr.h
@@ -37,7 +37,15 @@
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 GODOTVR_API_MAJOR 1
+#define GODOTVR_API_MINOR 0
+
typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
void *(*constructor)(godot_object *);
void (*destructor)(void *);
godot_string (*get_name)(const void *);
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index cfbe16fa7d..f28ba352ab 100644
--- a/modules/gdnative/include/nativescript/godot_nativescript.h
+++ b/modules/gdnative/include/nativescript/godot_nativescript.h
@@ -43,6 +43,9 @@ typedef enum {
GODOT_METHOD_RPC_MODE_SYNC,
GODOT_METHOD_RPC_MODE_MASTER,
GODOT_METHOD_RPC_MODE_SLAVE,
+ GODOT_METHOD_RPC_MODE_REMOTESYNC,
+ GODOT_METHOD_RPC_MODE_MASTERSYNC,
+ GODOT_METHOD_RPC_MODE_SLAVESYNC,
} godot_method_rpc_mode;
typedef enum {
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 cf8977f3ea..7bab718b81 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);
@@ -747,7 +766,7 @@ Ref<Script> NativeScriptInstance::get_script() const {
return script;
}
-NativeScriptInstance::RPCMode NativeScriptInstance::get_rpc_mode(const StringName &p_method) const {
+MultiplayerAPI::RPCMode NativeScriptInstance::get_rpc_mode(const StringName &p_method) const {
NativeScriptDesc *script_data = GET_SCRIPT_DESC();
@@ -757,27 +776,33 @@ NativeScriptInstance::RPCMode NativeScriptInstance::get_rpc_mode(const StringNam
if (E) {
switch (E->get().rpc_mode) {
case GODOT_METHOD_RPC_MODE_DISABLED:
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
case GODOT_METHOD_RPC_MODE_REMOTE:
- return RPC_MODE_REMOTE;
+ return MultiplayerAPI::RPC_MODE_REMOTE;
case GODOT_METHOD_RPC_MODE_SYNC:
- return RPC_MODE_SYNC;
+ return MultiplayerAPI::RPC_MODE_SYNC;
case GODOT_METHOD_RPC_MODE_MASTER:
- return RPC_MODE_MASTER;
+ return MultiplayerAPI::RPC_MODE_MASTER;
case GODOT_METHOD_RPC_MODE_SLAVE:
- return RPC_MODE_SLAVE;
+ return MultiplayerAPI::RPC_MODE_SLAVE;
+ case GODOT_METHOD_RPC_MODE_REMOTESYNC:
+ return MultiplayerAPI::RPC_MODE_REMOTESYNC;
+ case GODOT_METHOD_RPC_MODE_MASTERSYNC:
+ return MultiplayerAPI::RPC_MODE_MASTERSYNC;
+ case GODOT_METHOD_RPC_MODE_SLAVESYNC:
+ return MultiplayerAPI::RPC_MODE_SLAVESYNC;
default:
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
script_data = script_data->base_data;
}
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
-NativeScriptInstance::RPCMode NativeScriptInstance::get_rset_mode(const StringName &p_variable) const {
+MultiplayerAPI::RPCMode NativeScriptInstance::get_rset_mode(const StringName &p_variable) const {
NativeScriptDesc *script_data = GET_SCRIPT_DESC();
@@ -787,24 +812,24 @@ NativeScriptInstance::RPCMode NativeScriptInstance::get_rset_mode(const StringNa
if (E) {
switch (E.get().rset_mode) {
case GODOT_METHOD_RPC_MODE_DISABLED:
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
case GODOT_METHOD_RPC_MODE_REMOTE:
- return RPC_MODE_REMOTE;
+ return MultiplayerAPI::RPC_MODE_REMOTE;
case GODOT_METHOD_RPC_MODE_SYNC:
- return RPC_MODE_SYNC;
+ return MultiplayerAPI::RPC_MODE_SYNC;
case GODOT_METHOD_RPC_MODE_MASTER:
- return RPC_MODE_MASTER;
+ return MultiplayerAPI::RPC_MODE_MASTER;
case GODOT_METHOD_RPC_MODE_SLAVE:
- return RPC_MODE_SLAVE;
+ return MultiplayerAPI::RPC_MODE_SLAVE;
default:
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
script_data = script_data->base_data;
}
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
ScriptLanguage *NativeScriptInstance::get_language() {
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 68a8126a32..be093dde4b 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);
@@ -196,8 +199,8 @@ public:
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
virtual void notification(int p_notification);
virtual Ref<Script> get_script() const;
- virtual RPCMode get_rpc_mode(const StringName &p_method) const;
- virtual RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
virtual ScriptLanguage *get_language();
virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
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/gdnative/net/packet_peer_gdnative.h b/modules/gdnative/net/packet_peer_gdnative.h
new file mode 100644
index 0000000000..71814177ed
--- /dev/null
+++ b/modules/gdnative/net/packet_peer_gdnative.h
@@ -0,0 +1,59 @@
+/*************************************************************************/
+/* packet_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 PACKET_PEER_GDNATIVE_H
+#define PACKET_PEER_GDNATIVE_H
+
+#include "core/io/packet_peer.h"
+#include "modules/gdnative/gdnative.h"
+#include "modules/gdnative/include/net/godot_net.h"
+
+class PacketPeerGDNative : public PacketPeer {
+ GDCLASS(PacketPeerGDNative, PacketPeer)
+
+protected:
+ static void _bind_methods();
+ const godot_net_packet_peer *interface;
+
+public:
+ PacketPeerGDNative();
+ ~PacketPeerGDNative();
+
+ /* Sets the interface implementation from GDNative */
+ void set_native_packet_peer(const godot_net_packet_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;
+};
+
+#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/gdnative/net/stream_peer_gdnative.cpp b/modules/gdnative/net/stream_peer_gdnative.cpp
new file mode 100644
index 0000000000..4d1f2ec9a5
--- /dev/null
+++ b/modules/gdnative/net/stream_peer_gdnative.cpp
@@ -0,0 +1,77 @@
+/*************************************************************************/
+/* stream_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 "stream_peer_gdnative.h"
+
+StreamPeerGDNative::StreamPeerGDNative() {
+ interface = NULL;
+}
+
+StreamPeerGDNative::~StreamPeerGDNative() {
+}
+
+void StreamPeerGDNative::set_native_stream_peer(godot_net_stream_peer *p_interface) {
+ interface = p_interface;
+}
+
+void StreamPeerGDNative::_bind_methods() {
+}
+
+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));
+}
+
+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));
+}
+
+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));
+}
+
+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));
+}
+
+int StreamPeerGDNative::get_available_bytes() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_available_bytes(interface->data);
+}
+
+extern "C" {
+
+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);
+}
+}
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_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp
index 931ab0bfe4..13026e8e7a 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp
@@ -138,11 +138,11 @@ void PluginScriptInstance::notification(int p_notification) {
_desc->notification(_data, p_notification);
}
-ScriptInstance::RPCMode PluginScriptInstance::get_rpc_mode(const StringName &p_method) const {
+MultiplayerAPI::RPCMode PluginScriptInstance::get_rpc_mode(const StringName &p_method) const {
return _script->get_rpc_mode(p_method);
}
-ScriptInstance::RPCMode PluginScriptInstance::get_rset_mode(const StringName &p_variable) const {
+MultiplayerAPI::RPCMode PluginScriptInstance::get_rset_mode(const StringName &p_variable) const {
return _script->get_rset_mode(p_variable);
}
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h
index 3c7b360ad9..8be6a4ccaa 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.h
+++ b/modules/gdnative/pluginscript/pluginscript_instance.h
@@ -76,8 +76,8 @@ public:
void set_path(const String &p_path);
- virtual RPCMode get_rpc_mode(const StringName &p_method) const;
- virtual RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
virtual void refcount_incremented();
virtual bool refcount_decremented();
diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp
index 5ae7926f1b..c3a623e9a1 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_script.cpp
@@ -84,35 +84,20 @@ StringName PluginScript::get_instance_base_type() const {
}
void PluginScript::update_exports() {
-// TODO
#ifdef TOOLS_ENABLED
-#if 0
ASSERT_SCRIPT_VALID();
- if (/*changed &&*/ placeholders.size()) { //hm :(
+ if (placeholders.size()) {
//update placeholders if any
Map<StringName, Variant> propdefvalues;
List<PropertyInfo> propinfos;
- const String *props = (const String *)pybind_get_prop_list(_py_exposed_class);
- for (int i = 0; props[i] != ""; ++i) {
- const String propname = props[i];
- pybind_get_prop_default_value(_py_exposed_class, propname.c_str(), (godot_variant *)&propdefvalues[propname]);
- pybind_prop_info raw_info;
- pybind_get_prop_info(_py_exposed_class, propname.c_str(), &raw_info);
- PropertyInfo info;
- info.type = (Variant::Type)raw_info.type;
- info.name = propname;
- info.hint = (PropertyHint)raw_info.hint;
- info.hint_string = *(String *)&raw_info.hint_string;
- info.usage = raw_info.usage;
- propinfos.push_back(info);
- }
+
+ get_script_property_list(&propinfos);
for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(propinfos, propdefvalues);
+ E->get()->update(propinfos, _properties_default_values);
}
}
#endif
-#endif
}
// TODO: rename p_this "p_owner" ?
@@ -245,9 +230,9 @@ Error PluginScript::reload(bool p_keep_state) {
// rpc_mode is passed as an optional field and is not part of MethodInfo
Variant var = v["rpc_mode"];
if (var == Variant()) {
- _methods_rpc_mode[mi.name] = ScriptInstance::RPC_MODE_DISABLED;
+ _methods_rpc_mode[mi.name] = MultiplayerAPI::RPC_MODE_DISABLED;
} else {
- _methods_rpc_mode[mi.name] = ScriptInstance::RPCMode(int(var));
+ _methods_rpc_mode[mi.name] = MultiplayerAPI::RPCMode(int(var));
}
}
Array *signals = (Array *)&manifest.signals;
@@ -265,9 +250,9 @@ Error PluginScript::reload(bool p_keep_state) {
// rset_mode is passed as an optional field and is not part of PropertyInfo
Variant var = v["rset_mode"];
if (var == Variant()) {
- _methods_rpc_mode[pi.name] = ScriptInstance::RPC_MODE_DISABLED;
+ _methods_rpc_mode[pi.name] = MultiplayerAPI::RPC_MODE_DISABLED;
} else {
- _methods_rpc_mode[pi.name] = ScriptInstance::RPCMode(int(var));
+ _methods_rpc_mode[pi.name] = MultiplayerAPI::RPCMode(int(var));
}
}
// Manifest's attributes must be explicitly freed
@@ -402,23 +387,23 @@ int PluginScript::get_member_line(const StringName &p_member) const {
return -1;
}
-ScriptInstance::RPCMode PluginScript::get_rpc_mode(const StringName &p_method) const {
- ASSERT_SCRIPT_VALID_V(ScriptInstance::RPC_MODE_DISABLED);
- const Map<StringName, ScriptInstance::RPCMode>::Element *e = _methods_rpc_mode.find(p_method);
+MultiplayerAPI::RPCMode PluginScript::get_rpc_mode(const StringName &p_method) const {
+ ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
+ const Map<StringName, MultiplayerAPI::RPCMode>::Element *e = _methods_rpc_mode.find(p_method);
if (e != NULL) {
return e->get();
} else {
- return ScriptInstance::RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
-ScriptInstance::RPCMode PluginScript::get_rset_mode(const StringName &p_variable) const {
- ASSERT_SCRIPT_VALID_V(ScriptInstance::RPC_MODE_DISABLED);
- const Map<StringName, ScriptInstance::RPCMode>::Element *e = _variables_rset_mode.find(p_variable);
+MultiplayerAPI::RPCMode PluginScript::get_rset_mode(const StringName &p_variable) const {
+ ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
+ const Map<StringName, MultiplayerAPI::RPCMode>::Element *e = _variables_rset_mode.find(p_variable);
if (e != NULL) {
return e->get();
} else {
- return ScriptInstance::RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h
index 1be9e907c2..31c6c4d67f 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.h
+++ b/modules/gdnative/pluginscript/pluginscript_script.h
@@ -62,8 +62,8 @@ private:
Map<StringName, PropertyInfo> _properties_info;
Map<StringName, MethodInfo> _signals_info;
Map<StringName, MethodInfo> _methods_info;
- Map<StringName, ScriptInstance::RPCMode> _variables_rset_mode;
- Map<StringName, ScriptInstance::RPCMode> _methods_rpc_mode;
+ Map<StringName, MultiplayerAPI::RPCMode> _variables_rset_mode;
+ Map<StringName, MultiplayerAPI::RPCMode> _methods_rpc_mode;
Set<Object *> _instances;
//exported members
@@ -116,8 +116,8 @@ public:
virtual int get_member_line(const StringName &p_member) const;
- ScriptInstance::RPCMode get_rpc_mode(const StringName &p_method) const;
- ScriptInstance::RPCMode get_rset_mode(const StringName &p_variable) const;
+ MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
PluginScript();
void init(PluginScriptLanguage *language);
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/SCsub b/modules/gdscript/SCsub
index 13870170a5..73f09f1659 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -7,4 +7,7 @@ env_gdscript = env_modules.Clone()
env_gdscript.add_source_files(env.modules_sources, "*.cpp")
+if env['tools']:
+ env_gdscript.add_source_files(env.modules_sources, "./editor/*.cpp")
+
Export('env')
diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py
index 6496b59d75..95b40d90af 100644
--- a/modules/gdscript/config.py
+++ b/modules/gdscript/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index 40a435f459..632970f8c0 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -8,7 +8,7 @@
[method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/index.html
+ <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/modules/gdscript/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 4e89851bf2..fedc510f01 100644
--- a/modules/gdscript/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -29,6 +29,8 @@
/*************************************************************************/
#include "gdscript_highlighter.h"
+#include "../gdscript_tokenizer.h"
+#include "editor/editor_settings.h"
#include "scene/gui/text_edit.h"
inline bool _is_symbol(CharType c) {
@@ -61,12 +63,20 @@ static bool _is_hex_symbol(CharType c) {
Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) {
Map<int, TextEdit::HighlighterInfo> color_map;
+ Type next_type = NONE;
+ Type current_type = NONE;
+ Type previous_type = NONE;
+
+ String previous_text = "";
+ int previous_column = 0;
+
bool prev_is_char = false;
bool prev_is_number = false;
bool in_keyword = false;
bool in_word = false;
bool in_function_name = false;
bool in_member_variable = false;
+ bool in_node_path = false;
bool is_hex_notation = false;
Color keyword_color;
Color color;
@@ -214,18 +224,64 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
in_member_variable = false;
}
- if (in_region >= 0)
+ if (!in_node_path && in_region == -1 && str[j] == '$') {
+ in_node_path = true;
+ } else if (in_region != -1 || (is_symbol && str[j] != '/')) {
+ in_node_path = false;
+ }
+
+ if (in_region >= 0) {
+ next_type = REGION;
color = text_editor->_get_color_region(in_region).color;
- else if (in_keyword)
+ } else if (in_node_path) {
+ next_type = NODE_PATH;
+ color = node_path_color;
+ } else if (in_keyword) {
+ next_type = KEYWORD;
color = keyword_color;
- else if (in_member_variable)
+ } else if (in_member_variable) {
+ next_type = MEMBER;
color = member_color;
- else if (in_function_name)
- color = function_color;
- else if (is_symbol)
+ } else if (in_function_name) {
+ next_type = FUNCTION;
+
+ if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::TK_PR_FUNCTION)) {
+ color = function_definition_color;
+ } else {
+ color = function_color;
+ }
+ } else if (is_symbol) {
+ next_type = SYMBOL;
color = symbol_color;
- else if (is_number)
+ } else if (is_number) {
+ next_type = NUMBER;
color = number_color;
+ } else {
+ next_type = IDENTIFIER;
+ }
+
+ if (next_type != current_type) {
+ if (current_type == NONE) {
+ current_type = next_type;
+ } else {
+ previous_type = current_type;
+ current_type = next_type;
+
+ // no need to store regions...
+ if (previous_type == REGION) {
+ previous_text = "";
+ previous_column = j;
+ } else {
+ String text = str.substr(previous_column, j - previous_column).strip_edges();
+ previous_column = j;
+
+ // ignore if just whitespace
+ if (text != "") {
+ previous_text = text;
+ }
+ }
+ }
+ }
prev_is_char = is_char;
prev_is_number = is_number;
@@ -255,6 +311,25 @@ void GDScriptSyntaxHighlighter::_update_cache() {
function_color = text_editor->get_color("function_color");
number_color = text_editor->get_color("number_color");
member_color = text_editor->get_color("member_variable_color");
+
+ EditorSettings *settings = EditorSettings::get_singleton();
+ String text_editor_color_theme = settings->get("text_editor/theme/color_theme");
+
+ bool default_theme = text_editor_color_theme == "Default";
+ bool dark_theme = settings->is_dark_theme();
+
+ function_definition_color = Color::html(default_theme ? "#01e1ff" : dark_theme ? "#01e1ff" : "#00a5ba");
+ node_path_color = Color::html(default_theme ? "#64c15a" : dark_theme ? "64c15a" : "#518b4b");
+
+ EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color);
+ EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color);
+ if (text_editor_color_theme == "Adaptive" || default_theme) {
+ settings->set_initial_value("text_editor/highlighting/gdscript/function_definition_color", function_definition_color, true);
+ settings->set_initial_value("text_editor/highlighting/gdscript/node_path_color", node_path_color, true);
+ }
+
+ function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color");
+ node_path_color = EDITOR_GET("text_editor/highlighting/gdscript/node_path_color");
}
SyntaxHighlighter *GDScriptSyntaxHighlighter::create() {
diff --git a/modules/gdscript/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index ef1bdd4103..0296ab7652 100644
--- a/modules/gdscript/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -35,13 +35,27 @@
class GDScriptSyntaxHighlighter : public SyntaxHighlighter {
private:
+ enum Type {
+ NONE,
+ REGION,
+ NODE_PATH,
+ SYMBOL,
+ NUMBER,
+ FUNCTION,
+ KEYWORD,
+ MEMBER,
+ IDENTIFIER
+ };
+
// colours
Color font_color;
Color symbol_color;
Color function_color;
+ Color function_definition_color;
Color built_in_type_color;
Color number_color;
Color member_color;
+ Color node_path_color;
public:
static SyntaxHighlighter *create();
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 4e3ee4d22c..b3ebd4fe4b 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1220,7 +1220,7 @@ ScriptLanguage *GDScriptInstance::get_language() {
return GDScriptLanguage::get_singleton();
}
-GDScriptInstance::RPCMode GDScriptInstance::get_rpc_mode(const StringName &p_method) const {
+MultiplayerAPI::RPCMode GDScriptInstance::get_rpc_mode(const StringName &p_method) const {
const GDScript *cscript = script.ptr();
@@ -1228,17 +1228,17 @@ GDScriptInstance::RPCMode GDScriptInstance::get_rpc_mode(const StringName &p_met
const Map<StringName, GDScriptFunction *>::Element *E = cscript->member_functions.find(p_method);
if (E) {
- if (E->get()->get_rpc_mode() != RPC_MODE_DISABLED) {
+ if (E->get()->get_rpc_mode() != MultiplayerAPI::RPC_MODE_DISABLED) {
return E->get()->get_rpc_mode();
}
}
cscript = cscript->_base;
}
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
-GDScriptInstance::RPCMode GDScriptInstance::get_rset_mode(const StringName &p_variable) const {
+MultiplayerAPI::RPCMode GDScriptInstance::get_rset_mode(const StringName &p_variable) const {
const GDScript *cscript = script.ptr();
@@ -1253,7 +1253,7 @@ GDScriptInstance::RPCMode GDScriptInstance::get_rset_mode(const StringName &p_va
cscript = cscript->_base;
}
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
void GDScriptInstance::reload_members() {
@@ -1333,6 +1333,15 @@ void GDScriptLanguage::add_global_constant(const StringName &p_variable, const V
_add_global(p_variable, p_value);
}
+void GDScriptLanguage::add_named_global_constant(const StringName &p_name, const Variant &p_value) {
+ named_globals[p_name] = p_value;
+}
+
+void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
+ ERR_FAIL_COND(!named_globals.has(p_name));
+ named_globals.erase(p_name);
+}
+
void GDScriptLanguage::init() {
//populate global constants
@@ -1730,6 +1739,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"assert",
"breakpoint",
"class",
+ "class_name",
"extends",
"is",
"func",
@@ -1760,6 +1770,9 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"sync",
"master",
"slave",
+ "remotesync",
+ "mastersync",
+ "slavesync",
0
};
@@ -1776,6 +1789,50 @@ 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 && c->extends_used && c->extends_class.size() == 1) {
+ *r_base_type = c->extends_class[0]; //todo, should work much better
+ }
+ return c->name;
+ }
+
+ return String();
+}
+
GDScriptLanguage::GDScriptLanguage() {
calls = 0;
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 9566e3b32e..d1c57a0330 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -63,7 +63,7 @@ class GDScript : public Script {
int index;
StringName setter;
StringName getter;
- ScriptInstance::RPCMode rpc_mode;
+ MultiplayerAPI::RPCMode rpc_mode;
};
friend class GDScriptInstance;
@@ -248,8 +248,8 @@ public:
void reload_members();
- virtual RPCMode get_rpc_mode(const StringName &p_method) const;
- virtual RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
GDScriptInstance();
~GDScriptInstance();
@@ -262,6 +262,7 @@ class GDScriptLanguage : public ScriptLanguage {
Variant *_global_array;
Vector<Variant> global_array;
Map<StringName, int> globals;
+ Map<StringName, Variant> named_globals;
struct CallLevel {
@@ -369,7 +370,8 @@ public:
_FORCE_INLINE_ int get_global_array_size() const { return global_array.size(); }
_FORCE_INLINE_ Variant *get_global_array() { return _global_array; }
- _FORCE_INLINE_ const Map<StringName, int> &get_global_map() { return globals; }
+ _FORCE_INLINE_ const Map<StringName, int> &get_global_map() const { return globals; }
+ _FORCE_INLINE_ const Map<StringName, Variant> &get_named_globals_map() const { return named_globals; }
_FORCE_INLINE_ static GDScriptLanguage *get_singleton() { return singleton; }
@@ -403,6 +405,8 @@ public:
virtual String _get_indentation() const;
virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const;
virtual void add_global_constant(const StringName &p_variable, const Variant &p_value);
+ virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value);
+ virtual void remove_named_global_constant(const StringName &p_name);
/* DEBUGGER FUNCTIONS */
@@ -435,6 +439,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 048948dada..7ce19859ca 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))
@@ -278,6 +278,53 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
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)) {
+
+ int idx = codegen.named_globals.find(identifier);
+ if (idx == -1) {
+ idx = codegen.named_globals.size();
+ codegen.named_globals.push_back(identifier);
+ }
+ return idx | (GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL << GDScriptFunction::ADDR_BITS);
+ }
+#endif
+
//not found, error
_set_error("Identifier not found: " + String(identifier), p_expression);
@@ -726,6 +773,9 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
case GDScriptParser::OperatorNode::OP_NEG: {
if (!_create_unary_operator(codegen, on, Variant::OP_NEGATE, p_stack_level)) return -1;
} break;
+ case GDScriptParser::OperatorNode::OP_POS: {
+ if (!_create_unary_operator(codegen, on, Variant::OP_POSITIVE, p_stack_level)) return -1;
+ } break;
case GDScriptParser::OperatorNode::OP_NOT: {
if (!_create_unary_operator(codegen, on, Variant::OP_NOT, p_stack_level)) return -1;
} break;
@@ -1511,6 +1561,18 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
gdfunc->_global_names_count = 0;
}
+#ifdef TOOLS_ENABLED
+ // Named globals
+ if (codegen.named_globals.size()) {
+ gdfunc->named_globals.resize(codegen.named_globals.size());
+ gdfunc->_named_globals_ptr = gdfunc->named_globals.ptr();
+ for (int i = 0; i < codegen.named_globals.size(); i++) {
+ gdfunc->named_globals[i] = codegen.named_globals[i];
+ }
+ gdfunc->_named_globals_count = gdfunc->named_globals.size();
+ }
+#endif
+
if (codegen.opcodes.size()) {
gdfunc->code = codegen.opcodes;
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 62aafdbe01..237b0de9e7 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -94,6 +94,9 @@ class GDScriptCompiler {
HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
Map<StringName, int> name_map;
+#ifdef TOOLS_ENABLED
+ Vector<StringName> named_globals;
+#endif
int get_name_map_pos(const StringName &p_identifier) {
int ret;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 87e1276492..c0c3bd7b06 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -54,18 +54,18 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
}
Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
- 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 = 2\n"
+ "# var b = \"text\"\n"
+ "\n"
+ "# Called when the node enters the scene tree for the first time.\n"
+ "func _ready():\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):\n"
"#%TS%pass\n";
_template = _template.replace("%BASE%", p_base_class_name);
@@ -118,6 +118,13 @@ bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &
funcs[cl->static_functions[i]->line] = cl->static_functions[i]->name;
}
+ for (int i = 0; i < cl->subclasses.size(); i++) {
+ for (int j = 0; j < cl->subclasses[i]->functions.size(); j++) {
+
+ funcs[cl->subclasses[i]->functions[j]->line] = String(cl->subclasses[i]->name) + "." + String(cl->subclasses[i]->functions[j]->name);
+ }
+ }
+
for (Map<int, String>::Element *E = funcs.front(); E; E = E->next()) {
r_functions->push_back(E->get() + ":" + itos(E->key()));
@@ -416,7 +423,7 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na
s += p_args[i].get_slice(":", 0);
}
}
- s += "):\n" + _get_indentation() + "pass # replace with function body\n";
+ s += "):\n" + _get_indentation() + "pass # Replace with function body.\n";
return s;
}
@@ -2636,6 +2643,13 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
+ if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name = "@GDScript";
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+
GDScriptParser p;
p.parse(p_code, p_base_path, false, "", true);
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 1c5b8187ca..10599f0c38 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -108,6 +108,21 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta
#endif
return &GDScriptLanguage::get_singleton()->get_global_array()[address];
} break;
+#ifdef TOOLS_ENABLED
+ case ADDR_TYPE_NAMED_GLOBAL: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _named_globals_count, NULL);
+#endif
+ StringName id = _named_globals_ptr[address];
+
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) {
+ return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id];
+ } else {
+ r_error = "Autoload singleton '" + String(id) + "' has been removed.";
+ return NULL;
+ }
+ } break;
+#endif
case ADDR_TYPE_NIL: {
return &nil;
} break;
@@ -1440,7 +1455,7 @@ GDScriptFunction::GDScriptFunction() :
_stack_size = 0;
_call_size = 0;
- rpc_mode = ScriptInstance::RPC_MODE_DISABLED;
+ rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
name = "<anonymous>";
#ifdef DEBUG_ENABLED
_func_cname = NULL;
@@ -1537,7 +1552,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);
}
}
@@ -1545,10 +1560,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);
}
}
@@ -1599,7 +1614,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);
}
}
@@ -1607,10 +1622,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 dff4bdfaf2..770d5c8733 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -92,15 +92,8 @@ public:
ADDR_TYPE_STACK = 5,
ADDR_TYPE_STACK_VARIABLE = 6,
ADDR_TYPE_GLOBAL = 7,
- ADDR_TYPE_NIL = 8
- };
-
- enum RPCMode {
- RPC_DISABLED,
- RPC_ENABLED,
- RPC_SYNC,
- RPC_SYNC_MASTER,
- RPC_SYNC_SLAVE
+ ADDR_TYPE_NAMED_GLOBAL = 8,
+ ADDR_TYPE_NIL = 9
};
struct StackDebug {
@@ -121,6 +114,10 @@ private:
int _constant_count;
const StringName *_global_names_ptr;
int _global_names_count;
+#ifdef TOOLS_ENABLED
+ const StringName *_named_globals_ptr;
+ int _named_globals_count;
+#endif
const int *_default_arg_ptr;
int _default_arg_count;
const int *_code_ptr;
@@ -130,13 +127,16 @@ private:
int _call_size;
int _initial_line;
bool _static;
- ScriptInstance::RPCMode rpc_mode;
+ MultiplayerAPI::RPCMode rpc_mode;
GDScript *_script;
StringName name;
Vector<Variant> constants;
Vector<StringName> global_names;
+#ifdef TOOLS_ENABLED
+ Vector<StringName> named_globals;
+#endif
Vector<int> default_arguments;
Vector<int> code;
@@ -222,7 +222,7 @@ public:
Variant call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError &r_err, CallState *p_state = NULL);
- _FORCE_INLINE_ ScriptInstance::RPCMode get_rpc_mode() const { return rpc_mode; }
+ _FORCE_INLINE_ MultiplayerAPI::RPCMode get_rpc_mode() const { return rpc_mode; }
GDScriptFunction();
~GDScriptFunction();
};
@@ -234,7 +234,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..ce91e7dff3 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);
@@ -1716,6 +1751,14 @@ 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"));
mi.return_val.type = Variant::STRING;
@@ -1813,6 +1856,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"));
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 e7b0700e76..d62112d3f1 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -716,6 +716,14 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
expr = constant;
bfn = true;
}
+
+ if (!bfn && GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
+ //check from singletons
+ ConstantNode *constant = alloc_node<ConstantNode>();
+ constant->value = GDScriptLanguage::get_singleton()->get_named_globals_map()[identifier];
+ expr = constant;
+ bfn = true;
+ }
}
if (!bfn) {
@@ -3104,6 +3112,28 @@ 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;
+ }
+
+ tokenizer->advance(2);
+
+ } break;
case GDScriptTokenizer::TK_PR_TOOL: {
if (p_class->tool) {
@@ -3130,6 +3160,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
name = tokenizer->get_token_identifier(1);
tokenizer->advance(2);
+ 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 *newclass = alloc_node<ClassNode>();
newclass->initializer = alloc_node<BlockNode>();
newclass->initializer->parent_class = newclass;
@@ -3365,7 +3400,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
function->line = fnline;
function->rpc_mode = rpc_mode;
- rpc_mode = ScriptInstance::RPC_MODE_DISABLED;
+ rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
if (_static)
p_class->static_functions.push_back(function);
@@ -3923,10 +3958,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
}
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SYNC) {
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTESYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTERSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVESYNC) {
current_export = PropertyInfo();
- _set_error("Expected 'var', 'onready', 'remote', 'master', 'slave' or 'sync'.");
+ _set_error("Expected 'var', 'onready', 'remote', 'master', 'slave', 'sync', 'remotesync', 'mastersync', 'slavesync'.");
return;
}
@@ -3959,7 +3994,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
}
- rpc_mode = ScriptInstance::RPC_MODE_REMOTE;
+ rpc_mode = MultiplayerAPI::RPC_MODE_REMOTE;
continue;
} break;
@@ -3980,7 +4015,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
}
- rpc_mode = ScriptInstance::RPC_MODE_MASTER;
+ rpc_mode = MultiplayerAPI::RPC_MODE_MASTER;
continue;
} break;
case GDScriptTokenizer::TK_PR_SLAVE: {
@@ -4000,9 +4035,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
}
- rpc_mode = ScriptInstance::RPC_MODE_SLAVE;
+ rpc_mode = MultiplayerAPI::RPC_MODE_SLAVE;
continue;
} break;
+ case GDScriptTokenizer::TK_PR_REMOTESYNC:
case GDScriptTokenizer::TK_PR_SYNC: {
//may be fallthrough from export, ignore if so
@@ -4015,7 +4051,37 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- rpc_mode = ScriptInstance::RPC_MODE_SYNC;
+ rpc_mode = MultiplayerAPI::RPC_MODE_SYNC;
+ continue;
+ } break;
+ case GDScriptTokenizer::TK_PR_MASTERSYNC: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) {
+ if (current_export.type)
+ _set_error("Expected 'var'.");
+ else
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+
+ rpc_mode = MultiplayerAPI::RPC_MODE_MASTERSYNC;
+ continue;
+ } break;
+ case GDScriptTokenizer::TK_PR_SLAVESYNC: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) {
+ if (current_export.type)
+ _set_error("Expected 'var'.");
+ else
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+
+ rpc_mode = MultiplayerAPI::RPC_MODE_SLAVESYNC;
continue;
} break;
case GDScriptTokenizer::TK_PR_VAR: {
@@ -4045,7 +4111,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
- rpc_mode = ScriptInstance::RPC_MODE_DISABLED;
+ rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
@@ -4054,7 +4120,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;
@@ -4470,7 +4536,7 @@ void GDScriptParser::clear() {
current_class = NULL;
completion_found = false;
- rpc_mode = ScriptInstance::RPC_MODE_DISABLED;
+ rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
current_function = NULL;
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 485ba1263d..b88a59537c 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -89,7 +89,7 @@ public:
StringName getter;
int line;
Node *expression;
- ScriptInstance::RPCMode rpc_mode;
+ MultiplayerAPI::RPCMode rpc_mode;
};
struct Constant {
StringName identifier;
@@ -125,7 +125,7 @@ public:
struct FunctionNode : public Node {
bool _static;
- ScriptInstance::RPCMode rpc_mode;
+ MultiplayerAPI::RPCMode rpc_mode;
StringName name;
Vector<StringName> arguments;
Vector<Node *> default_values;
@@ -134,7 +134,7 @@ public:
FunctionNode() {
type = TYPE_FUNCTION;
_static = false;
- rpc_mode = ScriptInstance::RPC_MODE_DISABLED;
+ rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
}
};
@@ -493,7 +493,7 @@ private:
PropertyInfo current_export;
- ScriptInstance::RPCMode rpc_mode;
+ MultiplayerAPI::RPCMode rpc_mode;
void _set_error(const String &p_error, int p_line = -1, int p_column = -1);
bool _recover_from_completion();
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 6a844cd651..9517b95f3f 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",
@@ -110,6 +111,9 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"sync",
"master",
"slave",
+ "remotesync",
+ "mastersync",
+ "slavesync",
"'['",
"']'",
"'{'",
@@ -184,6 +188,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" },
@@ -201,6 +206,9 @@ static const _kws _keyword_list[] = {
{ GDScriptTokenizer::TK_PR_MASTER, "master" },
{ GDScriptTokenizer::TK_PR_SLAVE, "slave" },
{ GDScriptTokenizer::TK_PR_SYNC, "sync" },
+ { GDScriptTokenizer::TK_PR_REMOTESYNC, "remotesync" },
+ { GDScriptTokenizer::TK_PR_MASTERSYNC, "mastersync" },
+ { GDScriptTokenizer::TK_PR_SLAVESYNC, "slavesync" },
{ GDScriptTokenizer::TK_PR_CONST, "const" },
{ GDScriptTokenizer::TK_PR_ENUM, "enum" },
//controlflow
@@ -247,6 +255,9 @@ bool GDScriptTokenizer::is_token_literal(int p_offset, bool variable_safe) const
case TK_PR_MASTER:
case TK_PR_SLAVE:
case TK_PR_SYNC:
+ case TK_PR_REMOTESYNC:
+ case TK_PR_MASTERSYNC:
+ case TK_PR_SLAVESYNC:
return true;
// Literal for non-variables only:
@@ -1128,7 +1139,7 @@ void GDScriptTokenizerText::advance(int p_amount) {
//////////////////////////////////////////////////////////////////////////////////////////////////////
-#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 b020c85199..c1f611fe73 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,
@@ -115,6 +116,9 @@ public:
TK_PR_SYNC,
TK_PR_MASTER,
TK_PR_SLAVE,
+ TK_PR_REMOTESYNC,
+ TK_PR_MASTERSYNC,
+ TK_PR_SLAVESYNC,
TK_BRACKET_OPEN,
TK_BRACKET_CLOSE,
TK_CURLY_BRACKET_OPEN,
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 85c94c3596..422223370b 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -30,8 +30,8 @@
#include "register_types.h"
+#include "editor/gdscript_highlighter.h"
#include "gdscript.h"
-#include "gdscript_highlighter.h"
#include "gdscript_tokenizer.h"
#include "io/file_access_encrypted.h"
#include "io/resource_loader.h"
diff --git a/modules/gridmap/config.py b/modules/gridmap/config.py
index a93f4edb81..5022116c9b 100644
--- a/modules/gridmap/config.py
+++ b/modules/gridmap/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index bb652f3bdf..d5f9563600 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -10,7 +10,7 @@
A GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/tutorials/3d/using_gridmaps.html
+ <link>http://docs.godotengine.org/en/3.0/tutorials/3d/using_gridmaps.html</link>
</tutorials>
<demos>
</demos>
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/hdr/config.py b/modules/hdr/config.py
index 5f133eba90..1c8cd12a2d 100644
--- a/modules/hdr/config.py
+++ b/modules/hdr/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/jpg/config.py b/modules/jpg/config.py
index 5f133eba90..1c8cd12a2d 100644
--- a/modules/jpg/config.py
+++ b/modules/jpg/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
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/config.py b/modules/mbedtls/config.py
index 5f133eba90..1c8cd12a2d 100755
--- a/modules/mbedtls/config.py
+++ b/modules/mbedtls/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
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/mobile_vr/config.py b/modules/mobile_vr/config.py
index aa8ef111d3..4912457e2b 100644
--- a/modules/mobile_vr/config.py
+++ b/modules/mobile_vr/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
# should probably change this to only be true on iOS and Android
return False
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 18d9c67795..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(platform):
- if platform in ["javascript"]:
+def can_build(env, platform):
+ 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)
@@ -80,7 +107,11 @@ def configure(env):
if mono_static:
lib_suffix = Environment()['LIBSUFFIX']
- mono_static_lib_name = 'libmono-static-sgen'
+
+ if env.msvc:
+ mono_static_lib_name = 'libmono-static-sgen'
+ else:
+ mono_static_lib_name = 'libmonosgen-2.0'
if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)):
raise RuntimeError('Could not find static mono library in: ' + mono_lib_path)
@@ -93,7 +124,10 @@ def configure(env):
env.Append(LINKFLAGS='LIBCMT' + lib_suffix)
env.Append(LINKFLAGS='Psapi' + lib_suffix)
else:
- env.Append(LIBS=mono_static_lib_name)
+ env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix))
+
+ env.Append(LIBS='psapi')
+ env.Append(LIBS='version')
else:
mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
@@ -128,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)
@@ -144,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:
@@ -168,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'))
@@ -197,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 bbe245951e..62a6b96bb5 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)
@@ -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();
@@ -446,7 +446,7 @@ String CSharpLanguage::make_function(const String &p_class, const String &p_name
s += variant_type_to_managed_name(arg.get_slice(":", 1)) + " " + escape_csharp_keyword(arg.get_slice(":", 0));
}
- s += ")\n{\n // Replace with function body\n}\n";
+ s += ")\n{\n // Replace with function body.\n}\n";
return s;
#else
@@ -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
@@ -782,7 +781,7 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
}
if (Engine::get_singleton()->is_editor_hint()) {
- EditorNode::get_singleton()->get_property_editor()->update_tree();
+ EditorNode::get_singleton()->get_inspector()->update_tree();
NodeDock::singleton->update_lists();
}
}
@@ -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);
}
@@ -1310,21 +1309,27 @@ bool CSharpInstance::refcount_decremented() {
return ref_dying;
}
-ScriptInstance::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember *p_member) const {
+MultiplayerAPI::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember *p_member) const {
if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute)))
- return RPC_MODE_REMOTE;
+ return MultiplayerAPI::RPC_MODE_REMOTE;
if (p_member->has_attribute(CACHED_CLASS(SyncAttribute)))
- return RPC_MODE_SYNC;
+ return MultiplayerAPI::RPC_MODE_SYNC;
if (p_member->has_attribute(CACHED_CLASS(MasterAttribute)))
- return RPC_MODE_MASTER;
+ return MultiplayerAPI::RPC_MODE_MASTER;
if (p_member->has_attribute(CACHED_CLASS(SlaveAttribute)))
- return RPC_MODE_SLAVE;
+ return MultiplayerAPI::RPC_MODE_SLAVE;
+ if (p_member->has_attribute(CACHED_CLASS(RemoteSyncAttribute)))
+ return MultiplayerAPI::RPC_MODE_REMOTESYNC;
+ if (p_member->has_attribute(CACHED_CLASS(MasterSyncAttribute)))
+ return MultiplayerAPI::RPC_MODE_MASTERSYNC;
+ if (p_member->has_attribute(CACHED_CLASS(SlaveSyncAttribute)))
+ return MultiplayerAPI::RPC_MODE_SLAVESYNC;
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
-ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const {
+MultiplayerAPI::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const {
GDMonoClass *top = script->script_class;
@@ -1337,10 +1342,10 @@ ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method)
top = top->get_parent_class();
}
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
-ScriptInstance::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variable) const {
+MultiplayerAPI::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variable) const {
GDMonoClass *top = script->script_class;
@@ -1358,7 +1363,7 @@ ScriptInstance::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variab
top = top->get_parent_class();
}
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
void CSharpInstance::notification(int p_notification) {
@@ -1484,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);
}
@@ -1538,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);
}
@@ -1573,37 +1578,33 @@ bool CSharpScript::_update_exports() {
return false;
}
-bool CSharpScript::_update_signals() {
- if (!valid)
- return false;
-
- bool changed = false;
+void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class) {
- if (signals_invalidated) {
- signals_invalidated = false;
-
- 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) {
@@ -1726,6 +1727,12 @@ void CSharpScript::_clear() {
Variant CSharpScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
+ if (unlikely(GDMono::get_singleton() == NULL)) {
+ // Probably not the best error but eh.
+ r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return Variant();
+ }
+
GDMonoClass *top = script_class;
while (top && top != native) {
@@ -1836,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;
}
@@ -1906,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);
@@ -1954,11 +1963,6 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Variant::Call
ScriptInstance *CSharpScript::instance_create(Object *p_this) {
- if (!script_class) {
- ERR_EXPLAIN("Cannot find class " + name + " for script " + get_path());
- ERR_FAIL_V(NULL);
- }
-
ERR_FAIL_COND_V(!valid, NULL);
if (!tool && !ScriptServer::is_scripting_enabled()) {
@@ -1966,14 +1970,23 @@ 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;
#endif
}
- update_signals();
+ if (!script_class) {
+ if (GDMono::get_singleton()->get_project_assembly() == NULL) {
+ // The project assembly is not loaded
+ ERR_EXPLAIN("Cannot instance script because the project assembly is not loaded. Script: " + get_path());
+ ERR_FAIL_V(NULL);
+ }
+
+ // The project assembly is loaded, but the class could not found
+ ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path());
+ ERR_FAIL_V(NULL);
+ }
if (native) {
String native_name = native->get_name();
@@ -2095,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;
@@ -2154,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?
@@ -2222,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 8666149111..df597ba776 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;
@@ -192,7 +191,7 @@ class CSharpInstance : public ScriptInstance {
void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount);
- RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const;
+ MultiplayerAPI::RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const;
public:
MonoObject *get_mono_object() const;
@@ -213,8 +212,8 @@ public:
virtual void refcount_incremented();
virtual bool refcount_decremented();
- virtual RPCMode get_rpc_mode(const StringName &p_method) const;
- virtual RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
virtual void notification(int p_notification);
void call_notification_no_check(MonoObject *p_mono_object, int p_notification);
diff --git a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
index 04da0600cc..16beacb45c 100644
--- a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
@@ -16,24 +16,48 @@ namespace GodotSharpTools.Build
private extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode);
[MethodImpl(MethodImplOptions.InternalCall)]
- private extern static void godot_icall_BuildInstance_get_MSBuildInfo(ref string msbuildPath, ref string frameworkPath);
+ private extern static string godot_icall_BuildInstance_get_MSBuildPath();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static string godot_icall_BuildInstance_get_FrameworkPath();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static string godot_icall_BuildInstance_get_MonoWindowsBinDir();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static bool godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows();
- private struct MSBuildInfo
+ private static string GetMSBuildPath()
{
- public string path;
- public string frameworkPathOverride;
+ string msbuildPath = godot_icall_BuildInstance_get_MSBuildPath();
+
+ if (msbuildPath == null)
+ throw new FileNotFoundException("Cannot find the MSBuild executable.");
+
+ return msbuildPath;
}
- private static MSBuildInfo GetMSBuildInfo()
+ private static string GetFrameworkPath()
{
- MSBuildInfo msbuildInfo = new MSBuildInfo();
+ return godot_icall_BuildInstance_get_FrameworkPath();
+ }
- godot_icall_BuildInstance_get_MSBuildInfo(ref msbuildInfo.path, ref msbuildInfo.frameworkPathOverride);
+ private static string MonoWindowsBinDir
+ {
+ get
+ {
+ string monoWinBinDir = godot_icall_BuildInstance_get_MonoWindowsBinDir();
- if (msbuildInfo.path == null)
- throw new FileNotFoundException("Cannot find the MSBuild executable.");
+ if (monoWinBinDir == null)
+ throw new FileNotFoundException("Cannot find the Windows Mono binaries directory.");
- return msbuildInfo;
+ return monoWinBinDir;
+ }
+ }
+
+ private static bool UsingMonoMSBuildOnWindows
+ {
+ get
+ {
+ return godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows();
+ }
}
private string solution;
@@ -54,25 +78,38 @@ namespace GodotSharpTools.Build
public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
{
- MSBuildInfo msbuildInfo = GetMSBuildInfo();
+ bool debugMSBuild = IsDebugMSBuildRequested();
List<string> customPropertiesList = new List<string>();
if (customProperties != null)
customPropertiesList.AddRange(customProperties);
- if (msbuildInfo.frameworkPathOverride != null)
- customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride);
+ string frameworkPath = GetFrameworkPath();
+
+ if (!string.IsNullOrEmpty(frameworkPath))
+ customPropertiesList.Add("FrameworkPathOverride=" + frameworkPath);
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
- ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs);
+ ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs);
+
+ bool redirectOutput = !debugMSBuild;
- // No console output, thanks
- startInfo.RedirectStandardOutput = true;
- startInfo.RedirectStandardError = true;
+ startInfo.RedirectStandardOutput = redirectOutput;
+ startInfo.RedirectStandardError = redirectOutput;
startInfo.UseShellExecute = false;
+ if (UsingMonoMSBuildOnWindows)
+ {
+ // These environment variables are required for Mono's MSBuild to find the compilers.
+ // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
+ string monoWinBinDir = MonoWindowsBinDir;
+ startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
+ startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
+ startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
+ }
+
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
@@ -82,8 +119,11 @@ namespace GodotSharpTools.Build
process.Start();
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
+ if (redirectOutput)
+ {
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ }
process.WaitForExit();
@@ -95,28 +135,41 @@ namespace GodotSharpTools.Build
public bool BuildAsync(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
{
+ bool debugMSBuild = IsDebugMSBuildRequested();
+
if (process != null)
throw new InvalidOperationException("Already in use");
- MSBuildInfo msbuildInfo = GetMSBuildInfo();
-
List<string> customPropertiesList = new List<string>();
if (customProperties != null)
customPropertiesList.AddRange(customProperties);
- if (msbuildInfo.frameworkPathOverride.Length > 0)
- customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride);
+ string frameworkPath = GetFrameworkPath();
+
+ if (!string.IsNullOrEmpty(frameworkPath))
+ customPropertiesList.Add("FrameworkPathOverride=" + frameworkPath);
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
- ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs);
+ ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs);
- // No console output, thanks
- startInfo.RedirectStandardOutput = true;
- startInfo.RedirectStandardError = true;
+ bool redirectOutput = !debugMSBuild;
+
+ startInfo.RedirectStandardOutput = redirectOutput;
+ startInfo.RedirectStandardError = redirectOutput;
startInfo.UseShellExecute = false;
+ if (UsingMonoMSBuildOnWindows)
+ {
+ // These environment variables are required for Mono's MSBuild to find the compilers.
+ // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
+ string monoWinBinDir = MonoWindowsBinDir;
+ startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
+ startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
+ startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
+ }
+
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
@@ -127,8 +180,11 @@ namespace GodotSharpTools.Build
process.Start();
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
+ if (redirectOutput)
+ {
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ }
return true;
}
@@ -176,6 +232,11 @@ namespace GodotSharpTools.Build
Dispose();
}
+ private static bool IsDebugMSBuildRequested()
+ {
+ return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1";
+ }
+
public void Dispose()
{
if (process != null)
diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
index 981083a3c2..1c8714e31d 100644
--- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
+++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
@@ -11,7 +11,7 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
+ <DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
@@ -20,7 +20,7 @@
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>full</DebugType>
+ <DebugType>portable</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
index 6bf54a0156..1d863e6f61 100644
--- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
@@ -70,7 +70,7 @@ namespace GodotSharpTools.Project
var toolsGroup = root.AddPropertyGroup();
toolsGroup.Condition = " '$(Configuration)|$(Platform)' == 'Tools|AnyCPU' ";
toolsGroup.AddProperty("DebugSymbols", "true");
- toolsGroup.AddProperty("DebugType", "full");
+ toolsGroup.AddProperty("DebugType", "portable");
toolsGroup.AddProperty("Optimize", "false");
toolsGroup.AddProperty("DefineConstants", "DEBUG;TOOLS;");
toolsGroup.AddProperty("ErrorReport", "prompt");
@@ -148,7 +148,7 @@ namespace GodotSharpTools.Project
var debugGroup = root.AddPropertyGroup();
debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ";
debugGroup.AddProperty("DebugSymbols", "true");
- debugGroup.AddProperty("DebugType", "full");
+ debugGroup.AddProperty("DebugType", "portable");
debugGroup.AddProperty("Optimize", "false");
debugGroup.AddProperty("DefineConstants", "DEBUG;");
debugGroup.AddProperty("ErrorReport", "prompt");
@@ -157,7 +157,7 @@ namespace GodotSharpTools.Project
var releaseGroup = root.AddPropertyGroup();
releaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ";
- releaseGroup.AddProperty("DebugType", "full");
+ releaseGroup.AddProperty("DebugType", "portable");
releaseGroup.AddProperty("Optimize", "true");
releaseGroup.AddProperty("ErrorReport", "prompt");
releaseGroup.AddProperty("WarningLevel", "4");
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 2f2b5768db..b3b259e851 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -39,6 +39,10 @@
#include "bindings_generator.h"
#include "godotsharp_editor.h"
+#define PROP_NAME_MSBUILD_MONO "MSBuild (Mono)"
+#define PROP_NAME_MSBUILD_VS "MSBuild (VS Build Tools)"
+#define PROP_NAME_XBUILD "xbuild (Deprecated)"
+
void godot_icall_BuildInstance_ExitCallback(MonoString *p_solution, MonoString *p_config, int p_exit_code) {
String solution = GDMonoMarshal::mono_string_to_godot(p_solution);
@@ -60,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/"
};
@@ -76,46 +81,42 @@ String _find_build_engine_on_unix(const String &p_name) {
}
#endif
-void godot_icall_BuildInstance_get_MSBuildInfo(MonoString **r_msbuild_path, MonoString **r_framework_path) {
+MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool")));
#if defined(WINDOWS_ENABLED)
switch (build_tool) {
- case GodotSharpBuilds::MSBUILD: {
+ case GodotSharpBuilds::MSBUILD_VS: {
static String msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path();
if (msbuild_tools_path.length()) {
if (!msbuild_tools_path.ends_with("\\"))
msbuild_tools_path += "\\";
- // FrameworkPathOverride
- const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
- if (mono_reg_info.assembly_dir.length()) {
- *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
-
- String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5");
- *r_framework_path = GDMonoMarshal::mono_string_from_godot(framework_path);
- } else {
- ERR_PRINT("Cannot find Mono's assemblies directory in the registry");
- }
-
- return;
+ return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
}
if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n");
- } // fall through
+ OS::get_singleton()->print("Cannot find executable for '" PROP_NAME_MSBUILD_VS "'. Trying with '" PROP_NAME_MSBUILD_MONO "'...\n");
+ } // FALL THROUGH
case GodotSharpBuilds::MSBUILD_MONO: {
String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat");
if (!FileAccess::exists(msbuild_path)) {
- WARN_PRINTS("Cannot find msbuild ('mono/builds/build_tool'). Tried with path: " + msbuild_path);
+ WARN_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_MONO "'. Tried with path: " + msbuild_path);
}
- *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_path);
+ return GDMonoMarshal::mono_string_from_godot(msbuild_path);
+ } break;
+ case GodotSharpBuilds::XBUILD: {
+ String xbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("xbuild.bat");
- return;
+ if (!FileAccess::exists(xbuild_path)) {
+ WARN_PRINTS("Cannot find executable for '" PROP_NAME_XBUILD "'. Tried with path: " + xbuild_path);
+ }
+
+ return GDMonoMarshal::mono_string_from_godot(xbuild_path);
} break;
default:
ERR_EXPLAIN("You don't deserve to live");
@@ -125,31 +126,74 @@ void godot_icall_BuildInstance_get_MSBuildInfo(MonoString **r_msbuild_path, Mono
static String msbuild_path = _find_build_engine_on_unix("msbuild");
static String xbuild_path = _find_build_engine_on_unix("xbuild");
- if (build_tool != GodotSharpBuilds::XBUILD) {
- if (msbuild_path.empty()) {
- WARN_PRINT("Cannot find msbuild ('mono/builds/build_tool').");
- return;
+ if (build_tool == GodotSharpBuilds::XBUILD) {
+ if (xbuild_path.empty()) {
+ WARN_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'");
+ return NULL;
}
} else {
- if (xbuild_path.empty()) {
- WARN_PRINT("Cannot find xbuild ('mono/builds/build_tool').");
- return;
+ if (msbuild_path.empty()) {
+ WARN_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'");
+ return NULL;
}
}
- *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
+ return GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
+#else
+ (void)build_tool; // UNUSED
+
+ ERR_EXPLAIN("Not implemented on this platform");
+ ERR_FAIL_V(NULL);
+#endif
+}
+
+MonoString *godot_icall_BuildInstance_get_FrameworkPath() {
+
+#if defined(WINDOWS_ENABLED)
+ const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
+ if (mono_reg_info.assembly_dir.length()) {
+ String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5");
+ return GDMonoMarshal::mono_string_from_godot(framework_path);
+ }
+
+ ERR_EXPLAIN("Cannot find Mono's assemblies directory in the registry");
+ ERR_FAIL_V(NULL);
+#else
+ return NULL;
+#endif
+}
+
+MonoString *godot_icall_BuildInstance_get_MonoWindowsBinDir() {
+
+#if defined(WINDOWS_ENABLED)
+ const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
+ if (mono_reg_info.bin_dir.length()) {
+ return GDMonoMarshal::mono_string_from_godot(mono_reg_info.bin_dir);
+ }
+
+ ERR_EXPLAIN("Cannot find Mono's binaries directory in the registry");
+ ERR_FAIL_V(NULL);
+#else
+ return NULL;
+#endif
+}
- return;
+MonoBoolean godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows() {
+
+#if defined(WINDOWS_ENABLED)
+ return GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool"))) == GodotSharpBuilds::MSBUILD_MONO;
#else
- ERR_PRINT("Not implemented on this platform");
- return;
+ return false;
#endif
}
void GodotSharpBuilds::_register_internal_calls() {
mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback);
- mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildInfo", (void *)godot_icall_BuildInstance_get_MSBuildInfo);
+ mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath);
+ mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_FrameworkPath", (void *)godot_icall_BuildInstance_get_FrameworkPath);
+ mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MonoWindowsBinDir", (void *)godot_icall_BuildInstance_get_MonoWindowsBinDir);
+ mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows", (void *)godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows);
}
void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
@@ -386,20 +430,14 @@ GodotSharpBuilds::GodotSharpBuilds() {
// Build tool settings
EditorSettings *ed_settings = EditorSettings::get_singleton();
-#ifdef WINDOWS_ENABLED
- // TODO: Default to MSBUILD_MONO if its csc.exe issue is fixed in the installed mono version
- EDITOR_DEF("mono/builds/build_tool", MSBUILD);
-#else
EDITOR_DEF("mono/builds/build_tool", MSBUILD_MONO);
-#endif
ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM,
+ PROP_NAME_MSBUILD_MONO
#ifdef WINDOWS_ENABLED
- "MSBuild (Mono),MSBuild (System)"
-#else
- "MSBuild (Mono),xbuild (Deprecated)"
+ "," PROP_NAME_MSBUILD_VS
#endif
- ));
+ "," PROP_NAME_XBUILD));
}
GodotSharpBuilds::~GodotSharpBuilds() {
@@ -424,12 +462,12 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
exit_code = -1;
- String logs_dir = GodotSharpDirs::get_build_logs_dir().plus_file(build_info.solution.md5_text() + "_" + build_info.configuration);
+ String log_dirpath = build_info.get_log_dirpath();
if (build_tab) {
build_tab->on_build_start();
} else {
- build_tab = memnew(MonoBuildTab(build_info, logs_dir));
+ build_tab = memnew(MonoBuildTab(build_info, log_dirpath));
MonoBottomPanel::get_singleton()->add_build_tab(build_tab);
}
@@ -451,12 +489,12 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
// Remove old issues file
String issues_file = "msbuild_issues.csv";
- DirAccessRef d = DirAccess::create_for_path(logs_dir);
+ DirAccessRef d = DirAccess::create_for_path(log_dirpath);
if (d->file_exists(issues_file)) {
Error err = d->remove(issues_file);
if (err != OK) {
exited = true;
- String file_path = ProjectSettings::get_singleton()->localize_path(logs_dir).plus_file(issues_file);
+ String file_path = ProjectSettings::get_singleton()->localize_path(log_dirpath).plus_file(issues_file);
String message = "Cannot remove issues file: " + file_path;
build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message);
@@ -475,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();
@@ -490,20 +528,21 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
// Call Build
- Variant logger_assembly = OS::get_singleton()->get_executable_path().get_base_dir().plus_file(EDITOR_TOOLS_ASSEMBLY_NAME) + ".dll";
- Variant logger_output_dir = logs_dir;
+ String logger_assembly_path = GDMono::get_singleton()->get_editor_tools_assembly()->get_path();
+ Variant logger_assembly = ProjectSettings::get_singleton()->globalize_path(logger_assembly_path);
+ Variant logger_output_dir = log_dirpath;
Variant custom_props = build_info.custom_props;
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_builds.h b/modules/mono/editor/godotsharp_builds.h
index 27b771e324..4afc284d45 100644
--- a/modules/mono/editor/godotsharp_builds.h
+++ b/modules/mono/editor/godotsharp_builds.h
@@ -68,10 +68,9 @@ public:
enum BuildTool {
MSBUILD_MONO,
#ifdef WINDOWS_ENABLED
- MSBUILD
-#else
- XBUILD // Deprecated
+ MSBUILD_VS,
#endif
+ XBUILD // Deprecated
};
_FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; }
diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp
index 1b5a303835..9317550d28 100644
--- a/modules/mono/editor/mono_bottom_panel.cpp
+++ b/modules/mono/editor/mono_bottom_panel.cpp
@@ -73,7 +73,7 @@ void MonoBottomPanel::_update_build_tabs_list() {
if (no_current_tab || current_tab == i) {
build_tabs_list->select(i);
- _build_tab_item_selected(i);
+ _build_tabs_item_selected(i);
}
}
}
@@ -105,21 +105,27 @@ void MonoBottomPanel::show_build_tab() {
ERR_PRINT("Builds tab not found");
}
-void MonoBottomPanel::_build_tab_item_selected(int p_idx) {
+void MonoBottomPanel::_build_tabs_item_selected(int p_idx) {
ERR_FAIL_INDEX(p_idx, build_tabs->get_tab_count());
+
build_tabs->set_current_tab(p_idx);
+ if (!build_tabs->is_visible())
+ build_tabs->set_visible(true);
+
+ warnings_btn->set_visible(true);
+ errors_btn->set_visible(true);
+ view_log_btn->set_visible(true);
}
-void MonoBottomPanel::_build_tab_changed(int p_idx) {
+void MonoBottomPanel::_build_tabs_nothing_selected() {
- if (p_idx < 0 || p_idx >= build_tabs->get_tab_count()) {
- warnings_btn->set_visible(false);
- errors_btn->set_visible(false);
- } else {
- warnings_btn->set_visible(true);
- errors_btn->set_visible(true);
- }
+ if (build_tabs->get_tab_count() != 0) // just in case
+ build_tabs->set_visible(false);
+
+ warnings_btn->set_visible(false);
+ errors_btn->set_visible(false);
+ view_log_btn->set_visible(false);
}
void MonoBottomPanel::_warnings_toggled(bool p_pressed) {
@@ -148,6 +154,22 @@ void MonoBottomPanel::_build_project_pressed() {
CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true);
}
+void MonoBottomPanel::_view_log_pressed() {
+
+ if (build_tabs_list->is_anything_selected()) {
+ Vector<int> selected_items = build_tabs_list->get_selected_items();
+ CRASH_COND(selected_items.size() != 1);
+ int selected_item = selected_items[0];
+
+ MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_tab_control(selected_item));
+ ERR_FAIL_NULL(build_tab);
+
+ String log_dirpath = build_tab->get_build_info().get_log_dirpath();
+
+ OS::get_singleton()->shell_open(log_dirpath.plus_file("msbuild_log.txt"));
+ }
+}
+
void MonoBottomPanel::_notification(int p_what) {
switch (p_what) {
@@ -163,10 +185,11 @@ void MonoBottomPanel::_notification(int p_what) {
void MonoBottomPanel::_bind_methods() {
ClassDB::bind_method(D_METHOD("_build_project_pressed"), &MonoBottomPanel::_build_project_pressed);
+ ClassDB::bind_method(D_METHOD("_view_log_pressed"), &MonoBottomPanel::_view_log_pressed);
ClassDB::bind_method(D_METHOD("_warnings_toggled", "pressed"), &MonoBottomPanel::_warnings_toggled);
ClassDB::bind_method(D_METHOD("_errors_toggled", "pressed"), &MonoBottomPanel::_errors_toggled);
- ClassDB::bind_method(D_METHOD("_build_tab_item_selected", "idx"), &MonoBottomPanel::_build_tab_item_selected);
- ClassDB::bind_method(D_METHOD("_build_tab_changed", "idx"), &MonoBottomPanel::_build_tab_changed);
+ ClassDB::bind_method(D_METHOD("_build_tabs_item_selected", "idx"), &MonoBottomPanel::_build_tabs_item_selected);
+ ClassDB::bind_method(D_METHOD("_build_tabs_nothing_selected"), &MonoBottomPanel::_build_tabs_nothing_selected);
}
MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) {
@@ -223,6 +246,15 @@ MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) {
errors_btn->connect("toggled", this, "_errors_toggled");
toolbar_hbc->add_child(errors_btn);
+ toolbar_hbc->add_spacer();
+
+ view_log_btn = memnew(Button);
+ view_log_btn->set_text(TTR("View log"));
+ view_log_btn->set_focus_mode(FOCUS_NONE);
+ view_log_btn->set_visible(false);
+ view_log_btn->connect("pressed", this, "_view_log_pressed");
+ toolbar_hbc->add_child(view_log_btn);
+
HSplitContainer *hsc = memnew(HSplitContainer);
hsc->set_h_size_flags(SIZE_EXPAND_FILL);
hsc->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -230,14 +262,14 @@ MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) {
build_tabs_list = memnew(ItemList);
build_tabs_list->set_h_size_flags(SIZE_EXPAND_FILL);
- build_tabs_list->connect("item_selected", this, "_build_tab_item_selected");
+ build_tabs_list->connect("item_selected", this, "_build_tabs_item_selected");
+ build_tabs_list->connect("nothing_selected", this, "_build_tabs_nothing_selected");
hsc->add_child(build_tabs_list);
build_tabs = memnew(TabContainer);
build_tabs->set_tab_align(TabContainer::ALIGN_LEFT);
build_tabs->set_h_size_flags(SIZE_EXPAND_FILL);
build_tabs->set_tabs_visible(false);
- build_tabs->connect("tab_changed", this, "_build_tab_changed");
hsc->add_child(build_tabs);
}
}
diff --git a/modules/mono/editor/mono_bottom_panel.h b/modules/mono/editor/mono_bottom_panel.h
index a44d3a9af8..03240e9a13 100644
--- a/modules/mono/editor/mono_bottom_panel.h
+++ b/modules/mono/editor/mono_bottom_panel.h
@@ -53,16 +53,18 @@ class MonoBottomPanel : public VBoxContainer {
Button *warnings_btn;
Button *errors_btn;
+ Button *view_log_btn;
void _update_build_tabs_list();
- void _build_tab_item_selected(int p_idx);
- void _build_tab_changed(int p_idx);
+ void _build_tabs_item_selected(int p_idx);
+ void _build_tabs_nothing_selected();
void _warnings_toggled(bool p_pressed);
void _errors_toggled(bool p_pressed);
void _build_project_pressed();
+ void _view_log_pressed();
static MonoBottomPanel *singleton;
diff --git a/modules/mono/editor/mono_build_info.cpp b/modules/mono/editor/mono_build_info.cpp
new file mode 100644
index 0000000000..e4af2aac4f
--- /dev/null
+++ b/modules/mono/editor/mono_build_info.cpp
@@ -0,0 +1,62 @@
+/*************************************************************************/
+/* mono_build_info.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 "mono_build_info.h"
+
+#include "../godotsharp_dirs.h"
+#include "../mono_gd/gd_mono_utils.h"
+
+uint32_t MonoBuildInfo::Hasher::hash(const MonoBuildInfo &p_key) {
+
+ uint32_t hash = 0;
+
+ GDMonoUtils::hash_combine(hash, p_key.solution.hash());
+ GDMonoUtils::hash_combine(hash, p_key.configuration.hash());
+
+ return hash;
+}
+
+bool MonoBuildInfo::operator==(const MonoBuildInfo &p_b) const {
+
+ return p_b.solution == solution && p_b.configuration == configuration;
+}
+
+String MonoBuildInfo::get_log_dirpath() {
+
+ return GodotSharpDirs::get_build_logs_dir().plus_file(solution.md5_text() + "_" + configuration);
+}
+
+MonoBuildInfo::MonoBuildInfo() {}
+
+MonoBuildInfo::MonoBuildInfo(const String &p_solution, const String &p_config) {
+
+ solution = p_solution;
+ configuration = p_config;
+}
diff --git a/modules/mono/editor/mono_build_info.h b/modules/mono/editor/mono_build_info.h
index 4806764a61..64ba0f4037 100644
--- a/modules/mono/editor/mono_build_info.h
+++ b/modules/mono/editor/mono_build_info.h
@@ -31,35 +31,25 @@
#ifndef MONO_BUILD_INFO_H
#define MONO_BUILD_INFO_H
-#include "../mono_gd/gd_mono_utils.h"
+#include "core/ustring.h"
+#include "core/vector.h"
struct MonoBuildInfo {
struct Hasher {
- static _FORCE_INLINE_ uint32_t hash(const MonoBuildInfo &p_key) {
- uint32_t hash = 0;
-
- GDMonoUtils::hash_combine(hash, p_key.solution.hash());
- GDMonoUtils::hash_combine(hash, p_key.configuration.hash());
-
- return hash;
- }
+ static uint32_t hash(const MonoBuildInfo &p_key);
};
String solution;
String configuration;
Vector<String> custom_props;
- MonoBuildInfo() {}
+ bool operator==(const MonoBuildInfo &p_b) const;
- MonoBuildInfo(const String &p_solution, const String &p_config) {
- solution = p_solution;
- configuration = p_config;
- }
+ String get_log_dirpath();
- bool operator==(const MonoBuildInfo &p_b) const {
- return p_b.solution == solution && p_b.configuration == configuration;
- }
+ MonoBuildInfo();
+ MonoBuildInfo(const String &p_solution, const String &p_config);
};
#endif // MONO_BUILD_INFO_H
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/builtin_types_glue.h b/modules/mono/glue/builtin_types_glue.h
index 460de84b65..ef9f152682 100644
--- a/modules/mono/glue/builtin_types_glue.h
+++ b/modules/mono/glue/builtin_types_glue.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* builtin_types_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 BUILTIN_TYPES_GLUE_H
#define BUILTIN_TYPES_GLUE_H
diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs
index 929b13d70c..aa49a5e04f 100644
--- a/modules/mono/glue/cs_files/Basis.cs
+++ b/modules/mono/glue/cs_files/Basis.cs
@@ -446,6 +446,26 @@ namespace Godot
_z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy));
}
+ public Basis(Vector3 euler)
+ {
+ real_t c;
+ real_t s;
+
+ c = Mathf.Cos(euler.x);
+ s = Mathf.Sin(euler.x);
+ var xmat = new Basis(1, 0, 0, 0, c, -s, 0, s, c);
+
+ c = Mathf.Cos(euler.y);
+ s = Mathf.Sin(euler.y);
+ var ymat = new Basis(c, 0, s, 0, 1, 0, -s, 0, c);
+
+ c = Mathf.Cos(euler.z);
+ s = Mathf.Sin(euler.z);
+ var zmat = new Basis(c, -s, 0, s, c, 0, 0, 0, 1);
+
+ this = ymat * xmat * zmat;
+ }
+
public Basis(Vector3 axis, real_t phi)
{
var axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs
index af94bb616e..e0d6d27840 100644
--- a/modules/mono/glue/cs_files/Color.cs
+++ b/modules/mono/glue/cs_files/Color.cs
@@ -249,6 +249,15 @@ namespace Godot
);
}
+ public Color Darkened(float amount)
+ {
+ Color res = this;
+ res.r = res.r * (1.0f - amount);
+ res.g = res.g * (1.0f - amount);
+ res.b = res.b * (1.0f - amount);
+ return res;
+ }
+
public float Gray()
{
return (r + g + b) / 3.0f;
@@ -263,6 +272,15 @@ namespace Godot
);
}
+ public Color Lightened(float amount)
+ {
+ Color res = this;
+ res.r = res.r + (1.0f - res.r) * amount;
+ res.g = res.g + (1.0f - res.g) * amount;
+ res.b = res.b + (1.0f - res.b) * amount;
+ return res;
+ }
+
public Color LinearInterpolate(Color c, float t)
{
var res = this;
@@ -275,15 +293,15 @@ namespace Godot
return res;
}
- public int To32()
+ public int ToRgba32()
{
- int c = (byte)(a * 255);
- c <<= 8;
- c |= (byte)(r * 255);
+ int c = (byte)(r * 255);
c <<= 8;
c |= (byte)(g * 255);
c <<= 8;
c |= (byte)(b * 255);
+ c <<= 8;
+ c |= (byte)(a * 255);
return c;
}
diff --git a/modules/mono/glue/cs_files/GodotTaskScheduler.cs b/modules/mono/glue/cs_files/GodotTaskScheduler.cs
index 6bf25a89d2..3d23ec10f1 100644
--- a/modules/mono/glue/cs_files/GodotTaskScheduler.cs
+++ b/modules/mono/glue/cs_files/GodotTaskScheduler.cs
@@ -14,6 +14,7 @@ namespace Godot
public GodotTaskScheduler()
{
Context = new GodotSynchronizationContext();
+ SynchronizationContext.SetSynchronizationContext(Context);
}
protected sealed override void QueueTask(Task task)
@@ -57,7 +58,6 @@ namespace Godot
public void Activate()
{
- SynchronizationContext.SetSynchronizationContext(Context);
ExecuteQueuedTasks();
Context.ExecutePendingContinuations();
}
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/Plane.cs b/modules/mono/glue/cs_files/Plane.cs
index 8b92522029..1020f06bf5 100644
--- a/modules/mono/glue/cs_files/Plane.cs
+++ b/modules/mono/glue/cs_files/Plane.cs
@@ -9,17 +9,23 @@ namespace Godot
{
public struct Plane : IEquatable<Plane>
{
- Vector3 normal;
+ private Vector3 _normal;
+
+ public Vector3 Normal
+ {
+ get { return _normal; }
+ set { _normal = value; }
+ }
public real_t x
{
get
{
- return normal.x;
+ return _normal.x;
}
set
{
- normal.x = value;
+ _normal.x = value;
}
}
@@ -27,11 +33,11 @@ namespace Godot
{
get
{
- return normal.y;
+ return _normal.y;
}
set
{
- normal.y = value;
+ _normal.y = value;
}
}
@@ -39,62 +45,62 @@ namespace Godot
{
get
{
- return normal.z;
+ return _normal.z;
}
set
{
- normal.z = value;
+ _normal.z = value;
}
}
- real_t d;
+ public real_t D { get; set; }
public Vector3 Center
{
get
{
- return normal * d;
+ return _normal * D;
}
}
public real_t DistanceTo(Vector3 point)
{
- return normal.Dot(point) - d;
+ return _normal.Dot(point) - D;
}
public Vector3 GetAnyPoint()
{
- return normal * d;
+ return _normal * D;
}
public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon)
{
- real_t dist = normal.Dot(point) - d;
+ real_t dist = _normal.Dot(point) - D;
return Mathf.Abs(dist) <= epsilon;
}
public Vector3 Intersect3(Plane b, Plane c)
{
- real_t denom = normal.Cross(b.normal).Dot(c.normal);
+ real_t denom = _normal.Cross(b._normal).Dot(c._normal);
if (Mathf.Abs(denom) <= Mathf.Epsilon)
return new Vector3();
- Vector3 result = b.normal.Cross(c.normal) * d +
- c.normal.Cross(normal) * b.d +
- normal.Cross(b.normal) * c.d;
+ Vector3 result = b._normal.Cross(c._normal) * D +
+ c._normal.Cross(_normal) * b.D +
+ _normal.Cross(b._normal) * c.D;
return result / denom;
}
public Vector3 IntersectRay(Vector3 from, Vector3 dir)
{
- real_t den = normal.Dot(dir);
+ real_t den = _normal.Dot(dir);
if (Mathf.Abs(den) <= Mathf.Epsilon)
return new Vector3();
- real_t dist = (normal.Dot(from) - d) / den;
+ real_t dist = (_normal.Dot(from) - D) / den;
// This is a ray, before the emitting pos (from) does not exist
if (dist > Mathf.Epsilon)
@@ -106,12 +112,12 @@ namespace Godot
public Vector3 IntersectSegment(Vector3 begin, Vector3 end)
{
Vector3 segment = begin - end;
- real_t den = normal.Dot(segment);
+ real_t den = _normal.Dot(segment);
if (Mathf.Abs(den) <= Mathf.Epsilon)
return new Vector3();
- real_t dist = (normal.Dot(begin) - d) / den;
+ real_t dist = (_normal.Dot(begin) - D) / den;
if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon)
return new Vector3();
@@ -121,46 +127,46 @@ namespace Godot
public bool IsPointOver(Vector3 point)
{
- return normal.Dot(point) > d;
+ return _normal.Dot(point) > D;
}
public Plane Normalized()
{
- real_t len = normal.Length();
+ real_t len = _normal.Length();
if (len == 0)
return new Plane(0, 0, 0, 0);
- return new Plane(normal / len, d / len);
+ return new Plane(_normal / len, D / len);
}
public Vector3 Project(Vector3 point)
{
- return point - normal * DistanceTo(point);
+ return point - _normal * DistanceTo(point);
}
// Constructors
public Plane(real_t a, real_t b, real_t c, real_t d)
{
- normal = new Vector3(a, b, c);
- this.d = d;
+ _normal = new Vector3(a, b, c);
+ this.D = d;
}
public Plane(Vector3 normal, real_t d)
{
- this.normal = normal;
- this.d = d;
+ this._normal = normal;
+ this.D = d;
}
public Plane(Vector3 v1, Vector3 v2, Vector3 v3)
{
- normal = (v1 - v3).Cross(v1 - v2);
- normal.Normalize();
- d = normal.Dot(v1);
+ _normal = (v1 - v3).Cross(v1 - v2);
+ _normal.Normalize();
+ D = _normal.Dot(v1);
}
public static Plane operator -(Plane plane)
{
- return new Plane(-plane.normal, -plane.d);
+ return new Plane(-plane._normal, -plane.D);
}
public static bool operator ==(Plane left, Plane right)
@@ -185,20 +191,20 @@ namespace Godot
public bool Equals(Plane other)
{
- return normal == other.normal && d == other.d;
+ return _normal == other._normal && D == other.D;
}
public override int GetHashCode()
{
- return normal.GetHashCode() ^ d.GetHashCode();
+ return _normal.GetHashCode() ^ D.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1})", new object[]
{
- normal.ToString(),
- d.ToString()
+ _normal.ToString(),
+ D.ToString()
});
}
@@ -206,8 +212,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- normal.ToString(format),
- d.ToString(format)
+ _normal.ToString(format),
+ D.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/RPCAttributes.cs b/modules/mono/glue/cs_files/RPCAttributes.cs
index 08841ffd76..6bf9560bfa 100644
--- a/modules/mono/glue/cs_files/RPCAttributes.cs
+++ b/modules/mono/glue/cs_files/RPCAttributes.cs
@@ -13,4 +13,13 @@ namespace Godot
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
public class SlaveAttribute : Attribute {}
+
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+ public class RemoteSyncAttribute : Attribute {}
+
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+ public class MasterSyncAttribute : Attribute {}
+
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+ public class SlaveSyncAttribute : Attribute {}
}
diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/cs_files/StringExtensions.cs
index 21090fb68d..eaeed7b37b 100644
--- a/modules/mono/glue/cs_files/StringExtensions.cs
+++ b/modules/mono/glue/cs_files/StringExtensions.cs
@@ -225,7 +225,7 @@ namespace Godot
if (pos < 0)
return instance;
- return instance.Substring(pos + 1, instance.Length);
+ return instance.Substring(pos + 1);
}
// <summary>
diff --git a/modules/mono/glue/cs_files/VERSION.txt b/modules/mono/glue/cs_files/VERSION.txt
index 0cfbf08886..b8626c4cff 100755
--- a/modules/mono/glue/cs_files/VERSION.txt
+++ b/modules/mono/glue/cs_files/VERSION.txt
@@ -1 +1 @@
-2
+4
diff --git a/modules/mono/glue/cs_files/Vector2.cs b/modules/mono/glue/cs_files/Vector2.cs
index 9bc40cf8df..c274364895 100644
--- a/modules/mono/glue/cs_files/Vector2.cs
+++ b/modules/mono/glue/cs_files/Vector2.cs
@@ -62,7 +62,7 @@ namespace Godot
}
}
- private real_t Cross(Vector2 b)
+ public real_t Cross(Vector2 b)
{
return x * b.y - y * b.x;
}
@@ -210,6 +210,12 @@ namespace Godot
x = v.x;
y = v.y;
}
+
+ public Vector2 Slerp(Vector2 b, real_t t)
+ {
+ real_t theta = AngleTo(b);
+ return Rotated(theta * t);
+ }
public Vector2 Slide(Vector2 n)
{
diff --git a/modules/mono/glue/cs_files/Vector3.cs b/modules/mono/glue/cs_files/Vector3.cs
index 57e4494f7e..085a4f0043 100644
--- a/modules/mono/glue/cs_files/Vector3.cs
+++ b/modules/mono/glue/cs_files/Vector3.cs
@@ -242,6 +242,12 @@ namespace Godot
z = v.z;
}
+ public Vector3 Slerp(Vector3 b, real_t t)
+ {
+ real_t theta = AngleTo(b);
+ return Rotated(Cross(b), theta * t);
+ }
+
public Vector3 Slide(Vector3 n)
{
return this - n * Dot(n);
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..bc84f43b4f 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_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index d062d56dcf..9d3bee2176 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -33,9 +33,10 @@
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/tokentype.h>
-#include "list.h"
-#include "os/file_access.h"
-#include "os/os.h"
+#include "core/list.h"
+#include "core/os/file_access.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
#include "../godotsharp_dirs.h"
#include "gd_mono_class.h"
@@ -210,7 +211,7 @@ Error GDMonoAssembly::load(bool p_refonly) {
Vector<uint8_t> data = FileAccess::get_file_as_array(path);
ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ);
- String image_filename(path);
+ String image_filename = ProjectSettings::get_singleton()->globalize_path(path);
MonoImageOpenStatus status = MONO_IMAGE_OK;
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 aa1a8e39c7..31c5bbb2fb 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -606,11 +606,14 @@ MonoArray *Array_to_mono_array(const Array &p_array) {
Array mono_array_to_Array(MonoArray *p_array) {
Array ret;
+ if (!p_array)
+ return ret;
int length = mono_array_length(p_array);
+ ret.resize(length);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_array, MonoObject *, i);
- ret.push_back(mono_object_to_variant(elem));
+ ret[i] = mono_object_to_variant(elem);
}
return ret;
@@ -630,11 +633,13 @@ MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array) {
PoolIntArray mono_array_to_PoolIntArray(MonoArray *p_array) {
PoolIntArray ret;
+ if (!p_array)
+ return ret;
int length = mono_array_length(p_array);
-
+ ret.resize(length);
for (int i = 0; i < length; i++) {
int32_t elem = mono_array_get(p_array, int32_t, i);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -652,11 +657,14 @@ MonoArray *PoolByteArray_to_mono_array(const PoolByteArray &p_array) {
PoolByteArray mono_array_to_PoolByteArray(MonoArray *p_array) {
PoolByteArray ret;
+ if (!p_array)
+ return ret;
int length = mono_array_length(p_array);
+ ret.resize(length);
for (int i = 0; i < length; i++) {
uint8_t elem = mono_array_get(p_array, uint8_t, i);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -674,11 +682,14 @@ MonoArray *PoolRealArray_to_mono_array(const PoolRealArray &p_array) {
PoolRealArray mono_array_to_PoolRealArray(MonoArray *p_array) {
PoolRealArray ret;
+ if (!p_array)
+ return ret;
int length = mono_array_length(p_array);
+ ret.resize(length);
for (int i = 0; i < length; i++) {
real_t elem = mono_array_get(p_array, real_t, i);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -697,11 +708,14 @@ MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) {
PoolStringArray mono_array_to_PoolStringArray(MonoArray *p_array) {
PoolStringArray ret;
+ if (!p_array)
+ return ret;
int length = mono_array_length(p_array);
+ ret.resize(length);
for (int i = 0; i < length; i++) {
MonoString *elem = mono_array_get(p_array, MonoString *, i);
- ret.push_back(mono_string_to_godot(elem));
+ ret.set(i, mono_string_to_godot(elem));
}
return ret;
@@ -728,12 +742,15 @@ MonoArray *PoolColorArray_to_mono_array(const PoolColorArray &p_array) {
PoolColorArray mono_array_to_PoolColorArray(MonoArray *p_array) {
PoolColorArray ret;
+ if (!p_array)
+ return ret;
int length = mono_array_length(p_array);
+ ret.resize(length);
for (int i = 0; i < length; i++) {
real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 4, i);
MARSHALLED_IN(Color, raw_elem, elem);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -758,12 +775,15 @@ MonoArray *PoolVector2Array_to_mono_array(const PoolVector2Array &p_array) {
PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array) {
PoolVector2Array ret;
+ if (!p_array)
+ return ret;
int length = mono_array_length(p_array);
+ ret.resize(length);
for (int i = 0; i < length; i++) {
real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 2, i);
MARSHALLED_IN(Vector2, raw_elem, elem);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -789,12 +809,15 @@ MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array) {
PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
PoolVector3Array ret;
+ if (!p_array)
+ return ret;
int length = mono_array_length(p_array);
+ ret.resize(length);
for (int i = 0; i < length; i++) {
real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 3, i);
MARSHALLED_IN(Vector3, raw_elem, elem);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -814,11 +837,13 @@ MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) {
GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary);
- MonoObject *ex = NULL;
- MonoObject *ret = arrays_to_dict(keys, values, &ex);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoObject *ret = arrays_to_dict(keys, values, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V(NULL);
}
@@ -828,15 +853,20 @@ MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) {
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);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ dict_to_arrays(p_dict, &keys, &values, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V(Dictionary());
}
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..1f837a2d78 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -138,47 +138,53 @@ 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) {
+void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **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);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ mono_runtime_invoke_array(prop_method, p_object, 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);
}
}
}
-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 db136a1313..a229552b76 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"
@@ -120,6 +123,9 @@ void MonoCache::clear_members() {
class_SyncAttribute = NULL;
class_MasterAttribute = NULL;
class_SlaveAttribute = NULL;
+ class_RemoteSyncAttribute = NULL;
+ class_MasterSyncAttribute = NULL;
+ class_SlaveSyncAttribute = NULL;
class_GodotMethodAttribute = NULL;
field_GodotMethodAttribute_methodName = NULL;
@@ -208,6 +214,9 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(SyncAttribute, GODOT_API_CLASS(SyncAttribute));
CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute));
CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute));
+ CACHE_CLASS_AND_CHECK(RemoteSyncAttribute, GODOT_API_CLASS(RemoteSyncAttribute));
+ CACHE_CLASS_AND_CHECK(MasterSyncAttribute, GODOT_API_CLASS(MasterSyncAttribute));
+ CACHE_CLASS_AND_CHECK(SlaveSyncAttribute, GODOT_API_CLASS(SlaveSyncAttribute));
CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute));
CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
@@ -391,10 +400,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_ex) {
String res;
- MonoClass *klass = mono_object_get_class(p_ex);
+ MonoClass *klass = mono_object_get_class((MonoObject *)p_ex);
MonoType *type = mono_class_get_type(klass);
char *full_name = mono_type_full_name(type);
@@ -404,29 +413,31 @@ 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);
+ MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_ex, NULL, NULL);
res += GDMonoMarshal::mono_string_to_godot(msg);
return res;
}
-void print_unhandled_exception(MonoObject *p_exc) {
- print_unhandled_exception(p_exc, false);
+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);
@@ -435,24 +446,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]);
@@ -460,10 +463,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__;
@@ -475,4 +483,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 1a34180d15..4f8e5932cd 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -34,6 +34,8 @@
#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"
@@ -112,6 +114,9 @@ struct MonoCache {
GDMonoClass *class_ToolAttribute;
GDMonoClass *class_RemoteAttribute;
GDMonoClass *class_SyncAttribute;
+ GDMonoClass *class_RemoteSyncAttribute;
+ GDMonoClass *class_MasterSyncAttribute;
+ GDMonoClass *class_SlaveSyncAttribute;
GDMonoClass *class_MasterAttribute;
GDMonoClass *class_SlaveAttribute;
GDMonoClass *class_GodotMethodAttribute;
@@ -181,10 +186,28 @@ MonoObject *create_managed_from(const RID &p_from);
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_ex);
-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
@@ -203,4 +226,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/ogg/config.py b/modules/ogg/config.py
index 5f133eba90..1c8cd12a2d 100644
--- a/modules/ogg/config.py
+++ b/modules/ogg/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/opus/config.py b/modules/opus/config.py
index 60f8d838d6..a1cde10ea0 100644
--- a/modules/opus/config.py
+++ b/modules/opus/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/pvr/config.py b/modules/pvr/config.py
index 5f133eba90..1c8cd12a2d 100644
--- a/modules/pvr/config.py
+++ b/modules/pvr/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/recast/SCsub b/modules/recast/SCsub
index 530df9a37c..f56be72b24 100644
--- a/modules/recast/SCsub
+++ b/modules/recast/SCsub
@@ -1,8 +1,9 @@
#!/usr/bin/env python
Import('env')
+Import('env_modules')
-# Not building in a separate env as core needs it
+env_recast = env_modules.Clone()
# Thirdparty source files
if env['builtin_recast']:
@@ -22,13 +23,10 @@ if env['builtin_recast']:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/Include"])
-
- lib = env.add_library("recast_builtin", thirdparty_sources)
- env.Append(LIBS=[lib])
+ env_recast.add_source_files(env.modules_sources, thirdparty_sources)
+ env_recast.Append(CPPPATH=[thirdparty_dir + "/Include"])
# Godot source files
-env.add_source_files(env.modules_sources, "*.cpp")
-env.Append(CCFLAGS=['-DRECAST_ENABLED'])
+env_recast.add_source_files(env.modules_sources, "*.cpp")
Export('env')
diff --git a/modules/recast/config.py b/modules/recast/config.py
index fc074cf661..098f1eafa9 100644
--- a/modules/recast/config.py
+++ b/modules/recast/config.py
@@ -1,5 +1,5 @@
-def can_build(platform):
- return platform != "android"
+def can_build(env, platform):
+ return env['tools']
def configure(env):
pass
diff --git a/editor/plugins/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp
index da3c744324..8556b7aa0a 100644
--- a/editor/plugins/navigation_mesh_editor_plugin.cpp
+++ b/modules/recast/navigation_mesh_editor_plugin.cpp
@@ -29,13 +29,12 @@
/*************************************************************************/
#include "navigation_mesh_editor_plugin.h"
+
#include "io/marshalls.h"
#include "io/resource_saver.h"
#include "scene/3d/mesh_instance.h"
#include "scene/gui/box_container.h"
-#ifdef RECAST_ENABLED
-
void NavigationMeshEditor::_node_removed(Node *p_node) {
if (p_node == node) {
@@ -162,5 +161,3 @@ NavigationMeshEditorPlugin::NavigationMeshEditorPlugin(EditorNode *p_node) {
NavigationMeshEditorPlugin::~NavigationMeshEditorPlugin() {
}
-
-#endif // RECAST_ENABLED
diff --git a/editor/plugins/navigation_mesh_editor_plugin.h b/modules/recast/navigation_mesh_editor_plugin.h
index 9382467d85..4f3e222002 100644
--- a/editor/plugins/navigation_mesh_editor_plugin.h
+++ b/modules/recast/navigation_mesh_editor_plugin.h
@@ -31,8 +31,6 @@
#ifndef NAVIGATION_MESH_GENERATOR_PLUGIN_H
#define NAVIGATION_MESH_GENERATOR_PLUGIN_H
-#ifdef RECAST_ENABLED
-
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
#include "navigation_mesh_generator.h"
@@ -83,5 +81,4 @@ public:
~NavigationMeshEditorPlugin();
};
-#endif // RECAST_ENABLED
#endif // NAVIGATION_MESH_GENERATOR_PLUGIN_H
diff --git a/editor/plugins/navigation_mesh_generator.cpp b/modules/recast/navigation_mesh_generator.cpp
index 0537c5c31f..64c4b85269 100644
--- a/editor/plugins/navigation_mesh_generator.cpp
+++ b/modules/recast/navigation_mesh_generator.cpp
@@ -30,8 +30,6 @@
#include "navigation_mesh_generator.h"
-#ifdef RECAST_ENABLED
-
void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) {
p_verticies.push_back(p_vec3.x);
p_verticies.push_back(p_vec3.y);
@@ -304,5 +302,3 @@ void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) {
p_nav_mesh->set_vertices(PoolVector<Vector3>());
}
}
-
-#endif //RECAST_ENABLED
diff --git a/editor/plugins/navigation_mesh_generator.h b/modules/recast/navigation_mesh_generator.h
index d26f541b8d..3588539ef1 100644
--- a/editor/plugins/navigation_mesh_generator.h
+++ b/modules/recast/navigation_mesh_generator.h
@@ -31,16 +31,11 @@
#ifndef NAVIGATION_MESH_GENERATOR_H
#define NAVIGATION_MESH_GENERATOR_H
-#ifdef RECAST_ENABLED
-
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
-
+#include "os/thread.h"
#include "scene/3d/mesh_instance.h"
-
#include "scene/3d/navigation_mesh.h"
-
-#include "os/thread.h"
#include "scene/resources/shape.h"
#include <Recast.h>
@@ -61,6 +56,4 @@ public:
static void clear(Ref<NavigationMesh> p_nav_mesh);
};
-#endif // RECAST_ENABLED
-
#endif // NAVIGATION_MESH_GENERATOR_H
diff --git a/modules/recast/register_types.cpp b/modules/recast/register_types.cpp
index 913857c591..f2f18fc86f 100644
--- a/modules/recast/register_types.cpp
+++ b/modules/recast/register_types.cpp
@@ -30,5 +30,10 @@
#include "register_types.h"
-void register_recast_types() {}
+#include "navigation_mesh_editor_plugin.h"
+
+void register_recast_types() {
+ EditorPlugins::add_by_type<NavigationMeshEditorPlugin>();
+}
+
void unregister_recast_types() {}
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/regex/config.py b/modules/regex/config.py
index cb2da26738..42cfe3b43c 100644
--- a/modules/regex/config.py
+++ b/modules/regex/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/squish/config.py b/modules/squish/config.py
index 97c95999c8..098f1eafa9 100644
--- a/modules/squish/config.py
+++ b/modules/squish/config.py
@@ -1,9 +1,5 @@
-def can_build(platform):
- return True
+def can_build(env, platform):
+ return env['tools']
def configure(env):
- # Tools only, disabled for non-tools
- # TODO: Find a cleaner way to achieve that
- if not env['tools']:
- env['module_squish_enabled'] = False
- env.disabled_modules.append("squish")
+ pass
diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
index 18ab616826..c95a8ac2dd 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
@@ -263,8 +263,8 @@ float AudioStreamOGGVorbis::get_length() const {
void AudioStreamOGGVorbis::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_set_data", "data"), &AudioStreamOGGVorbis::set_data);
- ClassDB::bind_method(D_METHOD("_get_data"), &AudioStreamOGGVorbis::get_data);
+ ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamOGGVorbis::set_data);
+ ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamOGGVorbis::get_data);
ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOGGVorbis::set_loop);
ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOGGVorbis::has_loop);
@@ -272,7 +272,7 @@ void AudioStreamOGGVorbis::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOGGVorbis::set_loop_offset);
ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset);
- ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop", "has_loop");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "loop_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop_offset", "get_loop_offset");
}
diff --git a/modules/stb_vorbis/config.py b/modules/stb_vorbis/config.py
index defe8d0c94..d75e41797a 100644
--- a/modules/stb_vorbis/config.py
+++ b/modules/stb_vorbis/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml
index ac95a7197f..e2281babf7 100644
--- a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml
+++ b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml
@@ -13,6 +13,9 @@
<methods>
</methods>
<members>
+ <member name="data" type="PoolByteArray" setter="set_data" getter="get_data">
+ Contains the audio data in bytes.
+ </member>
<member name="loop" type="bool" setter="set_loop" getter="has_loop">
</member>
<member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset">
diff --git a/modules/svg/SCsub b/modules/svg/SCsub
index e12abac8c1..a41e0703bd 100644
--- a/modules/svg/SCsub
+++ b/modules/svg/SCsub
@@ -1,7 +1,6 @@
#!/usr/bin/env python
Import('env')
-from compat import isbasestring
# Thirdparty source files
thirdparty_dir = "#thirdparty/nanosvg/"
@@ -10,23 +9,7 @@ thirdparty_sources = [
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-# env.add_source_files(env.modules_sources, thirdparty_sources)
-
-lib = env.add_library("svg_builtin", thirdparty_sources)
-
-# Needs to be appended to arrive after libscene in the linker call,
-# but we don't want it to arrive *after* system libs, so manual hack
-# LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
-# and then plain strings for system library. We insert between the two.
-inserted = False
-for idx, linklib in enumerate(env["LIBS"]):
- if isbasestring(linklib): # first system lib such as "X11", otherwise SCons lib object
- env["LIBS"].insert(idx, lib)
- inserted = True
- break
-if not inserted:
- env.Append(LIBS=[lib])
-
+env.add_source_files(env.modules_sources, thirdparty_sources)
env.Append(CPPPATH=[thirdparty_dir])
env.Append(CCFLAGS=["-DSVG_ENABLED"])
diff --git a/modules/svg/config.py b/modules/svg/config.py
index 5f133eba90..1c8cd12a2d 100644
--- a/modules/svg/config.py
+++ b/modules/svg/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/tga/config.py b/modules/tga/config.py
index 5f133eba90..1c8cd12a2d 100644
--- a/modules/tga/config.py
+++ b/modules/tga/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
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/thekla_unwrap/config.py b/modules/thekla_unwrap/config.py
index b1ce7d4b91..bd092bdc16 100644
--- a/modules/thekla_unwrap/config.py
+++ b/modules/thekla_unwrap/config.py
@@ -1,7 +1,5 @@
-def can_build(platform):
- return platform != "android" and platform != "ios"
+def can_build(env, platform):
+ return (env['tools'] and platform not in ["android", "ios"])
def configure(env):
- if not env['tools']:
- env['builtin_thekla_atlas'] = False
- env.disabled_modules.append("thekla_unwrap")
+ pass
diff --git a/modules/theora/config.py b/modules/theora/config.py
index 34d34f8be2..7504166237 100644
--- a/modules/theora/config.py
+++ b/modules/theora/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
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/resource_importer_theora.cpp b/modules/theora/resource_importer_theora.cpp
deleted file mode 100644
index ee9bab74a7..0000000000
--- a/modules/theora/resource_importer_theora.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*************************************************************************/
-/* resource_importer_theora.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 "resource_importer_theora.h"
-
-#include "io/resource_saver.h"
-#include "os/file_access.h"
-#include "scene/resources/texture.h"
-
-String ResourceImporterTheora::get_importer_name() const {
-
- return "Theora";
-}
-
-String ResourceImporterTheora::get_visible_name() const {
-
- return "Theora";
-}
-void ResourceImporterTheora::get_recognized_extensions(List<String> *p_extensions) const {
-
- p_extensions->push_back("ogv");
- p_extensions->push_back("ogm");
-}
-
-String ResourceImporterTheora::get_save_extension() const {
- return "ogvstr";
-}
-
-String ResourceImporterTheora::get_resource_type() const {
-
- return "VideoStreamTheora";
-}
-
-bool ResourceImporterTheora::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
-
- return true;
-}
-
-int ResourceImporterTheora::get_preset_count() const {
- return 0;
-}
-String ResourceImporterTheora::get_preset_name(int p_idx) const {
-
- return String();
-}
-
-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() {
-}
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/tinyexr/config.py b/modules/tinyexr/config.py
index e12bb398ce..098f1eafa9 100644
--- a/modules/tinyexr/config.py
+++ b/modules/tinyexr/config.py
@@ -1,9 +1,5 @@
-def can_build(platform):
- return True
+def can_build(env, platform):
+ return env['tools']
def configure(env):
- # Tools only, disabled for non-tools
- # TODO: Find a cleaner way to achieve that
- if not env['tools']:
- env['module_tinyexr_enabled'] = False
- env.disabled_modules.append("tinyexr")
+ pass
diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub
new file mode 100644
index 0000000000..cde231867f
--- /dev/null
+++ b/modules/upnp/SCsub
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_upnp = env_modules.Clone()
+
+# Thirdparty source files
+
+if env['builtin_miniupnpc']:
+ thirdparty_dir = "#thirdparty/miniupnpc/"
+ thirdparty_sources = [
+ "miniupnpc.c",
+ "upnpcommands.c",
+ "miniwget.c",
+ "upnpdev.c",
+ "igd_desc_parse.c",
+ "minissdpc.c",
+ "minisoap.c",
+ "minixml.c",
+ "connecthostport.c",
+ "receivedata.c",
+ "portlistingparse.c",
+ "upnpreplyparse.c",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_upnp.add_source_files(env.modules_sources, thirdparty_sources)
+ env_upnp.Append(CPPPATH=[thirdparty_dir])
+ env_upnp.Append(CPPFLAGS=["-DMINIUPNP_STATICLIB"])
+
+env_upnp.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/upnp/config.py b/modules/upnp/config.py
new file mode 100644
index 0000000000..8724ff1a51
--- /dev/null
+++ b/modules/upnp/config.py
@@ -0,0 +1,14 @@
+def can_build(env, platform):
+ return True
+
+def configure(env):
+ pass
+
+def get_doc_classes():
+ return [
+ "UPNP",
+ "UPNPDevice"
+ ]
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml
new file mode 100644
index 0000000000..30be9c836b
--- /dev/null
+++ b/modules/upnp/doc_classes/UPNP.xml
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="UPNP" inherits="Reference" category="Core" version="3.1">
+ <brief_description>
+ UPNP network functions.
+ </brief_description>
+ <description>
+ Provides UPNP functionality to discover [UPNPDevice]s on the local network and execute commands on them, like managing port mappings (port forwarding) and querying the local and remote network IP address. Note that methods on this class are synchronous and block the calling thread.
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="add_device">
+ <return type="void">
+ </return>
+ <argument index="0" name="device" type="UPNPDevice">
+ </argument>
+ <description>
+ Adds the given [UPNPDevice] to the list of discovered devices.
+ </description>
+ </method>
+ <method name="add_port_mapping" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="port" type="int">
+ </argument>
+ <argument index="1" name="port_internal" type="int" default="0">
+ </argument>
+ <argument index="2" name="desc" type="String" default="&quot;&quot;">
+ </argument>
+ <argument index="3" name="proto" type="String" default="&quot;UDP&quot;">
+ </argument>
+ <argument index="4" name="duration" type="int" default="0">
+ </argument>
+ <description>
+ Adds a mapping to forward the external [code]port[/code] (between 1 and 65535) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]TCP[/code] or [code]UDP[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any.
+ If [code]internal_port[/code] is [code]0[/code] (the default), the same port number is used for both the external and the internal port (the [code]port[/code] value).
+ The description ([code]desc[/code]) is shown in some router UIs and can be used to point out which application added the mapping, and the lifetime of the mapping can be limited by [code]duration[/code]. However, some routers are incompatible with one or both of these, so use with caution and add fallback logic in case of errors to retry without them if in doubt.
+ See [enum UPNPResult] for possible return values.
+ </description>
+ </method>
+ <method name="clear_devices">
+ <return type="void">
+ </return>
+ <description>
+ Clears the list of discovered devices.
+ </description>
+ </method>
+ <method name="delete_port_mapping" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="port" type="int">
+ </argument>
+ <argument index="1" name="proto" type="String" default="&quot;UDP&quot;">
+ </argument>
+ <description>
+ Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]TCP[/code] or [code]UDP[/code]. See [enum UPNPResult] for possible return values.
+ </description>
+ </method>
+ <method name="discover">
+ <return type="int">
+ </return>
+ <argument index="0" name="timeout" type="int" default="2000">
+ </argument>
+ <argument index="1" name="ttl" type="int" default="2">
+ </argument>
+ <argument index="2" name="device_filter" type="String" default="&quot;InternetGatewayDevice&quot;">
+ </argument>
+ <description>
+ Discovers local [UPNPDevice]s. Clears the list of previously discovered devices.
+ Filters for IGD (InternetGatewayDevice) type devices by default, as those manage port forwarding. [code]timeout[/code] is the time to wait for responses in miliseconds. [code]ttl[/code] is the time-to-live; only touch this if you know what you're doing.
+ See [enum UPNPResult] for possible return values.
+ </description>
+ </method>
+ <method name="get_device" qualifiers="const">
+ <return type="UPNPDevice">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ Returns the [UPNPDevice] at the given [code]index[/code].
+ </description>
+ </method>
+ <method name="get_device_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the number of discovered [UPNPDevice]s.
+ </description>
+ </method>
+ <method name="get_gateway" qualifiers="const">
+ <return type="UPNPDevice">
+ </return>
+ <description>
+ Returns the default gateway. That is the first discovered [UPNPDevice] that is also a valid IGD (InternetGatewayDevice).
+ </description>
+ </method>
+ <method name="query_external_address" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ Returns the external [IP] address of the default gateway (see [method get_gateway]) as string. Returns an empty string on error.
+ </description>
+ </method>
+ <method name="remove_device">
+ <return type="void">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ Removes the device at [code]index[/code] from the list of discovered devices.
+ </description>
+ </method>
+ <method name="set_device">
+ <return type="void">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <argument index="1" name="device" type="UPNPDevice">
+ </argument>
+ <description>
+ Sets the device at [code]index[/code] from the list of discovered devices to [code]device[/code].
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="discover_ipv6" type="bool" setter="set_discover_ipv6" getter="is_discover_ipv6">
+ If [code]true[/code], IPv6 is used for [UPNPDevice] discovery.
+ </member>
+ <member name="discover_local_port" type="int" setter="set_discover_local_port" getter="get_discover_local_port">
+ If [code]0[/code], the local port to use for discovery is chosen automatically by the system. If [code]1[/code], discovery will be done from the source port 1900 (same as destination port). Otherwise, the value will be used as the port.
+ </member>
+ <member name="discover_multicast_if" type="String" setter="set_discover_multicast_if" getter="get_discover_multicast_if">
+ Multicast interface to use for discovery. Uses the default multicast interface if empty.
+ </member>
+ </members>
+ <constants>
+ <constant name="UPNP_RESULT_SUCCESS" value="0" enum="UPNPResult">
+ UPNP command or discovery was successful.
+ </constant>
+ <constant name="UPNP_RESULT_NOT_AUTHORIZED" value="1" enum="UPNPResult">
+ Not authorized to use the command on the [UPNPDevice]. May be returned when the user disabled UPNP on their router.
+ </constant>
+ <constant name="UPNP_RESULT_PORT_MAPPING_NOT_FOUND" value="2" enum="UPNPResult">
+ No port mapping was found for the given port, protocol combination on the given [UPNPDevice].
+ </constant>
+ <constant name="UPNP_RESULT_INCONSISTENT_PARAMETERS" value="3" enum="UPNPResult">
+ Inconsistent parameters.
+ </constant>
+ <constant name="UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY" value="4" enum="UPNPResult">
+ No such entry in array. May be returned if a given port, protocol combination is not found on an [UPNPDevice].
+ </constant>
+ <constant name="UPNP_RESULT_ACTION_FAILED" value="5" enum="UPNPResult">
+ The action failed.
+ </constant>
+ <constant name="UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED" value="6" enum="UPNPResult">
+ The [UPNPDevice] does not allow wildcard values for the source IP address.
+ </constant>
+ <constant name="UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED" value="7" enum="UPNPResult">
+ The [UPNPDevice] does not allow wildcard values for the external port.
+ </constant>
+ <constant name="UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED" value="8" enum="UPNPResult">
+ The [UPNPDevice] does not allow wildcard values for the internal port.
+ </constant>
+ <constant name="UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD" value="9" enum="UPNPResult">
+ The remote host value must be a wildcard.
+ </constant>
+ <constant name="UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD" value="10" enum="UPNPResult">
+ The external port value must be a wildcard.
+ </constant>
+ <constant name="UPNP_RESULT_NO_PORT_MAPS_AVAILABLE" value="11" enum="UPNPResult">
+ No port maps are available. May also be returned if port mapping functionality is not available.
+ </constant>
+ <constant name="UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM" value="12" enum="UPNPResult">
+ Conflict with other mechanism. May be returned instead of [code]UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING[/code] if a port mapping conflicts with an existing one.
+ </constant>
+ <constant name="UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING" value="13" enum="UPNPResult">
+ Conflict with an existing port mapping.
+ </constant>
+ <constant name="UPNP_RESULT_SAME_PORT_VALUES_REQUIRED" value="14" enum="UPNPResult">
+ External and internal port values must be the same.
+ </constant>
+ <constant name="UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED" value="15" enum="UPNPResult">
+ Only permanent leases are supported. Do not use the [code]duration[/code] parameter when adding port mappings.
+ </constant>
+ <constant name="UPNP_RESULT_INVALID_GATEWAY" value="16" enum="UPNPResult">
+ Invalid gateway.
+ </constant>
+ <constant name="UPNP_RESULT_INVALID_PORT" value="17" enum="UPNPResult">
+ Invalid port.
+ </constant>
+ <constant name="UPNP_RESULT_INVALID_PROTOCOL" value="18" enum="UPNPResult">
+ Invalid protocol.
+ </constant>
+ <constant name="UPNP_RESULT_INVALID_DURATION" value="19" enum="UPNPResult">
+ Invalid duration.
+ </constant>
+ <constant name="UPNP_RESULT_INVALID_ARGS" value="20" enum="UPNPResult">
+ Invalid arguments.
+ </constant>
+ <constant name="UPNP_RESULT_INVALID_RESPONSE" value="21" enum="UPNPResult">
+ Invalid response.
+ </constant>
+ <constant name="UPNP_RESULT_INVALID_PARAM" value="22" enum="UPNPResult">
+ Invalid parameter.
+ </constant>
+ <constant name="UPNP_RESULT_HTTP_ERROR" value="23" enum="UPNPResult">
+ HTTP error.
+ </constant>
+ <constant name="UPNP_RESULT_SOCKET_ERROR" value="24" enum="UPNPResult">
+ Socket error.
+ </constant>
+ <constant name="UPNP_RESULT_MEM_ALLOC_ERROR" value="25" enum="UPNPResult">
+ Error allocating memory.
+ </constant>
+ <constant name="UPNP_RESULT_NO_GATEWAY" value="26" enum="UPNPResult">
+ No gateway available. You may need to call [method discover] first, or discovery didn't detect any valid IGDs (InternetGatewayDevices).
+ </constant>
+ <constant name="UPNP_RESULT_NO_DEVICES" value="27" enum="UPNPResult">
+ No devices available. You may need to call [method discover] first, or discovery didn't detect any valid [UPNPDevice]s.
+ </constant>
+ <constant name="UPNP_RESULT_UNKNOWN_ERROR" value="28" enum="UPNPResult">
+ Unknown error.
+ </constant>
+ </constants>
+</class>
diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml
new file mode 100644
index 0000000000..9de8042daf
--- /dev/null
+++ b/modules/upnp/doc_classes/UPNPDevice.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="UPNPDevice" inherits="Reference" category="Core" version="3.1">
+ <brief_description>
+ UPNP device.
+ </brief_description>
+ <description>
+ UPNP device. See [UPNP] for UPNP discovery and utility functions. Provides low-level access to UPNP control commands. Allows to manage port mappings (port forwarding) and to query network information of the device (like local and external IP address and status). Note that methods on this class are synchronous and block the calling thread.
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="add_port_mapping" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="port" type="int">
+ </argument>
+ <argument index="1" name="port_internal" type="int" default="0">
+ </argument>
+ <argument index="2" name="desc" type="String" default="&quot;&quot;">
+ </argument>
+ <argument index="3" name="proto" type="String" default="&quot;UDP&quot;">
+ </argument>
+ <argument index="4" name="duration" type="int" default="0">
+ </argument>
+ <description>
+ Adds a port mapping to forward the given external port on this [UPNPDevice] for the given protocol to the local machine. See [method UPNP.add_port_mapping].
+ </description>
+ </method>
+ <method name="delete_port_mapping" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="port" type="int">
+ </argument>
+ <argument index="1" name="proto" type="String" default="&quot;UDP&quot;">
+ </argument>
+ <description>
+ Deletes the port mapping identified by the given port and protocol combination on this device. See [method UPNP.delete_port_mapping].
+ </description>
+ </method>
+ <method name="is_valid_gateway" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ Returns [code]true[/code] if this is a valid IGD (InternetGatewayDevice) which potentially supports port forwarding.
+ </description>
+ </method>
+ <method name="query_external_address" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ Returns the external IP address of this [UPNPDevice] or an empty string.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="description_url" type="String" setter="set_description_url" getter="get_description_url">
+ URL to the device description.
+ </member>
+ <member name="igd_control_url" type="String" setter="set_igd_control_url" getter="get_igd_control_url">
+ IDG control URL.
+ </member>
+ <member name="igd_our_addr" type="String" setter="set_igd_our_addr" getter="get_igd_our_addr">
+ Address of the local machine in the network connecting it to this [UPNPDevice].
+ </member>
+ <member name="igd_service_type" type="String" setter="set_igd_service_type" getter="get_igd_service_type">
+ IGD service type.
+ </member>
+ <member name="igd_status" type="int" setter="set_igd_status" getter="get_igd_status" enum="UPNPDevice.IGDStatus">
+ IGD status. See [enum IGDStatus].
+ </member>
+ <member name="service_type" type="String" setter="set_service_type" getter="get_service_type">
+ Service type.
+ </member>
+ </members>
+ <constants>
+ <constant name="IGD_STATUS_OK" value="0" enum="IGDStatus">
+ OK.
+ </constant>
+ <constant name="IGD_STATUS_HTTP_ERROR" value="1" enum="IGDStatus">
+ HTTP error.
+ </constant>
+ <constant name="IGD_STATUS_HTTP_EMPTY" value="2" enum="IGDStatus">
+ Empty HTTP response.
+ </constant>
+ <constant name="IGD_STATUS_NO_URLS" value="3" enum="IGDStatus">
+ Returned response contained no URLs.
+ </constant>
+ <constant name="IGD_STATUS_NO_IGD" value="4" enum="IGDStatus">
+ Not a valid IGD.
+ </constant>
+ <constant name="IGD_STATUS_DISCONNECTED" value="5" enum="IGDStatus">
+ Disconnected.
+ </constant>
+ <constant name="IGD_STATUS_UNKNOWN_DEVICE" value="6" enum="IGDStatus">
+ Unknown device.
+ </constant>
+ <constant name="IGD_STATUS_INVALID_CONTROL" value="7" enum="IGDStatus">
+ Invalid control.
+ </constant>
+ <constant name="IGD_STATUS_MALLOC_ERROR" value="8" enum="IGDStatus">
+ Memory allocation error.
+ </constant>
+ <constant name="IGD_STATUS_UNKNOWN_ERROR" value="9" enum="IGDStatus">
+ Unknown error.
+ </constant>
+ </constants>
+</class>
diff --git a/modules/upnp/register_types.cpp b/modules/upnp/register_types.cpp
new file mode 100644
index 0000000000..c79155c4d2
--- /dev/null
+++ b/modules/upnp/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 "error_macros.h"
+#include "upnp.h"
+#include "upnpdevice.h"
+
+void register_upnp_types() {
+
+ ClassDB::register_class<UPNP>();
+ ClassDB::register_class<UPNPDevice>();
+}
+
+void unregister_upnp_types() {
+}
diff --git a/modules/upnp/register_types.h b/modules/upnp/register_types.h
new file mode 100644
index 0000000000..2aeb06abc7
--- /dev/null
+++ b/modules/upnp/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_upnp_types();
+void unregister_upnp_types();
diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp
new file mode 100644
index 0000000000..32fdfe22f8
--- /dev/null
+++ b/modules/upnp/upnp.cpp
@@ -0,0 +1,401 @@
+/*************************************************************************/
+/* upnp.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 "upnp.h"
+#include "miniupnpc/miniwget.h"
+#include "upnpcommands.h"
+#include <stdlib.h>
+
+bool UPNP::is_common_device(const String &dev) const {
+ return dev.empty() ||
+ dev.find("InternetGatewayDevice") >= 0 ||
+ dev.find("WANIPConnection") >= 0 ||
+ dev.find("WANPPPConnection") >= 0 ||
+ dev.find("rootdevice") >= 0;
+}
+
+int UPNP::discover(int timeout, int ttl, const String &device_filter) {
+ ERR_FAIL_COND_V(timeout < 0, UPNP_RESULT_INVALID_PARAM);
+ ERR_FAIL_COND_V(ttl < 0, UPNP_RESULT_INVALID_PARAM);
+ ERR_FAIL_COND_V(ttl > 255, UPNP_RESULT_INVALID_PARAM);
+
+ devices.clear();
+
+ int error = 0;
+ struct UPNPDev *devlist;
+
+ if (is_common_device(device_filter)) {
+ devlist = upnpDiscover(timeout, discover_multicast_if.utf8().get_data(), NULL, discover_local_port, discover_ipv6, ttl, &error);
+ } else {
+ devlist = upnpDiscoverAll(timeout, discover_multicast_if.utf8().get_data(), NULL, discover_local_port, discover_ipv6, ttl, &error);
+ }
+
+ if (error != UPNPDISCOVER_SUCCESS) {
+ switch (error) {
+ case UPNPDISCOVER_SOCKET_ERROR:
+ return UPNP_RESULT_SOCKET_ERROR;
+ case UPNPDISCOVER_MEMORY_ERROR:
+ return UPNP_RESULT_MEM_ALLOC_ERROR;
+ default:
+ return UPNP_RESULT_UNKNOWN_ERROR;
+ }
+ }
+
+ if (!devlist) {
+ return UPNP_RESULT_NO_DEVICES;
+ }
+
+ struct UPNPDev *dev = devlist;
+
+ while (dev) {
+ if (device_filter.empty() || strstr(dev->st, device_filter.utf8().get_data())) {
+ add_device_to_list(dev, devlist);
+ }
+
+ dev = dev->pNext;
+ }
+
+ freeUPNPDevlist(devlist);
+
+ return UPNP_RESULT_SUCCESS;
+}
+
+void UPNP::add_device_to_list(UPNPDev *dev, UPNPDev *devlist) {
+ Ref<UPNPDevice> new_device;
+ new_device.instance();
+
+ new_device->set_description_url(dev->descURL);
+ new_device->set_service_type(dev->st);
+
+ parse_igd(new_device, devlist);
+
+ devices.push_back(new_device);
+}
+
+char *UPNP::load_description(const String &url, int *size, int *status_code) const {
+ return (char *)miniwget(url.utf8().get_data(), size, 0, status_code);
+}
+
+void UPNP::parse_igd(Ref<UPNPDevice> dev, UPNPDev *devlist) {
+ int size = 0;
+ int status_code = -1;
+ char *xml = load_description(dev->get_description_url(), &size, &status_code);
+
+ if (status_code != 200) {
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_ERROR);
+ return;
+ }
+
+ if (!xml || size < 1) {
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_EMPTY);
+ return;
+ }
+
+ struct UPNPUrls *urls = (UPNPUrls *)malloc(sizeof(struct UPNPUrls));
+
+ if (!urls) {
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_MALLOC_ERROR);
+ return;
+ }
+
+ struct IGDdatas data;
+
+ memset(urls, 0, sizeof(struct UPNPUrls));
+
+ parserootdesc(xml, size, &data);
+ free(xml);
+ xml = 0;
+
+ GetUPNPUrls(urls, &data, dev->get_description_url().utf8().get_data(), 0);
+
+ if (!urls) {
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_NO_URLS);
+ return;
+ }
+
+ char addr[16];
+ int i = UPNP_GetValidIGD(devlist, urls, &data, (char *)&addr, 16);
+
+ if (i != 1) {
+ FreeUPNPUrls(urls);
+
+ switch (i) {
+ case 0:
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_NO_IGD);
+ return;
+ case 2:
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_DISCONNECTED);
+ return;
+ case 3:
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_DEVICE);
+ return;
+ default:
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_ERROR);
+ return;
+ }
+ }
+
+ if (urls->controlURL[0] == '\0') {
+ FreeUPNPUrls(urls);
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_INVALID_CONTROL);
+ return;
+ }
+
+ dev->set_igd_control_url(urls->controlURL);
+ dev->set_igd_service_type(data.first.servicetype);
+ dev->set_igd_our_addr(addr);
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_OK);
+
+ FreeUPNPUrls(urls);
+}
+
+int UPNP::upnp_result(int in) {
+ switch (in) {
+ case UPNPCOMMAND_SUCCESS:
+ return UPNP_RESULT_SUCCESS;
+ case UPNPCOMMAND_UNKNOWN_ERROR:
+ return UPNP_RESULT_UNKNOWN_ERROR;
+ case UPNPCOMMAND_INVALID_ARGS:
+ return UPNP_RESULT_INVALID_ARGS;
+ case UPNPCOMMAND_HTTP_ERROR:
+ return UPNP_RESULT_HTTP_ERROR;
+ case UPNPCOMMAND_INVALID_RESPONSE:
+ return UPNP_RESULT_INVALID_RESPONSE;
+ case UPNPCOMMAND_MEM_ALLOC_ERROR:
+ return UPNP_RESULT_MEM_ALLOC_ERROR;
+
+ case 402:
+ return UPNP_RESULT_INVALID_ARGS;
+ case 403:
+ return UPNP_RESULT_NOT_AUTHORIZED;
+ case 501:
+ return UPNP_RESULT_ACTION_FAILED;
+ case 606:
+ return UPNP_RESULT_NOT_AUTHORIZED;
+ case 714:
+ return UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY;
+ case 715:
+ return UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED;
+ case 716:
+ return UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED;
+ case 718:
+ return UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING;
+ case 724:
+ return UPNP_RESULT_SAME_PORT_VALUES_REQUIRED;
+ case 725:
+ return UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED;
+ case 726:
+ return UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD;
+ case 727:
+ return UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD;
+ case 728:
+ return UPNP_RESULT_NO_PORT_MAPS_AVAILABLE;
+ case 729:
+ return UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM;
+ case 732:
+ return UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED;
+ case 733:
+ return UPNP_RESULT_INCONSISTENT_PARAMETERS;
+ }
+
+ return UPNP_RESULT_UNKNOWN_ERROR;
+}
+
+int UPNP::get_device_count() const {
+ return devices.size();
+}
+
+Ref<UPNPDevice> UPNP::get_device(int index) const {
+ ERR_FAIL_INDEX_V(index, devices.size(), NULL);
+
+ return devices.get(index);
+}
+
+void UPNP::add_device(Ref<UPNPDevice> device) {
+ ERR_FAIL_COND(device == NULL);
+
+ devices.push_back(device);
+}
+
+void UPNP::set_device(int index, Ref<UPNPDevice> device) {
+ ERR_FAIL_INDEX(index, devices.size());
+ ERR_FAIL_COND(device == NULL);
+
+ devices.set(index, device);
+}
+
+void UPNP::remove_device(int index) {
+ ERR_FAIL_INDEX(index, devices.size());
+
+ devices.remove(index);
+}
+
+void UPNP::clear_devices() {
+ devices.clear();
+}
+
+Ref<UPNPDevice> UPNP::get_gateway() const {
+ ERR_FAIL_COND_V(devices.size() < 1, NULL);
+
+ for (int i = 0; i < devices.size(); i++) {
+ Ref<UPNPDevice> dev = get_device(i);
+
+ if (dev != NULL && dev->is_valid_gateway()) {
+ return dev;
+ }
+ }
+
+ return NULL;
+}
+
+void UPNP::set_discover_multicast_if(const String &m_if) {
+ discover_multicast_if = m_if;
+}
+
+String UPNP::get_discover_multicast_if() const {
+ return discover_multicast_if;
+}
+
+void UPNP::set_discover_local_port(int port) {
+ discover_local_port = port;
+}
+
+int UPNP::get_discover_local_port() const {
+ return discover_local_port;
+}
+
+void UPNP::set_discover_ipv6(bool ipv6) {
+ discover_ipv6 = ipv6;
+}
+
+bool UPNP::is_discover_ipv6() const {
+ return discover_ipv6;
+}
+
+String UPNP::query_external_address() const {
+ Ref<UPNPDevice> dev = get_gateway();
+
+ if (dev == NULL) {
+ return "";
+ }
+
+ return dev->query_external_address();
+}
+
+int UPNP::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const {
+ Ref<UPNPDevice> dev = get_gateway();
+
+ if (dev == NULL) {
+ return UPNP_RESULT_NO_GATEWAY;
+ }
+
+ dev->delete_port_mapping(port, proto);
+
+ return dev->add_port_mapping(port, port_internal, desc, proto, duration);
+}
+
+int UPNP::delete_port_mapping(int port, String proto) const {
+ Ref<UPNPDevice> dev = get_gateway();
+
+ if (dev == NULL) {
+ return UPNP_RESULT_NO_GATEWAY;
+ }
+
+ return dev->delete_port_mapping(port, proto);
+}
+
+void UPNP::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_device_count"), &UPNP::get_device_count);
+ ClassDB::bind_method(D_METHOD("get_device", "index"), &UPNP::get_device);
+ ClassDB::bind_method(D_METHOD("add_device", "device"), &UPNP::add_device);
+ ClassDB::bind_method(D_METHOD("set_device", "index", "device"), &UPNP::set_device);
+ ClassDB::bind_method(D_METHOD("remove_device", "index"), &UPNP::remove_device);
+ ClassDB::bind_method(D_METHOD("clear_devices"), &UPNP::clear_devices);
+
+ ClassDB::bind_method(D_METHOD("get_gateway"), &UPNP::get_gateway);
+
+ ClassDB::bind_method(D_METHOD("discover", "timeout", "ttl", "device_filter"), &UPNP::discover, DEFVAL(2000), DEFVAL(2), DEFVAL("InternetGatewayDevice"));
+
+ ClassDB::bind_method(D_METHOD("query_external_address"), &UPNP::query_external_address);
+
+ ClassDB::bind_method(D_METHOD("add_port_mapping", "port", "port_internal", "desc", "proto", "duration"), &UPNP::add_port_mapping, DEFVAL(0), DEFVAL(""), DEFVAL("UDP"), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("delete_port_mapping", "port", "proto"), &UPNP::delete_port_mapping, DEFVAL("UDP"));
+
+ ClassDB::bind_method(D_METHOD("set_discover_multicast_if", "m_if"), &UPNP::set_discover_multicast_if);
+ ClassDB::bind_method(D_METHOD("get_discover_multicast_if"), &UPNP::get_discover_multicast_if);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "discover_multicast_if"), "set_discover_multicast_if", "get_discover_multicast_if");
+
+ ClassDB::bind_method(D_METHOD("set_discover_local_port", "port"), &UPNP::set_discover_local_port);
+ ClassDB::bind_method(D_METHOD("get_discover_local_port"), &UPNP::get_discover_local_port);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "discover_local_port", PROPERTY_HINT_RANGE, "0,65535"), "set_discover_local_port", "get_discover_local_port");
+
+ ClassDB::bind_method(D_METHOD("set_discover_ipv6", "ipv6"), &UPNP::set_discover_ipv6);
+ ClassDB::bind_method(D_METHOD("is_discover_ipv6"), &UPNP::is_discover_ipv6);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "discover_ipv6"), "set_discover_ipv6", "is_discover_ipv6");
+
+ BIND_ENUM_CONSTANT(UPNP_RESULT_SUCCESS);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_NOT_AUTHORIZED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_PORT_MAPPING_NOT_FOUND);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INCONSISTENT_PARAMETERS);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_ACTION_FAILED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_NO_PORT_MAPS_AVAILABLE);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_SAME_PORT_VALUES_REQUIRED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_GATEWAY);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_PORT);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_PROTOCOL);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_DURATION);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_ARGS);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_RESPONSE);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_PARAM);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_HTTP_ERROR);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_SOCKET_ERROR);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_MEM_ALLOC_ERROR);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_NO_GATEWAY);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_NO_DEVICES);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_UNKNOWN_ERROR);
+}
+
+UPNP::UPNP() {
+ discover_multicast_if = "";
+ discover_local_port = 0;
+ discover_ipv6 = false;
+}
+
+UPNP::~UPNP() {
+}
diff --git a/modules/upnp/upnp.h b/modules/upnp/upnp.h
new file mode 100644
index 0000000000..fb0c0f30a0
--- /dev/null
+++ b/modules/upnp/upnp.h
@@ -0,0 +1,124 @@
+/*************************************************************************/
+/* upnp.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_UPNP_H
+#define GODOT_UPNP_H
+
+#include "miniupnpc/miniupnpc.h"
+#include "upnpdevice.h"
+#include <reference.h>
+
+class UPNP : public Reference {
+
+ GDCLASS(UPNP, Reference);
+
+private:
+ String discover_multicast_if;
+ int discover_local_port;
+ bool discover_ipv6;
+
+ Vector<Ref<UPNPDevice> > devices;
+
+ bool is_common_device(const String &dev) const;
+ void add_device_to_list(UPNPDev *dev, UPNPDev *devlist);
+ void parse_igd(Ref<UPNPDevice> dev, UPNPDev *devlist);
+ char *load_description(const String &url, int *size, int *status_code) const;
+
+protected:
+ static void _bind_methods();
+
+public:
+ enum UPNPResult {
+
+ UPNP_RESULT_SUCCESS,
+ UPNP_RESULT_NOT_AUTHORIZED,
+ UPNP_RESULT_PORT_MAPPING_NOT_FOUND,
+ UPNP_RESULT_INCONSISTENT_PARAMETERS,
+ UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY,
+ UPNP_RESULT_ACTION_FAILED,
+ UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED,
+ UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED,
+ UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED,
+ UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD,
+ UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD,
+ UPNP_RESULT_NO_PORT_MAPS_AVAILABLE,
+ UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM,
+ UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING,
+ UPNP_RESULT_SAME_PORT_VALUES_REQUIRED,
+ UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED,
+ UPNP_RESULT_INVALID_GATEWAY,
+ UPNP_RESULT_INVALID_PORT,
+ UPNP_RESULT_INVALID_PROTOCOL,
+ UPNP_RESULT_INVALID_DURATION,
+ UPNP_RESULT_INVALID_ARGS,
+ UPNP_RESULT_INVALID_RESPONSE,
+ UPNP_RESULT_INVALID_PARAM,
+ UPNP_RESULT_HTTP_ERROR,
+ UPNP_RESULT_SOCKET_ERROR,
+ UPNP_RESULT_MEM_ALLOC_ERROR,
+ UPNP_RESULT_NO_GATEWAY,
+ UPNP_RESULT_NO_DEVICES,
+ UPNP_RESULT_UNKNOWN_ERROR,
+ };
+
+ static int upnp_result(int in);
+
+ int get_device_count() const;
+ Ref<UPNPDevice> get_device(int index) const;
+ void add_device(Ref<UPNPDevice> device);
+ void set_device(int index, Ref<UPNPDevice> device);
+ void remove_device(int index);
+ void clear_devices();
+
+ Ref<UPNPDevice> get_gateway() const;
+
+ int discover(int timeout = 2000, int ttl = 2, const String &device_filter = "InternetGatewayDevice");
+
+ String query_external_address() const;
+
+ int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const;
+ int delete_port_mapping(int port, String proto = "UDP") const;
+
+ void set_discover_multicast_if(const String &m_if);
+ String get_discover_multicast_if() const;
+
+ void set_discover_local_port(int port);
+ int get_discover_local_port() const;
+
+ void set_discover_ipv6(bool ipv6);
+ bool is_discover_ipv6() const;
+
+ UPNP();
+ ~UPNP();
+};
+
+VARIANT_ENUM_CAST(UPNP::UPNPResult)
+
+#endif // GODOT_UPNP_H
diff --git a/modules/upnp/upnpdevice.cpp b/modules/upnp/upnpdevice.cpp
new file mode 100644
index 0000000000..a5959cf649
--- /dev/null
+++ b/modules/upnp/upnpdevice.cpp
@@ -0,0 +1,197 @@
+/*************************************************************************/
+/* upnpdevice.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 "upnpdevice.h"
+#include "upnp.h"
+#include "upnpcommands.h"
+
+String UPNPDevice::query_external_address() const {
+ ERR_FAIL_COND_V(!is_valid_gateway(), "");
+
+ char addr[16];
+ int i = UPNP_GetExternalIPAddress(
+ igd_control_url.utf8().get_data(),
+ igd_service_type.utf8().get_data(),
+ (char *)&addr);
+
+ ERR_FAIL_COND_V(i != UPNPCOMMAND_SUCCESS, "");
+
+ return String(addr);
+}
+
+int UPNPDevice::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const {
+ ERR_FAIL_COND_V(!is_valid_gateway(), UPNP::UPNP_RESULT_INVALID_GATEWAY);
+ ERR_FAIL_COND_V(port < 1 || port > 65535, UPNP::UPNP_RESULT_INVALID_PORT);
+ ERR_FAIL_COND_V(port_internal < 0 || port_internal > 65535, UPNP::UPNP_RESULT_INVALID_PORT); // Needs to allow 0 because 0 signifies "use external port as internal port"
+ ERR_FAIL_COND_V(proto != "UDP" && proto != "TCP", UPNP::UPNP_RESULT_INVALID_PROTOCOL);
+ ERR_FAIL_COND_V(duration < 0, UPNP::UPNP_RESULT_INVALID_DURATION);
+
+ if (port_internal < 1) {
+ port_internal = port;
+ }
+
+ int i = UPNP_AddPortMapping(
+ igd_control_url.utf8().get_data(),
+ igd_service_type.utf8().get_data(),
+ itos(port).utf8().get_data(),
+ itos(port_internal).utf8().get_data(),
+ igd_our_addr.utf8().get_data(),
+ desc.empty() ? 0 : desc.utf8().get_data(),
+ proto.utf8().get_data(),
+ NULL, // Remote host, always NULL as IGDs don't support it
+ duration > 0 ? itos(duration).utf8().get_data() : 0);
+
+ ERR_FAIL_COND_V(i != UPNPCOMMAND_SUCCESS, UPNP::upnp_result(i));
+
+ return UPNP::UPNP_RESULT_SUCCESS;
+}
+
+int UPNPDevice::delete_port_mapping(int port, String proto) const {
+ ERR_FAIL_COND_V(port < 1 || port > 65535, UPNP::UPNP_RESULT_INVALID_PORT);
+ ERR_FAIL_COND_V(proto != "UDP" && proto != "TCP", UPNP::UPNP_RESULT_INVALID_PROTOCOL);
+
+ int i = UPNP_DeletePortMapping(
+ igd_control_url.utf8().get_data(),
+ igd_service_type.utf8().get_data(),
+ itos(port).utf8().get_data(),
+ proto.utf8().get_data(),
+ NULL // Remote host, always NULL as IGDs don't support it
+ );
+
+ ERR_FAIL_COND_V(i != UPNPCOMMAND_SUCCESS, UPNP::upnp_result(i));
+
+ return UPNP::UPNP_RESULT_SUCCESS;
+}
+
+void UPNPDevice::set_description_url(const String &url) {
+ description_url = url;
+}
+
+String UPNPDevice::get_description_url() const {
+ return description_url;
+}
+
+void UPNPDevice::set_service_type(const String &type) {
+ service_type = type;
+}
+
+String UPNPDevice::get_service_type() const {
+ return service_type;
+}
+
+void UPNPDevice::set_igd_control_url(const String &url) {
+ igd_control_url = url;
+}
+
+String UPNPDevice::get_igd_control_url() const {
+ return igd_control_url;
+}
+
+void UPNPDevice::set_igd_service_type(const String &type) {
+ igd_service_type = type;
+}
+
+String UPNPDevice::get_igd_service_type() const {
+ return igd_service_type;
+}
+
+void UPNPDevice::set_igd_our_addr(const String &addr) {
+ igd_our_addr = addr;
+}
+
+String UPNPDevice::get_igd_our_addr() const {
+ return igd_our_addr;
+}
+
+void UPNPDevice::set_igd_status(IGDStatus status) {
+ igd_status = status;
+}
+
+UPNPDevice::IGDStatus UPNPDevice::get_igd_status() const {
+ return igd_status;
+}
+
+bool UPNPDevice::is_valid_gateway() const {
+ return igd_status == IGD_STATUS_OK;
+}
+
+void UPNPDevice::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("is_valid_gateway"), &UPNPDevice::is_valid_gateway);
+ ClassDB::bind_method(D_METHOD("query_external_address"), &UPNPDevice::query_external_address);
+ ClassDB::bind_method(D_METHOD("add_port_mapping", "port", "port_internal", "desc", "proto", "duration"), &UPNPDevice::add_port_mapping, DEFVAL(0), DEFVAL(""), DEFVAL("UDP"), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("delete_port_mapping", "port", "proto"), &UPNPDevice::delete_port_mapping, DEFVAL("UDP"));
+
+ ClassDB::bind_method(D_METHOD("set_description_url", "url"), &UPNPDevice::set_description_url);
+ ClassDB::bind_method(D_METHOD("get_description_url"), &UPNPDevice::get_description_url);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "description_url"), "set_description_url", "get_description_url");
+
+ ClassDB::bind_method(D_METHOD("set_service_type", "type"), &UPNPDevice::set_service_type);
+ ClassDB::bind_method(D_METHOD("get_service_type"), &UPNPDevice::get_service_type);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "service_type"), "set_service_type", "get_service_type");
+
+ ClassDB::bind_method(D_METHOD("set_igd_control_url", "url"), &UPNPDevice::set_igd_control_url);
+ ClassDB::bind_method(D_METHOD("get_igd_control_url"), &UPNPDevice::get_igd_control_url);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "igd_control_url"), "set_igd_control_url", "get_igd_control_url");
+
+ ClassDB::bind_method(D_METHOD("set_igd_service_type", "type"), &UPNPDevice::set_igd_service_type);
+ ClassDB::bind_method(D_METHOD("get_igd_service_type"), &UPNPDevice::get_igd_service_type);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "igd_service_type"), "set_igd_service_type", "get_igd_service_type");
+
+ ClassDB::bind_method(D_METHOD("set_igd_our_addr", "addr"), &UPNPDevice::set_igd_our_addr);
+ ClassDB::bind_method(D_METHOD("get_igd_our_addr"), &UPNPDevice::get_igd_our_addr);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "igd_our_addr"), "set_igd_our_addr", "get_igd_our_addr");
+
+ ClassDB::bind_method(D_METHOD("set_igd_status", "status"), &UPNPDevice::set_igd_status);
+ ClassDB::bind_method(D_METHOD("get_igd_status"), &UPNPDevice::get_igd_status);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "igd_status", PROPERTY_HINT_ENUM), "set_igd_status", "get_igd_status");
+
+ BIND_ENUM_CONSTANT(IGD_STATUS_OK);
+ BIND_ENUM_CONSTANT(IGD_STATUS_HTTP_ERROR);
+ BIND_ENUM_CONSTANT(IGD_STATUS_HTTP_EMPTY);
+ BIND_ENUM_CONSTANT(IGD_STATUS_NO_URLS);
+ BIND_ENUM_CONSTANT(IGD_STATUS_NO_IGD);
+ BIND_ENUM_CONSTANT(IGD_STATUS_DISCONNECTED);
+ BIND_ENUM_CONSTANT(IGD_STATUS_UNKNOWN_DEVICE);
+ BIND_ENUM_CONSTANT(IGD_STATUS_INVALID_CONTROL);
+ BIND_ENUM_CONSTANT(IGD_STATUS_MALLOC_ERROR);
+ BIND_ENUM_CONSTANT(IGD_STATUS_UNKNOWN_ERROR);
+}
+
+UPNPDevice::UPNPDevice() {
+ description_url = "";
+ service_type = "";
+ igd_control_url = "";
+ igd_service_type = "";
+ igd_our_addr = "";
+ igd_status = IGD_STATUS_UNKNOWN_ERROR;
+}
+
+UPNPDevice::~UPNPDevice() {
+}
diff --git a/modules/upnp/upnpdevice.h b/modules/upnp/upnpdevice.h
new file mode 100644
index 0000000000..25c9fe1ba3
--- /dev/null
+++ b/modules/upnp/upnpdevice.h
@@ -0,0 +1,95 @@
+/*************************************************************************/
+/* upnpdevice.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_UPNPDEVICE_H
+#define GODOT_UPNPDEVICE_H
+
+#include <reference.h>
+
+class UPNPDevice : public Reference {
+
+ GDCLASS(UPNPDevice, Reference);
+
+public:
+ enum IGDStatus {
+
+ IGD_STATUS_OK,
+ IGD_STATUS_HTTP_ERROR,
+ IGD_STATUS_HTTP_EMPTY,
+ IGD_STATUS_NO_URLS,
+ IGD_STATUS_NO_IGD,
+ IGD_STATUS_DISCONNECTED,
+ IGD_STATUS_UNKNOWN_DEVICE,
+ IGD_STATUS_INVALID_CONTROL,
+ IGD_STATUS_MALLOC_ERROR,
+ IGD_STATUS_UNKNOWN_ERROR,
+ };
+
+ void set_description_url(const String &url);
+ String get_description_url() const;
+
+ void set_service_type(const String &type);
+ String get_service_type() const;
+
+ void set_igd_control_url(const String &url);
+ String get_igd_control_url() const;
+
+ void set_igd_service_type(const String &type);
+ String get_igd_service_type() const;
+
+ void set_igd_our_addr(const String &addr);
+ String get_igd_our_addr() const;
+
+ void set_igd_status(IGDStatus status);
+ IGDStatus get_igd_status() const;
+
+ bool is_valid_gateway() const;
+ String query_external_address() const;
+ int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const;
+ int delete_port_mapping(int port, String proto = "UDP") const;
+
+ UPNPDevice();
+ ~UPNPDevice();
+
+protected:
+ static void _bind_methods();
+
+private:
+ String description_url;
+ String service_type;
+ String igd_control_url;
+ String igd_service_type;
+ String igd_our_addr;
+ IGDStatus igd_status;
+};
+
+VARIANT_ENUM_CAST(UPNPDevice::IGDStatus)
+
+#endif // GODOT_UPNPDEVICE_H
diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py
index 6b1ce41014..07a0450734 100644
--- a/modules/visual_script/config.py
+++ b/modules/visual_script/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml
index dab186fb57..28764aca40 100644
--- a/modules/visual_script/doc_classes/VisualScript.xml
+++ b/modules/visual_script/doc_classes/VisualScript.xml
@@ -9,7 +9,7 @@
You are most likely to use this class via the Visual Script editor or when writing plugins for it.
</description>
<tutorials>
- http://docs.godotengine.org/en/3.0/getting_started/scripting/visual_script/index.html
+ <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/visual_script/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 03bc4c114a..9331092171 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -121,6 +121,10 @@ Array VisualScriptNode::_get_default_input_values() const {
return default_input_values;
}
+String VisualScriptNode::get_text() const {
+ return "";
+}
+
void VisualScriptNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_visual_script"), &VisualScriptNode::get_visual_script);
@@ -1967,11 +1971,11 @@ Ref<Script> VisualScriptInstance::get_script() const {
return script;
}
-ScriptInstance::RPCMode VisualScriptInstance::get_rpc_mode(const StringName &p_method) const {
+MultiplayerAPI::RPCMode VisualScriptInstance::get_rpc_mode(const StringName &p_method) const {
const Map<StringName, VisualScript::Function>::Element *E = script->functions.find(p_method);
if (!E) {
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
if (E->get().function_id >= 0 && E->get().nodes.has(E->get().function_id)) {
@@ -1983,12 +1987,12 @@ ScriptInstance::RPCMode VisualScriptInstance::get_rpc_mode(const StringName &p_m
}
}
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
-ScriptInstance::RPCMode VisualScriptInstance::get_rset_mode(const StringName &p_variable) const {
+MultiplayerAPI::RPCMode VisualScriptInstance::get_rset_mode(const StringName &p_variable) const {
- return RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_owner) {
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index dad9c68312..aaa6dfea11 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -78,7 +78,7 @@ public:
Variant get_default_input_value(int p_port) const;
virtual String get_caption() const = 0;
- virtual String get_text() const = 0;
+ virtual String get_text() const;
virtual String get_category() const = 0;
//used by editor, this is not really saved
@@ -433,8 +433,8 @@ public:
virtual ScriptLanguage *get_language();
- virtual RPCMode get_rpc_mode(const StringName &p_method) const;
- virtual RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
VisualScriptInstance();
~VisualScriptInstance();
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 7f0a42da82..73b6d702c1 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -666,12 +666,14 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
return PropertyInfo(t, "");
}
+/*
String VisualScriptBuiltinFunc::get_caption() const {
return "BuiltinFunc";
}
+*/
-String VisualScriptBuiltinFunc::get_text() const {
+String VisualScriptBuiltinFunc::get_caption() const {
return func_name[func];
}
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index f862d5c26f..3345762b9f 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -129,7 +129,7 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
+ //virtual String get_text() const;
virtual String get_category() const { return "functions"; }
void set_func(BuiltinFunc p_which);
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index eb10c5e99f..873cc293c9 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -268,7 +268,7 @@ protected:
if (String(p_name) == "export") {
script->set_variable_export(var, p_value);
- EditorNode::get_singleton()->get_property_editor()->update_tree();
+ EditorNode::get_singleton()->get_inspector()->update_tree();
return true;
}
@@ -527,6 +527,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
GraphNode *gnode = memnew(GraphNode);
gnode->set_title(node->get_caption());
+ gnode->set_offset(pos * EDSCALE);
if (error_line == E->get()) {
gnode->set_overlay(GraphNode::OVERLAY_POSITION);
} else if (node->is_breakpoint()) {
@@ -543,8 +544,10 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
gnode->set_show_close_button(true);
}
- if (Object::cast_to<VisualScriptExpression>(node.ptr())) {
+ bool has_gnode_text = false;
+ if (Object::cast_to<VisualScriptExpression>(node.ptr())) {
+ has_gnode_text = true;
LineEdit *line_edit = memnew(LineEdit);
line_edit->set_text(node->get_text());
line_edit->set_expand_to_text_length(true);
@@ -552,9 +555,13 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
gnode->add_child(line_edit);
line_edit->connect("text_changed", this, "_expression_text_changed", varray(E->get()));
} else {
- Label *text = memnew(Label);
- text->set_text(node->get_text());
- gnode->add_child(text);
+ String text = node->get_text();
+ if (!text.empty()) {
+ has_gnode_text = true;
+ Label *label = memnew(Label);
+ label->set_text(text);
+ gnode->add_child(label);
+ }
}
if (Object::cast_to<VisualScriptComment>(node.ptr())) {
@@ -589,9 +596,21 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
int slot_idx = 0;
bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String();
- gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
- gnode->set_offset(pos * EDSCALE);
- slot_idx++;
+ if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) {
+ // IF has_gnode_text is true BUT we have no sequence ports to draw (in here),
+ // we still draw the disabled default ones to shift up the slots by one,
+ // so the slots DON'T start with the content text.
+
+ // IF has_gnode_text is false, but we DO want to draw default sequence ports,
+ // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly.
+ if (!has_gnode_text) {
+ Label *dummy = memnew(Label);
+ dummy->set_text(" ");
+ gnode->add_child(dummy);
+ }
+ gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
+ slot_idx++;
+ }
int mixed_seq_ports = 0;
@@ -3370,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/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp
index 5c097dfa76..ea23ab1b2a 100644
--- a/modules/visual_script/visual_script_flow_control.cpp
+++ b/modules/visual_script/visual_script_flow_control.cpp
@@ -772,7 +772,7 @@ PropertyInfo VisualScriptTypeCast::get_output_value_port_info(int p_idx) const {
String VisualScriptTypeCast::get_caption() const {
- return "TypeCast";
+ return "Type Cast";
}
String VisualScriptTypeCast::get_text() const {
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index 187c9b0b9e..bdf5705ecd 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -262,26 +262,6 @@ PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) con
}
String VisualScriptFunctionCall::get_caption() const {
-
- static const char *cname[5] = {
- "CallSelf",
- "CallNode",
- "CallInstance",
- "CallBasic",
- "CallSingleton"
- };
-
- String caption = cname[call_mode];
-
- if (rpc_call_mode) {
- caption += " (RPC)";
- }
-
- return caption;
-}
-
-String VisualScriptFunctionCall::get_text() const {
-
if (call_mode == CALL_MODE_SELF)
return " " + String(function) + "()";
if (call_mode == CALL_MODE_SINGLETON)
@@ -294,6 +274,14 @@ String VisualScriptFunctionCall::get_text() const {
return " " + base_type + "." + String(function) + "()";
}
+String VisualScriptFunctionCall::get_text() const {
+
+ if (rpc_call_mode) {
+ return "RPC";
+ }
+ return "";
+}
+
void VisualScriptFunctionCall::set_basic_type(Variant::Type p_type) {
if (basic_type == p_type)
@@ -1075,36 +1063,31 @@ PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) cons
String VisualScriptPropertySet::get_caption() const {
- static const char *cname[4] = {
- "Self",
- "Node",
- "Instance",
- "Basic"
- };
-
static const char *opname[ASSIGN_OP_MAX] = {
- "Set", "Add", "Sub", "Mul", "Div", "Mod", "ShiftLeft", "ShiftRight", "BitAnd", "BitOr", "BitXor"
+ "Set", "Add", "Subtract", "Multiply", "Divide", "Mod", "ShiftLeft", "ShiftRight", "BitAnd", "BitOr", "BitXor"
};
- return String(cname[call_mode]) + opname[assign_op];
+
+ String prop = String(opname[assign_op]) + " " + property;
+ if (index != StringName()) {
+ prop += "." + String(index);
+ }
+
+ return prop;
}
String VisualScriptPropertySet::get_text() const {
- String prop;
+ if (call_mode == CALL_MODE_BASIC_TYPE) {
+ return String("On ") + Variant::get_type_name(basic_type);
+ }
- if (call_mode == CALL_MODE_BASIC_TYPE)
- prop = Variant::get_type_name(basic_type) + "." + property;
- else if (call_mode == CALL_MODE_NODE_PATH)
- prop = String(base_path) + ":" + property;
- else if (call_mode == CALL_MODE_SELF)
- prop = property;
- else if (call_mode == CALL_MODE_INSTANCE)
- prop = String(base_type) + ":" + property;
+ static const char *cname[3] = {
+ "Self",
+ "Scene Node",
+ "Instance"
+ };
- if (index != StringName()) {
- prop += "." + String(index);
- }
- return prop;
+ return String("On ") + cname[call_mode];
}
void VisualScriptPropertySet::_update_base_type() {
@@ -1838,30 +1821,22 @@ PropertyInfo VisualScriptPropertyGet::get_output_value_port_info(int p_idx) cons
String VisualScriptPropertyGet::get_caption() const {
- static const char *cname[4] = {
- "SelfGet",
- "NodeGet",
- "InstanceGet",
- "BasicGet"
- };
-
- return cname[call_mode];
+ return String("Get ") + property;
}
String VisualScriptPropertyGet::get_text() const {
- String prop;
+ if (call_mode == CALL_MODE_BASIC_TYPE) {
+ return String("On ") + Variant::get_type_name(basic_type);
+ }
- if (call_mode == CALL_MODE_BASIC_TYPE)
- prop = Variant::get_type_name(basic_type) + "." + property;
- else if (call_mode == CALL_MODE_NODE_PATH)
- prop = String(base_path) + ":" + property;
- else if (call_mode == CALL_MODE_SELF)
- prop = property;
- else if (call_mode == CALL_MODE_INSTANCE)
- prop = String(base_type) + ":" + property;
+ static const char *cname[3] = {
+ "Self",
+ "Scene Node",
+ "Instance"
+ };
- return prop;
+ return String("On ") + cname[call_mode];
}
void VisualScriptPropertyGet::set_base_type(const StringName &p_type) {
@@ -2399,12 +2374,7 @@ PropertyInfo VisualScriptEmitSignal::get_output_value_port_info(int p_idx) const
String VisualScriptEmitSignal::get_caption() const {
- return "EmitSignal";
-}
-
-String VisualScriptEmitSignal::get_text() const {
-
- return "emit " + String(name);
+ return "Emit " + String(name);
}
void VisualScriptEmitSignal::set_signal(const StringName &p_type) {
diff --git a/modules/visual_script/visual_script_func_nodes.h b/modules/visual_script/visual_script_func_nodes.h
index 0b30eae65a..3b522564d4 100644
--- a/modules/visual_script/visual_script_func_nodes.h
+++ b/modules/visual_script/visual_script_func_nodes.h
@@ -346,7 +346,7 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
+ //virtual String get_text() const;
virtual String get_category() const { return "functions"; }
void set_signal(const StringName &p_type);
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index c5654a5a20..8b7b809ec0 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -93,7 +93,7 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value
}
if (p_name == "rpc/mode") {
- rpc_mode = ScriptInstance::RPCMode(int(p_value));
+ rpc_mode = MultiplayerAPI::RPCMode(int(p_value));
return true;
}
@@ -267,11 +267,11 @@ int VisualScriptFunction::get_argument_count() const {
return arguments.size();
}
-void VisualScriptFunction::set_rpc_mode(ScriptInstance::RPCMode p_mode) {
+void VisualScriptFunction::set_rpc_mode(MultiplayerAPI::RPCMode p_mode) {
rpc_mode = p_mode;
}
-ScriptInstance::RPCMode VisualScriptFunction::get_rpc_mode() const {
+MultiplayerAPI::RPCMode VisualScriptFunction::get_rpc_mode() const {
return rpc_mode;
}
@@ -319,7 +319,7 @@ VisualScriptFunction::VisualScriptFunction() {
stack_size = 256;
stack_less = false;
sequenced = true;
- rpc_mode = ScriptInstance::RPC_MODE_DISABLED;
+ rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
}
void VisualScriptFunction::set_stack_less(bool p_enable) {
@@ -467,12 +467,12 @@ PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const {
static const char *op_names[] = {
//comparison
- "Equal", //OP_EQUAL,
- "NotEqual", //OP_NOT_EQUAL,
- "Less", //OP_LESS,
- "LessEqual", //OP_LESS_EQUAL,
- "Greater", //OP_GREATER,
- "GreaterEq", //OP_GREATER_EQUAL,
+ "Are Equal", //OP_EQUAL,
+ "Are Not Equal", //OP_NOT_EQUAL,
+ "Less Than", //OP_LESS,
+ "Less Than or Equal", //OP_LESS_EQUAL,
+ "Greater Than", //OP_GREATER,
+ "Greater Than or Equal", //OP_GREATER_EQUAL,
//mathematic
"Add", //OP_ADD,
"Subtract", //OP_SUBTRACT,
@@ -481,14 +481,14 @@ static const char *op_names[] = {
"Negate", //OP_NEGATE,
"Positive", //OP_POSITIVE,
"Remainder", //OP_MODULE,
- "Concat", //OP_STRING_CONCAT,
+ "Concatenate", //OP_STRING_CONCAT,
//bitwise
- "ShiftLeft", //OP_SHIFT_LEFT,
- "ShiftRight", //OP_SHIFT_RIGHT,
- "BitAnd", //OP_BIT_AND,
- "BitOr", //OP_BIT_OR,
- "BitXor", //OP_BIT_XOR,
- "BitNeg", //OP_BIT_NEGATE,
+ "Bit Shift Left", //OP_SHIFT_LEFT,
+ "Bit Shift Right", //OP_SHIFT_RIGHT,
+ "Bit And", //OP_BIT_AND,
+ "Bit Or", //OP_BIT_OR,
+ "Bit Xor", //OP_BIT_XOR,
+ "Bit Negate", //OP_BIT_NEGATE,
//logic
"And", //OP_AND,
"Or", //OP_OR,
@@ -500,11 +500,6 @@ static const char *op_names[] = {
String VisualScriptOperator::get_caption() const {
- return op_names[op];
-}
-
-String VisualScriptOperator::get_text() const {
-
static const wchar_t *op_names[] = {
//comparison
L"A = B", //OP_EQUAL,
@@ -803,14 +798,8 @@ PropertyInfo VisualScriptVariableGet::get_output_value_port_info(int p_idx) cons
String VisualScriptVariableGet::get_caption() const {
- return "Variable";
+ return "Get " + variable;
}
-
-String VisualScriptVariableGet::get_text() const {
-
- return variable;
-}
-
void VisualScriptVariableGet::set_variable(StringName p_variable) {
if (variable == p_variable)
@@ -928,12 +917,7 @@ PropertyInfo VisualScriptVariableSet::get_output_value_port_info(int p_idx) cons
String VisualScriptVariableSet::get_caption() const {
- return "VariableSet";
-}
-
-String VisualScriptVariableSet::get_text() const {
-
- return variable;
+ return "Set " + variable;
}
void VisualScriptVariableSet::set_variable(StringName p_variable) {
@@ -1044,7 +1028,7 @@ PropertyInfo VisualScriptConstant::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptConstant::get_output_value_port_info(int p_idx) const {
PropertyInfo pinfo;
- pinfo.name = "get";
+ pinfo.name = String(value);
pinfo.type = type;
return pinfo;
}
@@ -1054,11 +1038,6 @@ String VisualScriptConstant::get_caption() const {
return "Constant";
}
-String VisualScriptConstant::get_text() const {
-
- return String(value);
-}
-
void VisualScriptConstant::set_constant_type(Variant::Type p_type) {
if (type == p_type)
@@ -1174,10 +1153,20 @@ PropertyInfo VisualScriptPreload::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptPreload::get_output_value_port_info(int p_idx) const {
- PropertyInfo pinfo = PropertyInfo(Variant::OBJECT, "res");
+ PropertyInfo pinfo;
+ pinfo.type = Variant::OBJECT;
if (preload.is_valid()) {
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
pinfo.hint_string = preload->get_class();
+ if (preload->get_path().is_resource_file()) {
+ pinfo.name = preload->get_path();
+ } else if (preload->get_name() != String()) {
+ pinfo.name = preload->get_name();
+ } else {
+ pinfo.name = preload->get_class();
+ }
+ } else {
+ pinfo.name = "<empty>";
}
return pinfo;
@@ -1188,21 +1177,6 @@ String VisualScriptPreload::get_caption() const {
return "Preload";
}
-String VisualScriptPreload::get_text() const {
-
- if (preload.is_valid()) {
- if (preload->get_path().is_resource_file()) {
- return preload->get_path();
- } else if (preload->get_name() != String()) {
- return preload->get_name();
- } else {
- return preload->get_class();
- }
- } else {
- return "<empty>";
- }
-}
-
void VisualScriptPreload::set_preload(const Ref<Resource> &p_preload) {
if (preload == p_preload)
@@ -1291,12 +1265,7 @@ PropertyInfo VisualScriptIndexGet::get_output_value_port_info(int p_idx) const {
String VisualScriptIndexGet::get_caption() const {
- return "IndexGet";
-}
-
-String VisualScriptIndexGet::get_text() const {
-
- return String("get");
+ return "Get Index";
}
class VisualScriptNodeInstanceIndexGet : public VisualScriptNodeInstance {
@@ -1371,12 +1340,7 @@ PropertyInfo VisualScriptIndexSet::get_output_value_port_info(int p_idx) const {
String VisualScriptIndexSet::get_caption() const {
- return "IndexSet";
-}
-
-String VisualScriptIndexSet::get_text() const {
-
- return String("set");
+ return "Set Index";
}
class VisualScriptNodeInstanceIndexSet : public VisualScriptNodeInstance {
@@ -1439,18 +1403,13 @@ PropertyInfo VisualScriptGlobalConstant::get_input_value_port_info(int p_idx) co
}
PropertyInfo VisualScriptGlobalConstant::get_output_value_port_info(int p_idx) const {
-
- return PropertyInfo(Variant::REAL, "value");
+ String name = GlobalConstants::get_global_constant_name(index);
+ return PropertyInfo(Variant::REAL, name);
}
String VisualScriptGlobalConstant::get_caption() const {
- return "GlobalConst";
-}
-
-String VisualScriptGlobalConstant::get_text() const {
-
- return GlobalConstants::get_global_constant_name(index);
+ return "Global Constant";
}
void VisualScriptGlobalConstant::set_global_constant(int p_which) {
@@ -1539,17 +1498,12 @@ PropertyInfo VisualScriptClassConstant::get_input_value_port_info(int p_idx) con
PropertyInfo VisualScriptClassConstant::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::INT, "value");
+ return PropertyInfo(Variant::INT, String(base_type) + "." + String(name));
}
String VisualScriptClassConstant::get_caption() const {
- return "ClassConst";
-}
-
-String VisualScriptClassConstant::get_text() const {
-
- return String(base_type) + "." + String(name);
+ return "Class Constant";
}
void VisualScriptClassConstant::set_class_constant(const StringName &p_which) {
@@ -1673,7 +1627,7 @@ PropertyInfo VisualScriptBasicTypeConstant::get_output_value_port_info(int p_idx
String VisualScriptBasicTypeConstant::get_caption() const {
- return "BasicConst";
+ return "Basic Constant";
}
String VisualScriptBasicTypeConstant::get_text() const {
@@ -1828,17 +1782,12 @@ PropertyInfo VisualScriptMathConstant::get_input_value_port_info(int p_idx) cons
PropertyInfo VisualScriptMathConstant::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::REAL, "value");
+ return PropertyInfo(Variant::REAL, const_name[constant]);
}
String VisualScriptMathConstant::get_caption() const {
- return "MathConst";
-}
-
-String VisualScriptMathConstant::get_text() const {
-
- return const_name[constant];
+ return "Math Constant";
}
void VisualScriptMathConstant::set_math_constant(MathConstant p_which) {
@@ -1903,7 +1852,7 @@ VisualScriptMathConstant::VisualScriptMathConstant() {
}
//////////////////////////////////////////
-////////////////GLOBALSINGLETON///////////
+////////////////ENGINESINGLETON///////////
//////////////////////////////////////////
int VisualScriptEngineSingleton::get_output_sequence_port_count() const {
@@ -1937,17 +1886,12 @@ PropertyInfo VisualScriptEngineSingleton::get_input_value_port_info(int p_idx) c
PropertyInfo VisualScriptEngineSingleton::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "instance");
+ return PropertyInfo(Variant::OBJECT, singleton);
}
String VisualScriptEngineSingleton::get_caption() const {
- return "EngineSingleton";
-}
-
-String VisualScriptEngineSingleton::get_text() const {
-
- return singleton;
+ return "Get Engine Singleton";
}
void VisualScriptEngineSingleton::set_singleton(const String &p_string) {
@@ -2058,17 +2002,12 @@ PropertyInfo VisualScriptSceneNode::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptSceneNode::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "node");
+ return PropertyInfo(Variant::OBJECT, path.simplified());
}
String VisualScriptSceneNode::get_caption() const {
- return "SceneNode";
-}
-
-String VisualScriptSceneNode::get_text() const {
-
- return path.simplified();
+ return "Get Scene Node";
}
void VisualScriptSceneNode::set_node_path(const NodePath &p_path) {
@@ -2259,17 +2198,12 @@ PropertyInfo VisualScriptSceneTree::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptSceneTree::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "instance");
+ return PropertyInfo(Variant::OBJECT, "Scene Tree");
}
String VisualScriptSceneTree::get_caption() const {
- return "SceneTree";
-}
-
-String VisualScriptSceneTree::get_text() const {
-
- return "";
+ return "Get Scene Tree";
}
class VisualScriptNodeInstanceSceneTree : public VisualScriptNodeInstance {
@@ -2361,17 +2295,12 @@ PropertyInfo VisualScriptResourcePath::get_input_value_port_info(int p_idx) cons
PropertyInfo VisualScriptResourcePath::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::STRING, "path");
+ return PropertyInfo(Variant::STRING, path);
}
String VisualScriptResourcePath::get_caption() const {
- return "ResourcePath";
-}
-
-String VisualScriptResourcePath::get_text() const {
-
- return path;
+ return "Resource Path";
}
void VisualScriptResourcePath::set_resource_path(const String &p_path) {
@@ -2453,20 +2382,18 @@ PropertyInfo VisualScriptSelf::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptSelf::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "instance");
-}
-
-String VisualScriptSelf::get_caption() const {
+ String type_name;
+ if (get_visual_script().is_valid())
+ type_name = get_visual_script()->get_instance_base_type();
+ else
+ type_name = "instance";
- return "Self";
+ return PropertyInfo(Variant::OBJECT, type_name);
}
-String VisualScriptSelf::get_text() const {
+String VisualScriptSelf::get_caption() const {
- if (get_visual_script().is_valid())
- return get_visual_script()->get_instance_base_type();
- else
- return "";
+ return "Get Self";
}
class VisualScriptNodeInstanceSelf : public VisualScriptNodeInstance {
@@ -3032,12 +2959,7 @@ PropertyInfo VisualScriptConstructor::get_output_value_port_info(int p_idx) cons
String VisualScriptConstructor::get_caption() const {
- return "Construct";
-}
-
-String VisualScriptConstructor::get_text() const {
-
- return "new " + Variant::get_type_name(type) + "()";
+ return "Construct " + Variant::get_type_name(type);
}
String VisualScriptConstructor::get_category() const {
@@ -3163,17 +3085,12 @@ PropertyInfo VisualScriptLocalVar::get_input_value_port_info(int p_idx) const {
}
PropertyInfo VisualScriptLocalVar::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(type, "get");
+ return PropertyInfo(type, name);
}
String VisualScriptLocalVar::get_caption() const {
- return "LocalVarGet";
-}
-
-String VisualScriptLocalVar::get_text() const {
-
- return name;
+ return "Get Local Var";
}
String VisualScriptLocalVar::get_category() const {
@@ -3289,7 +3206,7 @@ PropertyInfo VisualScriptLocalVarSet::get_output_value_port_info(int p_idx) cons
String VisualScriptLocalVarSet::get_caption() const {
- return "LocalVarSet";
+ return "Set Local Var";
}
String VisualScriptLocalVarSet::get_text() const {
@@ -3427,12 +3344,7 @@ PropertyInfo VisualScriptInputAction::get_output_value_port_info(int p_idx) cons
String VisualScriptInputAction::get_caption() const {
- return "Action";
-}
-
-String VisualScriptInputAction::get_text() const {
-
- return name;
+ return "Action " + name;
}
String VisualScriptInputAction::get_category() const {
@@ -3600,12 +3512,7 @@ PropertyInfo VisualScriptDeconstruct::get_output_value_port_info(int p_idx) cons
String VisualScriptDeconstruct::get_caption() const {
- return "Deconstruct";
-}
-
-String VisualScriptDeconstruct::get_text() const {
-
- return "from " + Variant::get_type_name(type) + ":";
+ return "Deconstruct " + Variant::get_type_name(type);
}
String VisualScriptDeconstruct::get_category() const {
diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h
index a581e81c8c..9bfbd46e47 100644
--- a/modules/visual_script/visual_script_nodes.h
+++ b/modules/visual_script/visual_script_nodes.h
@@ -46,7 +46,7 @@ class VisualScriptFunction : public VisualScriptNode {
bool stack_less;
int stack_size;
- ScriptInstance::RPCMode rpc_mode;
+ MultiplayerAPI::RPCMode rpc_mode;
bool sequenced;
protected:
@@ -93,8 +93,8 @@ public:
void set_return_type(Variant::Type p_type);
Variant::Type get_return_type() const;
- void set_rpc_mode(ScriptInstance::RPCMode p_mode);
- ScriptInstance::RPCMode get_rpc_mode() const;
+ void set_rpc_mode(MultiplayerAPI::RPCMode p_mode);
+ MultiplayerAPI::RPCMode get_rpc_mode() const;
virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
@@ -124,7 +124,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "operators"; }
void set_operator(Variant::Operator p_op);
@@ -194,7 +193,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_variable(StringName p_variable);
@@ -228,7 +226,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_variable(StringName p_variable);
@@ -263,7 +260,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "constants"; }
void set_constant_type(Variant::Type p_type);
@@ -299,7 +295,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_preload(const Ref<Resource> &p_preload);
@@ -327,7 +322,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "operators"; }
virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
@@ -352,7 +346,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "operators"; }
virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
@@ -381,7 +374,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "constants"; }
void set_global_constant(int p_which);
@@ -416,7 +408,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "constants"; }
void set_class_constant(const StringName &p_which);
@@ -505,7 +496,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "constants"; }
void set_math_constant(MathConstant p_which);
@@ -539,7 +529,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_singleton(const String &p_string);
@@ -575,7 +564,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_node_path(const NodePath &p_path);
@@ -609,7 +597,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
@@ -641,7 +628,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_resource_path(const String &p_path);
@@ -672,7 +658,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
@@ -822,7 +807,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const;
void set_constructor_type(Variant::Type p_type);
@@ -859,7 +843,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const;
void set_var_name(const StringName &p_name);
@@ -942,7 +925,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const;
void set_action_name(const StringName &p_name);
@@ -993,7 +975,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const;
void set_deconstruct_type(Variant::Type p_type);
diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py
index 5f133eba90..1c8cd12a2d 100644
--- a/modules/vorbis/config.py
+++ b/modules/vorbis/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/webm/config.py b/modules/webm/config.py
index dcae4447d5..72a4073423 100644
--- a/modules/webm/config.py
+++ b/modules/webm/config.py
@@ -1,5 +1,5 @@
-def can_build(platform):
- return platform != 'iphone'
+def can_build(env, platform):
+ return platform not in ['iphone']
def configure(env):
pass
diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub
index b09c232b3c..c681e2b34f 100644
--- a/modules/webm/libvpx/SCsub
+++ b/modules/webm/libvpx/SCsub
@@ -263,7 +263,7 @@ if env["platform"] == 'uwp':
webm_cpu_x86 = True
else:
import platform
- is_x11_or_server_arm = ((env["platform"] == 'x11' or env["platform"] == 'server') and platform.machine().startswith('arm'))
+ is_x11_or_server_arm = ((env["platform"] == 'x11' or env["platform"] == 'server') and (platform.machine().startswith('arm') or platform.machine().startswith('aarch')))
is_ios_x86 = (env["platform"] == 'iphone' and env["ios_sim"])
is_android_x86 = (env["platform"] == 'android' and env["android_arch"] == 'x86')
if is_android_x86:
@@ -337,7 +337,6 @@ if webm_cpu_arm:
if webm_simd_optimizations == False:
print("WebM SIMD optimizations are disabled. Check if your CPU architecture, CPU bits or platform are supported!")
-
env_libvpx.add_source_files(env.modules_sources, libvpx_sources)
if webm_cpu_x86:
is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"]) or ("OSXCROSS_ROOT" in os.environ)
@@ -379,5 +378,5 @@ elif webm_cpu_arm:
env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_armasm_ms)
elif env["platform"] == 'iphone':
env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_gas_apple)
- elif not env["android_arch"] == 'arm64v8':
+ elif (is_x11_or_server_arm and cpu_bits == '32') or (env["platform"] == 'android' and not env["android_arch"] == 'arm64v8'):
env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_gas)
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/resource_importer_webm.cpp b/modules/webm/resource_importer_webm.cpp
deleted file mode 100644
index 7124a503e8..0000000000
--- a/modules/webm/resource_importer_webm.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*************************************************************************/
-/* resource_importer_webm.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 "resource_importer_webm.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";
-}
-
-String ResourceImporterWebm::get_visible_name() const {
-
- return "Webm";
-}
-void ResourceImporterWebm::get_recognized_extensions(List<String> *p_extensions) const {
-
- p_extensions->push_back("webm");
-}
-
-String ResourceImporterWebm::get_save_extension() const {
- return "webmstr";
-}
-
-String ResourceImporterWebm::get_resource_type() const {
-
- return "VideoStreamWebm";
-}
-
-bool ResourceImporterWebm::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
-
- return true;
-}
-
-int ResourceImporterWebm::get_preset_count() const {
- return 0;
-}
-String ResourceImporterWebm::get_preset_name(int p_idx) const {
-
- return String();
-}
-
-void ResourceImporterWebm::get_import_options(List<ImportOption> *r_options, int p_preset) const {
-
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
-}
-
-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);
-
- Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream);
-
- return ResourceSaver::save(p_save_path + ".webmstr", webm_stream);
-}
-
-ResourceImporterWebm::ResourceImporterWebm() {
-}
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/SCsub b/modules/webp/SCsub
index ea7af1bf9e..21ae0ce7c2 100644
--- a/modules/webp/SCsub
+++ b/modules/webp/SCsub
@@ -78,10 +78,12 @@ if env['builtin_libwebp']:
"dsp/upsampling_msa.c",
"dsp/upsampling_neon.c",
"dsp/upsampling_sse2.c",
+ "dsp/upsampling_sse41.c",
"dsp/yuv.c",
"dsp/yuv_mips32.c",
"dsp/yuv_mips_dsp_r2.c",
"dsp/yuv_sse2.c",
+ "dsp/yuv_sse41.c",
"enc/alpha_enc.c",
"enc/analysis_enc.c",
"enc/backward_references_cost_enc.c",
diff --git a/modules/webp/config.py b/modules/webp/config.py
index 5f133eba90..1c8cd12a2d 100644
--- a/modules/webp/config.py
+++ b/modules/webp/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index b36f1beacd..15a88773e7 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -7,47 +7,57 @@ Import('env_modules')
env_lws = env_modules.Clone()
-thirdparty_dir = "#thirdparty/lws/"
+thirdparty_dir = "#thirdparty/libwebsockets/"
helper_dir = "win32helpers/"
thirdparty_sources = [
- "client/client.c",
- "client/client-handshake.c",
- "client/client-parser.c",
- "client/ssl-client.c",
-
- "ext/extension.c",
- "ext/extension-permessage-deflate.c",
-
- "server/fops-zip.c",
- "server/lejp-conf.c",
- "server/parsers.c",
- "server/ranges.c",
- "server/server.c",
- "server/server-handshake.c",
- "server/ssl-server.c",
+
+ "core/alloc.c",
+ "core/context.c",
+ "core/libwebsockets.c",
+ "core/output.c",
+ "core/pollfd.c",
+ "core/service.c",
+
+ "event-libs/poll/poll.c",
"misc/base64-decode.c",
"misc/lejp.c",
"misc/sha-1.c",
- "alloc.c",
- "context.c",
- "handshake.c",
- "header.c",
- "libwebsockets.c",
- "output.c",
- "pollfd.c",
- "service.c",
- "ssl.c",
-
- "mbedtls_wrapper/library/ssl_cert.c",
- "mbedtls_wrapper/library/ssl_pkey.c",
- "mbedtls_wrapper/library/ssl_stack.c",
- "mbedtls_wrapper/library/ssl_methods.c",
- "mbedtls_wrapper/library/ssl_lib.c",
- "mbedtls_wrapper/library/ssl_x509.c",
- "mbedtls_wrapper/platform/ssl_port.c",
- "mbedtls_wrapper/platform/ssl_pm.c",
+ "roles/h1/ops-h1.c",
+ "roles/http/header.c",
+ "roles/http/client/client.c",
+ "roles/http/client/client-handshake.c",
+ "roles/http/server/fops-zip.c",
+ "roles/http/server/lejp-conf.c",
+ "roles/http/server/parsers.c",
+ "roles/http/server/server.c",
+ "roles/listen/ops-listen.c",
+ "roles/pipe/ops-pipe.c",
+ "roles/raw/ops-raw.c",
+
+ "roles/ws/client-ws.c",
+ "roles/ws/client-parser-ws.c",
+ "roles/ws/ops-ws.c",
+ "roles/ws/server-ws.c",
+
+ "tls/tls.c",
+ "tls/tls-client.c",
+ "tls/tls-server.c",
+
+ "tls/mbedtls/wrapper/library/ssl_cert.c",
+ "tls/mbedtls/wrapper/library/ssl_pkey.c",
+ "tls/mbedtls/wrapper/library/ssl_stack.c",
+ "tls/mbedtls/wrapper/library/ssl_methods.c",
+ "tls/mbedtls/wrapper/library/ssl_lib.c",
+ "tls/mbedtls/wrapper/library/ssl_x509.c",
+ "tls/mbedtls/wrapper/platform/ssl_port.c",
+ "tls/mbedtls/wrapper/platform/ssl_pm.c",
+ "tls/mbedtls/lws-genhash.c",
+ "tls/mbedtls/mbedtls-client.c",
+ "tls/mbedtls/lws-genrsa.c",
+ "tls/mbedtls/ssl.c",
+ "tls/mbedtls/mbedtls-server.c"
]
if env_lws["platform"] == "android": # Builtin getifaddrs
@@ -67,7 +77,7 @@ else:
env_lws.add_source_files(env.modules_sources, thirdparty_sources)
env_lws.Append(CPPPATH=[thirdparty_dir])
- wrapper_includes = ["#thirdparty/lws/mbedtls_wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
+ wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
env_lws.Prepend(CPPPATH=wrapper_includes)
if env['builtin_mbedtls']:
diff --git a/modules/websocket/config.py b/modules/websocket/config.py
index fb920482f5..f59ef432b4 100644
--- a/modules/websocket/config.py
+++ b/modules/websocket/config.py
@@ -1,7 +1,16 @@
-
-def can_build(platform):
+def can_build(env, platform):
return True
-
def configure(env):
pass
+
+def get_doc_classes():
+ return [
+ "WebSocketClient",
+ "WebSocketMultiplayerPeer",
+ "WebSocketPeer",
+ "WebSocketServer"
+ ]
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml
new file mode 100644
index 0000000000..2e11e1a44c
--- /dev/null
+++ b/modules/websocket/doc_classes/WebSocketClient.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebSocketClient" inherits="WebSocketMultiplayerPeer" category="Core" version="3.1">
+ <brief_description>
+ A WebSocket client implementation
+ </brief_description>
+ <description>
+ This class implements a WebSocket client compatible with any RFC 6455 complaint WebSocket server.
+ This client can be optionally used as a network peer for the [MultiplayerAPI].
+ After starting the client ([method connect_to_url]), you will need to [method NetworkedMultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]).
+ You will received appropriate signals when connecting, disconnecting, or when new data is available.
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="connect_to_url">
+ <return type="int" enum="Error">
+ </return>
+ <argument index="0" name="url" type="String">
+ </argument>
+ <argument index="1" name="protocols" type="PoolStringArray" default="PoolStringArray( )">
+ </argument>
+ <argument index="2" name="gd_mp_api" type="bool" default="false">
+ </argument>
+ <description>
+ Connect to the given URL requesting one of the given [code]protocols[/code] as sub-protocol.
+ If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI]. Note: connnections to non Godot servers will not work, and [signal data_received] will not be emitted when this option is true.
+ </description>
+ </method>
+ <method name="disconnect_from_host">
+ <return type="void">
+ </return>
+ <description>
+ Disconnect from the server if currently connected.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled">
+ Enable or disable SSL certificate verification. Note: You must specify the certificates to be used in the project settings for it to work when exported.
+ </member>
+ </members>
+ <signals>
+ <signal name="connection_closed">
+ <description>
+ Emitted when the connection to the server is closed.
+ </description>
+ </signal>
+ <signal name="connection_error">
+ <description>
+ Emitted when the connection to the server fails.
+ </description>
+ </signal>
+ <signal name="connection_established">
+ <argument index="0" name="protocol" type="String">
+ </argument>
+ <description>
+ Emitted when a connection with the server is established, [code]protocol[/code] will contain the sub-protocol agreed with the server.
+ </description>
+ </signal>
+ <signal name="data_received">
+ <description>
+ Emitted when a WebSocket message is received. Note: This signal is NOT emitted when used as high level multiplayer peer.
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
index a1014350f5..1a841f85ed 100644
--- a/doc/classes/WebSocketMultiplayerPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="WebSocketMultiplayerPeer" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1">
<brief_description>
+ Base class for WebSocket server and client.
</brief_description>
<description>
+ Base class for WebSocket server and client, allowing them to be used as network peer for the [MultiplayerAPI].
</description>
<tutorials>
</tutorials>
@@ -15,6 +17,7 @@
<argument index="0" name="peer_id" type="int">
</argument>
<description>
+ Returns the [WebSocketPeer] associated to the given [code]peer_id[/code].
</description>
</method>
</methods>
@@ -23,6 +26,7 @@
<argument index="0" name="peer_source" type="int">
</argument>
<description>
+ Emitted when a packet is received from a peer. Note: this signal is only emitted when the client or server is configured to use Godot multiplayer API.
</description>
</signal>
</signals>
diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml
new file mode 100644
index 0000000000..85a08e0c0b
--- /dev/null
+++ b/modules/websocket/doc_classes/WebSocketPeer.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebSocketPeer" inherits="PacketPeer" category="Core" version="3.1">
+ <brief_description>
+ A class representing a specific WebSocket connection.
+ </brief_description>
+ <description>
+ This class represent a specific WebSocket connection, you can do lower level operations with it.
+ You can choose to write to the socket in binary or text mode, and you can recognize the mode used for writing by the other peer.
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="close">
+ <return type="void">
+ </return>
+ <description>
+ Close this WebSocket connection, actively disconnecting the peer.
+ </description>
+ </method>
+ <method name="get_connected_host" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ Returns the IP Address of the connected peer. (Not available in HTML5 export)
+ </description>
+ </method>
+ <method name="get_connected_port" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the remote port of the connected peer. (Not available in HTML5 export)
+ </description>
+ </method>
+ <method name="get_write_mode" qualifiers="const">
+ <return type="int" enum="WebSocketPeer.WriteMode">
+ </return>
+ <description>
+ Get the current selected write mode. See [enum WriteMode].
+ </description>
+ </method>
+ <method name="is_connected_to_host" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ Returns [code]true[/code] if this peer is currently connected.
+ </description>
+ </method>
+ <method name="set_write_mode">
+ <return type="void">
+ </return>
+ <argument index="0" name="mode" type="int" enum="WebSocketPeer.WriteMode">
+ </argument>
+ <description>
+ Sets the socket to use the given [enum WriteMode].
+ </description>
+ </method>
+ <method name="was_string_packet" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ Returns [code]true[/code] if the last received packet was sent as a text payload. See [enum WriteMode]
+ </description>
+ </method>
+ </methods>
+ <constants>
+ <constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode">
+ Specify that WebSockets messages should be transferred as text payload (only valid UTF-8 is allowed).
+ </constant>
+ <constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode">
+ Specify that WebSockets messages should be transferred as binary payload (any byte combination is allowed).
+ </constant>
+ </constants>
+</class>
diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml
new file mode 100644
index 0000000000..a1061e446b
--- /dev/null
+++ b/modules/websocket/doc_classes/WebSocketServer.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebSocketServer" inherits="WebSocketMultiplayerPeer" category="Core" version="3.1">
+ <brief_description>
+ A WebSocket server implementation
+ </brief_description>
+ <description>
+ This class implements a WebSocket server that can also support the high level multiplayer API.
+ After starting the server ([method listen]), you will need to [method NetworkedMultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]). When clients connect, disconnect, or send data, you will receive the appropriate signal.
+ Note: This class will not work in HTML5 exports due to browser restrictions.
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="disconnect_peer">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Disconnects the given peer.
+ </description>
+ </method>
+ <method name="get_peer_address" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Returns the IP address of the given peer.
+ </description>
+ </method>
+ <method name="get_peer_port" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Returns the remote port of the given peer.
+ </description>
+ </method>
+ <method name="has_peer" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Returns [code]true[/code] if a peer with the given ID is connected.
+ </description>
+ </method>
+ <method name="is_listening" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ Returns [code]true[/code] if the server is actively listening on a port.
+ </description>
+ </method>
+ <method name="listen">
+ <return type="int" enum="Error">
+ </return>
+ <argument index="0" name="port" type="int">
+ </argument>
+ <argument index="1" name="protocols" type="PoolStringArray" default="PoolStringArray( )">
+ </argument>
+ <argument index="2" name="gd_mp_api" type="bool" default="false">
+ </argument>
+ <description>
+ Start listening on the given port.
+ You can specify the desired subprotocols via the "protocols" array. If the list empty (default), "binary" will be used.
+ You can use this server as a network peer for [MultiplayerAPI] by passing true as "gd_mp_api". Note: [signal data_received] will not be fired and clients other than Godot will not work in this case.
+ </description>
+ </method>
+ <method name="stop">
+ <return type="void">
+ </return>
+ <description>
+ Stop the server and clear its state.
+ </description>
+ </method>
+ </methods>
+ <signals>
+ <signal name="client_connected">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="protocol" type="String">
+ </argument>
+ <description>
+ Emitted when a new client connects. "protocol" will be the sub-protocol agreed with the client.
+ </description>
+ </signal>
+ <signal name="client_disconnected">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Emitted when a client disconnects.
+ </description>
+ </signal>
+ <signal name="data_received">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Emitted when a new message is received. Note: This signal is NOT emitted when used as high level multiplayer peer.
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp
index 2220c9adf2..06f97aaf05 100644
--- a/modules/websocket/lws_client.cpp
+++ b/modules/websocket/lws_client.cpp
@@ -32,6 +32,7 @@
#include "lws_client.h"
#include "core/io/ip.h"
#include "core/io/stream_peer_ssl.h"
+#include "tls/mbedtls/wrapper/include/openssl/ssl.h"
Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
@@ -140,7 +141,7 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
destroy_context();
return -1; // we should close the connection (would probably happen anyway)
- case LWS_CALLBACK_CLOSED:
+ case LWS_CALLBACK_CLIENT_CLOSED:
peer_data->in_count = 0;
peer_data->out_count = 0;
peer_data->rbw.resize(0);
diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h
index a850a545d3..a4920c3d54 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';
}
diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp
index 3855a39aef..96acb99cc4 100644
--- a/modules/websocket/lws_peer.cpp
+++ b/modules/websocket/lws_peer.cpp
@@ -191,8 +191,8 @@ IP_Address LWSPeer::get_connected_host() const {
IP_Address ip;
int port = 0;
- socklen_t len = 0;
struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
int fd = lws_get_socket_fd(wsi);
ERR_FAIL_COND_V(fd == -1, IP_Address());
@@ -212,8 +212,8 @@ uint16_t LWSPeer::get_connected_port() const {
IP_Address ip;
int port = 0;
- socklen_t len = 0;
struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
int fd = lws_get_socket_fd(wsi);
ERR_FAIL_COND_V(fd == -1, 0);
diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp
index 2693b26e47..53dd7b51b7 100644
--- a/modules/websocket/websocket_server.cpp
+++ b/modules/websocket/websocket_server.cpp
@@ -44,9 +44,9 @@ void WebSocketServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(PoolVector<String>()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop);
ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer);
- ClassDB::bind_method(D_METHOD("get_peer_address"), &WebSocketServer::get_peer_address);
- ClassDB::bind_method(D_METHOD("get_peer_port"), &WebSocketServer::get_peer_port);
- ClassDB::bind_method(D_METHOD("disconnect_peer"), &WebSocketServer::disconnect_peer);
+ ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketServer::get_peer_address);
+ ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &WebSocketServer::get_peer_port);
+ ClassDB::bind_method(D_METHOD("disconnect_peer", "id"), &WebSocketServer::disconnect_peer);
ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol")));
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/build.gradle.template b/platform/android/build.gradle.template
index 7269e658b4..cc45fee95f 100644
--- a/platform/android/build.gradle.template
+++ b/platform/android/build.gradle.template
@@ -21,7 +21,6 @@ allprojects {
}
dependencies {
- compile 'com.android.support:support-v4:27.+' // can be removed if minSdkVersion 16 and modify DownloadNotification.java & V14CustomNotification.java
$$GRADLE_DEPENDENCIES$$
}
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 6fe137a386..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()));
@@ -1023,7 +1094,7 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE, "apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,65535,1"), 1));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name"), "org.godotengine.$genname"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name"), ""));
@@ -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/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
index 73e6f83bec..a9f674803c 100644
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
+++ b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
@@ -27,7 +27,6 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Messenger;
-import android.support.v4.app.NotificationCompat;
/**
* This class handles displaying the notification associated with the download
@@ -49,9 +48,8 @@ public class DownloadNotification implements IDownloaderClient {
private IDownloaderClient mClientProxy;
final ICustomNotification mCustomNotification;
- // NotificationCompat.Builder is used to support API < 16. This can be changed to Notification.Builder if minimum API >= 16.
- private NotificationCompat.Builder mNotificationBuilder;
- private NotificationCompat.Builder mCurrentNotificationBuilder;
+ private Notification.Builder mNotificationBuilder;
+ private Notification.Builder mCurrentNotificationBuilder;
private CharSequence mLabel;
private String mCurrentText;
private PendingIntent mContentIntent;
@@ -187,7 +185,7 @@ public class DownloadNotification implements IDownloaderClient {
void setTimeRemaining(long timeRemaining);
- NotificationCompat.Builder updateNotification(Context c);
+ Notification.Builder updateNotification(Context c);
}
/**
@@ -220,7 +218,7 @@ public class DownloadNotification implements IDownloaderClient {
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mCustomNotification = CustomNotificationFactory
.createCustomNotification();
- mNotificationBuilder = new NotificationCompat.Builder(ctx);
+ mNotificationBuilder = new Notification.Builder(ctx);
mCurrentNotificationBuilder = mNotificationBuilder;
}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/V14CustomNotification.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/V14CustomNotification.java
index 390bde96e9..56b2331e31 100644
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/V14CustomNotification.java
+++ b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/V14CustomNotification.java
@@ -22,7 +22,6 @@ import com.google.android.vending.expansion.downloader.Helpers;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
-import android.support.v4.app.NotificationCompat;
public class V14CustomNotification implements DownloadNotification.ICustomNotification {
@@ -54,14 +53,13 @@ public class V14CustomNotification implements DownloadNotification.ICustomNotifi
mCurrentKB = currentBytes;
}
- void setProgress(NotificationCompat.Builder builder) {
+ void setProgress(Notification.Builder builder) {
}
@Override
- public NotificationCompat.Builder updateNotification(Context c) {
- // NotificationCompat.Builder is used to support API < 16. This can be changed to Notification.Builder if minimum API >= 16.
- NotificationCompat.Builder builder = new NotificationCompat.Builder(c);
+ public Notification.Builder updateNotification(Context c) {
+ Notification.Builder builder = new Notification.Builder(c);
builder.setContentTitle(mTitle);
if (mTotalKB > 0 && -1 != mCurrentKB) {
builder.setProgress((int) (mTotalKB >> 8), (int) (mCurrentKB >> 8), false);
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/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_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..9188f09f21 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"
@@ -125,13 +126,20 @@ void OS_Android::set_opengl_extensions(const char *p_gl_extensions) {
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();
+ } else {
+ RasterizerGLES3::register_config();
+ RasterizerGLES3::make_current();
+ }
visual_server = memnew(VisualServerRaster);
/* if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
@@ -684,7 +692,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 +714,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..ac901d4832 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;
@@ -239,7 +241,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/detect.py b/platform/haiku/detect.py
index 7c62654ef6..2959023204 100644
--- a/platform/haiku/detect.py
+++ b/platform/haiku/detect.py
@@ -22,7 +22,7 @@ def get_opts():
from SCons.Variables import EnumVariable
return [
- EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')),
+ EnumVariable('debug_symbols', 'Add debugging symbols to release builds', 'yes', ('yes', 'no', 'full')),
]
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/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index 7ed1328b20..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);
@@ -709,21 +709,18 @@ static int frame_count = 0;
iphone_finish();
};
-- (void)applicationDidEnterBackground:(UIApplication *)application {
- ///@TODO maybe add pause motionManager? and where would we unpause it?
+// When application goes to background (e.g. user switches to another app or presses Home),
+// then applicationWillResignActive -> applicationDidEnterBackground are called.
+// When user opens the inactive app again,
+// applicationWillEnterForeground -> applicationDidBecomeActive are called.
- on_focus_out(view_controller, &is_focus_out);
-}
-
-- (void)applicationWillEnterForeground:(UIApplication *)application {
- // OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
- [view_controller.view startAnimation];
-}
+// There are cases when applicationWillResignActive -> applicationDidBecomeActive
+// sequence is called without the app going to background. For example, that happens
+// if you open the app list without switching to another app or open/close the
+// notification panel by swiping from the upper part of the screen.
- (void)applicationWillResignActive:(UIApplication *)application {
- // OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
- [view_controller.view
- stopAnimation]; // FIXME: pause seems to be recommended elsewhere
+ on_focus_out(view_controller, &is_focus_out);
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
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..4caf4bd933 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) {
@@ -100,12 +95,6 @@ void OSIPhone::initialize_core() {
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);
-
RasterizerGLES3::register_config();
RasterizerGLES3::make_current();
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 7d73a6fe5c..8dc1ae6dc2 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;
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/SCsub b/platform/javascript/SCsub
index 5991075e29..98988d97fd 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -30,8 +30,8 @@ zip_files = env.InstallAs([
zip_dir.File('godot.wasm'),
zip_dir.File('godot.html')
], [
- js_wrapped,
- wasm,
- '#misc/dist/html/default.html'
+ js_wrapped,
+ wasm,
+ '#misc/dist/html/default.html'
])
env.Zip('#bin/godot', zip_files, ZIPROOT=zip_dir, ZIPSUFFIX='${PROGSUFFIX}${ZIPSUFFIX}', ZIPCOMSTR='Archving $SOURCES as $TARGET')
diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp
index 5bf345e6cd..7a6613bb32 100644
--- a/platform/javascript/audio_driver_javascript.cpp
+++ b/platform/javascript/audio_driver_javascript.cpp
@@ -32,113 +32,134 @@
#include <emscripten.h>
-AudioDriverJavaScript *AudioDriverJavaScript::singleton_js = NULL;
+AudioDriverJavaScript *AudioDriverJavaScript::singleton = NULL;
const char *AudioDriverJavaScript::get_name() const {
return "JavaScript";
}
-extern "C" EMSCRIPTEN_KEEPALIVE void js_audio_driver_mix_function(int p_frames) {
+extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_js_mix() {
- //print_line("MIXI! "+itos(p_frames));
- AudioDriverJavaScript::singleton_js->mix_to_js(p_frames);
+ AudioDriverJavaScript::singleton->mix_to_js();
}
-void AudioDriverJavaScript::mix_to_js(int p_frames) {
+void AudioDriverJavaScript::mix_to_js() {
- int todo = p_frames;
- int offset = 0;
+ int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode());
+ int sample_count = memarr_len(internal_buffer) / channel_count;
+ int32_t *stream_buffer = reinterpret_cast<int32_t *>(internal_buffer);
+ audio_server_process(sample_count, stream_buffer);
+ for (int i = 0; i < sample_count * channel_count; i++) {
+ internal_buffer[i] = float(stream_buffer[i] >> 16) / 32768.0;
+ }
+}
- while (todo) {
+Error AudioDriverJavaScript::init() {
- int tomix = MIN(todo, INTERNAL_BUFFER_SIZE);
+ /* clang-format off */
+ EM_ASM({
+ _audioDriver_audioContext = new (window.AudioContext || window.webkitAudioContext);
+ _audioDriver_scriptNode = null;
+ });
+ /* clang-format on */
- audio_server_process(p_frames, stream_buffer);
- for (int i = 0; i < tomix * internal_buffer_channels; i++) {
- internal_buffer[i] = float(stream_buffer[i] >> 16) / 32768.0;
+ int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode());
+ /* clang-format off */
+ int buffer_length = EM_ASM_INT({
+ var CHANNEL_COUNT = $0;
+
+ var channelCount = _audioDriver_audioContext.destination.channelCount;
+ try {
+ // Try letting the browser recommend a buffer length.
+ _audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(0, 0, channelCount);
+ } catch (e) {
+ // ...otherwise, default to 4096.
+ _audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(4096, 0, channelCount);
}
+ _audioDriver_scriptNode.connect(_audioDriver_audioContext.destination);
- /* clang-format off */
- EM_ASM_ARGS({
- var data = HEAPF32.subarray($0 / 4, $0 / 4 + $2 * 2);
-
- for (var channel = 0; channel < _as_output_buffer.numberOfChannels; channel++) {
- var outputData = _as_output_buffer.getChannelData(channel);
- // Loop through samples
- for (var sample = 0; sample < $2; sample++) {
- // make output equal to the same as the input
- outputData[sample + $1] = data[sample * 2 + channel];
- }
- }
- }, internal_buffer, offset, tomix);
- /* clang-format on */
-
- todo -= tomix;
- offset += tomix;
+ return _audioDriver_scriptNode.bufferSize;
+ }, channel_count);
+ /* clang-format on */
+ if (!buffer_length) {
+ return FAILED;
}
-}
-
-Error AudioDriverJavaScript::init() {
- return OK;
+ if (!internal_buffer || memarr_len(internal_buffer) != buffer_length * channel_count) {
+ if (internal_buffer)
+ memdelete_arr(internal_buffer);
+ internal_buffer = memnew_arr(float, buffer_length *channel_count);
+ }
+ return internal_buffer ? OK : ERR_OUT_OF_MEMORY;
}
void AudioDriverJavaScript::start() {
- internal_buffer = memnew_arr(float, INTERNAL_BUFFER_SIZE *internal_buffer_channels);
- stream_buffer = memnew_arr(int32_t, INTERNAL_BUFFER_SIZE * 4); //max 4 channels
-
/* clang-format off */
- mix_rate = EM_ASM_INT({
- _as_audioctx = new (window.AudioContext || window.webkitAudioContext);
- _as_script_node = _as_audioctx.createScriptProcessor($0, 0, $1);
- _as_script_node.connect(_as_audioctx.destination);
- console.log(_as_script_node.bufferSize);
- var jsAudioDriverMixFunction = cwrap('js_audio_driver_mix_function', null, ['number']);
-
- _as_script_node.onaudioprocess = function(audioProcessingEvent) {
- // The output buffer contains the samples that will be modified and played
- _as_output_buffer = audioProcessingEvent.outputBuffer;
- jsAudioDriverMixFunction([_as_output_buffer.getChannelData(0).length]);
+ EM_ASM({
+ var INTERNAL_BUFFER_PTR = $0;
+
+ var audioDriverMixFunction = cwrap('audio_driver_js_mix');
+ _audioDriver_scriptNode.onaudioprocess = function(audioProcessingEvent) {
+ audioDriverMixFunction();
+ // The output buffer contains the samples that will be modified and played.
+ var output = audioProcessingEvent.outputBuffer;
+ var input = HEAPF32.subarray(
+ INTERNAL_BUFFER_PTR / HEAPF32.BYTES_PER_ELEMENT,
+ INTERNAL_BUFFER_PTR / HEAPF32.BYTES_PER_ELEMENT + output.length * output.numberOfChannels);
+
+ for (var channel = 0; channel < output.numberOfChannels; channel++) {
+ var outputData = output.getChannelData(channel);
+ // Loop through samples.
+ for (var sample = 0; sample < outputData.length; sample++) {
+ // Set output equal to input.
+ outputData[sample] = input[sample * output.numberOfChannels + channel];
+ }
+ }
};
- return _as_audioctx.sampleRate;
- }, INTERNAL_BUFFER_SIZE, internal_buffer_channels);
+ }, internal_buffer);
/* clang-format on */
}
int AudioDriverJavaScript::get_mix_rate() const {
- return mix_rate;
+ /* clang-format off */
+ return EM_ASM_INT_V({
+ return _audioDriver_audioContext.sampleRate;
+ });
+ /* clang-format on */
}
AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const {
- return SPEAKER_MODE_STEREO;
+ /* clang-format off */
+ return get_speaker_mode_by_total_channels(EM_ASM_INT_V({
+ return _audioDriver_audioContext.destination.channelCount;
+ }));
+ /* clang-format on */
}
+// No locking, as threads are not supported.
void AudioDriverJavaScript::lock() {
-
- /*no locking, as threads are not supported
- if (active && mutex)
- mutex->lock();
- */
}
void AudioDriverJavaScript::unlock() {
-
- /*no locking, as threads are not supported
- if (active && mutex)
- mutex->unlock();
- */
}
void AudioDriverJavaScript::finish() {
+
+ /* clang-format off */
+ EM_ASM({
+ _audioDriver_audioContext = null;
+ _audioDriver_scriptNode = null;
+ });
+ /* clang-format on */
+ memdelete_arr(internal_buffer);
+ internal_buffer = NULL;
}
AudioDriverJavaScript::AudioDriverJavaScript() {
- internal_buffer_channels = 2;
- mix_rate = DEFAULT_MIX_RATE;
- singleton_js = this;
+ singleton = this;
}
diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h
index d78ab8eea4..a65a8ec29f 100644
--- a/platform/javascript/audio_driver_javascript.h
+++ b/platform/javascript/audio_driver_javascript.h
@@ -35,18 +35,11 @@
class AudioDriverJavaScript : public AudioDriver {
- enum {
- INTERNAL_BUFFER_SIZE = 4096,
- };
-
- int mix_rate;
float *internal_buffer;
- int internal_buffer_channels;
- int32_t *stream_buffer;
public:
- void mix_to_js(int p_frames);
- static AudioDriverJavaScript *singleton_js;
+ void mix_to_js();
+ static AudioDriverJavaScript *singleton;
virtual const char *get_name() const;
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index a48cb872ee..fc909f6619 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -1,5 +1,4 @@
import os
-import string
import sys
@@ -39,7 +38,7 @@ def configure(env):
## Build type
- if env['target'] == 'release' or env['target'] == 'profile':
+ if env['target'] != 'debug':
# Use -Os to prioritize optimizing for reduced file size. This is
# particularly valuable for the web platform because it directly
# decreases download time.
@@ -48,17 +47,11 @@ def configure(env):
# run-time performance.
env.Append(CCFLAGS=['-Os'])
env.Append(LINKFLAGS=['-Os'])
- if env['target'] == 'profile':
+ if env['target'] == 'release_debug':
+ env.Append(CPPDEFINES=['DEBUG_ENABLED'])
+ # Retain function names for backtraces at the cost of file size.
env.Append(LINKFLAGS=['--profiling-funcs'])
-
- elif env['target'] == 'release_debug':
- env.Append(CPPDEFINES=['DEBUG_ENABLED'])
- env.Append(CCFLAGS=['-O2'])
- env.Append(LINKFLAGS=['-O2'])
- # Retain function names for backtraces at the cost of file size.
- env.Append(LINKFLAGS=['--profiling-funcs'])
-
- elif env['target'] == 'debug':
+ else:
env.Append(CPPDEFINES=['DEBUG_ENABLED'])
env.Append(CCFLAGS=['-O1', '-g'])
env.Append(LINKFLAGS=['-O1', '-g'])
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/engine.js b/platform/javascript/engine.js
index e4839af433..c3ef5bbbb5 100644
--- a/platform/javascript/engine.js
+++ b/platform/javascript/engine.js
@@ -10,6 +10,7 @@
var DOWNLOAD_ATTEMPTS_MAX = 4;
var basePath = null;
+ var wasmFilenameExtensionOverride = null;
var engineLoadPromise = null;
var loadingFiles = {};
@@ -129,13 +130,17 @@
this.startGame = function(mainPack) {
executableName = getBaseName(mainPack);
+ var mainArgs = [];
+ if (!getPathLeaf(mainPack).endsWith('.pck')) {
+ mainArgs = ['--main-pack', getPathLeaf(mainPack)];
+ }
return Promise.all([
// Load from directory,
this.init(getBasePath(mainPack)),
// ...but write to root where the engine expects it.
this.preloadFile(mainPack, getPathLeaf(mainPack))
]).then(
- Function.prototype.apply.bind(synchronousStart, this, [])
+ Function.prototype.apply.bind(synchronousStart, this, mainArgs)
);
};
@@ -161,6 +166,10 @@
actualCanvas.style.padding = 0;
actualCanvas.style.borderWidth = 0;
actualCanvas.style.borderStyle = 'none';
+ // disable right-click context menu
+ actualCanvas.addEventListener('contextmenu', function(ev) {
+ ev.preventDefault();
+ }, false);
// until context restoration is implemented
actualCanvas.addEventListener('webglcontextlost', function(ev) {
alert("WebGL context lost, please reload the page");
@@ -299,6 +308,14 @@
return !!testContext;
};
+ Engine.setWebAssemblyFilenameExtension = function(override) {
+
+ if (String(override).length === 0) {
+ throw new Error('Invalid WebAssembly filename extension override');
+ }
+ wasmFilenameExtensionOverride = String(override);
+ }
+
Engine.load = function(newBasePath) {
if (newBasePath !== undefined) basePath = getBasePath(newBasePath);
@@ -306,7 +323,7 @@
if (typeof WebAssembly !== 'object')
return Promise.reject(new Error("Browser doesn't support WebAssembly"));
// TODO cache/retrieve module to/from idb
- engineLoadPromise = loadPromise(basePath + '.wasm').then(function(xhr) {
+ engineLoadPromise = loadPromise(basePath + '.' + (wasmFilenameExtensionOverride || 'wasm')).then(function(xhr) {
return xhr.response;
});
engineLoadPromise = engineLoadPromise.catch(function(err) {
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index d81aa25c32..9591850662 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -117,7 +117,7 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_GLOBAL_FILE, "html"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "html"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "zip"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), ""));
diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp
index 54d4755bd7..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,20 +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[]) {
- printf("let it go dude!\n");
-
- // 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');
@@ -70,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 1b5463e40d..c05ae03ec6 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
- if (!is_canvas_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 selection dragging
- 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(ev->get_position() - _input->get_mouse_position());
- _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) {
+EM_BOOL OS_JavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
- ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYDOWN, false);
-
- 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) {
+EM_BOOL OS_JavaScript::keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
- ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYUP, false);
-
- 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) {
-
- main_loop = p_main_loop;
- input->set_main_loop(p_main_loop);
-}
+EM_BOOL OS_JavaScript::mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) {
-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,303 @@ 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
- 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;
+ }
+ 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());
+ }
+
+ 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);
- if (main_loop)
- main_loop->init();
+ 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 +742,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 +804,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 +919,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..503c92585b 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -32,52 +32,56 @@
#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;
- GetUserDataDirFunc get_user_data_dir_func;
+ bool idb_available;
+ int64_t sync_wait_time;
+ int64_t last_sync_check_time;
- static void _close_notification_funcs(const String &p_file, int p_flags);
+ 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);
- void process_joypads();
+ 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);
- void set_css_cursor(const char *);
- const char *get_css_cursor() const;
+ 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);
-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 EM_BOOL wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data);
- virtual int get_audio_driver_count() const;
- virtual const char *get_audio_driver_name(int p_driver) const;
+ 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();
+ static void main_loop_callback();
+
+ static void file_access_close_callback(const String &p_file, int p_flags);
+
+protected:
virtual void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
@@ -86,77 +90,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 1e9631fae0..72b8aa99f8 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -23,8 +23,8 @@ def get_opts():
return [
('osxcross_sdk', 'OSXCross SDK version', 'darwin14'),
- EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')),
- BoolVariable('separate_debug_symbols', 'Create a separate file with the debug symbols', False),
+ 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),
]
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 fee25e98cb..7bd5b16f36 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -99,6 +99,8 @@ public:
id pixelFormat;
id context;
+ bool layered_window;
+
CursorShape cursor_shape;
NSCursor *cursors[CURSOR_MAX];
MouseMode mouse_mode;
@@ -107,6 +109,7 @@ public:
bool minimized;
bool maximized;
bool zoomed;
+ bool resizable;
Size2 window_size;
Rect2 restore_rect;
@@ -114,6 +117,7 @@ public:
String open_with_filename;
Point2 im_position;
+ bool im_active;
ImeCallback im_callback;
void *im_target;
@@ -226,6 +230,11 @@ public:
virtual void set_borderless_window(bool p_borderless);
virtual bool get_borderless_window();
+
+ virtual bool get_window_per_pixel_transparency_enabled() const;
+ virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
+
+ virtual void set_ime_active(const bool p_active);
virtual void set_ime_position(const Point2 &p_pos);
virtual void set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp);
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index fbefd41bb7..41a19ac992 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -541,7 +541,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
}
- (void)cursorUpdate:(NSEvent *)event {
- //setModeCursor(window, window->cursorMode);
+ OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape;
+ OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
+ OS_OSX::singleton->set_cursor_shape(p_shape);
}
static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
@@ -623,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 {
@@ -635,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);
- _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false);
+ } else if ((int)[event buttonNumber] == 3) {
+ _mouseDownEvent(event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, false);
+
+ } else if ((int)[event buttonNumber] == 4) {
+ _mouseDownEvent(event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, false);
+
+ } else {
+ return;
+ }
}
- (void)mouseExited:(NSEvent *)event {
@@ -656,11 +674,12 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
return;
if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
- if (OS_OSX::singleton->input) {
+ if (OS_OSX::singleton->input)
OS_OSX::singleton->input->set_mouse_in_window(true);
- OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
- OS_OSX::singleton->set_cursor_shape(OS::CURSOR_ARROW);
- }
+
+ OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape;
+ OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
+ OS_OSX::singleton->set_cursor_shape(p_shape);
}
- (void)magnifyWithEvent:(NSEvent *)event {
@@ -847,16 +866,16 @@ struct _KeyCodeMap {
static const _KeyCodeMap _keycodes[55] = {
{ '`', KEY_QUOTELEFT },
{ '~', KEY_ASCIITILDE },
- { '0', KEY_KP_0 },
- { '1', KEY_KP_1 },
- { '2', KEY_KP_2 },
- { '3', KEY_KP_3 },
- { '4', KEY_KP_4 },
- { '5', KEY_KP_5 },
- { '6', KEY_KP_6 },
- { '7', KEY_KP_7 },
- { '8', KEY_KP_8 },
- { '9', KEY_KP_9 },
+ { '0', KEY_0 },
+ { '1', KEY_1 },
+ { '2', KEY_2 },
+ { '3', KEY_3 },
+ { '4', KEY_4 },
+ { '5', KEY_5 },
+ { '6', KEY_6 },
+ { '7', KEY_7 },
+ { '8', KEY_8 },
+ { '9', KEY_9 },
{ '-', KEY_MINUS },
{ '_', KEY_UNDERSCORE },
{ '=', KEY_EQUAL },
@@ -910,7 +929,7 @@ static int remapKey(unsigned int key) {
CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
if (!layoutData)
- return nil;
+ return 0;
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
@@ -956,7 +975,7 @@ static int remapKey(unsigned int key) {
push_to_key_event_buffer(ke);
}
- if ((OS_OSX::singleton->im_position.x != 0) && (OS_OSX::singleton->im_position.y != 0))
+ if (OS_OSX::singleton->im_active == true)
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
}
@@ -1126,6 +1145,10 @@ String OS_OSX::get_unique_id() const {
return serial_number;
}
+void OS_OSX::set_ime_active(const bool p_active) {
+ im_active = p_active;
+}
+
void OS_OSX::set_ime_position(const Point2 &p_pos) {
im_position = p_pos;
}
@@ -1181,6 +1204,7 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
if (p_desired.borderless_window) {
styleMask = NSWindowStyleMaskBorderless;
} else {
+ resizable = p_desired.resizable;
styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (p_desired.resizable ? NSWindowStyleMaskResizable : 0);
}
@@ -1330,6 +1354,9 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
restore_rect = Rect2(get_window_position(), get_window_size());
+ if (p_desired.layered_splash) {
+ set_window_per_pixel_transparency_enabled(true);
+ }
return OK;
}
@@ -1474,7 +1501,7 @@ void OS_OSX::set_cursor_shape(CursorShape p_shape) {
if (cursor_shape == p_shape)
return;
- if (mouse_mode != MOUSE_MODE_VISIBLE) {
+ if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
cursor_shape = p_shape;
return;
}
@@ -1510,48 +1537,98 @@ void OS_OSX::set_cursor_shape(CursorShape p_shape) {
void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
if (p_cursor.is_valid()) {
Ref<Texture> texture = p_cursor;
- Ref<Image> image = texture->get_data();
+ Ref<AtlasTexture> atlas_texture = p_cursor;
+ Ref<Image> image;
+ Size2 texture_size;
+ Rect2 atlas_rect;
+
+ if (texture.is_valid()) {
+ image = texture->get_data();
+ }
+
+ if (!image.is_valid() && atlas_texture.is_valid()) {
+ texture = atlas_texture->get_atlas();
+
+ atlas_rect.size.width = texture->get_width();
+ atlas_rect.size.height = texture->get_height();
+ atlas_rect.position.x = atlas_texture->get_region().position.x;
+ atlas_rect.position.y = atlas_texture->get_region().position.y;
+
+ texture_size.width = atlas_texture->get_region().size.x;
+ texture_size.height = atlas_texture->get_region().size.y;
+ } else if (image.is_valid()) {
+ texture_size.width = texture->get_width();
+ texture_size.height = texture->get_height();
+ }
+
+ ERR_FAIL_COND(!texture.is_valid());
+ ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
+
+ image = texture->get_data();
- ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256);
+ ERR_FAIL_COND(!image.is_valid());
- NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc]
+ NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
- pixelsWide:image->get_width()
- pixelsHigh:image->get_height()
+ pixelsWide:int(texture_size.width)
+ pixelsHigh:int(texture_size.height)
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
- bytesPerRow:image->get_width() * 4
- bitsPerPixel:32] autorelease];
+ bytesPerRow:int(texture_size.width) * 4
+ bitsPerPixel:32];
ERR_FAIL_COND(imgrep == nil);
uint8_t *pixels = [imgrep bitmapData];
- int len = image->get_width() * image->get_height();
+ int len = int(texture_size.width * texture_size.height);
PoolVector<uint8_t> data = image->get_data();
PoolVector<uint8_t>::Read r = data.read();
+ image->lock();
+
/* Premultiply the alpha channel */
for (int i = 0; i < len; i++) {
- uint8_t alpha = r[i * 4 + 3];
- pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
- pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
- pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
+ int row_index = floor(i / texture_size.width) + atlas_rect.position.y;
+ int column_index = (i % int(texture_size.width)) + atlas_rect.position.x;
+
+ if (atlas_texture.is_valid()) {
+ column_index = MIN(column_index, atlas_rect.size.width - 1);
+ row_index = MIN(row_index, atlas_rect.size.height - 1);
+ }
+
+ uint32_t color = image->get_pixel(column_index, row_index).to_argb32();
+
+ uint8_t alpha = (color >> 24) & 0xFF;
+ pixels[i * 4 + 0] = ((color >> 16) & 0xFF) * alpha / 255;
+ pixels[i * 4 + 1] = ((color >> 8) & 0xFF) * alpha / 255;
+ pixels[i * 4 + 2] = ((color)&0xFF) * alpha / 255;
pixels[i * 4 + 3] = alpha;
}
- NSImage *nsimage = [[[NSImage alloc] initWithSize:NSMakeSize(image->get_width(), image->get_height())] autorelease];
+ image->unlock();
+
+ NSImage *nsimage = [[NSImage alloc] initWithSize:NSMakeSize(texture_size.width, texture_size.height)];
[nsimage addRepresentation:imgrep];
NSCursor *cursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(p_hotspot.x, p_hotspot.y)];
+ [cursors[p_shape] release];
cursors[p_shape] = cursor;
if (p_shape == CURSOR_ARROW) {
[cursor set];
}
+
+ [imgrep release];
+ [nsimage release];
+ } else {
+ // Reset to default system cursor
+ cursors[p_shape] = NULL;
+ cursor_shape = CURSOR_MAX;
+ set_cursor_shape(p_shape);
}
}
@@ -1690,7 +1767,8 @@ String OS_OSX::get_godot_dir_name() const {
String OS_OSX::get_system_dir(SystemDir p_dir) const {
- NSSearchPathDirectory id = 0;
+ NSSearchPathDirectory id;
+ bool found = true;
switch (p_dir) {
case SYSTEM_DIR_DESKTOP: {
@@ -1711,10 +1789,13 @@ String OS_OSX::get_system_dir(SystemDir p_dir) const {
case SYSTEM_DIR_PICTURES: {
id = NSPicturesDirectory;
} break;
+ default: {
+ found = false;
+ }
}
String ret;
- if (id) {
+ if (found) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(id, NSUserDomainMask, YES);
if (paths && [paths count] >= 1) {
@@ -2042,6 +2123,8 @@ void OS_OSX::set_window_size(const Size2 p_size) {
void OS_OSX::set_window_fullscreen(bool p_enabled) {
if (zoomed != p_enabled) {
+ if (layered_window)
+ set_window_per_pixel_transparency_enabled(false);
[window_object toggleFullScreen:nil];
}
zoomed = p_enabled;
@@ -2058,6 +2141,8 @@ void OS_OSX::set_window_resizable(bool p_enabled) {
[window_object setStyleMask:[window_object styleMask] | NSWindowStyleMaskResizable];
else
[window_object setStyleMask:[window_object styleMask] & ~NSWindowStyleMaskResizable];
+
+ resizable = p_enabled;
};
bool OS_OSX::is_window_resizable() const {
@@ -2123,6 +2208,39 @@ void OS_OSX::request_attention() {
[NSApp requestUserAttention:NSCriticalRequest];
}
+bool OS_OSX::get_window_per_pixel_transparency_enabled() const {
+
+ if (!is_layered_allowed()) return false;
+ return layered_window;
+}
+
+void OS_OSX::set_window_per_pixel_transparency_enabled(bool p_enabled) {
+
+ if (!is_layered_allowed()) return;
+ if (layered_window != p_enabled) {
+ if (p_enabled) {
+ set_borderless_window(true);
+ GLint opacity = 0;
+ [window_object setBackgroundColor:[NSColor clearColor]];
+ [window_object setOpaque:NO];
+ [window_object setHasShadow:NO];
+ [context setValues:&opacity forParameter:NSOpenGLCPSurfaceOpacity];
+ layered_window = true;
+ } else {
+ GLint opacity = 1;
+ [window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
+ [window_object setOpaque:YES];
+ [window_object setHasShadow:YES];
+ [context setValues:&opacity forParameter:NSOpenGLCPSurfaceOpacity];
+ layered_window = false;
+ }
+ [context update];
+ NSRect frame = [window_object frame];
+ [window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, 1, 1) display:YES];
+ [window_object setFrame:frame display:YES];
+ }
+}
+
void OS_OSX::set_borderless_window(bool p_borderless) {
// OrderOut prevents a lose focus bug with the window
@@ -2131,7 +2249,10 @@ void OS_OSX::set_borderless_window(bool p_borderless) {
if (p_borderless) {
[window_object setStyleMask:NSWindowStyleMaskBorderless];
} else {
- [window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable];
+ if (layered_window)
+ set_window_per_pixel_transparency_enabled(false);
+
+ [window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (resizable ? NSWindowStyleMaskResizable : 0)];
// Force update of the window styles
NSRect frameRect = [window_object frame];
@@ -2339,12 +2460,21 @@ void OS_OSX::run() {
//int frames=0;
//uint64_t frame=0;
- while (!force_quit) {
+ bool quit = false;
- process_events(); // get rid of pending events
- joypad_osx->process_joypads();
- if (Main::iteration() == true)
- break;
+ while (!force_quit && !quit) {
+
+ @try {
+
+ process_events(); // get rid of pending events
+ joypad_osx->process_joypads();
+
+ if (Main::iteration() == true) {
+ quit = true;
+ }
+ } @catch (NSException *exception) {
+ ERR_PRINTS("NSException: " + String([exception reason].UTF8String));
+ }
};
main_loop->finish();
@@ -2432,9 +2562,11 @@ OS_OSX::OS_OSX() {
mouse_mode = OS::MOUSE_MODE_VISIBLE;
main_loop = NULL;
singleton = this;
+ im_active = false;
im_position = Point2();
im_callback = NULL;
im_target = NULL;
+ layered_window = false;
autoreleasePool = [[NSAutoreleasePool alloc] init];
eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
@@ -2517,6 +2649,7 @@ OS_OSX::OS_OSX() {
minimized = false;
window_size = Vector2(1024, 600);
zoomed = false;
+ resizable = false;
Vector<Logger *> loggers;
loggers.push_back(memnew(OSXTerminalLogger));
diff --git a/platform/server/detect.py b/platform/server/detect.py
index 7bf445b43f..266b0c5cc9 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -67,9 +67,6 @@ def configure(env):
# FIXME: Check for existence of the libs before parsing their flags with pkg-config
- if not env['builtin_libwebp']:
- env.ParseConfig('pkg-config libwebp --cflags --libs')
-
# freetype depends on libpng and zlib, so bundling one of them while keeping others
# as shared libraries leads to weird issues
if env['builtin_freetype'] or env['builtin_libpng'] or env['builtin_zlib']:
@@ -124,6 +121,21 @@ def configure(env):
if not env['builtin_libogg']:
env.ParseConfig('pkg-config ogg --cflags --libs')
+ if not env['builtin_libwebp']:
+ env.ParseConfig('pkg-config libwebp --cflags --libs')
+
+ if not env['builtin_mbedtls']:
+ # mbedTLS does not provide a pkgconfig config yet. See https://github.com/ARMmbed/mbedtls/issues/228
+ env.Append(LIBS=['mbedtls', 'mbedcrypto', 'mbedx509'])
+
+ if not env['builtin_libwebsockets']:
+ env.ParseConfig('pkg-config libwebsockets --cflags --libs')
+
+ if not env['builtin_miniupnpc']:
+ # No pkgconfig file so far, hardcode default paths.
+ env.Append(CPPPATH=["/usr/include/miniupnpc"])
+ env.Append(LIBS=["miniupnpc"])
+
# On Linux wchar_t should be 32-bits
# 16-bit library shouldn't be required due to compiler optimisations
if not env['builtin_pcre2']:
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 a8be4fbc35..3b1be780d4 100644
--- a/platform/server/os_server.cpp
+++ b/platform/server/os_server.cpp
@@ -30,6 +30,7 @@
#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>
@@ -83,6 +84,9 @@ Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int
_ensure_user_data_dir();
+ resource_loader_dummy = memnew(ResourceFormatDummyTexture);
+ ResourceLoader::add_resource_format_loader(resource_loader_dummy);
+
return OK;
}
@@ -99,6 +103,8 @@ void OS_Server::finalize() {
memdelete(power_manager);
+ memdelete(resource_loader_dummy);
+
args.clear();
}
diff --git a/platform/server/os_server.h b/platform/server/os_server.h
index 2cc6f0c47e..f1a880ecc2 100644
--- a/platform/server/os_server.h
+++ b/platform/server/os_server.h
@@ -32,6 +32,7 @@
#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"
@@ -65,6 +66,8 @@ class OS_Server : public OS_Unix {
CrashHandler crash_handler;
+ ResourceFormatDummyTexture *resource_loader_dummy;
+
protected:
virtual int get_video_driver_count() const;
virtual const char *get_video_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/detect.py b/platform/uwp/detect.py
index 0e7b125dc5..559f23ca5b 100644
--- a/platform/uwp/detect.py
+++ b/platform/uwp/detect.py
@@ -28,8 +28,8 @@ def get_opts():
from SCons.Variables import BoolVariable
return [
- ('msvc_version', 'MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.', None),
- BoolVariable('use_mingw', 'Use the Mingw compiler, even if MSVC is installed. Only used on Windows.', False),
+ ('msvc_version', 'MSVC version to use (ignored if the VCINSTALLDIR environment variable is set)', None),
+ BoolVariable('use_mingw', 'Use the MinGW compiler even if MSVC is installed (only used on Windows)', False),
]
@@ -112,7 +112,7 @@ def configure(env):
env["bits"] = "32"
print("Compiled program architecture will be a x86 executable. (forcing bits=32).")
else:
- print("Failed to detect MSVC compiler architecture version... Defaulting to 32bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup.")
+ print("Failed to detect MSVC compiler architecture version... Defaulting to 32-bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup.")
env["bits"] = "32"
if (env["bits"] == "32"):
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index 3c537b3b58..35c0b30ce4 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -777,7 +777,7 @@ class EditorExportUWP : public EditorExportPlatform {
result = result.replace("$version_string$", version);
Platform arch = (Platform)(int)p_preset->get("architecture/target");
- String architecture = arch == ARM ? "ARM" : arch == X86 ? "x86" : "x64";
+ String architecture = arch == ARM ? "arm" : arch == X86 ? "x86" : "x64";
result = result.replace("$architecture$", architecture);
result = result.replace("$display_name$", String(p_preset->get("package/display_name")).empty() ? (String)ProjectSettings::get_singleton()->get("application/config/name") : String(p_preset->get("package/display_name")));
@@ -1046,7 +1046,7 @@ public:
}
virtual void get_export_options(List<ExportOption> *r_options) {
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "architecture/target", PROPERTY_HINT_ENUM, "ARM,x86,x64"), 1));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "architecture/target", PROPERTY_HINT_ENUM, "arm,x86,x64"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
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..6e6e0fb1aa 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 {
@@ -133,18 +129,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 +169,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 +183,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,8 +228,13 @@ 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);
visual_server = memnew(VisualServerRaster);
@@ -297,7 +290,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;
}
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index 95afdab469..0f7f068652 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -96,6 +96,7 @@ private:
int pressrc;
ContextEGL *gl_context;
+ Windows::UI::Core::CoreWindow ^ window;
VideoMode video_mode;
@@ -153,10 +154,6 @@ 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 void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
@@ -231,7 +228,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 6d559520d7..05806d2fe8 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -62,8 +62,8 @@ def get_opts():
# XP support dropped after EOL due to missing API for IPv6 and other issues
# Vista support dropped after EOL due to GH-10243
('target_win_version', 'Targeted Windows version, >= 0x0601 (Windows 7)', '0x0601'),
- EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')),
- BoolVariable('separate_debug_symbols', 'Create a separate file with the debug symbols', False),
+ 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),
('msvc_version', 'MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.', None),
BoolVariable('use_mingw', 'Use the Mingw compiler, even if MSVC is installed. Only used on Windows.', False),
]
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 7b46608c55..05d16a5964 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,168 +496,178 @@ 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: {
- if (--pressrc <= 0) {
- ReleaseCapture();
- pressrc = 0;
- }
+ 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));
}
- } 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: {
+
+ 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: {
- ScreenToClient(hWnd, &coords);
+ 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_position(Vector2(coords.x, coords.y));
- }
+ 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);
- mb->set_global_position(mb->get_position());
+ last_button_state = mb->get_button_mask();
+ mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
- 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 (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 (--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);
int window_h = HIWORD(lParam);
- if (window_w > 0 && window_h > 0) {
+ if (window_w > 0 && window_h > 0 && !preserve_window_size) {
video_mode.width = window_w;
video_mode.height = window_h;
+ } else {
+ preserve_window_size = false;
+ set_window_size(Size2(video_mode.width, video_mode.height));
}
if (wParam == SIZE_MAXIMIZED) {
maximized = true;
@@ -637,6 +679,28 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
maximized = false;
minimized = false;
}
+ if (is_layered_allowed() && layered_window) {
+ DeleteObject(hBitmap);
+
+ RECT r;
+ GetWindowRect(hWnd, &r);
+ dib_size = Size2(r.right - r.left, r.bottom - r.top);
+
+ BITMAPINFO bmi;
+ ZeroMemory(&bmi, sizeof(BITMAPINFO));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = dib_size.x;
+ bmi.bmiHeader.biHeight = dib_size.y;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = dib_size.x, dib_size.y * 4;
+ hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
+ SelectObject(hDC_dib, hBitmap);
+
+ ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
+ }
+ //return 0; // Jump Back
} break;
case WM_ENTERSIZEMOVE: {
@@ -717,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;
@@ -755,7 +824,9 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
SetCursor(NULL);
} else {
if (hCursor != NULL) {
- SetCursor(hCursor);
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ set_cursor_shape(c);
hCursor = NULL;
}
}
@@ -949,6 +1020,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) {
@@ -993,6 +1065,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);
@@ -1000,6 +1073,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);
@@ -1151,6 +1234,18 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
SetFocus(hWnd); // Sets Keyboard Focus To
}
+ if (p_desired.layered_splash) {
+ set_window_per_pixel_transparency_enabled(true);
+ }
+
+ // IME
+ im_himc = ImmGetContext(hWnd);
+ ImmReleaseContext(hWnd, im_himc);
+
+ im_position = Vector2();
+
+ set_ime_active(false);
+
return OK;
}
@@ -1412,12 +1507,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;
@@ -1437,24 +1526,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() };
@@ -1514,16 +1585,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);
@@ -1536,12 +1607,24 @@ void OS_Windows::set_window_size(const Size2 p_size) {
}
MoveWindow(hWnd, rect.left, rect.top, w, h, TRUE);
+
+ // Don't let the mouse leave the window when resizing to a smaller resolution
+ if (mouse_mode == MOUSE_MODE_CONFINED) {
+ RECT rect;
+ GetClientRect(hWnd, &rect);
+ ClientToScreen(hWnd, (POINT *)&rect.left);
+ ClientToScreen(hWnd, (POINT *)&rect.right);
+ ClipCursor(&rect);
+ }
}
void OS_Windows::set_window_fullscreen(bool p_enabled) {
if (video_mode.fullscreen == p_enabled)
return;
+ if (layered_window)
+ set_window_per_pixel_transparency_enabled(false);
+
if (p_enabled) {
if (pre_fs_valid) {
@@ -1646,12 +1729,100 @@ bool OS_Windows::is_window_always_on_top() const {
return video_mode.always_on_top;
}
+bool OS_Windows::get_window_per_pixel_transparency_enabled() const {
+
+ if (!is_layered_allowed()) return false;
+ return layered_window;
+}
+
+void OS_Windows::set_window_per_pixel_transparency_enabled(bool p_enabled) {
+
+ if (!is_layered_allowed()) return;
+ if (layered_window != p_enabled) {
+ if (p_enabled) {
+ set_borderless_window(true);
+ //enable per-pixel alpha
+ hDC_dib = CreateCompatibleDC(GetDC(hWnd));
+
+ SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
+
+ RECT r;
+ GetWindowRect(hWnd, &r);
+ dib_size = Size2(r.right - r.left, r.bottom - r.top);
+
+ BITMAPINFO bmi;
+ ZeroMemory(&bmi, sizeof(BITMAPINFO));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = dib_size.x;
+ bmi.bmiHeader.biHeight = dib_size.y;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
+ hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
+ SelectObject(hDC_dib, hBitmap);
+
+ ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
+
+ layered_window = true;
+ } else {
+ //disable per-pixel alpha
+ layered_window = false;
+
+ SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
+
+ //cleanup
+ DeleteObject(hBitmap);
+ DeleteDC(hDC_dib);
+ }
+ }
+}
+
+uint8_t *OS_Windows::get_layered_buffer_data() {
+
+ return (is_layered_allowed() && layered_window) ? dib_data : NULL;
+}
+
+Size2 OS_Windows::get_layered_buffer_size() {
+
+ return (is_layered_allowed() && layered_window) ? dib_size : Size2();
+}
+
+void OS_Windows::swap_layered_buffer() {
+
+ if (is_layered_allowed() && layered_window) {
+
+ //premultiply alpha
+ for (int y = 0; y < dib_size.y; y++) {
+ for (int x = 0; x < dib_size.x; x++) {
+ float alpha = (float)dib_data[y * (int)dib_size.x * 4 + x * 4 + 3] / (float)0xFF;
+ dib_data[y * (int)dib_size.x * 4 + x * 4 + 0] *= alpha;
+ dib_data[y * (int)dib_size.x * 4 + x * 4 + 1] *= alpha;
+ dib_data[y * (int)dib_size.x * 4 + x * 4 + 2] *= alpha;
+ }
+ }
+ //swap layered window buffer
+ POINT ptSrc = { 0, 0 };
+ SIZE sizeWnd = { (long)dib_size.x, (long)dib_size.y };
+ BLENDFUNCTION bf;
+ bf.BlendOp = AC_SRC_OVER;
+ bf.BlendFlags = 0;
+ bf.AlphaFormat = AC_SRC_ALPHA;
+ bf.SourceConstantAlpha = 0xFF;
+ UpdateLayeredWindow(hWnd, NULL, NULL, &sizeWnd, hDC_dib, &ptSrc, 0, &bf, ULW_ALPHA);
+ }
+}
+
void OS_Windows::set_borderless_window(bool p_borderless) {
if (video_mode.borderless_window == p_borderless)
return;
+ if (!p_borderless && layered_window)
+ set_window_per_pixel_transparency_enabled(false);
+
video_mode.borderless_window = p_borderless;
+ preserve_window_size = true;
_update_window_style();
}
@@ -1670,7 +1841,7 @@ void OS_Windows::_update_window_style(bool repaint) {
}
}
- SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
if (repaint) {
RECT rect;
@@ -1881,7 +2052,7 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) {
if (cursor_shape == p_shape)
return;
- if (mouse_mode != MOUSE_MODE_VISIBLE) {
+ if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
cursor_shape = p_shape;
return;
}
@@ -1918,27 +2089,59 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) {
void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
if (p_cursor.is_valid()) {
Ref<Texture> texture = p_cursor;
- Ref<Image> image = texture->get_data();
+ Ref<AtlasTexture> atlas_texture = p_cursor;
+ Ref<Image> image;
+ Size2 texture_size;
+ Rect2 atlas_rect;
- UINT image_size = texture->get_width() * texture->get_height();
- UINT size = sizeof(UINT) * image_size;
+ if (texture.is_valid()) {
+ image = texture->get_data();
+ }
+
+ if (!image.is_valid() && atlas_texture.is_valid()) {
+ texture = atlas_texture->get_atlas();
- ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256);
+ atlas_rect.size.width = texture->get_width();
+ atlas_rect.size.height = texture->get_height();
+ atlas_rect.position.x = atlas_texture->get_region().position.x;
+ atlas_rect.position.y = atlas_texture->get_region().position.y;
+
+ texture_size.width = atlas_texture->get_region().size.x;
+ texture_size.height = atlas_texture->get_region().size.y;
+ } else if (image.is_valid()) {
+ texture_size.width = texture->get_width();
+ texture_size.height = texture->get_height();
+ }
+
+ ERR_FAIL_COND(!texture.is_valid());
+ ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
+
+ image = texture->get_data();
+
+ ERR_FAIL_COND(!image.is_valid());
+
+ UINT image_size = texture_size.width * texture_size.height;
+ UINT size = sizeof(UINT) * image_size;
// Create the BITMAP with alpha channel
- COLORREF *buffer = (COLORREF *)malloc(sizeof(COLORREF) * image_size);
+ COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size);
image->lock();
for (UINT index = 0; index < image_size; index++) {
- int row_index = floor(index / texture->get_width());
- int column_index = index % texture->get_width();
+ int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
+ int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
+
+ if (atlas_texture.is_valid()) {
+ column_index = MIN(column_index, atlas_rect.size.width - 1);
+ row_index = MIN(row_index, atlas_rect.size.height - 1);
+ }
*(buffer + index) = image->get_pixel(column_index, row_index).to_argb32();
}
image->unlock();
// Using 4 channels, so 4 * 8 bits
- HBITMAP bitmap = CreateBitmap(texture->get_width(), texture->get_height(), 1, 4 * 8, buffer);
+ HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer);
COLORREF clrTransparent = -1;
// Create the AND and XOR masks for the bitmap
@@ -1948,6 +2151,8 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap
GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask);
if (NULL == hAndMask || NULL == hXorMask) {
+ memfree(buffer);
+ DeleteObject(bitmap);
return;
}
@@ -1972,6 +2177,14 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap
if (hXorMask != NULL) {
DeleteObject(hXorMask);
}
+
+ memfree(buffer);
+ DeleteObject(bitmap);
+ } else {
+ // Reset to default system cursor
+ cursors[p_shape] = NULL;
+ cursor_shape = CURSOR_MAX;
+ set_cursor_shape(p_shape);
}
}
@@ -2037,10 +2250,6 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
argss += String(" \"") + E->get() + "\"";
}
- //print_line("ARGS: "+argss);
- //argss+"\"";
- //argss+=" 2>nul";
-
FILE *f = _wpopen(argss.c_str(), L"r");
ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
@@ -2067,15 +2276,12 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
I = I->next();
};
- //cmdline+="\"";
-
ProcessInfo pi;
ZeroMemory(&pi.si, sizeof(pi.si));
pi.si.cb = sizeof(pi.si);
ZeroMemory(&pi.pi, sizeof(pi.pi));
LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
- print_line("running cmdline: " + cmdline);
Vector<CharType> modstr; //windows wants to change this no idea why
modstr.resize(cmdline.size());
for (int i = 0; i < cmdline.size(); i++)
@@ -2494,13 +2700,29 @@ String OS_Windows::get_unique_id() const {
return String(HwProfInfo.szHwProfileGuid);
}
+void OS_Windows::set_ime_active(const bool p_active) {
+
+ if (p_active) {
+ ImmAssociateContext(hWnd, im_himc);
+
+ set_ime_position(im_position);
+ } else {
+ ImmAssociateContext(hWnd, (HIMC)0);
+ }
+}
+
void OS_Windows::set_ime_position(const Point2 &p_pos) {
+ im_position = p_pos;
+
HIMC himc = ImmGetContext(hWnd);
+ if (himc == (HIMC)0)
+ return;
+
COMPOSITIONFORM cps;
cps.dwStyle = CFS_FORCE_POSITION;
- cps.ptCurrentPos.x = p_pos.x;
- cps.ptCurrentPos.y = p_pos.y;
+ cps.ptCurrentPos.x = im_position.x;
+ cps.ptCurrentPos.y = im_position.y;
ImmSetCompositionWindow(himc, &cps);
ImmReleaseContext(hWnd, himc);
}
@@ -2582,6 +2804,8 @@ Error OS_Windows::move_to_trash(const String &p_path) {
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
key_event_pos = 0;
+ layered_window = false;
+ hBitmap = NULL;
force_quit = false;
alt_mem = false;
gr_mem = false;
@@ -2615,6 +2839,10 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) {
}
OS_Windows::~OS_Windows() {
+ if (is_layered_allowed() && layered_window) {
+ DeleteObject(hBitmap);
+ DeleteDC(hDC_dib);
+ }
#ifdef STDOUT_FILE
fclose(stdo);
#endif
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 584f6fb334..19af63bae0 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -93,17 +93,28 @@ class OS_Windows : public OS {
HINSTANCE hInstance; // Holds The Instance Of The Application
HWND hWnd;
+ HBITMAP hBitmap; //DIB section for layered window
+ uint8_t *dib_data;
+ Size2 dib_size;
+ HDC hDC_dib;
+ bool layered_window;
+
uint32_t move_timer_id;
HCURSOR hCursor;
Size2 window_rect;
VideoMode video_mode;
+ bool preserve_window_size = false;
MainLoop *main_loop;
WNDPROC user_proc;
+ // IME
+ HIMC im_himc;
+ Vector2 im_position;
+
MouseMode mouse_mode;
bool alt_mem;
bool gr_mem;
@@ -212,6 +223,13 @@ public:
virtual void set_borderless_window(bool p_borderless);
virtual bool get_borderless_window();
+ virtual bool get_window_per_pixel_transparency_enabled() const;
+ virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
+
+ virtual uint8_t *get_layered_buffer_data();
+ virtual Size2 get_layered_buffer_size();
+ virtual void swap_layered_buffer();
+
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
virtual Error close_dynamic_library(void *p_library_handle);
virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false);
@@ -268,6 +286,7 @@ public:
virtual String get_unique_id() const;
+ virtual void set_ime_active(const bool p_active);
virtual void set_ime_position(const Point2 &p_pos);
virtual void release_rendering_thread();
diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp
index 1a7cbc0d6d..cd76667c64 100644
--- a/platform/x11/context_gl_x11.cpp
+++ b/platform/x11/context_gl_x11.cpp
@@ -116,32 +116,76 @@ Error ContextGL_X11::initialize() {
None
};
+ static int visual_attribs_layered[] = {
+ GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
+ GLX_DOUBLEBUFFER, true,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
+ GLX_DEPTH_SIZE, 24,
+ None
+ };
+
int fbcount;
- GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount);
- ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED);
+ GLXFBConfig fbconfig;
+ XVisualInfo *vi = NULL;
+
+ if (OS::get_singleton()->is_layered_allowed()) {
+ GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs_layered, &fbcount);
+ ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED);
+
+ for (int i = 0; i < fbcount; i++) {
+ vi = (XVisualInfo *)glXGetVisualFromFBConfig(x11_display, fbc[i]);
+ if (!vi)
+ continue;
+
+ XRenderPictFormat *pict_format = XRenderFindVisualFormat(x11_display, vi->visual);
+ if (!pict_format) {
+ XFree(vi);
+ vi = NULL;
+ continue;
+ }
+
+ fbconfig = fbc[i];
+ if (pict_format->direct.alphaMask > 0) {
+ break;
+ }
+ }
+ ERR_FAIL_COND_V(!fbconfig, ERR_UNCONFIGURED);
+
+ XSetWindowAttributes swa;
+
+ swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);
+ swa.border_pixel = 0;
+ swa.background_pixmap = None;
+ swa.background_pixel = 0;
+ swa.border_pixmap = None;
+ swa.event_mask = StructureNotifyMask;
+
+ x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask | CWBackPixel, &swa);
- XVisualInfo *vi = glXGetVisualFromFBConfig(x11_display, fbc[0]);
+ } else {
+ GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount);
+ ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED);
- XSetWindowAttributes swa;
+ vi = glXGetVisualFromFBConfig(x11_display, fbc[0]);
- swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);
- swa.border_pixel = 0;
- swa.event_mask = StructureNotifyMask;
+ fbconfig = fbc[0];
- /*
- char* windowid = getenv("GODOT_WINDOWID");
- if (windowid) {
+ XSetWindowAttributes swa;
+
+ swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);
+ swa.border_pixel = 0;
+ swa.event_mask = StructureNotifyMask;
+
+ x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa);
+ }
- //freopen("/home/punto/stdout", "w", stdout);
- //reopen("/home/punto/stderr", "w", stderr);
- x11_window = atol(windowid);
- } else {
- */
- x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa);
ERR_FAIL_COND_V(!x11_window, ERR_UNCONFIGURED);
set_class_hint(x11_display, x11_window);
XMapWindow(x11_display, x11_window);
- //};
int (*oldHandler)(Display *, XErrorEvent *) =
XSetErrorHandler(&ctxErrorHandler);
@@ -160,7 +204,7 @@ Error ContextGL_X11::initialize() {
None
};
- p->glx_context = glXCreateContextAttribsARB(x11_display, fbc[0], NULL, true, context_attribs);
+ p->glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, NULL, true, context_attribs);
ERR_EXPLAIN("Could not obtain an OpenGL 3.0 context!");
ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED);
} break;
@@ -175,7 +219,7 @@ Error ContextGL_X11::initialize() {
None
};
- p->glx_context = glXCreateContextAttribsARB(x11_display, fbc[0], NULL, true, context_attribs);
+ p->glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, NULL, true, context_attribs);
ERR_EXPLAIN("Could not obtain an OpenGL 3.3 context!");
ERR_FAIL_COND_V(ctxErrorOccurred || !p->glx_context, ERR_UNCONFIGURED);
} break;
@@ -195,7 +239,6 @@ Error ContextGL_X11::initialize() {
//glXMakeCurrent(x11_display, None, NULL);
XFree(vi);
- XFree(fbc);
return OK;
}
diff --git a/platform/x11/context_gl_x11.h b/platform/x11/context_gl_x11.h
index b54cc84fac..b8f3eb95d4 100644
--- a/platform/x11/context_gl_x11.h
+++ b/platform/x11/context_gl_x11.h
@@ -41,6 +41,7 @@
#include "drivers/gl_context/context_gl.h"
#include "os/os.h"
#include <X11/Xlib.h>
+#include <X11/extensions/Xrender.h>
struct ContextGL_X11_Private;
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 5820a926e9..09e16ad078 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -42,6 +42,11 @@ def can_build():
print("xrandr not found.. x11 disabled.")
return False
+ x11_error = os.system("pkg-config xrender --modversion > /dev/null ")
+ if (x11_error):
+ print("xrender not found.. x11 disabled.")
+ return False
+
return True
def get_opts():
@@ -54,8 +59,8 @@ def get_opts():
BoolVariable('use_leak_sanitizer', 'Use LLVM compiler memory leaks sanitizer (implies use_sanitizer)', False),
BoolVariable('pulseaudio', 'Detect & use pulseaudio', True),
BoolVariable('udev', 'Use udev for gamepad connection callbacks', False),
- EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')),
- BoolVariable('separate_debug_symbols', 'Create a separate file with the debug symbols', False),
+ 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),
]
@@ -141,6 +146,7 @@ def configure(env):
env.ParseConfig('pkg-config xcursor --cflags --libs')
env.ParseConfig('pkg-config xinerama --cflags --libs')
env.ParseConfig('pkg-config xrandr --cflags --libs')
+ env.ParseConfig('pkg-config xrender --cflags --libs')
if (env['touch']):
x11_error = os.system("pkg-config xi --modversion > /dev/null ")
@@ -152,14 +158,6 @@ def configure(env):
# FIXME: Check for existence of the libs before parsing their flags with pkg-config
- if not env['builtin_mbedtls']:
- # mbedTLS does not provide a pkgconfig config yet. See https://github.com/ARMmbed/mbedtls/issues/228
- env.Append(LIBS=['mbedtls', 'mbedcrypto', 'mbedx509'])
-
- if not env['builtin_libwebp']:
- env.ParseConfig('pkg-config libwebp --cflags --libs')
-
-
# freetype depends on libpng and zlib, so bundling one of them while keeping others
# as shared libraries leads to weird issues
if env['builtin_freetype'] or env['builtin_libpng'] or env['builtin_zlib']:
@@ -199,6 +197,10 @@ def configure(env):
env['builtin_libogg'] = False # Needed to link against system libtheora
env['builtin_libvorbis'] = False # Needed to link against system libtheora
env.ParseConfig('pkg-config theora theoradec --cflags --libs')
+ else:
+ list_of_x86 = ['x86_64', 'x86', 'i386', 'i586']
+ if any(platform.machine() in s for s in list_of_x86):
+ env["x86_libtheora_opt_gcc"] = True
if not env['builtin_libvpx']:
env.ParseConfig('pkg-config vpx --cflags --libs')
@@ -214,10 +216,20 @@ def configure(env):
if not env['builtin_libogg']:
env.ParseConfig('pkg-config ogg --cflags --libs')
- if env['builtin_libtheora']:
- list_of_x86 = ['x86_64', 'x86', 'i386', 'i586']
- if any(platform.machine() in s for s in list_of_x86):
- env["x86_libtheora_opt_gcc"] = True
+ if not env['builtin_libwebp']:
+ env.ParseConfig('pkg-config libwebp --cflags --libs')
+
+ if not env['builtin_mbedtls']:
+ # mbedTLS does not provide a pkgconfig config yet. See https://github.com/ARMmbed/mbedtls/issues/228
+ env.Append(LIBS=['mbedtls', 'mbedcrypto', 'mbedx509'])
+
+ if not env['builtin_libwebsockets']:
+ env.ParseConfig('pkg-config libwebsockets --cflags --libs')
+
+ if not env['builtin_miniupnpc']:
+ # No pkgconfig file so far, hardcode default paths.
+ env.Append(CPPPATH=["/usr/include/miniupnpc"])
+ env.Append(LIBS=["miniupnpc"])
# On Linux wchar_t should be 32-bits
# 16-bit library shouldn't be required due to compiler optimisations
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 336068cb1e..2bc85f76c9 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -391,6 +391,9 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true);
XSetWMProtocols(x11_display, x11_window, &wm_delete, 1);
+ im_active = false;
+ im_position = Vector2();
+
if (xim && xim_style) {
xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, x11_window, XNFocusWindow, x11_window, (char *)NULL);
@@ -400,7 +403,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
xic = NULL;
}
if (xic) {
- XSetICFocus(xic);
+ XUnsetICFocus(xic);
} else {
WARN_PRINT("XCreateIC couldn't create xic");
}
@@ -517,6 +520,10 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
power_manager = memnew(PowerX11);
+ if (p_desired.layered_splash) {
+ set_window_per_pixel_transparency_enabled(true);
+ }
+
XEvent xevent;
while (XPending(x11_display) > 0) {
XNextEvent(x11_display, &xevent);
@@ -537,8 +544,25 @@ void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data,
os->xic = NULL;
}
+void OS_X11::set_ime_active(const bool p_active) {
+
+ im_active = p_active;
+
+ if (!xic)
+ return;
+
+ if (p_active) {
+ XSetICFocus(xic);
+ set_ime_position(im_position);
+ } else {
+ XUnsetICFocus(xic);
+ }
+}
+
void OS_X11::set_ime_position(const Point2 &p_pos) {
+ im_position = p_pos;
+
if (!xic)
return;
@@ -707,6 +731,25 @@ Point2 OS_X11::get_mouse_position() const {
return last_mouse_pos;
}
+bool OS_X11::get_window_per_pixel_transparency_enabled() const {
+
+ if (!is_layered_allowed()) return false;
+ return layered_window;
+}
+
+void OS_X11::set_window_per_pixel_transparency_enabled(bool p_enabled) {
+
+ if (!is_layered_allowed()) return;
+ if (layered_window != p_enabled) {
+ if (p_enabled) {
+ set_borderless_window(true);
+ layered_window = true;
+ } else {
+ layered_window = false;
+ }
+ }
+}
+
void OS_X11::set_window_title(const String &p_title) {
XStoreName(x11_display, x11_window, p_title.utf8().get_data());
@@ -1006,9 +1049,13 @@ void OS_X11::set_window_size(const Size2 p_size) {
}
void OS_X11::set_window_fullscreen(bool p_enabled) {
+
if (current_videomode.fullscreen == p_enabled)
return;
+ if (layered_window)
+ set_window_per_pixel_transparency_enabled(false);
+
if (p_enabled && current_videomode.always_on_top) {
// Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity)
set_window_maximized(true);
@@ -1254,6 +1301,9 @@ void OS_X11::set_borderless_window(bool p_borderless) {
if (current_videomode.borderless_window == p_borderless)
return;
+ if (!p_borderless && layered_window)
+ set_window_per_pixel_transparency_enabled(false);
+
current_videomode.borderless_window = p_borderless;
Hints hints;
@@ -1262,6 +1312,9 @@ void OS_X11::set_borderless_window(bool p_borderless) {
hints.decorations = current_videomode.borderless_window ? 0 : 1;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+
+ // Preserve window size
+ set_window_size(Size2(current_videomode.width, current_videomode.height));
}
bool OS_X11::get_borderless_window() {
@@ -1901,6 +1954,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
@@ -1912,6 +1966,7 @@ void OS_X11::process_xevents() {
if (filter) {
break;
}
+#endif
if (mouse_mode == MOUSE_MODE_CAPTURED) {
@@ -2377,7 +2432,7 @@ void OS_X11::set_cursor_shape(CursorShape p_shape) {
if (p_shape == current_cursor)
return;
- if (mouse_mode == MOUSE_MODE_VISIBLE) {
+ if (mouse_mode == MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
if (cursors[p_shape] != None)
XDefineCursor(x11_display, x11_window, cursors[p_shape]);
else if (cursors[CURSOR_ARROW] != None)
@@ -2390,13 +2445,40 @@ void OS_X11::set_cursor_shape(CursorShape p_shape) {
void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
if (p_cursor.is_valid()) {
Ref<Texture> texture = p_cursor;
- Ref<Image> image = texture->get_data();
+ Ref<AtlasTexture> atlas_texture = p_cursor;
+ Ref<Image> image;
+ Size2 texture_size;
+ Rect2 atlas_rect;
- ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256);
+ if (texture.is_valid()) {
+ image = texture->get_data();
+ }
+
+ if (!image.is_valid() && atlas_texture.is_valid()) {
+ texture = atlas_texture->get_atlas();
+
+ atlas_rect.size.width = texture->get_width();
+ atlas_rect.size.height = texture->get_height();
+ atlas_rect.position.x = atlas_texture->get_region().position.x;
+ atlas_rect.position.y = atlas_texture->get_region().position.y;
+
+ texture_size.width = atlas_texture->get_region().size.x;
+ texture_size.height = atlas_texture->get_region().size.y;
+ } else if (image.is_valid()) {
+ texture_size.width = texture->get_width();
+ texture_size.height = texture->get_height();
+ }
+
+ ERR_FAIL_COND(!texture.is_valid());
+ ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
+
+ image = texture->get_data();
+
+ ERR_FAIL_COND(!image.is_valid());
// Create the cursor structure
- XcursorImage *cursor_image = XcursorImageCreate(texture->get_width(), texture->get_height());
- XcursorUInt image_size = texture->get_width() * texture->get_height();
+ XcursorImage *cursor_image = XcursorImageCreate(texture_size.width, texture_size.height);
+ XcursorUInt image_size = texture_size.width * texture_size.height;
XcursorDim size = sizeof(XcursorPixel) * image_size;
cursor_image->version = 1;
@@ -2405,13 +2487,18 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
cursor_image->yhot = p_hotspot.y;
// allocate memory to contain the whole file
- cursor_image->pixels = (XcursorPixel *)malloc(size);
+ cursor_image->pixels = (XcursorPixel *)memalloc(size);
image->lock();
for (XcursorPixel index = 0; index < image_size; index++) {
- int row_index = floor(index / texture->get_width());
- int column_index = index % texture->get_width();
+ int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
+ int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
+
+ if (atlas_texture.is_valid()) {
+ column_index = MIN(column_index, atlas_rect.size.width - 1);
+ row_index = MIN(row_index, atlas_rect.size.height - 1);
+ }
*(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32();
}
@@ -2426,22 +2513,39 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
if (p_shape == CURSOR_ARROW) {
XDefineCursor(x11_display, x11_window, cursors[p_shape]);
}
+
+ memfree(cursor_image->pixels);
+ XcursorImageDestroy(cursor_image);
+ } else {
+ // Reset to default system cursor
+ if (img[p_shape]) {
+ cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]);
+ }
+
+ current_cursor = CURSOR_MAX;
+ set_cursor_shape(p_shape);
}
}
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) {
@@ -2535,8 +2639,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 {
@@ -2715,6 +2821,7 @@ OS_X11::OS_X11() {
AudioDriverManager::add_driver(&driver_alsa);
#endif
+ layered_window = false;
minimized = false;
xim_style = 0L;
mouse_mode = MOUSE_MODE_VISIBLE;
diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h
index 0a39da77de..8cab23fe63 100644
--- a/platform/x11/os_x11.h
+++ b/platform/x11/os_x11.h
@@ -116,6 +116,10 @@ class OS_X11 : public OS_Unix {
static void xim_destroy_callback(::XIM im, ::XPointer client_data,
::XPointer call_data);
+ // IME
+ bool im_active;
+ Vector2 im_position;
+
Point2i last_mouse_pos;
bool last_mouse_pos_valid;
Point2i last_click_pos;
@@ -172,6 +176,8 @@ class OS_X11 : public OS_Unix {
PowerX11 *power_manager;
+ bool layered_window;
+
CrashHandler crash_handler;
int audio_driver_index;
@@ -263,6 +269,11 @@ public:
virtual void set_borderless_window(bool p_borderless);
virtual bool get_borderless_window();
+
+ virtual bool get_window_per_pixel_transparency_enabled() const;
+ virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
+
+ virtual void set_ime_active(const bool p_active);
virtual void set_ime_position(const Point2 &p_pos);
virtual String get_unique_id() const;
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 60a7961293..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);
@@ -361,7 +390,7 @@ void AnimatedSprite::_notification(int p_what) {
if (timeout <= 0) {
- timeout = 1.0 / speed;
+ timeout = _get_frame_duration();
int fc = frames->get_frame_count(animation);
if (frame >= fc - 1) {
@@ -483,7 +512,13 @@ int AnimatedSprite::get_frame() const {
void AnimatedSprite::set_speed_scale(float p_speed_scale) {
+ float elapsed = _get_frame_duration() - timeout;
+
speed_scale = MAX(p_speed_scale, 0.0f);
+
+ // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed
+ _reset_timeout();
+ timeout -= elapsed;
}
float AnimatedSprite::get_speed_scale() const {
@@ -574,21 +609,22 @@ bool AnimatedSprite::is_playing() const {
return playing;
}
-void AnimatedSprite::_reset_timeout() {
-
- if (!playing)
- return;
-
+float AnimatedSprite::_get_frame_duration() {
if (frames.is_valid() && frames->has_animation(animation)) {
float speed = frames->get_animation_speed(animation) * speed_scale;
if (speed > 0) {
- timeout = 1.0 / speed;
- } else {
- timeout = 0;
+ return 1.0 / speed;
}
- } else {
- timeout = 0;
}
+ return 0.0;
+}
+
+void AnimatedSprite::_reset_timeout() {
+
+ if (!playing)
+ return;
+
+ timeout = _get_frame_duration();
}
void AnimatedSprite::set_animation(const StringName &p_animation) {
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index 7b91a1faef..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;
@@ -141,9 +142,11 @@ class AnimatedSprite : public Node2D {
void _res_changed();
+ float _get_frame_duration();
void _reset_timeout();
void _set_playing(bool p_playing);
bool _is_playing() const;
+ Rect2 _get_rect() const;
protected:
static void _bind_methods();
@@ -160,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/area_2d.cpp b/scene/2d/area_2d.cpp
index bb914b90fc..c375374dce 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -666,11 +666,11 @@ void Area2D::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001"), "set_gravity_distance_scale", "get_gravity_distance_scale");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_vec"), "set_gravity_vector", "get_gravity_vector");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.001"), "set_gravity", "get_gravity");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_linear_damp", "get_linear_damp");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_angular_damp", "get_angular_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_linear_damp", "get_linear_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_angular_damp", "get_angular_damp");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable");
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index f998f23d3b..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,8 +499,9 @@ 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::REAL, "max_distance", PROPERTY_HINT_RANGE, "1,65536,1"), "set_max_distance", "get_max_distance");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation", PROPERTY_HINT_EXP_EASING), "set_attenuation", "get_attenuation");
+ 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");
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
@@ -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/camera_2d.cpp b/scene/2d/camera_2d.cpp
index d172da5bd9..3b86ca76ea 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -91,8 +91,8 @@ Transform2D Camera2D::get_camera_transform() {
if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
if (h_drag_enabled && !Engine::get_singleton()->is_editor_hint()) {
- camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT]));
- camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT]));
+ camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom.x * drag_margin[MARGIN_LEFT]));
+ camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom.x * drag_margin[MARGIN_RIGHT]));
} else {
if (h_ofs < 0) {
@@ -104,8 +104,8 @@ Transform2D Camera2D::get_camera_transform() {
if (v_drag_enabled && !Engine::get_singleton()->is_editor_hint()) {
- camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP]));
- camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM]));
+ camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom.y * drag_margin[MARGIN_TOP]));
+ camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom.y * drag_margin[MARGIN_BOTTOM]));
} else {
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index 27bdeda4a8..47326b9be2 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -272,8 +272,7 @@ bool CanvasItem::is_visible_in_tree() const {
void CanvasItem::_propagate_visibility_changed(bool p_visible) {
- if (!first_draw)
- notification(NOTIFICATION_VISIBILITY_CHANGED);
+ notification(NOTIFICATION_VISIBILITY_CHANGED);
if (p_visible)
update(); //todo optimize
@@ -322,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()) {
@@ -995,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);
@@ -1176,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/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index adbb227d0c..9f21fe1a1f 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* mesh_instance_2d.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 "mesh_instance_2d.h"
void MeshInstance2D::_notification(int p_what) {
diff --git a/scene/2d/mesh_instance_2d.h b/scene/2d/mesh_instance_2d.h
index d1d1ade0ae..c9889c1c03 100644
--- a/scene/2d/mesh_instance_2d.h
+++ b/scene/2d/mesh_instance_2d.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* mesh_instance_2d.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 MESH_INSTANCE_2D_H
#define MESH_INSTANCE_2D_H
diff --git a/scene/2d/navigation2d.cpp b/scene/2d/navigation2d.cpp
index 4737e14111..16e0386952 100644
--- a/scene/2d/navigation2d.cpp
+++ b/scene/2d/navigation2d.cpp
@@ -671,7 +671,7 @@ Object *Navigation2D::get_closest_point_owner(const Vector2 &p_point) {
if (Geometry::is_point_in_triangle(p_point, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) {
- E->get().owner;
+ return E->get().owner;
}
}
}
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/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index 584c2f2c85..2ac6c76032 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -120,7 +120,6 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, float p_sc
if (mirroring.x) {
double den = mirroring.x * p_scale;
- double before = new_ofs.x;
new_ofs.x -= den * ceil(new_ofs.x / den);
}
diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp
index 7eaa5bb88c..1da1d44b17 100644
--- a/scene/2d/particles_2d.cpp
+++ b/scene/2d/particles_2d.cpp
@@ -367,9 +367,9 @@ void Particles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("restart"), &Particles2D::restart);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,100000,1"), "set_amount", "get_amount");
+ 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_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "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_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");
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index 7377591f7d..658b998d17 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -299,7 +299,7 @@ void PathFollow2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lookahead", "lookahead"), &PathFollow2D::set_lookahead);
ClassDB::bind_method(D_METHOD("get_lookahead"), &PathFollow2D::get_lookahead);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01"), "set_offset", "get_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_EXP_RANGE, "0,10000,0.01,or_greater"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index feb11089d0..8787a2c735 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -32,8 +32,8 @@
#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) {
/*
@@ -971,11 +971,11 @@ RigidBody2D::~RigidBody2D() {
//////////////////////////
-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 +989,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 +1039,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 +1077,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 +1147,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 +1233,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 +1301,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 +1318,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..7bda6ce817 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -276,6 +276,7 @@ public:
Vector2 normal;
Vector2 collider_vel;
ObjectID collider;
+ RID collider_rid;
int collider_shape;
Variant collider_metadata;
Vector2 remainder;
@@ -287,29 +288,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 +330,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/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 2363c791fa..8ceffb3c27 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* skeleton_2d.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 "skeleton_2d.h"
void Bone2D::_notification(int p_what) {
@@ -59,8 +89,8 @@ void Bone2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_length", "default_length"), &Bone2D::set_default_length);
ClassDB::bind_method(D_METHOD("get_default_length"), &Bone2D::get_default_length);
- ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D,"rest"),"set_rest","get_rest");
- ADD_PROPERTY(PropertyInfo(Variant::REAL,"default_length",PROPERTY_HINT_RANGE,"1,1024,1"),"set_default_length","get_default_length");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "rest"), "set_rest", "get_rest");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "default_length", PROPERTY_HINT_RANGE, "1,1024,1"), "set_default_length", "get_default_length");
}
void Bone2D::set_rest(const Transform2D &p_rest) {
@@ -90,8 +120,7 @@ void Bone2D::apply_rest() {
void Bone2D::set_default_length(float p_length) {
- default_length=p_length;
-
+ default_length = p_length;
}
float Bone2D::get_default_length() const {
@@ -99,7 +128,7 @@ float Bone2D::get_default_length() const {
}
int Bone2D::get_index_in_skeleton() const {
- ERR_FAIL_COND_V(!skeleton,-1);
+ ERR_FAIL_COND_V(!skeleton, -1);
skeleton->_update_bone_setup();
return skeleton_index;
}
@@ -107,22 +136,21 @@ String Bone2D::get_configuration_warning() const {
String warning = Node2D::get_configuration_warning();
if (!skeleton) {
- if (warning!=String()) {
- warning+="\n";
+ if (warning != String()) {
+ warning += "\n";
}
if (parent_bone) {
- warning+=TTR("This Bone2D chain should end at a Skeleton2D node.");
+ warning += TTR("This Bone2D chain should end at a Skeleton2D node.");
} else {
- warning+=TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node.");
+ warning += TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node.");
}
}
- if (rest==Transform2D(0,0,0,0,0,0)) {
- if (warning!=String()) {
- warning+="\n";
+ if (rest == Transform2D(0, 0, 0, 0, 0, 0)) {
+ if (warning != String()) {
+ warning += "\n";
}
- warning+=TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one.");
-
+ warning += TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one.");
}
return warning;
@@ -131,12 +159,12 @@ String Bone2D::get_configuration_warning() const {
Bone2D::Bone2D() {
skeleton = NULL;
parent_bone = NULL;
- skeleton_index=-1;
- default_length=16;
+ skeleton_index = -1;
+ default_length = 16;
set_notify_local_transform(true);
//this is a clever hack so the bone knows no rest has been set yet, allowing to show an error.
- for(int i=0;i<3;i++) {
- rest[i]=Vector2(0,0);
+ for (int i = 0; i < 3; i++) {
+ rest[i] = Vector2(0, 0);
}
}
@@ -164,12 +192,12 @@ void Skeleton2D::_update_bone_setup() {
for (int i = 0; i < bones.size(); i++) {
bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
- bones[i].bone->skeleton_index=i;
+ bones[i].bone->skeleton_index = i;
Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent());
if (parent_bone) {
- bones[i].parent_index=parent_bone->skeleton_index;
+ bones[i].parent_index = parent_bone->skeleton_index;
} else {
- bones[i].parent_index=-1;
+ bones[i].parent_index = -1;
}
}
@@ -200,8 +228,8 @@ void Skeleton2D::_update_transform() {
for (int i = 0; i < bones.size(); i++) {
- ERR_CONTINUE(bones[i].parent_index>=i);
- if (bones[i].parent_index>=0) {
+ ERR_CONTINUE(bones[i].parent_index >= i);
+ if (bones[i].parent_index >= 0) {
bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
} else {
bones[i].accum_transform = bones[i].bone->get_transform();
@@ -247,7 +275,7 @@ void Skeleton2D::_notification(int p_what) {
}
if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- VS::get_singleton()->skeleton_set_base_transform_2d(skeleton,get_global_transform());
+ VS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform());
}
}
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index cd270dac85..9d0a061457 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* skeleton_2d.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 SKELETON_2D_H
#define SKELETON_2D_H
@@ -8,12 +38,13 @@ class Skeleton2D;
class Bone2D : public Node2D {
GDCLASS(Bone2D, Node2D)
+ friend class Skeleton2D;
+
Bone2D *parent_bone;
Skeleton2D *skeleton;
Transform2D rest;
float default_length;
-friend class Skeleton2D;
int skeleton_index;
protected:
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 60766862cc..9a343ca0f0 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -61,6 +61,7 @@ void TileMap::_notification(int p_what) {
}
pending_update = true;
+ _recreate_quadrants();
_update_dirty_quadrants();
RID space = get_world_2d()->get_space();
_update_quadrant_transform();
@@ -465,10 +466,12 @@ void TileMap::_update_dirty_quadrants() {
Transform2D xform;
xform.set_origin(offset.floor());
- Vector2 shape_ofs = tile_set->tile_get_shape_offset(c.id, i);
+ Vector2 shape_ofs = shapes[i].shape_transform.get_origin();
_fix_cell_transform(xform, c, shape_ofs + center_ofs, s);
+ xform *= shapes[i].shape_transform.untranslated();
+
if (debug_canvas_item.is_valid()) {
vs->canvas_item_add_set_transform(debug_canvas_item, xform);
shape->draw(debug_canvas_item, debug_collision_color);
@@ -705,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())
@@ -716,7 +719,10 @@ void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q) {
pending_update = true;
if (!is_inside_tree())
return;
- call_deferred("_update_dirty_quadrants");
+
+ if (update) {
+ _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) {
@@ -724,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);
@@ -844,16 +855,37 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) {
if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
mask |= TileSet::BIND_BOTTOMRIGHT;
}
- } else if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_3X3) {
- if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1))) {
- mask |= TileSet::BIND_TOPLEFT;
+ } else {
+ if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_3X3_MINIMAL) {
+ if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) {
+ mask |= TileSet::BIND_TOPLEFT;
+ }
+ if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
+ mask |= TileSet::BIND_TOPRIGHT;
+ }
+ if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) {
+ mask |= TileSet::BIND_BOTTOMLEFT;
+ }
+ if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
+ mask |= TileSet::BIND_BOTTOMRIGHT;
+ }
+ } else {
+ if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1))) {
+ mask |= TileSet::BIND_TOPLEFT;
+ }
+ if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1))) {
+ mask |= TileSet::BIND_TOPRIGHT;
+ }
+ if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1))) {
+ mask |= TileSet::BIND_BOTTOMLEFT;
+ }
+ if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1))) {
+ mask |= TileSet::BIND_BOTTOMRIGHT;
+ }
}
if (tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1))) {
mask |= TileSet::BIND_TOP;
}
- if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1))) {
- mask |= TileSet::BIND_TOPRIGHT;
- }
if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) {
mask |= TileSet::BIND_LEFT;
}
@@ -861,15 +893,9 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) {
if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
mask |= TileSet::BIND_RIGHT;
}
- if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1))) {
- mask |= TileSet::BIND_BOTTOMLEFT;
- }
if (tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1))) {
mask |= TileSet::BIND_BOTTOM;
}
- if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1))) {
- mask |= TileSet::BIND_BOTTOMRIGHT;
- }
}
Vector2 coord = tile_set->autotile_get_subtile_for_bitmask(id, mask, this, Vector2(p_x, p_y));
E->get().autotile_coord_x = (int)coord.x;
@@ -961,6 +987,14 @@ void TileMap::set_cell_autotile_coord(int p_x, int p_y, const Vector2 &p_coord)
c.autotile_coord_x = p_coord.x;
c.autotile_coord_y = p_coord.y;
tile_map[pk] = c;
+
+ PosKey qk(p_x / _get_quadrant_size(), p_y / _get_quadrant_size());
+ Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk);
+
+ if (!Q)
+ return;
+
+ _make_quadrant_dirty(Q);
}
Vector2 TileMap::get_cell_autotile_coord(int p_x, int p_y) const {
@@ -990,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() {
@@ -1117,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;
@@ -1586,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);
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 07947004b3..79d79ca59f 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -188,7 +188,7 @@ 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();
@@ -241,12 +241,10 @@ 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());
diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp
index 21f471039f..6ea980ec97 100644
--- a/scene/3d/area.cpp
+++ b/scene/3d/area.cpp
@@ -711,7 +711,7 @@ void Area::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001"), "set_gravity_distance_scale", "get_gravity_distance_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_vec"), "set_gravity_vector", "get_gravity_vector");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_gravity", "get_gravity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,1024,0.001"), "set_linear_damp", "get_linear_damp");
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 c2a50ec7bb..d46231a677 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,7 +933,8 @@ 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::REAL, "max_distance", PROPERTY_HINT_RANGE, "0,65536,1"), "set_max_distance", "get_max_distance");
+ 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");
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
@@ -949,6 +985,9 @@ 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");
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/camera.cpp b/scene/3d/camera.cpp
index 9de189c158..e11e8abe5b 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -480,8 +480,8 @@ void Camera::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "near"), "set_znear", "get_znear");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "far"), "set_zfar", "get_zfar");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_znear", "get_znear");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar");
BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE);
BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL);
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp
new file mode 100644
index 0000000000..2e897c1c73
--- /dev/null
+++ b/scene/3d/cpu_particles.cpp
@@ -0,0 +1,1409 @@
+#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");
+#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) {
+
+ 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);
+ 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");
+#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");
+#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");
+#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/light.cpp b/scene/3d/light.cpp
index 240bd631a1..7c42638107 100644
--- a/scene/3d/light.cpp
+++ b/scene/3d/light.cpp
@@ -375,7 +375,7 @@ void DirectionalLight::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_normal_bias", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_bias_split_scale", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_BIAS_SPLIT_SCALE);
ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_depth_range", PROPERTY_HINT_ENUM, "Stable,Optimized"), "set_shadow_depth_range", "get_shadow_depth_range");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_max_distance", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_max_distance", PROPERTY_HINT_EXP_RANGE, "0,8192,0.1,or_greater"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE);
BIND_ENUM_CONSTANT(SHADOW_ORTHOGONAL);
BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS);
@@ -428,8 +428,8 @@ void OmniLight::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_shadow_detail"), &OmniLight::get_shadow_detail);
ADD_GROUP("Omni", "omni_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_range", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_RANGE);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_ATTENUATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1,or_greater"), "set_param", "get_param", PARAM_RANGE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION);
ADD_PROPERTY(PropertyInfo(Variant::INT, "omni_shadow_mode", PROPERTY_HINT_ENUM, "Dual Paraboloid,Cube"), "set_shadow_mode", "get_shadow_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "omni_shadow_detail", PROPERTY_HINT_ENUM, "Vertical,Horizontal"), "set_shadow_detail", "get_shadow_detail");
@@ -450,8 +450,8 @@ OmniLight::OmniLight() :
void SpotLight::_bind_methods() {
ADD_GROUP("Spot", "spot_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_range", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_RANGE);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_ATTENUATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1,or_greater"), "set_param", "get_param", PARAM_RANGE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_param", "get_param", PARAM_SPOT_ANGLE);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_SPOT_ATTENUATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_SPOT_ATTENUATION);
}
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
index 80bae911d4..e836a6154a 100644
--- a/scene/3d/mesh_instance.cpp
+++ b/scene/3d/mesh_instance.cpp
@@ -371,7 +371,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/particles.cpp b/scene/3d/particles.cpp
index a39ac5a8f5..2b3a62fcdc 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -324,11 +324,11 @@ void Particles::_bind_methods() {
ClassDB::bind_method(D_METHOD("capture_aabb"), &Particles::capture_aabb);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,100000,1"), "set_amount", "get_amount");
+ 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_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime");
+ 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_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
+ 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");
@@ -1444,7 +1444,7 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_color_modifier", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_trail_color_modifier", "get_trail_color_modifier");
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::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "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::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_point_texture", "get_emission_point_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_normal_texture", "get_emission_normal_texture");
@@ -1460,38 +1460,38 @@ void ParticlesMaterial::_bind_methods() {
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"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY);
+ 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, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGULAR_VELOCITY);
ADD_GROUP("Orbit Velocity", "orbit_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01"), "set_param", "get_param", PARAM_ORBIT_VELOCITY);
+ 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, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ORBIT_VELOCITY);
ADD_GROUP("Linear Accel", "linear_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01"), "set_param", "get_param", PARAM_LINEAR_ACCEL);
+ 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, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_LINEAR_ACCEL);
ADD_GROUP("Radial Accel", "radial_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01"), "set_param", "get_param", PARAM_RADIAL_ACCEL);
+ 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, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_RADIAL_ACCEL);
ADD_GROUP("Tangential Accel", "tangential_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL);
+ 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, "CurveTexture"), "set_param_texture", "get_param_texture", 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", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "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, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING);
ADD_GROUP("Angle", "");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1"), "set_param", "get_param", PARAM_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, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE);
ADD_GROUP("Scale", "");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_param", "get_param", PARAM_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, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE);
ADD_GROUP("Color", "");
@@ -1503,7 +1503,7 @@ void ParticlesMaterial::_bind_methods() {
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, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION);
ADD_GROUP("Animation", "anim_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_param", "get_param", PARAM_ANIM_SPEED);
+ 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, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp
index 57d79c960f..9acaa15641 100644
--- a/scene/3d/path.cpp
+++ b/scene/3d/path.cpp
@@ -230,7 +230,7 @@ void PathFollow::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow::set_loop);
ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow::has_loop);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01"), "set_offset", "get_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_EXP_RANGE, "0,10000,0.01,or_greater"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
@@ -329,3 +329,219 @@ PathFollow::PathFollow() {
cubic = true;
loop = true;
}
+
+//////////////
+
+void OrientedPathFollow::_update_transform() {
+
+ if (!path)
+ return;
+
+ Ref<Curve3D> c = path->get_curve();
+ if (!c.is_valid())
+ return;
+
+ int count = c->get_point_count();
+ if (count < 2)
+ return;
+
+ if (delta_offset == 0) {
+ return;
+ }
+
+ float offset = get_offset();
+ float bl = c->get_baked_length();
+ float bi = c->get_bake_interval();
+ float o = offset;
+ float o_next = offset + bi;
+
+ if (has_loop()) {
+ o = Math::fposmod(o, bl);
+ o_next = Math::fposmod(o_next, bl);
+ } else if (o_next >= bl) {
+ o = bl - bi;
+ o_next = bl;
+ }
+
+ bool cubic = get_cubic_interpolation();
+ Vector3 pos = c->interpolate_baked(o, cubic);
+ Vector3 forward = c->interpolate_baked(o_next, cubic) - pos;
+
+ if (forward.length_squared() < CMP_EPSILON2)
+ forward = Vector3(0, 0, 1);
+ else
+ forward.normalize();
+
+ Vector3 up = c->interpolate_baked_up_vector(o, true);
+
+ if (o_next < o) {
+ Vector3 up1 = c->interpolate_baked_up_vector(o_next, true);
+ Vector3 axis = up.cross(up1);
+
+ if (axis.length_squared() < CMP_EPSILON2)
+ axis = forward;
+ else
+ axis.normalize();
+
+ up.rotate(axis, up.angle_to(up1) * 0.5f);
+ }
+
+ Transform t = get_transform();
+ Vector3 scale = t.basis.get_scale();
+
+ Vector3 sideways = up.cross(forward).normalized();
+ up = forward.cross(sideways).normalized();
+
+ t.basis.set(sideways, up, forward);
+ t.basis.scale_local(scale);
+
+ t.origin = pos + sideways * get_h_offset() + up * get_v_offset();
+
+ set_transform(t);
+}
+
+void OrientedPathFollow::_notification(int p_what) {
+
+ switch (p_what) {
+
+ case NOTIFICATION_ENTER_TREE: {
+
+ Node *parent = get_parent();
+ if (parent) {
+ path = Object::cast_to<Path>(parent);
+ if (path) {
+ _update_transform();
+ }
+ }
+
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+
+ path = NULL;
+ } break;
+ }
+}
+
+void OrientedPathFollow::set_cubic_interpolation(bool p_enable) {
+
+ cubic = p_enable;
+}
+
+bool OrientedPathFollow::get_cubic_interpolation() const {
+
+ return cubic;
+}
+
+void OrientedPathFollow::_validate_property(PropertyInfo &property) const {
+
+ if (property.name == "offset") {
+
+ float max = 10000;
+ if (path && path->get_curve().is_valid())
+ max = path->get_curve()->get_baked_length();
+
+ property.hint_string = "0," + rtos(max) + ",0.01";
+ }
+}
+
+void OrientedPathFollow::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_offset", "offset"), &OrientedPathFollow::set_offset);
+ ClassDB::bind_method(D_METHOD("get_offset"), &OrientedPathFollow::get_offset);
+
+ ClassDB::bind_method(D_METHOD("set_h_offset", "h_offset"), &OrientedPathFollow::set_h_offset);
+ ClassDB::bind_method(D_METHOD("get_h_offset"), &OrientedPathFollow::get_h_offset);
+
+ ClassDB::bind_method(D_METHOD("set_v_offset", "v_offset"), &OrientedPathFollow::set_v_offset);
+ ClassDB::bind_method(D_METHOD("get_v_offset"), &OrientedPathFollow::get_v_offset);
+
+ ClassDB::bind_method(D_METHOD("set_unit_offset", "unit_offset"), &OrientedPathFollow::set_unit_offset);
+ ClassDB::bind_method(D_METHOD("get_unit_offset"), &OrientedPathFollow::get_unit_offset);
+
+ ClassDB::bind_method(D_METHOD("set_cubic_interpolation", "enable"), &OrientedPathFollow::set_cubic_interpolation);
+ ClassDB::bind_method(D_METHOD("get_cubic_interpolation"), &OrientedPathFollow::get_cubic_interpolation);
+
+ ClassDB::bind_method(D_METHOD("set_loop", "loop"), &OrientedPathFollow::set_loop);
+ ClassDB::bind_method(D_METHOD("has_loop"), &OrientedPathFollow::has_loop);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01"), "set_offset", "get_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
+}
+
+void OrientedPathFollow::set_offset(float p_offset) {
+ delta_offset = p_offset - offset;
+ offset = p_offset;
+
+ if (path)
+ _update_transform();
+ _change_notify("offset");
+ _change_notify("unit_offset");
+}
+
+void OrientedPathFollow::set_h_offset(float p_h_offset) {
+
+ h_offset = p_h_offset;
+ if (path)
+ _update_transform();
+}
+
+float OrientedPathFollow::get_h_offset() const {
+
+ return h_offset;
+}
+
+void OrientedPathFollow::set_v_offset(float p_v_offset) {
+
+ v_offset = p_v_offset;
+ if (path)
+ _update_transform();
+}
+
+float OrientedPathFollow::get_v_offset() const {
+
+ return v_offset;
+}
+
+float OrientedPathFollow::get_offset() const {
+
+ return offset;
+}
+
+void OrientedPathFollow::set_unit_offset(float p_unit_offset) {
+
+ if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length())
+ set_offset(p_unit_offset * path->get_curve()->get_baked_length());
+}
+
+float OrientedPathFollow::get_unit_offset() const {
+
+ if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length())
+ return get_offset() / path->get_curve()->get_baked_length();
+ else
+ return 0;
+}
+
+void OrientedPathFollow::set_loop(bool p_loop) {
+
+ loop = p_loop;
+}
+
+bool OrientedPathFollow::has_loop() const {
+
+ return loop;
+}
+
+OrientedPathFollow::OrientedPathFollow() {
+
+ offset = 0;
+ delta_offset = 0;
+ h_offset = 0;
+ v_offset = 0;
+ path = NULL;
+ cubic = true;
+ loop = true;
+}
diff --git a/scene/3d/path.h b/scene/3d/path.h
index 2ed686ac3c..f73bf17dfe 100644
--- a/scene/3d/path.h
+++ b/scene/3d/path.h
@@ -111,4 +111,47 @@ public:
VARIANT_ENUM_CAST(PathFollow::RotationMode);
+class OrientedPathFollow : public Spatial {
+
+ GDCLASS(OrientedPathFollow, Spatial);
+
+private:
+ Path *path;
+ real_t delta_offset; // change in offset since last _update_transform
+ real_t offset;
+ real_t h_offset;
+ real_t v_offset;
+ bool cubic;
+ bool loop;
+
+ void _update_transform();
+
+protected:
+ virtual void _validate_property(PropertyInfo &property) const;
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_offset(float p_offset);
+ float get_offset() const;
+
+ void set_h_offset(float p_h_offset);
+ float get_h_offset() const;
+
+ void set_v_offset(float p_v_offset);
+ float get_v_offset() const;
+
+ void set_unit_offset(float p_unit_offset);
+ float get_unit_offset() const;
+
+ void set_loop(bool p_loop);
+ bool has_loop() const;
+
+ void set_cubic_interpolation(bool p_enable);
+ bool get_cubic_interpolation() const;
+
+ OrientedPathFollow();
+};
+
#endif // PATH_H
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 4528799985..e851c8d643 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -979,7 +979,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 +1128,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);
@@ -1400,7 +1400,7 @@ void PhysicalBone::ConeJointData::_get_property_list(List<PropertyInfo> *p_list)
JointData::_get_property_list(p_list);
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/swing_span", PROPERTY_HINT_RANGE, "-180,180,0.01"));
- p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/twist_span", PROPERTY_HINT_RANGE, "-40000,40000,0.1"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/twist_span", PROPERTY_HINT_RANGE, "-40000,40000,0.1,or_lesser,or_greater"));
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/relaxation", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
@@ -1825,6 +1825,7 @@ void PhysicalBone::_notification(int p_what) {
parent_skeleton = find_skeleton_parent(get_parent());
update_bone_id();
reset_to_rest_position();
+ _reset_physics_simulation_state();
break;
case NOTIFICATION_EXIT_TREE:
if (parent_skeleton) {
@@ -1886,10 +1887,8 @@ void PhysicalBone::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_body_offset", "offset"), &PhysicalBone::set_body_offset);
ClassDB::bind_method(D_METHOD("get_body_offset"), &PhysicalBone::get_body_offset);
- ClassDB::bind_method(D_METHOD("set_static_body", "simulate"), &PhysicalBone::set_static_body);
ClassDB::bind_method(D_METHOD("is_static_body"), &PhysicalBone::is_static_body);
- ClassDB::bind_method(D_METHOD("set_simulate_physics", "simulate"), &PhysicalBone::set_simulate_physics);
ClassDB::bind_method(D_METHOD("get_simulate_physics"), &PhysicalBone::get_simulate_physics);
ClassDB::bind_method(D_METHOD("is_simulating_physics"), &PhysicalBone::is_simulating_physics);
@@ -1913,15 +1912,20 @@ void PhysicalBone::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "joint_type", PROPERTY_HINT_ENUM, "None,PinJoint,ConeJoint,HingeJoint,SliderJoint,6DOFJoint"), "set_joint_type", "get_joint_type");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "joint_offset"), "set_joint_offset", "get_joint_offset");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simulate_physics"), "set_simulate_physics", "get_simulate_physics");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "body_offset"), "set_body_offset", "get_body_offset");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "static_body"), "set_static_body", "is_static_body");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "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::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale");
+
+ BIND_ENUM_CONSTANT(JOINT_TYPE_NONE);
+ BIND_ENUM_CONSTANT(JOINT_TYPE_PIN);
+ BIND_ENUM_CONSTANT(JOINT_TYPE_CONE);
+ BIND_ENUM_CONSTANT(JOINT_TYPE_HINGE);
+ BIND_ENUM_CONSTANT(JOINT_TYPE_SLIDER);
+ BIND_ENUM_CONSTANT(JOINT_TYPE_6DOF);
}
Skeleton *PhysicalBone::find_skeleton_parent(Node *p_parent) {
@@ -2248,8 +2252,8 @@ PhysicalBone::PhysicalBone() :
joint_data(NULL),
static_body(false),
simulate_physics(false),
- _internal_static_body(!static_body),
- _internal_simulate_physics(simulate_physics),
+ _internal_static_body(false),
+ _internal_simulate_physics(false),
bone_id(-1),
parent_skeleton(NULL),
bone_name(""),
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index 17d2769c79..7236eba685 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -294,7 +294,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 +303,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 2178da02b5..4d50945062 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -240,9 +240,9 @@ void ReflectionProbe::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once,Always"), "set_update_mode", "get_update_mode");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "intensity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_intensity", "get_intensity");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "0,16384,0.1"), "set_max_distance", "get_max_distance");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "extents"), "set_extents", "get_extents");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "origin_offset"), "set_origin_offset", "get_origin_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,16384,0.1,or_greater"), "set_max_distance", "get_max_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "origin_offset"), "set_origin_offset", "get_origin_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "box_projection"), "set_enable_box_projection", "is_box_projection_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_shadows"), "set_enable_shadows", "are_shadows_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
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/scenario_fx.cpp b/scene/3d/scenario_fx.cpp
index d5bff676cb..26cbfc0b11 100644
--- a/scene/3d/scenario_fx.cpp
+++ b/scene/3d/scenario_fx.cpp
@@ -93,9 +93,10 @@ String WorldEnvironment::get_configuration_warning() const {
return TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes).");
}
- if (environment.is_valid() && get_viewport() && !get_viewport()->get_camera() && environment->get_background() != Environment::BG_CANVAS) {
- return TTR("This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set this environment's Background Mode to Canvas (for 2D scenes).");
- }
+ // Commenting this warning for now, I think it makes no sense. If anyone can figure out what its supposed to do, feedback welcome. Else it should be deprecated.
+ //if (environment.is_valid() && get_viewport() && !get_viewport()->get_camera() && environment->get_background() != Environment::BG_CANVAS) {
+ // return TTR("This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set this environment's Background Mode to Canvas (for 2D scenes).");
+ //}
return String();
}
diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp
index a7eb54c85d..8d91b6f09f 100644
--- a/scene/3d/skeleton.cpp
+++ b/scene/3d/skeleton.cpp
@@ -330,7 +330,7 @@ void Skeleton::add_bone(const String &p_name) {
_make_dirty();
update_gizmo();
}
-int Skeleton::find_bone(String p_name) const {
+int Skeleton::find_bone(const String &p_name) const {
for (int i = 0; i < bones.size(); i++) {
@@ -347,6 +347,19 @@ String Skeleton::get_bone_name(int p_bone) const {
return bones[p_bone].name;
}
+bool Skeleton::is_bone_parent_of(int p_bone, int p_parent_bone_id) const {
+
+ int parent_of_bone = get_bone_parent(p_bone);
+
+ if (-1 == parent_of_bone)
+ return false;
+
+ if (parent_of_bone == p_parent_bone_id)
+ return true;
+
+ return is_bone_parent_of(parent_of_bone, p_parent_bone_id);
+}
+
int Skeleton::get_bone_count() const {
return bones.size();
@@ -534,17 +547,7 @@ void Skeleton::localize_rests() {
}
}
-void _notify_physical_bones_simulation(bool start, Node *p_node) {
-
- for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
- _notify_physical_bones_simulation(start, p_node->get_child(i));
- }
-
- PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node);
- if (pb) {
- pb->set_simulate_physics(start);
- }
-}
+#ifndef _3D_DISABLED
void Skeleton::bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone) {
ERR_FAIL_INDEX(p_bone, bones.size());
@@ -603,8 +606,67 @@ void Skeleton::_rebuild_physical_bones_cache() {
}
}
-void Skeleton::physical_bones_simulation(bool start) {
- _notify_physical_bones_simulation(start, this);
+void _pb_stop_simulation(Node *p_node) {
+
+ for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
+ _pb_stop_simulation(p_node->get_child(i));
+ }
+
+ PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node);
+ if (pb) {
+ pb->set_simulate_physics(false);
+ pb->set_static_body(false);
+ }
+}
+
+void Skeleton::physical_bones_stop_simulation() {
+ _pb_stop_simulation(this);
+}
+
+void _pb_start_simulation(const Skeleton *p_skeleton, Node *p_node, const Vector<int> &p_sim_bones) {
+
+ for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
+ _pb_start_simulation(p_skeleton, p_node->get_child(i), p_sim_bones);
+ }
+
+ PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node);
+ if (pb) {
+ bool sim = false;
+ for (int i = p_sim_bones.size() - 1; 0 <= i; --i) {
+ if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
+ sim = true;
+ break;
+ }
+ }
+
+ pb->set_simulate_physics(true);
+ if (sim) {
+ pb->set_static_body(false);
+ } else {
+ pb->set_static_body(true);
+ }
+ }
+}
+
+void Skeleton::physical_bones_start_simulation_on(const Array &p_bones) {
+
+ Vector<int> sim_bones;
+ if (p_bones.size() <= 0) {
+ sim_bones.push_back(0); // if no bones is specified, activate ragdoll on full body
+ } else {
+ sim_bones.resize(p_bones.size());
+ int c = 0;
+ for (int i = sim_bones.size() - 1; 0 <= i; --i) {
+ if (Variant::STRING == p_bones.get(i).get_type()) {
+ int bone_id = find_bone(p_bones.get(i));
+ if (bone_id != -1)
+ sim_bones[c++] = bone_id;
+ }
+ }
+ sim_bones.resize(c);
+ }
+
+ _pb_start_simulation(this, this, sim_bones);
}
void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) {
@@ -631,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);
@@ -667,10 +731,15 @@ void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform);
- ClassDB::bind_method(D_METHOD("physical_bones_simulation", "start"), &Skeleton::physical_bones_simulation);
+#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);
}
@@ -683,6 +752,5 @@ Skeleton::Skeleton() {
}
Skeleton::~Skeleton() {
-
VisualServer::get_singleton()->free(skeleton);
}
diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h
index f0e71c8b4f..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
}
};
@@ -120,9 +127,11 @@ public:
// skeleton creation api
void add_bone(const String &p_name);
- int find_bone(String p_name) const;
+ int find_bone(const String &p_name) const;
String get_bone_name(int p_bone) const;
+ bool is_bone_parent_of(int p_bone_id, int p_parent_bone_id) const;
+
void set_bone_parent(int p_bone, int p_parent);
int get_bone_parent(int p_bone) const;
@@ -162,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);
@@ -176,9 +186,11 @@ private:
void _rebuild_physical_bones_cache();
public:
- void physical_bones_simulation(bool start);
+ void physical_bones_stop_simulation();
+ 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/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 232855c978..036a748c83 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -185,6 +185,9 @@ void SpriteBase3D::_queue_update() {
if (pending_update)
return;
+ triangle_mesh.unref();
+ update_gizmo();
+
pending_update = true;
call_deferred(SceneStringNames::get_singleton()->_im_update);
}
@@ -198,6 +201,66 @@ PoolVector<Face3> SpriteBase3D::get_faces(uint32_t p_usage_flags) const {
return PoolVector<Face3>();
}
+Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const {
+ if (triangle_mesh.is_valid())
+ return triangle_mesh;
+
+ PoolVector<Vector3> faces;
+ faces.resize(6);
+ PoolVector<Vector3>::Write facesw = faces.write();
+
+ Rect2 final_rect = get_item_rect();
+
+ if (final_rect.size.x == 0 || final_rect.size.y == 0)
+ return Ref<TriangleMesh>();
+
+ float pixel_size = get_pixel_size();
+
+ Vector2 vertices[4] = {
+
+ (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
+ (final_rect.position + final_rect.size) * pixel_size,
+ (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
+ final_rect.position * pixel_size,
+
+ };
+
+ int x_axis = ((axis + 1) % 3);
+ int y_axis = ((axis + 2) % 3);
+
+ if (axis != Vector3::AXIS_Z) {
+ SWAP(x_axis, y_axis);
+
+ for (int i = 0; i < 4; i++) {
+ if (axis == Vector3::AXIS_Y) {
+ vertices[i].y = -vertices[i].y;
+ } else if (axis == Vector3::AXIS_X) {
+ vertices[i].x = -vertices[i].x;
+ }
+ }
+ }
+
+ static const int indices[6] = {
+ 0, 1, 2,
+ 0, 2, 3
+ };
+
+ for (int j = 0; j < 6; j++) {
+ int i = indices[j];
+ Vector3 vtx;
+ vtx[x_axis] = vertices[i][0];
+ vtx[y_axis] = vertices[i][1];
+ facesw[j] = vtx;
+ }
+
+ facesw = PoolVector<Vector3>::Write();
+
+ triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh));
+ triangle_mesh->create(faces);
+
+ return triangle_mesh;
+}
+
void SpriteBase3D::set_draw_flag(DrawFlags p_flag, bool p_enable) {
ERR_FAIL_INDEX(p_flag, FLAG_MAX);
@@ -255,6 +318,7 @@ void SpriteBase3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_alpha_cut_mode"), &SpriteBase3D::get_alpha_cut_mode);
ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect);
+ ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh);
ClassDB::bind_method(D_METHOD("_queue_update"), &SpriteBase3D::_queue_update);
ClassDB::bind_method(D_METHOD("_im_update"), &SpriteBase3D::_im_update);
@@ -366,6 +430,16 @@ void Sprite3D::_draw() {
final_rect.position * pixel_size,
};
+
+ Vector2 src_tsize = Vector2(texture->get_width(), texture->get_height());
+
+ // Properly setup UVs for impostor textures (AtlasTexture).
+ Ref<AtlasTexture> atlas_tex = texture;
+ if (atlas_tex != NULL) {
+ src_tsize[0] = atlas_tex->get_atlas()->get_width();
+ src_tsize[1] = atlas_tex->get_atlas()->get_height();
+ }
+
Vector2 uvs[4] = {
final_src_rect.position / tsize,
(final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / tsize,
@@ -656,6 +730,16 @@ void AnimatedSprite3D::_draw() {
final_rect.position * pixel_size,
};
+
+ Vector2 src_tsize = Vector2(texture->get_width(), texture->get_height());
+
+ // Properly setup UVs for impostor textures (AtlasTexture).
+ Ref<AtlasTexture> atlas_tex = texture;
+ if (atlas_tex != NULL) {
+ src_tsize[0] = atlas_tex->get_atlas()->get_width();
+ src_tsize[1] = atlas_tex->get_atlas()->get_height();
+ }
+
Vector2 uvs[4] = {
final_src_rect.position / tsize,
(final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / tsize,
@@ -881,7 +965,7 @@ Rect2 AnimatedSprite3D::get_item_rect() const {
return Rect2(0, 0, 1, 1);
Size2i s = t->get_size();
- Point2 ofs = offset;
+ Point2 ofs = get_offset();
if (centered)
ofs -= s / 2;
diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h
index 23e1d96b4b..a4705a8970 100644
--- a/scene/3d/sprite_3d.h
+++ b/scene/3d/sprite_3d.h
@@ -38,6 +38,8 @@ class SpriteBase3D : public GeometryInstance {
GDCLASS(SpriteBase3D, GeometryInstance);
+ mutable Ref<TriangleMesh> triangle_mesh; //cached
+
public:
enum DrawFlags {
FLAG_TRANSPARENT,
@@ -133,6 +135,7 @@ public:
virtual AABB get_aabb() const;
virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
+ Ref<TriangleMesh> generate_triangle_mesh() const;
SpriteBase3D();
~SpriteBase3D();
@@ -192,7 +195,6 @@ class AnimatedSprite3D : public SpriteBase3D {
int frame;
bool centered;
- Point2 offset;
float timeout;
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index b72665aa2b..385956dc16 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -583,11 +583,14 @@ void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vec
rel_vel = normal.dot(vel);
// !BAS! We had this set to 0.4, in bullet its 0.2
- // real_t contactDamping = real_t(0.2);
+ real_t contactDamping = real_t(0.2);
+
+ if (p_rollInfluence > 0.0) {
+ // !BAS! But seeing we apply this frame by frame, makes more sense to me to make this time based
+ // keeping in mind our anti roll factor if it is set
+ contactDamping = s->get_step() / p_rollInfluence;
+ }
- // !BAS! But seeing we apply this frame by frame, makes more sense to me to make this time based
- // keeping in mind our anti roll factor
- real_t contactDamping = s->get_step() / p_rollInfluence;
#define ONLY_USE_LINEAR_MASS
#ifdef ONLY_USE_LINEAR_MASS
real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass);
@@ -672,13 +675,8 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) {
m_forwardImpulse.resize(numWheel);
m_sideImpulse.resize(numWheel);
- int numWheelsOnGround = 0;
-
//collapse all those loops into one!
for (int i = 0; i < wheels.size(); i++) {
- VehicleWheel &wheelInfo = *wheels[i];
- if (wheelInfo.m_raycastInfo.m_isInContact)
- numWheelsOnGround++;
m_sideImpulse[i] = real_t(0.);
m_forwardImpulse[i] = real_t(0.);
}
@@ -929,7 +927,7 @@ void VehicleBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_steering"), &VehicleBody::get_steering);
ADD_GROUP("Motion", "");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01"), "set_engine_force", "get_engine_force");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering");
}
diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp
index 13700e0bd3..670df5cc7f 100644
--- a/scene/3d/voxel_light_baker.cpp
+++ b/scene/3d/voxel_light_baker.cpp
@@ -2316,13 +2316,10 @@ Ref<MultiMesh> VoxelLightBaker::create_debug_multimesh(DebugMode p_mode) {
PoolVector<Vector3> vertices;
PoolVector<Color> colors;
-
- int vtx_idx = 0;
#define ADD_VTX(m_idx) \
; \
vertices.push_back(face_points[m_idx]); \
- colors.push_back(Color(1, 1, 1, 1)); \
- vtx_idx++;
+ colors.push_back(Color(1, 1, 1, 1));
for (int i = 0; i < 6; i++) {
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
new file mode 100644
index 0000000000..d3d2870c3f
--- /dev/null
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -0,0 +1,294 @@
+#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..774894ef4b
--- /dev/null
+++ b/scene/animation/animation_blend_space_1d.h
@@ -0,0 +1,71 @@
+#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_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
new file mode 100644
index 0000000000..82db647124
--- /dev/null
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -0,0 +1,567 @@
+#include "animation_blend_space_2d.h"
+#include "math/delaunay.h"
+
+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());
+ 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];
+ }
+ for (int i = 0; i < triangles.size(); i++) {
+ for (int j = 0; j < 3; j++) {
+ if (triangles[i].points[j] >= p_at_index) {
+ triangles[i].points[j]++;
+ }
+ }
+ }
+ }
+ 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++;
+
+ if (auto_triangles) {
+ trianges_dirty = true;
+ }
+}
+
+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 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_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());
+}
+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> 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 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_tree(NULL);
+
+ for (int i = 0; i < triangles.size(); i++) {
+ bool erase = false;
+ for (int j = 0; j < 3; j++) {
+ if (triangles[i].points[j] == p_point) {
+ erase = true;
+ break;
+ } else if (triangles[i].points[j] > p_point) {
+ triangles[i].points[j]--;
+ }
+ }
+ if (erase) {
+ triangles.remove(i);
+
+ i--;
+ }
+ }
+
+ for (int i = p_point; i < blend_points_used - 1; i++) {
+ blend_points[i] = blend_points[i + 1];
+ }
+ blend_points_used--;
+}
+
+int AnimationNodeBlendSpace2D::get_blend_point_count() const {
+
+ return blend_points_used;
+}
+
+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);
+ ERR_FAIL_INDEX_V(p_z, blend_points_used, false);
+
+ BlendTriangle t;
+ t.points[0] = p_x;
+ t.points[1] = p_y;
+ t.points[2] = p_z;
+
+ SortArray<int> sort;
+ sort.sort(t.points, 3);
+
+ for (int i = 0; i < triangles.size(); i++) {
+ bool all_equal = true;
+ for (int j = 0; j < 3; j++) {
+ if (triangles[i].points[j] != t.points[j]) {
+ all_equal = false;
+ break;
+ }
+ }
+ if (all_equal)
+ return true;
+ }
+
+ return false;
+}
+
+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);
+ ERR_FAIL_INDEX(p_z, blend_points_used);
+
+ _update_triangles();
+
+ BlendTriangle t;
+ t.points[0] = p_x;
+ t.points[1] = p_y;
+ t.points[2] = p_z;
+
+ SortArray<int> sort;
+ sort.sort(t.points, 3);
+
+ for (int i = 0; i < triangles.size(); i++) {
+ bool all_equal = true;
+ for (int j = 0; j < 3; j++) {
+ if (triangles[i].points[j] != t.points[j]) {
+ all_equal = false;
+ break;
+ }
+ }
+ ERR_FAIL_COND(all_equal);
+ }
+
+ if (p_at_index == -1 || p_at_index == triangles.size()) {
+ triangles.push_back(t);
+ } else {
+ triangles.insert(p_at_index, t);
+ }
+}
+int AnimationNodeBlendSpace2D::get_triangle_point(int p_triangle, int p_point) {
+
+ _update_triangles();
+
+ ERR_FAIL_INDEX_V(p_point, 3, -1);
+ ERR_FAIL_INDEX_V(p_triangle, triangles.size(), -1);
+ return triangles[p_triangle].points[p_point];
+}
+void AnimationNodeBlendSpace2D::remove_triangle(int p_triangle) {
+ ERR_FAIL_INDEX(p_triangle, triangles.size());
+
+ triangles.remove(p_triangle);
+}
+
+int AnimationNodeBlendSpace2D::get_triangle_count() const {
+ return triangles.size();
+}
+
+void AnimationNodeBlendSpace2D::set_min_space(const Vector2 &p_min) {
+
+ min_space = p_min;
+ if (min_space.x >= max_space.x) {
+ min_space.x = max_space.x - 1;
+ }
+ if (min_space.y >= max_space.y) {
+ min_space.y = max_space.y - 1;
+ }
+}
+Vector2 AnimationNodeBlendSpace2D::get_min_space() const {
+ return min_space;
+}
+
+void AnimationNodeBlendSpace2D::set_max_space(const Vector2 &p_max) {
+
+ max_space = p_max;
+ if (max_space.x <= min_space.x) {
+ max_space.x = min_space.x + 1;
+ }
+ if (max_space.y <= min_space.y) {
+ max_space.y = min_space.y + 1;
+ }
+}
+Vector2 AnimationNodeBlendSpace2D::get_max_space() const {
+ return max_space;
+}
+
+void AnimationNodeBlendSpace2D::set_snap(const Vector2 &p_snap) {
+ snap = p_snap;
+}
+Vector2 AnimationNodeBlendSpace2D::get_snap() const {
+ return snap;
+}
+
+void AnimationNodeBlendSpace2D::set_blend_position(const Vector2 &p_pos) {
+ blend_pos = p_pos;
+}
+Vector2 AnimationNodeBlendSpace2D::get_blend_position() const {
+ return blend_pos;
+}
+
+void AnimationNodeBlendSpace2D::set_x_label(const String &p_label) {
+ x_label = p_label;
+}
+String AnimationNodeBlendSpace2D::get_x_label() const {
+ return x_label;
+}
+
+void AnimationNodeBlendSpace2D::set_y_label(const String &p_label) {
+ y_label = p_label;
+}
+String AnimationNodeBlendSpace2D::get_y_label() const {
+ return y_label;
+}
+
+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 {
+ set_blend_point_node(p_index, p_node);
+ }
+}
+
+void AnimationNodeBlendSpace2D::_set_triangles(const Vector<int> &p_triangles) {
+
+ if (auto_triangles)
+ return;
+ ERR_FAIL_COND(p_triangles.size() % 3 != 0);
+ for (int i = 0; i < p_triangles.size(); i += 3) {
+ add_triangle(p_triangles[i + 0], p_triangles[i + 1], p_triangles[i + 2]);
+ }
+}
+
+Vector<int> AnimationNodeBlendSpace2D::_get_triangles() const {
+
+ Vector<int> t;
+ if (auto_triangles && trianges_dirty)
+ return t;
+
+ t.resize(triangles.size() * 3);
+ for (int i = 0; i < triangles.size(); i++) {
+ t[i * 3 + 0] = triangles[i].points[0];
+ t[i * 3 + 1] = triangles[i].points[1];
+ t[i * 3 + 2] = triangles[i].points[2];
+ }
+ return t;
+}
+
+void AnimationNodeBlendSpace2D::_update_triangles() {
+
+ if (!auto_triangles || !trianges_dirty)
+ return;
+
+ trianges_dirty = false;
+ triangles.clear();
+ if (blend_points_used < 3)
+ return;
+
+ Vector<Vector2> points;
+ points.resize(blend_points_used);
+ for (int i = 0; i < blend_points_used; i++) {
+ points[i] = blend_points[i].position;
+ }
+
+ Vector<Delaunay2D::Triangle> triangles = Delaunay2D::triangulate(points);
+
+ for (int i = 0; i < triangles.size(); i++) {
+ add_triangle(triangles[i].points[0], triangles[i].points[1], triangles[i].points[2]);
+ }
+}
+
+Vector2 AnimationNodeBlendSpace2D::get_closest_point(const Vector2 &p_point) {
+
+ _update_triangles();
+
+ if (triangles.size() == 0)
+ return Vector2();
+
+ Vector2 best_point;
+ bool first = true;
+
+ for (int i = 0; i < triangles.size(); i++) {
+ Vector2 points[3];
+ for (int j = 0; j < 3; j++) {
+ points[j] = get_blend_point_position(get_triangle_point(i, j));
+ }
+
+ if (Geometry::is_point_in_triangle(p_point, points[0], points[1], points[2])) {
+
+ return p_point;
+ }
+
+ for (int j = 0; j < 3; j++) {
+ Vector2 s[2] = {
+ points[j],
+ points[(j + 1) % 3]
+ };
+ Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, s);
+ if (first || closest.distance_to(p_point) < best_point.distance_to(p_point)) {
+ best_point = closest;
+ first = false;
+ }
+ }
+ }
+
+ return best_point;
+}
+
+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;
+ r_weights[1] = 0;
+ r_weights[2] = 0;
+ return;
+ }
+ if (p_pos.distance_squared_to(p_points[1]) < CMP_EPSILON2) {
+ r_weights[0] = 0;
+ r_weights[1] = 1;
+ r_weights[2] = 0;
+ return;
+ }
+ if (p_pos.distance_squared_to(p_points[2]) < CMP_EPSILON2) {
+ r_weights[0] = 0;
+ r_weights[1] = 0;
+ r_weights[2] = 1;
+ return;
+ }
+
+ Vector2 v0 = p_points[1] - p_points[0];
+ Vector2 v1 = p_points[2] - p_points[0];
+ Vector2 v2 = p_pos - p_points[0];
+
+ float d00 = v0.dot(v0);
+ float d01 = v0.dot(v1);
+ float d11 = v1.dot(v1);
+ float d20 = v2.dot(v0);
+ float d21 = v2.dot(v1);
+ float denom = (d00 * d11 - d01 * d01);
+ if (denom == 0) {
+ r_weights[0] = 1;
+ r_weights[1] = 0;
+ r_weights[2] = 0;
+ return;
+ }
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+
+ r_weights[0] = u;
+ r_weights[1] = v;
+ r_weights[2] = w;
+}
+
+float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) {
+
+ _update_triangles();
+
+ if (triangles.size() == 0)
+ return 0;
+
+ Vector2 best_point;
+ bool first = true;
+ int blend_triangle = -1;
+ float blend_weights[3] = { 0, 0, 0 };
+
+ for (int i = 0; i < triangles.size(); i++) {
+ Vector2 points[3];
+ for (int j = 0; j < 3; j++) {
+ points[j] = get_blend_point_position(get_triangle_point(i, j));
+ }
+
+ if (Geometry::is_point_in_triangle(blend_pos, points[0], points[1], points[2])) {
+
+ blend_triangle = i;
+ _blend_triangle(blend_pos, points, blend_weights);
+ break;
+ }
+
+ for (int j = 0; j < 3; j++) {
+ Vector2 s[2] = {
+ points[j],
+ points[(j + 1) % 3]
+ };
+ Vector2 closest = Geometry::get_closest_point_to_segment_2d(blend_pos, s);
+ if (first || closest.distance_to(blend_pos) < best_point.distance_to(blend_pos)) {
+ best_point = closest;
+ blend_triangle = i;
+ first = false;
+ float d = s[0].distance_to(s[1]);
+ if (d == 0.0) {
+ blend_weights[j] = 1.0;
+ blend_weights[(j + 1) % 3] = 0.0;
+ blend_weights[(j + 2) % 3] = 0.0;
+ } else {
+ float c = s[0].distance_to(closest) / d;
+
+ blend_weights[j] = 1.0 - c;
+ blend_weights[(j + 1) % 3] = c;
+ blend_weights[(j + 2) % 3] = 0.0;
+ }
+ }
+ }
+ }
+
+ ERR_FAIL_COND_V(blend_triangle == -1, 0); //should never reach here
+
+ int triangle_points[3];
+ for (int j = 0; j < 3; j++) {
+ triangle_points[j] = get_triangle_point(blend_triangle, j);
+ }
+
+ first = true;
+ float mind;
+ for (int i = 0; i < blend_points_used; i++) {
+
+ bool found = false;
+ for (int j = 0; j < 3; j++) {
+ if (i == triangle_points[j]) {
+ //blend with the given weight
+ float t = blend_node(blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
+ if (first || t < mind) {
+ mind = t;
+ first = false;
+ }
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ //ignore
+ blend_node(blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false);
+ }
+ }
+ return mind;
+}
+
+String AnimationNodeBlendSpace2D::get_caption() const {
+ return "BlendSpace2D";
+}
+
+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();
+ if (idx >= blend_points_used) {
+ property.usage = 0;
+ }
+ }
+ AnimationRootNode::_validate_property(property);
+}
+
+void AnimationNodeBlendSpace2D::set_auto_triangles(bool p_enable) {
+ auto_triangles = p_enable;
+ if (auto_triangles) {
+ trianges_dirty = true;
+ }
+}
+
+bool AnimationNodeBlendSpace2D::get_auto_triangles() const {
+ return auto_triangles;
+}
+
+void AnimationNodeBlendSpace2D::_bind_methods() {
+
+ 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"), &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"), &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"), &AnimationNodeBlendSpace2D::set_max_space);
+ ClassDB::bind_method(D_METHOD("get_max_space"), &AnimationNodeBlendSpace2D::get_max_space);
+
+ 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_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"), &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"), &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"), &AnimationNodeBlendSpace2D::_add_blend_point);
+
+ 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"), &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");
+
+ 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::VECTOR2, "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::POOL_INT_ARRAY, "triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_triangles", "_get_triangles");
+
+ 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_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");
+}
+
+AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() {
+
+ auto_triangles = true;
+ blend_points_used = 0;
+ max_space = Vector2(1, 1);
+ min_space = Vector2(-1, -1);
+ snap = Vector2(0.1, 0.1);
+ x_label = "x";
+ y_label = "y";
+ trianges_dirty = false;
+}
+
+AnimationNodeBlendSpace2D::~AnimationNodeBlendSpace2D() {
+
+ 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_2d.h b/scene/animation/animation_blend_space_2d.h
new file mode 100644
index 0000000000..4778299df1
--- /dev/null
+++ b/scene/animation/animation_blend_space_2d.h
@@ -0,0 +1,97 @@
+#ifndef ANIMATION_BLEND_SPACE_2D_H
+#define ANIMATION_BLEND_SPACE_2D_H
+
+#include "scene/animation/animation_tree.h"
+
+class AnimationNodeBlendSpace2D : public AnimationRootNode {
+ GDCLASS(AnimationNodeBlendSpace2D, AnimationRootNode)
+
+ enum {
+ MAX_BLEND_POINTS = 64
+ };
+
+ struct BlendPoint {
+ Ref<AnimationRootNode> node;
+ Vector2 position;
+ };
+
+ BlendPoint blend_points[MAX_BLEND_POINTS];
+ int blend_points_used;
+
+ struct BlendTriangle {
+ int points[3];
+ };
+
+ Vector<BlendTriangle> triangles;
+
+ Vector2 blend_pos;
+ Vector2 max_space;
+ Vector2 min_space;
+ Vector2 snap;
+ String x_label;
+ String y_label;
+
+ void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
+ void _set_triangles(const Vector<int> &p_triangles);
+ Vector<int> _get_triangles() const;
+
+ void _blend_triangle(const Vector2 &p_pos, const Vector2 *p_points, float *r_weights);
+
+ bool auto_triangles;
+ bool trianges_dirty;
+
+ void _update_triangles();
+
+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, 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);
+ Vector2 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;
+
+ bool has_triangle(int p_x, int p_y, int p_z) const;
+ void add_triangle(int p_x, int p_y, int p_z, int p_at_index = -1);
+ int get_triangle_point(int p_triangle, int p_point);
+ void remove_triangle(int p_triangle);
+ int get_triangle_count() const;
+
+ void set_min_space(const Vector2 &p_min);
+ Vector2 get_min_space() const;
+
+ void set_max_space(const Vector2 &p_max);
+ Vector2 get_max_space() const;
+
+ void set_snap(const Vector2 &p_snap);
+ Vector2 get_snap() 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;
+
+ void set_y_label(const String &p_label);
+ String get_y_label() 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;
+
+ AnimationNodeBlendSpace2D();
+ ~AnimationNodeBlendSpace2D();
+};
+
+#endif // ANIMATION_BLEND_SPACE_2D_H
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
new file mode 100644
index 0000000000..65904410d3
--- /dev/null
+++ b/scene/animation/animation_blend_tree.cpp
@@ -0,0 +1,1170 @@
+#include "animation_blend_tree.h"
+#include "scene/scene_string_names.h"
+
+void AnimationNodeAnimation::set_animation(const StringName &p_name) {
+ animation = p_name;
+}
+
+StringName AnimationNodeAnimation::get_animation() const {
+ return animation;
+}
+
+float AnimationNodeAnimation::get_playback_time() const {
+ return time;
+}
+
+void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const {
+
+ if (property.name == "animation") {
+ 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) {
+ List<StringName> names;
+ ap->get_animation_list(&names);
+ String anims;
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ if (E != names.front()) {
+ anims += ",";
+ }
+ anims += String(E->get());
+ }
+ if (anims != String()) {
+ property.hint = PROPERTY_HINT_ENUM;
+ property.hint_string = anims;
+ }
+ }
+ }
+ }
+
+ AnimationRootNode::_validate_property(property);
+}
+
+float AnimationNodeAnimation::process(float p_time, bool p_seek) {
+
+ AnimationPlayer *ap = get_player();
+ ERR_FAIL_COND_V(!ap, 0);
+
+ Ref<Animation> anim = ap->get_animation(animation);
+ if (!anim.is_valid()) {
+
+ Ref<AnimationNodeBlendTree> tree = get_parent();
+ if (tree.is_valid()) {
+ String name = tree->get_node_name(Ref<AnimationNodeAnimation>(this));
+ make_invalid(vformat(RTR("On BlendTree node '%s', animation not found: '%s'"), name, animation));
+
+ } else {
+ make_invalid(vformat(RTR("Animation not found: '%s'"), animation));
+ }
+
+ return 0;
+ }
+
+ if (p_seek) {
+ time = p_time;
+ step = 0;
+ } else {
+ time = MAX(0, time + p_time);
+ step = p_time;
+ }
+
+ float anim_size = anim->get_length();
+
+ if (anim->has_loop()) {
+
+ if (anim_size) {
+ time = Math::fposmod(time, anim_size);
+ }
+
+ } else if (time > anim_size) {
+
+ time = anim_size;
+ }
+
+ blend_animation(animation, time, step, p_seek, 1.0);
+
+ return anim_size - time;
+}
+
+String AnimationNodeAnimation::get_caption() const {
+ return "Animation";
+}
+
+void AnimationNodeAnimation::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimationNodeAnimation::set_animation);
+ ClassDB::bind_method(D_METHOD("get_animation"), &AnimationNodeAnimation::get_animation);
+
+ ClassDB::bind_method(D_METHOD("get_playback_time"), &AnimationNodeAnimation::get_playback_time);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation");
+}
+
+AnimationNodeAnimation::AnimationNodeAnimation() {
+ last_version = 0;
+ skip = false;
+ time = 0;
+ step = 0;
+}
+
+////////////////////////////////////////////////////////
+
+void AnimationNodeOneShot::set_fadein_time(float p_time) {
+
+ fade_in = p_time;
+}
+
+void AnimationNodeOneShot::set_fadeout_time(float p_time) {
+
+ fade_out = p_time;
+}
+
+float AnimationNodeOneShot::get_fadein_time() const {
+
+ return fade_in;
+}
+float AnimationNodeOneShot::get_fadeout_time() const {
+
+ return fade_out;
+}
+
+void AnimationNodeOneShot::set_autorestart(bool p_active) {
+
+ autorestart = p_active;
+}
+void AnimationNodeOneShot::set_autorestart_delay(float p_time) {
+
+ autorestart_delay = p_time;
+}
+void AnimationNodeOneShot::set_autorestart_random_delay(float p_time) {
+
+ autorestart_random_delay = p_time;
+}
+
+bool AnimationNodeOneShot::has_autorestart() const {
+
+ return autorestart;
+}
+float AnimationNodeOneShot::get_autorestart_delay() const {
+
+ return autorestart_delay;
+}
+float AnimationNodeOneShot::get_autorestart_random_delay() const {
+
+ return autorestart_random_delay;
+}
+
+void AnimationNodeOneShot::set_mix_mode(MixMode p_mix) {
+
+ mix = p_mix;
+}
+AnimationNodeOneShot::MixMode AnimationNodeOneShot::get_mix_mode() const {
+
+ return mix;
+}
+
+void AnimationNodeOneShot::start() {
+ active = true;
+ do_start = true;
+}
+void AnimationNodeOneShot::stop() {
+ active = false;
+}
+bool AnimationNodeOneShot::is_active() const {
+
+ return active;
+}
+
+String AnimationNodeOneShot::get_caption() const {
+ return "OneShot";
+}
+
+bool AnimationNodeOneShot::has_filter() const {
+ return true;
+}
+
+float AnimationNodeOneShot::process(float p_time, bool p_seek) {
+
+ if (!active) {
+ //make it as if this node doesn't exist, pass input 0 by.
+ return blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
+ }
+
+ bool os_seek = p_seek;
+
+ if (p_seek)
+ time = p_time;
+ if (do_start) {
+ time = 0;
+ os_seek = true;
+ }
+
+ float blend;
+
+ if (time < fade_in) {
+
+ if (fade_in > 0)
+ blend = time / fade_in;
+ else
+ blend = 0; //wtf
+
+ } else if (!do_start && remaining < fade_out) {
+
+ if (fade_out)
+ blend = (remaining / fade_out);
+ else
+ blend = 1.0;
+ } else
+ blend = 1.0;
+
+ float main_rem;
+ if (mix == MIX_MODE_ADD) {
+ main_rem = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
+ } else {
+ main_rem = blend_input(0, p_time, p_seek, 1.0 - blend, FILTER_BLEND, !sync);
+ }
+
+ float os_rem = blend_input(1, os_seek ? time : p_time, os_seek, blend, FILTER_PASS, false);
+
+ if (do_start) {
+ remaining = os_rem;
+ do_start = false;
+ }
+
+ if (!p_seek) {
+ time += p_time;
+ remaining = os_rem;
+ if (remaining <= 0)
+ active = false;
+ }
+
+ return MAX(main_rem, remaining);
+}
+void AnimationNodeOneShot::set_use_sync(bool p_sync) {
+
+ sync = p_sync;
+}
+
+bool AnimationNodeOneShot::is_using_sync() const {
+
+ return sync;
+}
+
+void AnimationNodeOneShot::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_fadein_time", "time"), &AnimationNodeOneShot::set_fadein_time);
+ ClassDB::bind_method(D_METHOD("get_fadein_time"), &AnimationNodeOneShot::get_fadein_time);
+
+ ClassDB::bind_method(D_METHOD("set_fadeout_time", "time"), &AnimationNodeOneShot::set_fadeout_time);
+ ClassDB::bind_method(D_METHOD("get_fadeout_time"), &AnimationNodeOneShot::get_fadeout_time);
+
+ ClassDB::bind_method(D_METHOD("set_autorestart", "enable"), &AnimationNodeOneShot::set_autorestart);
+ ClassDB::bind_method(D_METHOD("has_autorestart"), &AnimationNodeOneShot::has_autorestart);
+
+ ClassDB::bind_method(D_METHOD("set_autorestart_delay", "enable"), &AnimationNodeOneShot::set_autorestart_delay);
+ ClassDB::bind_method(D_METHOD("get_autorestart_delay"), &AnimationNodeOneShot::get_autorestart_delay);
+
+ ClassDB::bind_method(D_METHOD("set_autorestart_random_delay", "enable"), &AnimationNodeOneShot::set_autorestart_random_delay);
+ ClassDB::bind_method(D_METHOD("get_autorestart_random_delay"), &AnimationNodeOneShot::get_autorestart_random_delay);
+
+ ClassDB::bind_method(D_METHOD("set_mix_mode", "mode"), &AnimationNodeOneShot::set_mix_mode);
+ ClassDB::bind_method(D_METHOD("get_mix_mode"), &AnimationNodeOneShot::get_mix_mode);
+
+ ClassDB::bind_method(D_METHOD("start"), &AnimationNodeOneShot::start);
+ ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeOneShot::stop);
+ ClassDB::bind_method(D_METHOD("is_active"), &AnimationNodeOneShot::is_active);
+
+ ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeOneShot::set_use_sync);
+ ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeOneShot::is_using_sync);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadein_time", "get_fadein_time");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadeout_time", "get_fadeout_time");
+
+ ADD_GROUP("autorestart_", "Auto Restart");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autorestart"), "set_autorestart", "has_autorestart");
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "autorestart_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_autorestart_delay", "get_autorestart_delay");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "autorestart_random_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_autorestart_random_delay", "get_autorestart_random_delay");
+
+ ADD_GROUP("", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
+
+ BIND_ENUM_CONSTANT(MIX_MODE_BLEND)
+ BIND_ENUM_CONSTANT(MIX_MODE_ADD)
+}
+
+AnimationNodeOneShot::AnimationNodeOneShot() {
+
+ add_input("in");
+ add_input("shot");
+
+ time = 0;
+ fade_in = 0.1;
+ fade_out = 0.1;
+ autorestart = false;
+ autorestart_delay = 1;
+ autorestart_remaining = 0;
+ mix = MIX_MODE_BLEND;
+ active = false;
+ do_start = false;
+ sync = false;
+}
+
+////////////////////////////////////////////////
+
+void AnimationNodeAdd2::set_amount(float p_amount) {
+ amount = p_amount;
+}
+
+float AnimationNodeAdd2::get_amount() const {
+ return amount;
+}
+
+String AnimationNodeAdd2::get_caption() const {
+ return "Add2";
+}
+void AnimationNodeAdd2::set_use_sync(bool p_sync) {
+
+ sync = p_sync;
+}
+
+bool AnimationNodeAdd2::is_using_sync() const {
+
+ return sync;
+}
+
+bool AnimationNodeAdd2::has_filter() const {
+
+ return true;
+}
+
+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);
+
+ return rem0;
+}
+
+void AnimationNodeAdd2::_bind_methods() {
+
+ 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"), &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");
+}
+
+AnimationNodeAdd2::AnimationNodeAdd2() {
+
+ add_input("in");
+ add_input("add");
+ amount = 0;
+ 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) {
+ amount = p_amount;
+}
+
+float AnimationNodeBlend2::get_amount() const {
+ return amount;
+}
+String AnimationNodeBlend2::get_caption() const {
+ return "Blend2";
+}
+
+float AnimationNodeBlend2::process(float p_time, bool p_seek) {
+
+ float rem0 = blend_input(0, p_time, p_seek, 1.0 - amount, FILTER_BLEND, !sync);
+ float rem1 = blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync);
+
+ return amount > 0.5 ? rem1 : rem0; //hacky but good enough
+}
+
+void AnimationNodeBlend2::set_use_sync(bool p_sync) {
+
+ sync = p_sync;
+}
+
+bool AnimationNodeBlend2::is_using_sync() const {
+
+ return sync;
+}
+
+bool AnimationNodeBlend2::has_filter() const {
+
+ return true;
+}
+void AnimationNodeBlend2::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeBlend2::set_amount);
+ ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeBlend2::get_amount);
+
+ ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend2::set_use_sync);
+ ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend2::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");
+}
+AnimationNodeBlend2::AnimationNodeBlend2() {
+ add_input("in");
+ add_input("blend");
+ sync = false;
+
+ amount = 0;
+}
+
+//////////////////////////////////////
+
+void AnimationNodeBlend3::set_amount(float p_amount) {
+ amount = p_amount;
+}
+
+float AnimationNodeBlend3::get_amount() const {
+ return amount;
+}
+
+String AnimationNodeBlend3::get_caption() const {
+ return "Blend3";
+}
+
+void AnimationNodeBlend3::set_use_sync(bool p_sync) {
+
+ sync = p_sync;
+}
+
+bool AnimationNodeBlend3::is_using_sync() const {
+
+ return sync;
+}
+
+float AnimationNodeBlend3::process(float p_time, bool p_seek) {
+
+ float rem0 = blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_IGNORE, !sync);
+ float rem1 = blend_input(1, p_time, p_seek, 1.0 - ABS(amount), FILTER_IGNORE, !sync);
+ float rem2 = blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_IGNORE, !sync);
+
+ return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); //hacky but good enough
+}
+
+void AnimationNodeBlend3::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeBlend3::set_amount);
+ ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeBlend3::get_amount);
+
+ ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend3::set_use_sync);
+ ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend3::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");
+}
+AnimationNodeBlend3::AnimationNodeBlend3() {
+ add_input("-blend");
+ add_input("in");
+ add_input("+blend");
+ sync = false;
+ amount = 0;
+}
+
+/////////////////////////////////
+
+void AnimationNodeTimeScale::set_scale(float p_scale) {
+ scale = p_scale;
+}
+
+float AnimationNodeTimeScale::get_scale() const {
+ return scale;
+}
+
+String AnimationNodeTimeScale::get_caption() const {
+ return "TimeScale";
+}
+
+float AnimationNodeTimeScale::process(float p_time, bool p_seek) {
+
+ if (p_seek) {
+ return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false);
+ } else {
+ return blend_input(0, p_time * scale, false, 1.0, FILTER_IGNORE, false);
+ }
+}
+
+void AnimationNodeTimeScale::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_scale", "scale"), &AnimationNodeTimeScale::set_scale);
+ ClassDB::bind_method(D_METHOD("get_scale"), &AnimationNodeTimeScale::get_scale);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,32,0.01,or_greater"), "set_scale", "get_scale");
+}
+AnimationNodeTimeScale::AnimationNodeTimeScale() {
+ add_input("in");
+ scale = 1.0;
+}
+
+////////////////////////////////////
+
+void AnimationNodeTimeSeek::set_seek_pos(float p_seek_pos) {
+ seek_pos = p_seek_pos;
+}
+
+float AnimationNodeTimeSeek::get_seek_pos() const {
+ return seek_pos;
+}
+
+String AnimationNodeTimeSeek::get_caption() const {
+ return "Seek";
+}
+
+float AnimationNodeTimeSeek::process(float p_time, bool p_seek) {
+
+ if (p_seek) {
+ return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false);
+ } else if (seek_pos >= 0) {
+ float ret = blend_input(0, seek_pos, true, 1.0, FILTER_IGNORE, false);
+ seek_pos = -1;
+ _change_notify("seek_pos");
+ return ret;
+ } else {
+ return blend_input(0, p_time, false, 1.0, FILTER_IGNORE, false);
+ }
+}
+
+void AnimationNodeTimeSeek::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_seek_pos", "seek_pos"), &AnimationNodeTimeSeek::set_seek_pos);
+ ClassDB::bind_method(D_METHOD("get_seek_pos"), &AnimationNodeTimeSeek::get_seek_pos);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "seek_pos", PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater"), "set_seek_pos", "get_seek_pos");
+}
+AnimationNodeTimeSeek::AnimationNodeTimeSeek() {
+ add_input("in");
+ seek_pos = -1;
+}
+
+/////////////////////////////////////////////////
+
+String AnimationNodeTransition::get_caption() const {
+ return "Transition";
+}
+
+void AnimationNodeTransition::_update_inputs() {
+ while (get_input_count() < enabled_inputs) {
+ add_input(inputs[get_input_count()].name);
+ }
+
+ while (get_input_count() > enabled_inputs) {
+ remove_input(get_input_count() - 1);
+ }
+}
+
+void AnimationNodeTransition::set_enabled_inputs(int p_inputs) {
+ ERR_FAIL_INDEX(p_inputs, MAX_INPUTS);
+ enabled_inputs = p_inputs;
+ _update_inputs();
+}
+
+int AnimationNodeTransition::get_enabled_inputs() {
+ return enabled_inputs;
+}
+
+void AnimationNodeTransition::set_input_as_auto_advance(int p_input, bool p_enable) {
+ ERR_FAIL_INDEX(p_input, MAX_INPUTS);
+ inputs[p_input].auto_advance = p_enable;
+}
+
+bool AnimationNodeTransition::is_input_set_as_auto_advance(int p_input) const {
+ ERR_FAIL_INDEX_V(p_input, MAX_INPUTS, false);
+ return inputs[p_input].auto_advance;
+}
+
+void AnimationNodeTransition::set_input_caption(int p_input, const String &p_name) {
+ ERR_FAIL_INDEX(p_input, MAX_INPUTS);
+ inputs[p_input].name = p_name;
+ set_input_name(p_input, p_name);
+}
+
+String AnimationNodeTransition::get_input_caption(int p_input) const {
+ ERR_FAIL_INDEX_V(p_input, MAX_INPUTS, String());
+ return inputs[p_input].name;
+}
+
+void AnimationNodeTransition::set_current(int p_current) {
+
+ if (current == p_current)
+ return;
+ ERR_FAIL_INDEX(p_current, enabled_inputs);
+
+ Ref<AnimationNodeBlendTree> tree = get_parent();
+
+ if (tree.is_valid() && current >= 0) {
+ prev = current;
+ prev_xfading = xfade;
+ prev_time = time;
+ time = 0;
+ current = p_current;
+ switched = true;
+ _change_notify("current");
+ } else {
+ current = p_current;
+ }
+}
+
+int AnimationNodeTransition::get_current() const {
+ return current;
+}
+void AnimationNodeTransition::set_cross_fade_time(float p_fade) {
+ xfade = p_fade;
+}
+
+float AnimationNodeTransition::get_cross_fade_time() const {
+ return xfade;
+}
+
+float AnimationNodeTransition::process(float p_time, bool p_seek) {
+
+ if (prev < 0) { // process current animation, check for transition
+
+ float rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false);
+
+ if (p_seek)
+ time = p_time;
+ else
+ time += p_time;
+
+ if (inputs[current].auto_advance && rem <= xfade) {
+
+ set_current((current + 1) % enabled_inputs);
+ }
+
+ return rem;
+ } else { // cross-fading from prev to current
+
+ float blend = xfade ? (prev_xfading / xfade) : 1;
+
+ float rem;
+
+ if (!p_seek && switched) { //just switched, seek to start of current
+
+ rem = blend_input(current, 0, true, 1.0 - blend, FILTER_IGNORE, false);
+ } else {
+
+ rem = blend_input(current, p_time, p_seek, 1.0 - blend, FILTER_IGNORE, false);
+ }
+
+ switched = false;
+
+ if (p_seek) { // don't seek prev animation
+ blend_input(prev, 0, false, blend, FILTER_IGNORE, false);
+ time = p_time;
+ } else {
+ blend_input(prev, p_time, false, blend, FILTER_IGNORE, false);
+ time += p_time;
+ prev_xfading -= p_time;
+ if (prev_xfading < 0) {
+ prev = -1;
+ }
+ }
+
+ return rem;
+ }
+}
+
+void AnimationNodeTransition::_validate_property(PropertyInfo &property) const {
+
+ if (property.name == "current" && enabled_inputs > 0) {
+ property.hint = PROPERTY_HINT_ENUM;
+ String anims;
+ for (int i = 0; i < enabled_inputs; i++) {
+ if (i > 0) {
+ anims += ",";
+ }
+ anims += inputs[i].name;
+ }
+ property.hint_string = anims;
+ }
+
+ if (property.name.begins_with("input_")) {
+ String n = property.name.get_slicec('/', 0).get_slicec('_', 1);
+ if (n != "count") {
+ int idx = n.to_int();
+ if (idx >= enabled_inputs) {
+ property.usage = 0;
+ }
+ }
+ }
+
+ AnimationNode::_validate_property(property);
+}
+
+void AnimationNodeTransition::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_enabled_inputs", "amount"), &AnimationNodeTransition::set_enabled_inputs);
+ ClassDB::bind_method(D_METHOD("get_enabled_inputs"), &AnimationNodeTransition::get_enabled_inputs);
+
+ ClassDB::bind_method(D_METHOD("set_input_as_auto_advance", "input", "enable"), &AnimationNodeTransition::set_input_as_auto_advance);
+ ClassDB::bind_method(D_METHOD("is_input_set_as_auto_advance", "input"), &AnimationNodeTransition::is_input_set_as_auto_advance);
+
+ ClassDB::bind_method(D_METHOD("set_input_caption", "input", "caption"), &AnimationNodeTransition::set_input_caption);
+ ClassDB::bind_method(D_METHOD("get_input_caption", "input"), &AnimationNodeTransition::get_input_caption);
+
+ ClassDB::bind_method(D_METHOD("set_current", "index"), &AnimationNodeTransition::set_current);
+ ClassDB::bind_method(D_METHOD("get_current"), &AnimationNodeTransition::get_current);
+
+ ClassDB::bind_method(D_METHOD("set_cross_fade_time", "time"), &AnimationNodeTransition::set_cross_fade_time);
+ ClassDB::bind_method(D_METHOD("get_cross_fade_time"), &AnimationNodeTransition::get_cross_fade_time);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "current", PROPERTY_HINT_RANGE, "0,64,1"), "set_current", "get_current");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01"), "set_cross_fade_time", "get_cross_fade_time");
+
+ for (int i = 0; i < MAX_INPUTS; i++) {
+ ADD_PROPERTYI(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name"), "set_input_caption", "get_input_caption", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/auto_advance"), "set_input_as_auto_advance", "is_input_set_as_auto_advance", i);
+ }
+}
+
+AnimationNodeTransition::AnimationNodeTransition() {
+ enabled_inputs = 0;
+ xfade = 0;
+ current = -1;
+ prev = -1;
+ prev_time = 0;
+ prev_xfading = 0;
+ switched = false;
+ for (int i = 0; i < MAX_INPUTS; i++) {
+ inputs[i].auto_advance = false;
+ inputs[i].name = itos(i + 1);
+ }
+}
+
+/////////////////////
+
+String AnimationNodeOutput::get_caption() const {
+ return "Output";
+}
+
+float AnimationNodeOutput::process(float p_time, bool p_seek) {
+ return blend_input(0, p_time, p_seek, 1.0);
+}
+
+AnimationNodeOutput::AnimationNodeOutput() {
+ add_input("output");
+}
+
+///////////////////////////////////////////////////////
+void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNode> p_node) {
+
+ 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_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_tree(get_tree());
+
+ emit_changed();
+}
+
+Ref<AnimationNode> AnimationNodeBlendTree::get_node(const StringName &p_name) const {
+
+ ERR_FAIL_COND_V(!nodes.has(p_name), Ref<AnimationNode>());
+
+ return nodes[p_name];
+}
+
+StringName AnimationNodeBlendTree::get_node_name(const Ref<AnimationNode> &p_node) const {
+ for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) {
+ if (E->get() == p_node) {
+ return E->key();
+ }
+ }
+
+ ERR_FAIL_V(StringName());
+}
+bool AnimationNodeBlendTree::has_node(const StringName &p_name) const {
+ return nodes.has(p_name);
+}
+void AnimationNodeBlendTree::remove_node(const StringName &p_name) {
+
+ ERR_FAIL_COND(!nodes.has(p_name));
+ ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); //can't delete output
+
+ {
+ //erase node connections
+ Ref<AnimationNode> node = nodes[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);
+ }
+
+ nodes.erase(p_name);
+
+ //erase connections to name
+ for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) {
+ Ref<AnimationNode> node = E->get();
+ for (int i = 0; i < node->get_input_count(); i++) {
+ if (node->get_input_connection(i) == p_name) {
+ node->set_input_connection(i, StringName());
+ }
+ }
+ }
+
+ emit_changed();
+}
+
+void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringName &p_new_name) {
+
+ ERR_FAIL_COND(!nodes.has(p_name));
+ ERR_FAIL_COND(nodes.has(p_new_name));
+ ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output);
+ ERR_FAIL_COND(p_new_name == SceneStringNames::get_singleton()->output);
+
+ nodes[p_new_name] = nodes[p_name];
+ nodes.erase(p_name);
+
+ //rename connections
+ for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) {
+ Ref<AnimationNode> node = E->get();
+ for (int i = 0; i < node->get_input_count(); i++) {
+ if (node->get_input_connection(i) == p_name) {
+ node->set_input_connection(i, p_new_name);
+ }
+ }
+ }
+}
+
+void AnimationNodeBlendTree::connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) {
+
+ ERR_FAIL_COND(!nodes.has(p_output_node));
+ ERR_FAIL_COND(!nodes.has(p_input_node));
+ ERR_FAIL_COND(p_output_node == SceneStringNames::get_singleton()->output);
+ ERR_FAIL_COND(p_input_node == p_output_node);
+
+ Ref<AnimationNode> input = nodes[p_input_node];
+ ERR_FAIL_INDEX(p_input_index, input->get_input_count());
+
+ for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) {
+ Ref<AnimationNode> node = E->get();
+ for (int i = 0; i < node->get_input_count(); i++) {
+ StringName output = node->get_input_connection(i);
+ ERR_FAIL_COND(output == p_output_node);
+ }
+ }
+
+ input->set_input_connection(p_input_index, p_output_node);
+ emit_changed();
+}
+
+void AnimationNodeBlendTree::disconnect_node(const StringName &p_node, int p_input_index) {
+
+ ERR_FAIL_COND(!nodes.has(p_node));
+
+ Ref<AnimationNode> input = nodes[p_node];
+ ERR_FAIL_INDEX(p_input_index, input->get_input_count());
+
+ input->set_input_connection(p_input_index, StringName());
+}
+
+float AnimationNodeBlendTree::get_connection_activity(const StringName &p_input_node, int p_input_index) const {
+
+ ERR_FAIL_COND_V(!nodes.has(p_input_node), 0);
+
+ Ref<AnimationNode> input = nodes[p_input_node];
+ ERR_FAIL_INDEX_V(p_input_index, input->get_input_count(), 0);
+
+ return input->get_input_activity(p_input_index);
+}
+
+AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) const {
+
+ if (!nodes.has(p_output_node) || p_output_node == SceneStringNames::get_singleton()->output) {
+ return CONNECTION_ERROR_NO_OUTPUT;
+ }
+
+ if (!nodes.has(p_input_node)) {
+ return CONNECTION_ERROR_NO_INPUT;
+ }
+
+ if (!nodes.has(p_input_node)) {
+ return CONNECTION_ERROR_SAME_NODE;
+ }
+
+ Ref<AnimationNode> input = nodes[p_input_node];
+
+ if (p_input_index < 0 || p_input_index >= input->get_input_count()) {
+ return CONNECTION_ERROR_NO_INPUT_INDEX;
+ }
+
+ if (input->get_input_connection(p_input_index) != StringName()) {
+ return CONNECTION_ERROR_CONNECTION_EXISTS;
+ }
+
+ for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) {
+ Ref<AnimationNode> node = E->get();
+ for (int i = 0; i < node->get_input_count(); i++) {
+ StringName output = node->get_input_connection(i);
+ if (output == p_output_node) {
+ return CONNECTION_ERROR_CONNECTION_EXISTS;
+ }
+ }
+ }
+ return CONNECTION_OK;
+}
+
+void AnimationNodeBlendTree::get_node_connections(List<NodeConnection> *r_connections) const {
+
+ for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) {
+ Ref<AnimationNode> node = E->get();
+ for (int i = 0; i < node->get_input_count(); i++) {
+ StringName output = node->get_input_connection(i);
+ if (output != StringName()) {
+ NodeConnection nc;
+ nc.input_node = E->key();
+ nc.input_index = i;
+ nc.output_node = output;
+ r_connections->push_back(nc);
+ }
+ }
+ }
+}
+
+String AnimationNodeBlendTree::get_caption() const {
+ return "BlendTree";
+}
+
+float AnimationNodeBlendTree::process(float p_time, bool p_seek) {
+
+ Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output];
+ return blend_node(output, p_time, p_seek, 1.0);
+}
+
+void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) {
+
+ for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) {
+ r_list->push_back(E->key());
+ }
+}
+
+void AnimationNodeBlendTree::set_graph_offset(const Vector2 &p_graph_offset) {
+
+ graph_offset = p_graph_offset;
+}
+
+Vector2 AnimationNodeBlendTree::get_graph_offset() const {
+
+ return graph_offset;
+}
+
+void AnimationNodeBlendTree::set_tree(AnimationTree *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_tree(p_player);
+ }
+}
+
+bool AnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_value) {
+
+ String name = p_name;
+ if (name.begins_with("nodes/")) {
+
+ 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 (nodes.has(node_name)) {
+ nodes[node_name]->set_position(p_value);
+ }
+ return true;
+ }
+ } else if (name == "node_connections") {
+
+ Array conns = p_value;
+ ERR_FAIL_COND_V(conns.size() % 3 != 0, false);
+
+ for (int i = 0; i < conns.size(); i += 3) {
+ connect_node(conns[i], conns[i + 1], conns[i + 2]);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) const {
+
+ String name = p_name;
+ if (name.begins_with("nodes/")) {
+ String node_name = name.get_slicec('/', 1);
+ String what = name.get_slicec('/', 2);
+
+ if (what == "node") {
+ if (nodes.has(node_name)) {
+ r_ret = nodes[node_name];
+ return true;
+ }
+ }
+
+ if (what == "position") {
+
+ if (nodes.has(node_name)) {
+ r_ret = nodes[node_name]->get_position();
+ return true;
+ }
+ }
+ } else if (name == "node_connections") {
+ List<NodeConnection> nc;
+ get_node_connections(&nc);
+ Array conns;
+ conns.resize(nc.size() * 3);
+
+ int idx = 0;
+ for (List<NodeConnection>::Element *E = nc.front(); E; E = E->next()) {
+ conns[idx * 3 + 0] = E->get().input_node;
+ conns[idx * 3 + 1] = E->get().input_index;
+ conns[idx * 3 + 2] = E->get().output_node;
+ idx++;
+ }
+
+ r_ret = conns;
+ return true;
+ }
+
+ return false;
+}
+void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ List<StringName> names;
+ for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.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();
+ if (name != "output") {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+ }
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+}
+
+void AnimationNodeBlendTree::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("add_node", "name", "node"), &AnimationNodeBlendTree::add_node);
+ ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeBlendTree::get_node);
+ ClassDB::bind_method(D_METHOD("remove_node", "name"), &AnimationNodeBlendTree::remove_node);
+ ClassDB::bind_method(D_METHOD("rename_node", "name", "new_name"), &AnimationNodeBlendTree::rename_node);
+ ClassDB::bind_method(D_METHOD("has_node", "name"), &AnimationNodeBlendTree::has_node);
+ ClassDB::bind_method(D_METHOD("connect_node", "input_node", "input_index", "output_node"), &AnimationNodeBlendTree::connect_node);
+ ClassDB::bind_method(D_METHOD("disconnect_node", "input_node", "input_index"), &AnimationNodeBlendTree::disconnect_node);
+
+ ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeBlendTree::set_graph_offset);
+ ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeBlendTree::get_graph_offset);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset");
+
+ BIND_CONSTANT(CONNECTION_OK);
+ BIND_CONSTANT(CONNECTION_ERROR_NO_INPUT);
+ BIND_CONSTANT(CONNECTION_ERROR_NO_INPUT_INDEX);
+ BIND_CONSTANT(CONNECTION_ERROR_NO_OUTPUT);
+ BIND_CONSTANT(CONNECTION_ERROR_SAME_NODE);
+ BIND_CONSTANT(CONNECTION_ERROR_CONNECTION_EXISTS);
+}
+
+AnimationNodeBlendTree::AnimationNodeBlendTree() {
+
+ Ref<AnimationNodeOutput> output;
+ output.instance();
+ output->set_position(Vector2(300, 150));
+ output->set_parent(this);
+ nodes["output"] = output;
+}
+
+AnimationNodeBlendTree::~AnimationNodeBlendTree() {
+
+ for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) {
+ E->get()->set_parent(NULL);
+ E->get()->set_tree(NULL);
+ }
+}
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
new file mode 100644
index 0000000000..e86cc2e823
--- /dev/null
+++ b/scene/animation/animation_blend_tree.h
@@ -0,0 +1,352 @@
+#ifndef ANIMATION_BLEND_TREE_H
+#define ANIMATION_BLEND_TREE_H
+
+#include "scene/animation/animation_tree.h"
+
+class AnimationNodeAnimation : public AnimationRootNode {
+
+ GDCLASS(AnimationNodeAnimation, AnimationRootNode);
+
+ StringName animation;
+
+ uint64_t last_version;
+ float time;
+ float step;
+ bool skip;
+
+protected:
+ void _validate_property(PropertyInfo &property) const;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+ virtual float process(float p_time, bool p_seek);
+
+ void set_animation(const StringName &p_name);
+ StringName get_animation() const;
+
+ float get_playback_time() const;
+
+ AnimationNodeAnimation();
+};
+
+class AnimationNodeOneShot : public AnimationNode {
+ GDCLASS(AnimationNodeOneShot, AnimationNode);
+
+public:
+ enum MixMode {
+ MIX_MODE_BLEND,
+ MIX_MODE_ADD
+ };
+
+private:
+ bool active;
+ bool do_start;
+ float fade_in;
+ float fade_out;
+
+ bool autorestart;
+ float autorestart_delay;
+ float autorestart_random_delay;
+ MixMode mix;
+
+ float time;
+ float remaining;
+ float autorestart_remaining;
+ bool sync;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ void set_fadein_time(float p_time);
+ void set_fadeout_time(float p_time);
+
+ float get_fadein_time() const;
+ float get_fadeout_time() const;
+
+ void set_autorestart(bool p_active);
+ void set_autorestart_delay(float p_time);
+ void set_autorestart_random_delay(float p_time);
+
+ bool has_autorestart() const;
+ float get_autorestart_delay() const;
+ float get_autorestart_random_delay() const;
+
+ void set_mix_mode(MixMode p_mix);
+ MixMode get_mix_mode() const;
+
+ void start();
+ void stop();
+ bool is_active() 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);
+
+ AnimationNodeOneShot();
+};
+
+VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode)
+
+class AnimationNodeAdd2 : public AnimationNode {
+ GDCLASS(AnimationNodeAdd2, 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);
+
+ 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 {
+ GDCLASS(AnimationNodeBlend2, AnimationNode);
+
+ float amount;
+ bool sync;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+ virtual float process(float p_time, bool p_seek);
+
+ 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;
+ AnimationNodeBlend2();
+};
+
+class AnimationNodeBlend3 : public AnimationNode {
+ GDCLASS(AnimationNodeBlend3, 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;
+
+ float process(float p_time, bool p_seek);
+ AnimationNodeBlend3();
+};
+
+class AnimationNodeTimeScale : public AnimationNode {
+ GDCLASS(AnimationNodeTimeScale, AnimationNode);
+
+ float scale;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ void set_scale(float p_scale);
+ float get_scale() const;
+
+ float process(float p_time, bool p_seek);
+
+ AnimationNodeTimeScale();
+};
+
+class AnimationNodeTimeSeek : public AnimationNode {
+ GDCLASS(AnimationNodeTimeSeek, AnimationNode);
+
+ float seek_pos;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ void set_seek_pos(float p_sec);
+ float get_seek_pos() const;
+
+ float process(float p_time, bool p_seek);
+
+ AnimationNodeTimeSeek();
+};
+
+class AnimationNodeTransition : public AnimationNode {
+ GDCLASS(AnimationNodeTransition, AnimationNode);
+
+ enum {
+ MAX_INPUTS = 32
+ };
+ struct InputData {
+
+ String name;
+ bool auto_advance;
+ InputData() { auto_advance = false; }
+ };
+
+ InputData inputs[MAX_INPUTS];
+ int enabled_inputs;
+
+ float prev_time;
+ float prev_xfading;
+ int prev;
+ bool switched;
+
+ float time;
+ int current;
+
+ float xfade;
+
+ void _update_inputs();
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const;
+
+public:
+ virtual String get_caption() const;
+
+ void set_enabled_inputs(int p_inputs);
+ int get_enabled_inputs();
+
+ void set_input_as_auto_advance(int p_input, bool p_enable);
+ bool is_input_set_as_auto_advance(int p_input) const;
+
+ void set_input_caption(int p_input, const String &p_name);
+ String get_input_caption(int p_input) const;
+
+ void set_current(int p_current);
+ int get_current() const;
+
+ void set_cross_fade_time(float p_fade);
+ float get_cross_fade_time() const;
+
+ float process(float p_time, bool p_seek);
+
+ AnimationNodeTransition();
+};
+
+class AnimationNodeOutput : public AnimationNode {
+ GDCLASS(AnimationNodeOutput, AnimationNode)
+public:
+ virtual String get_caption() const;
+ virtual float process(float p_time, bool p_seek);
+ AnimationNodeOutput();
+};
+
+/////
+
+class AnimationNodeBlendTree : public AnimationRootNode {
+ GDCLASS(AnimationNodeBlendTree, AnimationRootNode)
+
+ Map<StringName, Ref<AnimationNode> > nodes;
+
+ Vector2 graph_offset;
+
+protected:
+ 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 ConnectionError {
+ CONNECTION_OK,
+ CONNECTION_ERROR_NO_INPUT,
+ CONNECTION_ERROR_NO_INPUT_INDEX,
+ CONNECTION_ERROR_NO_OUTPUT,
+ CONNECTION_ERROR_SAME_NODE,
+ CONNECTION_ERROR_CONNECTION_EXISTS,
+ //no need to check for cycles due to tree topology
+ };
+
+ 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 connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node);
+ void disconnect_node(const StringName &p_node, int p_input_index);
+ float get_connection_activity(const StringName &p_input_node, int p_input_index) const;
+
+ struct NodeConnection {
+ StringName input_node;
+ int input_index;
+ StringName output_node;
+ };
+
+ ConnectionError can_connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) const;
+ void get_node_connections(List<NodeConnection> *r_connections) const;
+
+ virtual String get_caption() const;
+ virtual float process(float p_time, bool p_seek);
+
+ void get_node_list(List<StringName> *r_list);
+
+ void set_graph_offset(const Vector2 &p_graph_offset);
+ Vector2 get_graph_offset() const;
+
+ virtual void set_tree(AnimationTree *p_player);
+ AnimationNodeBlendTree();
+ ~AnimationNodeBlendTree();
+};
+
+VARIANT_ENUM_CAST(AnimationNodeBlendTree::ConnectionError)
+
+#endif // ANIMATION_BLEND_TREE_H
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 c0d1e62e07..eac2c8d0c1 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -33,7 +33,7 @@
#include "engine.h"
#include "message_queue.h"
#include "scene/scene_string_names.h"
-
+#include "servers/audio/audio_stream.h"
#ifdef TOOLS_ENABLED
void AnimatedValuesBackup::update_skeletons() {
@@ -325,10 +325,27 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) {
p_anim->node_cache[i]->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa;
}
}
+
+ if (a->track_get_type(i) == Animation::TYPE_BEZIER && leftover_path.size()) {
+
+ if (!p_anim->node_cache[i]->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
+
+ TrackNodeCache::BezierAnim ba;
+ String path = leftover_path[leftover_path.size() - 1];
+ Vector<String> index = path.split(".");
+ for (int j = 0; j < index.size(); j++) {
+ ba.bezier_property.push_back(index[j]);
+ }
+ ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
+ ba.owner = p_anim->node_cache[i];
+
+ p_anim->node_cache[i]->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba;
+ }
+ }
}
}
-void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float p_time, float p_delta, float p_interp, bool p_allow_discrete) {
+void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float p_time, float p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started) {
_ensure_node_caches(p_anim);
ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count());
@@ -394,7 +411,51 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
TrackNodeCache::PropertyAnim *pa = &E->get();
- if (a->value_track_get_update_mode(i) == Animation::UPDATE_CONTINUOUS || (p_delta == 0 && a->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE)) { //delta == 0 means seek
+ Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
+
+ if (update_mode == Animation::UPDATE_CAPTURE) {
+
+ if (p_started) {
+ pa->capture = pa->object->get_indexed(pa->subpath);
+ }
+
+ 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 = 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);
+
+ if (pa->accum_pass != accum_pass) {
+ ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX);
+ cache_update_prop[cache_update_prop_size++] = pa;
+ pa->value_accum = interp_value;
+ pa->accum_pass = accum_pass;
+ } else {
+ Variant::interpolate(pa->value_accum, interp_value, p_interp, pa->value_accum);
+ }
+
+ continue; //handled
+ }
+ }
+
+ if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE || (p_delta == 0 && update_mode == Animation::UPDATE_DISCRETE)) { //delta == 0 means seek
Variant value = a->value_track_interpolate(i, p_time);
@@ -415,7 +476,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
Variant::interpolate(pa->value_accum, value, p_interp, pa->value_accum);
}
- } else if (p_allow_discrete && p_delta != 0) {
+ } else if (p_is_current && p_delta != 0) {
List<int> indices;
a->value_track_get_key_indices(i, p_time, p_delta, &indices);
@@ -470,9 +531,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
if (!nc->node)
continue;
- if (p_delta == 0)
+ if (p_delta == 0) {
continue;
- if (!p_allow_discrete)
+ }
+ if (!p_is_current)
break;
List<int> indices;
@@ -500,14 +562,197 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
}
} break;
+ case Animation::TYPE_BEZIER: {
+
+ if (!nc->node)
+ continue;
+
+ Map<StringName, TrackNodeCache::BezierAnim>::Element *E = nc->bezier_anim.find(a->track_get_path(i).get_concatenated_subnames());
+ ERR_CONTINUE(!E); //should it continue, or create a new one?
+
+ TrackNodeCache::BezierAnim *ba = &E->get();
+
+ float bezier = a->bezier_track_interpolate(i, p_time);
+ if (ba->accum_pass != accum_pass) {
+ ERR_CONTINUE(cache_update_bezier_size >= NODE_CACHE_UPDATE_MAX);
+ cache_update_bezier[cache_update_bezier_size++] = ba;
+ ba->bezier_accum = bezier;
+ ba->accum_pass = accum_pass;
+ } else {
+ ba->bezier_accum = Math::lerp(ba->bezier_accum, bezier, p_interp);
+ }
+
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ if (!nc->node)
+ continue;
+ if (p_delta == 0) {
+ continue;
+ }
+
+ if (p_seeked) {
+ //find whathever should be playing
+ int idx = a->track_find_key(i, p_time);
+ if (idx < 0)
+ continue;
+
+ Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
+ if (!stream.is_valid()) {
+ nc->node->call("stop");
+ nc->audio_playing = false;
+ playing_caches.erase(nc);
+ } else {
+ float start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ start_ofs += p_time - a->track_get_key_time(i, idx);
+ float end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ float len = stream->get_length();
+
+ if (start_ofs > len - end_ofs) {
+ nc->node->call("stop");
+ nc->audio_playing = false;
+ playing_caches.erase(nc);
+ continue;
+ }
+
+ nc->node->call("set_stream", stream);
+ nc->node->call("play", start_ofs);
+
+ nc->audio_playing = true;
+ playing_caches.insert(nc);
+ if (len && end_ofs > 0) { //force a end at a time
+ nc->audio_len = len - start_ofs - end_ofs;
+ } else {
+ nc->audio_len = 0;
+ }
+
+ nc->audio_start = p_time;
+ }
+
+ } else {
+ //find stuff to play
+ List<int> to_play;
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play);
+ if (to_play.size()) {
+ int idx = to_play.back()->get();
+
+ Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
+ if (!stream.is_valid()) {
+ nc->node->call("stop");
+ nc->audio_playing = false;
+ playing_caches.erase(nc);
+ } else {
+ float start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ float end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ float len = stream->get_length();
+
+ nc->node->call("set_stream", stream);
+ nc->node->call("play", start_ofs);
+
+ nc->audio_playing = true;
+ playing_caches.insert(nc);
+ if (len && end_ofs > 0) { //force a end at a time
+ nc->audio_len = len - start_ofs - end_ofs;
+ } else {
+ nc->audio_len = 0;
+ }
+
+ nc->audio_start = p_time;
+ }
+ } else if (nc->audio_playing) {
+
+ 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;
+ playing_caches.erase(nc);
+ }
+ }
+ }
+
+ } break;
+ case Animation::TYPE_ANIMATION: {
+
+ AnimationPlayer *player = Object::cast_to<AnimationPlayer>(nc->node);
+ if (!player)
+ continue;
+
+ if (p_delta == 0 || p_seeked) {
+ //seek
+ int idx = a->track_find_key(i, p_time);
+ if (idx < 0)
+ continue;
+
+ float pos = a->track_get_key_time(i, idx);
+
+ StringName anim_name = a->animation_track_get_key_animation(i, idx);
+ if (String(anim_name) == "[stop]" || !player->has_animation(anim_name))
+ continue;
+
+ Ref<Animation> anim = player->get_animation(anim_name);
+
+ float at_anim_pos;
+
+ if (anim->has_loop()) {
+ at_anim_pos = Math::fposmod(p_time - pos, anim->get_length()); //seek to loop
+ } else {
+ at_anim_pos = MAX(anim->get_length(), p_time - pos); //seek to end
+ }
+
+ if (player->is_playing() || p_seeked) {
+ player->play(anim_name);
+ player->seek(at_anim_pos);
+ nc->animation_playing = true;
+ playing_caches.insert(nc);
+ } else {
+ player->set_assigned_animation(anim_name);
+ player->seek(at_anim_pos, true);
+ }
+ } else {
+ //find stuff to play
+ List<int> to_play;
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play);
+ if (to_play.size()) {
+ int idx = to_play.back()->get();
+
+ StringName anim_name = a->animation_track_get_key_animation(i, idx);
+ if (String(anim_name) == "[stop]" || !player->has_animation(anim_name)) {
+
+ if (playing_caches.has(nc)) {
+ playing_caches.erase(nc);
+ player->stop();
+ nc->animation_playing = false;
+ }
+ } else {
+ player->play(anim_name);
+ nc->animation_playing = true;
+ playing_caches.insert(nc);
+ }
+ }
+ }
+
+ } break;
}
}
}
-void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, float p_blend) {
+void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, float p_blend, bool p_seeked, bool p_started) {
float delta = p_delta * speed_scale * cd.speed_scale;
- bool backwards = delta < 0;
float next_pos = cd.pos + delta;
float len = cd.from->animation->get_length();
@@ -525,6 +770,8 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, f
if (&cd == &playback.current) {
+ bool backwards = delta < 0;
+
if (!backwards && cd.pos <= len && next_pos == len /*&& playback.blend.empty()*/) {
//playback finished
end_reached = true;
@@ -552,22 +799,25 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, f
cd.pos = next_pos;
- _animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current);
+ _animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started);
}
-void AnimationPlayer::_animation_process2(float p_delta) {
+void AnimationPlayer::_animation_process2(float p_delta, bool p_started) {
Playback &c = playback;
accum_pass++;
- _animation_process_data(c.current, p_delta, 1.0f);
+ _animation_process_data(c.current, p_delta, 1.0f, c.seeked && p_delta != 0, p_started);
+ if (p_delta != 0) {
+ c.seeked = false;
+ }
List<Blend>::Element *prev = NULL;
for (List<Blend>::Element *E = c.blend.back(); E; E = prev) {
Blend &b = E->get();
float blend = b.blend_left / b.blend_time;
- _animation_process_data(b.data, p_delta, blend);
+ _animation_process_data(b.data, p_delta, blend, false, false);
b.blend_left -= Math::absf(speed_scale * p_delta);
@@ -651,6 +901,16 @@ void AnimationPlayer::_animation_update_transforms() {
}
cache_update_prop_size = 0;
+
+ for (int i = 0; i < cache_update_bezier_size; i++) {
+
+ TrackNodeCache::BezierAnim *ba = cache_update_bezier[i];
+
+ ERR_CONTINUE(ba->accum_pass != accum_pass);
+ ba->object->set_indexed(ba->bezier_property, ba->bezier_accum);
+ }
+
+ cache_update_bezier_size = 0;
}
void AnimationPlayer::_animation_process(float p_delta) {
@@ -659,7 +919,12 @@ void AnimationPlayer::_animation_process(float p_delta) {
end_reached = false;
end_notify = false;
- _animation_process2(p_delta);
+ _animation_process2(p_delta, playback.started);
+
+ if (playback.started) {
+ playback.started = false;
+ }
+
_animation_update_transforms();
if (end_reached) {
if (queued.size()) {
@@ -864,7 +1129,7 @@ void AnimationPlayer::queue(const StringName &p_name) {
void AnimationPlayer::clear_queue() {
queued.clear();
-};
+}
void AnimationPlayer::play_backwards(const StringName &p_name, float p_custom_blend) {
@@ -929,10 +1194,14 @@ void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float
}
}
+ _stop_playing_caches();
+
c.current.from = &animation_set[name];
c.current.pos = p_from_end ? c.current.from->animation->get_length() : 0;
c.current.speed_scale = p_custom_scale;
c.assigned = p_name;
+ c.seeked = false;
+ c.started = true;
if (!end_reached)
queued.clear();
@@ -1003,6 +1272,7 @@ String AnimationPlayer::get_assigned_animation() const {
void AnimationPlayer::stop(bool p_reset) {
+ _stop_playing_caches();
Playback &c = playback;
c.blend.clear();
if (p_reset) {
@@ -1041,6 +1311,7 @@ void AnimationPlayer::seek(float p_time, bool p_update) {
}
playback.current.pos = p_time;
+ playback.seeked = true;
if (p_update) {
_animation_process(0);
}
@@ -1083,6 +1354,25 @@ float AnimationPlayer::get_current_animation_length() const {
void AnimationPlayer::_animation_changed() {
clear_caches();
+ emit_signal("caches_cleared");
+}
+
+void AnimationPlayer::_stop_playing_caches() {
+
+ for (Set<TrackNodeCache *>::Element *E = playing_caches.front(); E; E = E->next()) {
+
+ if (E->get()->node && E->get()->audio_playing) {
+ E->get()->node->call("stop");
+ }
+ if (E->get()->node && E->get()->animation_playing) {
+ AnimationPlayer *player = Object::cast_to<AnimationPlayer>(E->get()->node);
+ if (!player)
+ continue;
+ player->stop();
+ }
+ }
+
+ playing_caches.clear();
}
void AnimationPlayer::_node_removed(Node *p_node) {
@@ -1092,6 +1382,8 @@ void AnimationPlayer::_node_removed(Node *p_node) {
void AnimationPlayer::clear_caches() {
+ _stop_playing_caches();
+
node_cache_map.clear();
for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) {
@@ -1101,6 +1393,7 @@ void AnimationPlayer::clear_caches() {
cache_update_size = 0;
cache_update_prop_size = 0;
+ cache_update_bezier_size = 0;
}
void AnimationPlayer::set_active(bool p_active) {
@@ -1357,6 +1650,7 @@ void AnimationPlayer::_bind_methods() {
ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING, "anim_name")));
ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name")));
ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING, "anim_name")));
+ ADD_SIGNAL(MethodInfo("caches_cleared"));
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS);
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE);
@@ -1367,6 +1661,7 @@ AnimationPlayer::AnimationPlayer() {
accum_pass = 1;
cache_update_size = 0;
cache_update_prop_size = 0;
+ cache_update_bezier_size = 0;
speed_scale = 1;
end_reached = false;
end_notify = false;
@@ -1376,6 +1671,8 @@ AnimationPlayer::AnimationPlayer() {
root = SceneStringNames::get_singleton()->path_pp;
playing = false;
active = true;
+ playback.seeked = false;
+ playback.started = false;
}
AnimationPlayer::~AnimationPlayer() {
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index af2022ddac..49c73e54ad 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -98,6 +98,12 @@ private:
Vector3 scale_accum;
uint64_t accum_pass;
+ bool audio_playing;
+ float audio_start;
+ float audio_len;
+
+ bool animation_playing;
+
struct PropertyAnim {
TrackNodeCache *owner;
@@ -106,6 +112,7 @@ private:
Object *object;
Variant value_accum;
uint64_t accum_pass;
+ Variant capture;
PropertyAnim() {
accum_pass = 0;
object = NULL;
@@ -114,6 +121,22 @@ private:
Map<StringName, PropertyAnim> property_anim;
+ struct BezierAnim {
+
+ Vector<StringName> bezier_property;
+ TrackNodeCache *owner;
+ float bezier_accum;
+ Object *object;
+ uint64_t accum_pass;
+ BezierAnim() {
+ accum_pass = 0;
+ bezier_accum = 0;
+ object = NULL;
+ }
+ };
+
+ Map<StringName, BezierAnim> bezier_anim;
+
TrackNodeCache() {
skeleton = NULL;
spatial = NULL;
@@ -121,6 +144,8 @@ private:
accum_pass = 0;
bone_idx = -1;
node_2d = NULL;
+ audio_playing = false;
+ animation_playing = false;
}
};
@@ -146,6 +171,10 @@ private:
int cache_update_size;
TrackNodeCache::PropertyAnim *cache_update_prop[NODE_CACHE_UPDATE_MAX];
int cache_update_prop_size;
+ TrackNodeCache::BezierAnim *cache_update_bezier[NODE_CACHE_UPDATE_MAX];
+ int cache_update_bezier_size;
+ Set<TrackNodeCache *> playing_caches;
+
Map<Ref<Animation>, int> used_anims;
uint64_t accum_pass;
@@ -202,6 +231,8 @@ private:
List<Blend> blend;
PlaybackData current;
StringName assigned;
+ bool seeked;
+ bool started;
} playback;
List<StringName> queued;
@@ -216,15 +247,16 @@ private:
NodePath root;
- void _animation_process_animation(AnimationData *p_anim, float p_time, float p_delta, float p_interp, bool p_allow_discrete = true);
+ void _animation_process_animation(AnimationData *p_anim, float p_time, float p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false);
void _ensure_node_caches(AnimationData *p_anim);
- void _animation_process_data(PlaybackData &cd, float p_delta, float p_blend);
- void _animation_process2(float p_delta);
+ void _animation_process_data(PlaybackData &cd, float p_delta, float p_blend, bool p_seeked, bool p_started);
+ void _animation_process2(float p_delta, bool p_started);
void _animation_update_transforms();
void _animation_process(float p_delta);
void _node_removed(Node *p_node);
+ void _stop_playing_caches();
// bind helpers
PoolVector<String> _get_animation_list() const {
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
new file mode 100644
index 0000000000..4fa66e8ede
--- /dev/null
+++ b/scene/animation/animation_tree.cpp
@@ -0,0 +1,1339 @@
+#include "animation_tree.h"
+#include "animation_blend_tree.h"
+#include "core/method_bind_ext.gen.inc"
+#include "engine.h"
+#include "scene/scene_string_names.h"
+#include "servers/audio/audio_stream.h"
+
+void AnimationNode::blend_animation(const StringName &p_animation, float p_time, float p_delta, bool p_seeked, float p_blend) {
+
+ ERR_FAIL_COND(!state);
+ ERR_FAIL_COND(!state->player->has_animation(p_animation));
+
+ Ref<Animation> animation = state->player->get_animation(p_animation);
+
+ if (animation.is_null()) {
+
+ Ref<AnimationNodeBlendTree> btree = get_parent();
+ if (btree.is_valid()) {
+ String name = btree->get_node_name(Ref<AnimationNodeAnimation>(this));
+ make_invalid(vformat(RTR("In node '%s', invalid animation: '%s'."), name, p_animation));
+ } else {
+ make_invalid(vformat(RTR("Invalid animation: '%s'."), p_animation));
+ }
+ return;
+ }
+
+ ERR_FAIL_COND(!animation.is_valid());
+
+ AnimationState anim_state;
+ anim_state.blend = p_blend;
+ anim_state.track_blends = &blends;
+ anim_state.delta = p_delta;
+ anim_state.time = p_time;
+ anim_state.animation = animation;
+ anim_state.seeked = p_seeked;
+
+ state->animation_states.push_back(anim_state);
+}
+
+float AnimationNode::_pre_process(State *p_state, float p_time, bool p_seek) {
+ state = p_state;
+ float t = process(p_time, p_seek);
+ state = NULL;
+ return t;
+}
+
+void AnimationNode::make_invalid(const String &p_reason) {
+ ERR_FAIL_COND(!state);
+ state->valid = false;
+ if (state->invalid_reasons != String()) {
+ state->invalid_reasons += "\n";
+ }
+ state->invalid_reasons += "- " + 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_tree(), 0); //should not happen, but used to catch bugs
+
+ Ref<AnimationNodeBlendTree> tree = get_parent();
+
+ 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;
+ }
+
+ ERR_FAIL_COND_V(!tree.is_valid(), 0); //should not happen
+
+ StringName anim_name = inputs[p_input].connected_to;
+
+ Ref<AnimationNode> node = tree->get_node(anim_name);
+
+ if (node.is_null()) {
+
+ String name = tree->get_node_name(Ref<AnimationNodeAnimation>(this));
+ make_invalid(vformat(RTR("Nothing connected to input '%s' of node '%s'."), get_input_name(p_input), name));
+ return 0;
+ }
+
+ inputs[p_input].last_pass = state->last_pass;
+
+ return _blend_node(node, p_time, p_seek, p_blend, p_filter, p_optimize, &inputs[p_input].activity);
+}
+
+float AnimationNode::blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) {
+
+ return _blend_node(p_node, p_time, p_seek, p_blend, p_filter, p_optimize);
+}
+
+float AnimationNode::_blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize, float *r_max) {
+
+ ERR_FAIL_COND_V(!p_node.is_valid(), 0);
+ ERR_FAIL_COND_V(!state, 0);
+
+ int blend_count = blends.size();
+
+ if (p_node->blends.size() != blend_count) {
+ p_node->blends.resize(blend_count);
+ }
+
+ float *blendw = p_node->blends.ptrw();
+ const float *blendr = blends.ptr();
+
+ bool any_valid = false;
+
+ if (has_filter() && is_filter_enabled() && p_filter != FILTER_IGNORE) {
+
+ for (int i = 0; i < blend_count; i++) {
+ blendw[i] = 0.0; //all to zero by default
+ }
+
+ const NodePath *K = NULL;
+ while ((K = filter.next(K))) {
+ if (!state->track_map.has(*K)) {
+ continue;
+ }
+ int idx = state->track_map[*K];
+ blendw[idx] = 1.0; //filtered goes to one
+ }
+
+ switch (p_filter) {
+ case FILTER_IGNORE:
+ break; //will not happen anyway
+ case FILTER_PASS: {
+ //values filtered pass, the rest dont
+ for (int i = 0; i < blend_count; i++) {
+ if (blendw[i] == 0) //not filtered, does not pass
+ continue;
+
+ blendw[i] = blendr[i] * p_blend;
+ if (blendw[i] > CMP_EPSILON) {
+ any_valid = true;
+ }
+ }
+
+ } break;
+ case FILTER_STOP: {
+
+ //values filtered dont pass, the rest are blended
+
+ for (int i = 0; i < blend_count; i++) {
+ if (blendw[i] > 0) //filtered, does not pass
+ continue;
+
+ blendw[i] = blendr[i] * p_blend;
+ if (blendw[i] > CMP_EPSILON) {
+ any_valid = true;
+ }
+ }
+
+ } break;
+ case FILTER_BLEND: {
+
+ //filtered values are blended, the rest are passed without blending
+
+ for (int i = 0; i < blend_count; i++) {
+ if (blendw[i] == 1.0) {
+ blendw[i] = blendr[i] * p_blend; //filtered, blend
+ } else {
+ blendw[i] = blendr[i]; //not filtered, do not blend
+ }
+
+ if (blendw[i] > CMP_EPSILON) {
+ any_valid = true;
+ }
+ }
+
+ } break;
+ }
+ } else {
+ for (int i = 0; i < blend_count; i++) {
+
+ //regular blend
+ blendw[i] = blendr[i] * p_blend;
+ if (blendw[i] > CMP_EPSILON) {
+ any_valid = true;
+ }
+ }
+ }
+
+ if (r_max) {
+ *r_max = 0;
+ for (int i = 0; i < blend_count; i++) {
+ *r_max = MAX(*r_max, blendw[i]);
+ }
+ }
+
+ if (!p_seek && p_optimize && !any_valid) //pointless to go on, all are zero
+ return 0;
+
+ return p_node->_pre_process(state, p_time, p_seek);
+}
+
+int AnimationNode::get_input_count() const {
+
+ return inputs.size();
+}
+String AnimationNode::get_input_name(int p_input) {
+ ERR_FAIL_INDEX_V(p_input, inputs.size(), String());
+ return inputs[p_input].name;
+}
+
+float AnimationNode::get_input_activity(int p_input) const {
+
+ ERR_FAIL_INDEX_V(p_input, inputs.size(), 0);
+ if (!get_tree())
+ return 0;
+
+ if (get_tree()->get_last_process_pass() != inputs[p_input].last_pass) {
+ return 0;
+ }
+ return inputs[p_input].activity;
+}
+StringName AnimationNode::get_input_connection(int p_input) {
+
+ ERR_FAIL_INDEX_V(p_input, inputs.size(), StringName());
+ return inputs[p_input].connected_to;
+}
+
+void AnimationNode::set_input_connection(int p_input, const StringName &p_connection) {
+
+ ERR_FAIL_INDEX(p_input, inputs.size());
+ inputs[p_input].connected_to = p_connection;
+}
+
+String AnimationNode::get_caption() const {
+
+ if (get_script_instance()) {
+ return get_script_instance()->call("get_caption");
+ }
+
+ return "Node";
+}
+
+void AnimationNode::add_input(const String &p_name) {
+ //root nodes cant add inputs
+ ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != NULL)
+ Input input;
+ ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1);
+ input.name = p_name;
+ input.activity = 0;
+ input.last_pass = 0;
+ inputs.push_back(input);
+ emit_changed();
+}
+
+void AnimationNode::set_input_name(int p_input, const String &p_name) {
+ ERR_FAIL_INDEX(p_input, inputs.size());
+ ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1);
+ inputs[p_input].name = p_name;
+ emit_changed();
+}
+
+void AnimationNode::remove_input(int p_index) {
+ ERR_FAIL_INDEX(p_index, inputs.size());
+ inputs.remove(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 {
+ if (parent) {
+ return Ref<AnimationNode>(parent);
+ }
+
+ return Ref<AnimationNode>();
+}
+
+AnimationTree *AnimationNode::get_tree() const {
+
+ return player;
+}
+
+AnimationPlayer *AnimationNode::get_player() const {
+ ERR_FAIL_COND_V(!state, NULL);
+ return state->player;
+}
+
+float AnimationNode::process(float p_time, bool p_seek) {
+
+ if (get_script_instance()) {
+ return get_script_instance()->call("process", p_time, p_seek);
+ }
+
+ return 0;
+}
+
+void AnimationNode::set_filter_path(const NodePath &p_path, bool p_enable) {
+ if (p_enable) {
+ filter[p_path] = true;
+ } else {
+ filter.erase(p_path);
+ }
+}
+
+void AnimationNode::set_filter_enabled(bool p_enable) {
+ filter_enabled = p_enable;
+}
+
+bool AnimationNode::is_filter_enabled() const {
+ return filter_enabled;
+}
+
+bool AnimationNode::is_path_filtered(const NodePath &p_path) const {
+ return filter.has(p_path);
+}
+
+bool AnimationNode::has_filter() const {
+ return false;
+}
+
+void AnimationNode::set_position(const Vector2 &p_position) {
+ position = p_position;
+}
+
+Vector2 AnimationNode::get_position() const {
+ return position;
+}
+
+void AnimationNode::set_tree(AnimationTree *p_player) {
+
+ if (player != NULL && p_player == NULL) {
+ emit_signal("removed_from_graph");
+ }
+ player = p_player;
+}
+
+Array AnimationNode::_get_filters() const {
+
+ Array paths;
+
+ const NodePath *K = NULL;
+ while ((K = filter.next(K))) {
+ paths.push_back(String(*K)); //use strings, so sorting is possible
+ }
+ paths.sort(); //done so every time the scene is saved, it does not change
+
+ return paths;
+}
+void AnimationNode::_set_filters(const Array &p_filters) {
+ filter.clear();
+ for (int i = 0; i < p_filters.size(); i++) {
+ set_filter_path(p_filters[i], true);
+ }
+}
+
+void AnimationNode::_validate_property(PropertyInfo &property) const {
+ if (!has_filter() && (property.name == "filter_enabled" || property.name == "filters")) {
+ property.usage = 0;
+ }
+}
+
+void AnimationNode::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("get_input_count"), &AnimationNode::get_input_count);
+ ClassDB::bind_method(D_METHOD("get_input_name", "input"), &AnimationNode::get_input_name);
+ ClassDB::bind_method(D_METHOD("get_input_connection", "input"), &AnimationNode::get_input_connection);
+ ClassDB::bind_method(D_METHOD("get_input_activity", "input"), &AnimationNode::get_input_activity);
+
+ ClassDB::bind_method(D_METHOD("add_input", "name"), &AnimationNode::add_input);
+ ClassDB::bind_method(D_METHOD("remove_input", "index"), &AnimationNode::remove_input);
+
+ ClassDB::bind_method(D_METHOD("set_filter_path", "path", "enable"), &AnimationNode::set_filter_path);
+ ClassDB::bind_method(D_METHOD("is_path_filtered", "path"), &AnimationNode::is_path_filtered);
+
+ ClassDB::bind_method(D_METHOD("set_filter_enabled", "enable"), &AnimationNode::set_filter_enabled);
+ ClassDB::bind_method(D_METHOD("is_filter_enabled"), &AnimationNode::is_filter_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_position", "position"), &AnimationNode::set_position);
+ ClassDB::bind_method(D_METHOD("get_position"), &AnimationNode::get_position);
+
+ ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters);
+ ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters);
+
+ ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend"), &AnimationNode::blend_animation);
+ 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);
+ BIND_ENUM_CONSTANT(FILTER_PASS);
+ BIND_ENUM_CONSTANT(FILTER_STOP);
+ BIND_ENUM_CONSTANT(FILTER_BLEND);
+}
+
+AnimationNode::AnimationNode() {
+
+ state = NULL;
+ parent = NULL;
+ player = NULL;
+ set_local_to_scene(true);
+ filter_enabled = false;
+}
+
+////////////////////
+
+void AnimationTree::set_tree_root(const Ref<AnimationNode> &p_root) {
+
+ if (root.is_valid()) {
+ root->set_tree(NULL);
+ }
+ if (p_root.is_valid()) {
+ ERR_EXPLAIN("root node already set to another player");
+ ERR_FAIL_COND(p_root->player);
+ }
+ root = p_root;
+
+ if (root.is_valid()) {
+ root->set_tree(this);
+ }
+
+ update_configuration_warning();
+}
+
+Ref<AnimationNode> AnimationTree::get_tree_root() const {
+ return root;
+}
+
+void AnimationTree::set_active(bool p_active) {
+
+ if (active == p_active)
+ return;
+
+ active = p_active;
+ started = active;
+
+ if (process_mode == ANIMATION_PROCESS_IDLE) {
+ set_process_internal(active);
+ } else {
+
+ set_physics_process_internal(active);
+ }
+
+ if (!active && is_inside_tree()) {
+ for (Set<TrackCache *>::Element *E = playing_caches.front(); E; E = E->next()) {
+
+ if (ObjectDB::get_instance(E->get()->object_id)) {
+ E->get()->object->call("stop");
+ }
+ }
+
+ playing_caches.clear();
+ }
+}
+
+bool AnimationTree::is_active() const {
+
+ return active;
+}
+
+void AnimationTree::set_process_mode(AnimationProcessMode p_mode) {
+
+ if (process_mode == p_mode)
+ return;
+
+ bool was_active = is_active();
+ if (was_active) {
+ set_active(false);
+ }
+
+ process_mode = p_mode;
+
+ if (was_active) {
+ set_active(true);
+ }
+}
+
+AnimationTree::AnimationProcessMode AnimationTree::get_process_mode() const {
+ return process_mode;
+}
+
+void AnimationTree::_node_removed(Node *p_node) {
+ cache_valid = false;
+}
+
+bool AnimationTree::_update_caches(AnimationPlayer *player) {
+
+ setup_pass++;
+
+ if (!player->has_node(player->get_root())) {
+ ERR_PRINT("AnimationTree: AnimationPlayer root is invalid.");
+ set_active(false);
+ return false;
+ }
+ Node *parent = player->get_node(player->get_root());
+
+ List<StringName> sname;
+ player->get_animation_list(&sname);
+
+ for (List<StringName>::Element *E = sname.front(); E; E = E->next()) {
+ Ref<Animation> anim = player->get_animation(E->get());
+ for (int i = 0; i < anim->get_track_count(); i++) {
+ NodePath path = anim->track_get_path(i);
+ Animation::TrackType track_type = anim->track_get_type(i);
+
+ TrackCache *track = NULL;
+ if (track_cache.has(path)) {
+ track = track_cache.get(path);
+ }
+
+ //if not valid, delete track
+ if (track && (track->type != track_type || ObjectDB::get_instance(track->object_id) == NULL)) {
+ playing_caches.erase(track);
+ memdelete(track);
+ track_cache.erase(path);
+ track = NULL;
+ }
+
+ if (!track) {
+
+ RES resource;
+ Vector<StringName> leftover_path;
+ Node *child = parent->get_node_and_resource(path, resource, leftover_path);
+
+ if (!child) {
+ ERR_PRINTS("AnimationTree: '" + String(E->get()) + "', couldn't resolve track: '" + String(path) + "'");
+ continue;
+ }
+
+ if (!child->is_connected("tree_exited", this, "_node_removed")) {
+ child->connect("tree_exited", this, "_node_removed", varray(child));
+ }
+
+ switch (track_type) {
+ case Animation::TYPE_VALUE: {
+
+ TrackCacheValue *track_value = memnew(TrackCacheValue);
+
+ if (resource.is_valid()) {
+ track_value->object = resource.ptr();
+ } else {
+ track_value->object = child;
+ }
+
+ track_value->subpath = leftover_path;
+ track_value->object_id = track_value->object->get_instance_id();
+
+ track = track_value;
+
+ } break;
+ case Animation::TYPE_TRANSFORM: {
+
+ Spatial *spatial = Object::cast_to<Spatial>(child);
+
+ if (!spatial) {
+ ERR_PRINTS("AnimationTree: '" + String(E->get()) + "', transform track does not point to spatial: '" + String(path) + "'");
+ continue;
+ }
+
+ TrackCacheTransform *track_xform = memnew(TrackCacheTransform);
+
+ track_xform->spatial = spatial;
+ track_xform->skeleton = NULL;
+ track_xform->bone_idx = -1;
+
+ if (path.get_subname_count() == 1 && Object::cast_to<Skeleton>(spatial)) {
+
+ Skeleton *sk = Object::cast_to<Skeleton>(spatial);
+ int bone_idx = sk->find_bone(path.get_subname(0));
+ if (bone_idx != -1 && !sk->is_bone_ignore_animation(bone_idx)) {
+
+ track_xform->skeleton = sk;
+ track_xform->bone_idx = bone_idx;
+ }
+ }
+
+ track_xform->object = spatial;
+ track_xform->object_id = track_xform->object->get_instance_id();
+
+ track = track_xform;
+
+ } break;
+ case Animation::TYPE_METHOD: {
+
+ TrackCacheMethod *track_method = memnew(TrackCacheMethod);
+
+ if (resource.is_valid()) {
+ track_method->object = resource.ptr();
+ } else {
+ track_method->object = child;
+ }
+
+ track_method->object_id = track_method->object->get_instance_id();
+
+ track = track_method;
+
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ TrackCacheBezier *track_bezier = memnew(TrackCacheBezier);
+
+ if (resource.is_valid()) {
+ track_bezier->object = resource.ptr();
+ } else {
+ track_bezier->object = child;
+ }
+
+ track_bezier->subpath = leftover_path;
+ track_bezier->object_id = track_bezier->object->get_instance_id();
+
+ track = track_bezier;
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ TrackCacheAudio *track_audio = memnew(TrackCacheAudio);
+
+ track_audio->object = child;
+ track_audio->object_id = track_audio->object->get_instance_id();
+
+ track = track_audio;
+
+ } break;
+ case Animation::TYPE_ANIMATION: {
+
+ TrackCacheAnimation *track_animation = memnew(TrackCacheAnimation);
+
+ track_animation->object = child;
+ track_animation->object_id = track_animation->object->get_instance_id();
+
+ track = track_animation;
+
+ } break;
+ }
+
+ track_cache[path] = track;
+ }
+
+ track->setup_pass = setup_pass;
+ }
+ }
+
+ List<NodePath> to_delete;
+
+ const NodePath *K = NULL;
+ while ((K = track_cache.next(K))) {
+ TrackCache *tc = track_cache[*K];
+ if (tc->setup_pass != setup_pass) {
+ to_delete.push_back(*K);
+ }
+ }
+
+ while (to_delete.front()) {
+ NodePath np = to_delete.front()->get();
+ memdelete(track_cache[np]);
+ track_cache.erase(np);
+ to_delete.pop_front();
+ }
+
+ state.track_map.clear();
+
+ K = NULL;
+ int idx = 0;
+ while ((K = track_cache.next(K))) {
+ state.track_map[*K] = idx;
+ idx++;
+ }
+
+ state.track_count = idx;
+
+ cache_valid = true;
+
+ return true;
+}
+
+void AnimationTree::_clear_caches() {
+
+ const NodePath *K = NULL;
+ while ((K = track_cache.next(K))) {
+ memdelete(track_cache[*K]);
+ }
+ playing_caches.clear();
+
+ track_cache.clear();
+ cache_valid = false;
+}
+
+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("AnimationTree: root AnimationNode is not set, disabling playback.");
+ set_active(false);
+ cache_valid = false;
+ return;
+ }
+
+ if (!has_node(animation_player)) {
+ ERR_PRINT("AnimationTree: no valid AnimationPlayer path set, disabling playback");
+ set_active(false);
+ cache_valid = false;
+ return;
+ }
+
+ AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player));
+
+ if (!player) {
+ ERR_PRINT("AnimationTree: path points to a node not an AnimationPlayer, disabling playback");
+ set_active(false);
+ cache_valid = false;
+ return;
+ }
+
+ if (!cache_valid) {
+ if (!_update_caches(player)) {
+ return;
+ }
+ }
+
+ { //setup
+
+ process_pass++;
+
+ state.valid = true;
+ state.invalid_reasons = "";
+ state.animation_states.clear(); //will need to be re-created
+ state.valid = true;
+ state.player = player;
+ state.last_pass = process_pass;
+
+ // root source blends
+
+ root->blends.resize(state.track_count);
+ float *src_blendsw = root->blends.ptrw();
+ for (int i = 0; i < state.track_count; i++) {
+ src_blendsw[i] = 1.0; //by default all go to 1 for the root input
+ }
+ }
+
+ //process
+
+ {
+
+ if (started) {
+ //if started, seek
+ root->_pre_process(&state, 0, true);
+ started = false;
+ }
+
+ root->_pre_process(&state, p_delta, false);
+ }
+
+ if (!state.valid) {
+ return; //state is not valid. do nothing.
+ }
+ //apply value/transform/bezier blends to track caches and execute method/audio/animation tracks
+
+ {
+
+ bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint();
+
+ for (List<AnimationNode::AnimationState>::Element *E = state.animation_states.front(); E; E = E->next()) {
+
+ const AnimationNode::AnimationState &as = E->get();
+
+ Ref<Animation> a = as.animation;
+ float time = as.time;
+ float delta = as.delta;
+ bool seeked = as.seeked;
+
+ for (int i = 0; i < a->get_track_count(); i++) {
+
+ NodePath path = a->track_get_path(i);
+ TrackCache *track = track_cache[path];
+ if (track->type != a->track_get_type(i)) {
+ 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];
+
+ ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count);
+
+ float blend = (*as.track_blends)[blend_idx];
+
+ if (blend < CMP_EPSILON)
+ continue; //nothing to blend
+
+ switch (track->type) {
+
+ case Animation::TYPE_TRANSFORM: {
+
+ TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+
+ 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();
+ }
+
+ 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: {
+
+ TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
+
+ Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
+
+ if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { //delta == 0 means seek
+
+ Variant value = a->value_track_interpolate(i, time);
+
+ if (value == Variant())
+ continue;
+
+ if (t->process_pass != process_pass) {
+ Variant::CallError ce;
+ t->value = Variant::construct(value.get_type(), NULL, 0, ce); //reset
+ t->process_pass = process_pass;
+ }
+
+ Variant::interpolate(t->value, value, blend, t->value);
+
+ } else if (delta != 0) {
+
+ List<int> indices;
+ a->value_track_get_key_indices(i, time, delta, &indices);
+
+ for (List<int>::Element *F = indices.front(); F; F = F->next()) {
+
+ Variant value = a->track_get_key_value(i, F->get());
+ t->object->set_indexed(t->subpath, value);
+ }
+ }
+
+ } break;
+ case Animation::TYPE_METHOD: {
+
+ if (delta == 0) {
+ continue;
+ }
+ TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track);
+
+ List<int> indices;
+
+ a->method_track_get_key_indices(i, time, delta, &indices);
+
+ for (List<int>::Element *E = indices.front(); E; E = E->next()) {
+
+ StringName method = a->method_track_get_name(i, E->get());
+ Vector<Variant> params = a->method_track_get_params(i, E->get());
+
+ int s = params.size();
+
+ ERR_CONTINUE(s > VARIANT_ARG_MAX);
+ if (can_call) {
+ t->object->call_deferred(
+ method,
+ s >= 1 ? params[0] : Variant(),
+ s >= 2 ? params[1] : Variant(),
+ s >= 3 ? params[2] : Variant(),
+ s >= 4 ? params[3] : Variant(),
+ s >= 5 ? params[4] : Variant());
+ }
+ }
+
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
+
+ float bezier = a->bezier_track_interpolate(i, time);
+
+ if (t->process_pass != process_pass) {
+ t->value = 0;
+ t->process_pass = process_pass;
+ }
+
+ t->value = Math::lerp(t->value, bezier, blend);
+
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
+
+ if (seeked) {
+ //find whathever should be playing
+ int idx = a->track_find_key(i, time);
+ if (idx < 0)
+ continue;
+
+ Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
+ if (!stream.is_valid()) {
+ t->object->call("stop");
+ t->playing = false;
+ playing_caches.erase(t);
+ } else {
+ float start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ start_ofs += time - a->track_get_key_time(i, idx);
+ float end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ float len = stream->get_length();
+
+ if (start_ofs > len - end_ofs) {
+ t->object->call("stop");
+ t->playing = false;
+ playing_caches.erase(t);
+ continue;
+ }
+
+ t->object->call("set_stream", stream);
+ t->object->call("play", start_ofs);
+
+ t->playing = true;
+ playing_caches.insert(t);
+ if (len && end_ofs > 0) { //force a end at a time
+ t->len = len - start_ofs - end_ofs;
+ } else {
+ t->len = 0;
+ }
+
+ t->start = time;
+ }
+
+ } else {
+ //find stuff to play
+ List<int> to_play;
+ a->track_get_key_indices_in_range(i, time, delta, &to_play);
+ if (to_play.size()) {
+ int idx = to_play.back()->get();
+
+ Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
+ if (!stream.is_valid()) {
+ t->object->call("stop");
+ t->playing = false;
+ playing_caches.erase(t);
+ } else {
+ float start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ float end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ float len = stream->get_length();
+
+ t->object->call("set_stream", stream);
+ t->object->call("play", start_ofs);
+
+ t->playing = true;
+ playing_caches.insert(t);
+ if (len && end_ofs > 0) { //force a end at a time
+ t->len = len - start_ofs - end_ofs;
+ } else {
+ t->len = 0;
+ }
+
+ t->start = time;
+ }
+ } else if (t->playing) {
+
+ 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;
+ playing_caches.erase(t);
+ }
+ }
+ }
+
+ 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: {
+
+ TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track);
+
+ AnimationPlayer *player = Object::cast_to<AnimationPlayer>(t->object);
+
+ if (!player)
+ continue;
+
+ if (delta == 0 || seeked) {
+ //seek
+ int idx = a->track_find_key(i, time);
+ if (idx < 0)
+ continue;
+
+ float pos = a->track_get_key_time(i, idx);
+
+ StringName anim_name = a->animation_track_get_key_animation(i, idx);
+ if (String(anim_name) == "[stop]" || !player->has_animation(anim_name))
+ continue;
+
+ Ref<Animation> anim = player->get_animation(anim_name);
+
+ float at_anim_pos;
+
+ if (anim->has_loop()) {
+ at_anim_pos = Math::fposmod(time - pos, anim->get_length()); //seek to loop
+ } else {
+ at_anim_pos = MAX(anim->get_length(), time - pos); //seek to end
+ }
+
+ if (player->is_playing() || seeked) {
+ player->play(anim_name);
+ player->seek(at_anim_pos);
+ t->playing = true;
+ playing_caches.insert(t);
+ } else {
+ player->set_assigned_animation(anim_name);
+ player->seek(at_anim_pos, true);
+ }
+ } else {
+ //find stuff to play
+ List<int> to_play;
+ a->track_get_key_indices_in_range(i, time, delta, &to_play);
+ if (to_play.size()) {
+ int idx = to_play.back()->get();
+
+ StringName anim_name = a->animation_track_get_key_animation(i, idx);
+ if (String(anim_name) == "[stop]" || !player->has_animation(anim_name)) {
+
+ if (playing_caches.has(t)) {
+ playing_caches.erase(t);
+ player->stop();
+ t->playing = false;
+ }
+ } else {
+ player->play(anim_name);
+ t->playing = true;
+ playing_caches.insert(t);
+ }
+ }
+ }
+
+ } break;
+ }
+ }
+ }
+ }
+
+ {
+ // finally, set the tracks
+ const NodePath *K = NULL;
+ while ((K = track_cache.next(K))) {
+ TrackCache *track = track_cache[*K];
+ if (track->process_pass != process_pass)
+ continue; //not processed, ignore
+
+ switch (track->type) {
+
+ case Animation::TYPE_TRANSFORM: {
+
+ TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+
+ Transform xform;
+ xform.origin = t->loc;
+
+ 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->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);
+
+ } else {
+
+ t->spatial->set_transform(xform);
+ }
+
+ } break;
+ case Animation::TYPE_VALUE: {
+
+ TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
+
+ t->object->set_indexed(t->subpath, t->value);
+
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
+
+ t->object->set_indexed(t->subpath, t->value);
+
+ } break;
+ default: {} //the rest dont matter
+ }
+ }
+ }
+}
+
+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());
+ }
+
+ if (active && p_what == NOTIFICATION_INTERNAL_PROCESS && process_mode == ANIMATION_PROCESS_IDLE) {
+ _process_graph(get_process_delta_time());
+ }
+
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ _clear_caches();
+ }
+}
+
+void AnimationTree::set_animation_player(const NodePath &p_player) {
+ animation_player = p_player;
+ update_configuration_warning();
+}
+
+NodePath AnimationTree::get_animation_player() const {
+ return animation_player;
+}
+
+bool AnimationTree::is_state_invalid() const {
+
+ return !state.valid;
+}
+String AnimationTree::get_invalid_state_reason() const {
+
+ return state.invalid_reasons;
+}
+
+uint64_t AnimationTree::get_last_process_pass() const {
+ return process_pass;
+}
+
+String AnimationTree::get_configuration_warning() const {
+
+ String warning = Node::get_configuration_warning();
+
+ if (!root.is_valid()) {
+ if (warning != String()) {
+ warning += "\n";
+ }
+ warning += TTR("A root AnimationNode for the graph is not set.");
+ }
+
+ if (!has_node(animation_player)) {
+
+ if (warning != String()) {
+ warning += "\n";
+ }
+
+ warning += TTR("Path to an AnimationPlayer node containing animations is not set.");
+ return warning;
+ }
+
+ AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player));
+
+ if (!player) {
+ if (warning != String()) {
+ warning += "\n";
+ }
+
+ warning += TTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node.");
+ return warning;
+ }
+
+ if (!player->has_node(player->get_root())) {
+ if (warning != String()) {
+ warning += "\n";
+ }
+
+ warning += TTR("AnimationPlayer root is not a valid node.");
+ return warning;
+ }
+
+ return warning;
+}
+
+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_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_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_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("get_root_motion_transform"), &AnimationTree::get_root_motion_transform);
+
+ 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);
+}
+
+AnimationTree::AnimationTree() {
+
+ process_mode = ANIMATION_PROCESS_IDLE;
+ active = false;
+ cache_valid = false;
+ setup_pass = 1;
+ started = true;
+}
+
+AnimationTree::~AnimationTree() {
+ if (root.is_valid()) {
+ root->player = NULL;
+ }
+}
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
new file mode 100644
index 0000000000..540c36437a
--- /dev/null
+++ b/scene/animation/animation_tree.h
@@ -0,0 +1,281 @@
+#ifndef ANIMATION_GRAPH_PLAYER_H
+#define ANIMATION_GRAPH_PLAYER_H
+
+#include "animation_player.h"
+#include "scene/3d/skeleton.h"
+#include "scene/3d/spatial.h"
+#include "scene/resources/animation.h"
+
+class AnimationNodeBlendTree;
+class AnimationPlayer;
+class AnimationTree;
+
+class AnimationNode : public Resource {
+ GDCLASS(AnimationNode, Resource)
+public:
+ enum FilterAction {
+ FILTER_IGNORE,
+ FILTER_PASS,
+ FILTER_STOP,
+ FILTER_BLEND
+ };
+
+ struct Input {
+
+ String name;
+ StringName connected_to;
+ float activity;
+ uint64_t last_pass;
+ };
+
+ Vector<Input> inputs;
+
+ float process_input(int p_input, float p_time, bool p_seek, float p_blend);
+
+ friend class AnimationTree;
+
+ struct AnimationState {
+
+ Ref<Animation> animation;
+ float time;
+ float delta;
+ const Vector<float> *track_blends;
+ float blend;
+ bool seeked;
+ };
+
+ struct State {
+
+ int track_count;
+ HashMap<NodePath, int> track_map;
+ List<AnimationState> animation_states;
+ bool valid;
+ AnimationPlayer *player;
+ String invalid_reasons;
+ uint64_t last_pass;
+ };
+
+ Vector<float> blends;
+ State *state;
+ float _pre_process(State *p_state, float p_time, bool p_seek);
+ void _pre_update_animations(HashMap<NodePath, int> *track_map);
+ Vector2 position;
+
+ AnimationNode *parent;
+ 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);
+
+ HashMap<NodePath, bool> filter;
+ bool filter_enabled;
+
+ Array _get_filters() const;
+ void _set_filters(const Array &p_filters);
+
+protected:
+ void blend_animation(const StringName &p_animation, float p_time, float p_delta, bool p_seeked, float p_blend);
+ 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 blend_input(int p_input, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+ void make_invalid(const String &p_reason);
+
+ static void _bind_methods();
+
+ 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_tree(AnimationTree *p_player);
+ AnimationTree *get_tree() const;
+ AnimationPlayer *get_player() const;
+
+ virtual float process(float p_time, bool p_seek);
+ virtual String get_caption() const;
+
+ int get_input_count() const;
+ String get_input_name(int p_input);
+ StringName get_input_connection(int p_input);
+ void set_input_connection(int p_input, const StringName &p_connection);
+ float get_input_activity(int p_input) const;
+
+ void add_input(const String &p_name);
+ void set_input_name(int p_input, const String &p_name);
+ void remove_input(int p_index);
+
+ void set_filter_path(const NodePath &p_path, bool p_enable);
+ bool is_path_filtered(const NodePath &p_path) const;
+
+ void set_filter_enabled(bool p_enable);
+ bool is_filter_enabled() const;
+
+ virtual bool has_filter() const;
+
+ void set_position(const Vector2 &p_position);
+ Vector2 get_position() const;
+
+ AnimationNode();
+};
+
+VARIANT_ENUM_CAST(AnimationNode::FilterAction)
+
+//root node does not allow inputs
+class AnimationRootNode : public AnimationNode {
+ GDCLASS(AnimationRootNode, AnimationNode)
+public:
+ AnimationRootNode() {}
+};
+
+class AnimationTree : public Node {
+ GDCLASS(AnimationTree, Node)
+public:
+ enum AnimationProcessMode {
+ ANIMATION_PROCESS_PHYSICS,
+ ANIMATION_PROCESS_IDLE,
+ };
+
+private:
+ struct TrackCache {
+
+ bool root_motion;
+ uint64_t setup_pass;
+ uint64_t process_pass;
+ Animation::TrackType type;
+ Object *object;
+ ObjectID object_id;
+
+ TrackCache() {
+ root_motion = false;
+ setup_pass = 0;
+ process_pass = 0;
+ object = NULL;
+ object_id = 0;
+ }
+ virtual ~TrackCache() {}
+ };
+
+ struct TrackCacheTransform : public TrackCache {
+ Spatial *spatial;
+ Skeleton *skeleton;
+ int bone_idx;
+ Vector3 loc;
+ Quat rot;
+ float rot_blend_accum;
+ Vector3 scale;
+
+ TrackCacheTransform() {
+ type = Animation::TYPE_TRANSFORM;
+ spatial = NULL;
+ bone_idx = -1;
+ skeleton = NULL;
+ }
+ };
+
+ struct TrackCacheValue : public TrackCache {
+
+ Variant value;
+ Vector<StringName> subpath;
+ TrackCacheValue() { type = Animation::TYPE_VALUE; }
+ };
+
+ struct TrackCacheMethod : public TrackCache {
+
+ TrackCacheMethod() { type = Animation::TYPE_METHOD; }
+ };
+
+ struct TrackCacheBezier : public TrackCache {
+
+ float value;
+ Vector<StringName> subpath;
+ TrackCacheBezier() {
+ type = Animation::TYPE_BEZIER;
+ value = 0;
+ }
+ };
+
+ struct TrackCacheAudio : public TrackCache {
+
+ bool playing;
+ float start;
+ float len;
+
+ TrackCacheAudio() {
+ type = Animation::TYPE_AUDIO;
+ playing = false;
+ start = 0;
+ len = 0;
+ }
+ };
+
+ struct TrackCacheAnimation : public TrackCache {
+
+ bool playing;
+
+ TrackCacheAnimation() {
+ type = Animation::TYPE_ANIMATION;
+ playing = false;
+ }
+ };
+
+ HashMap<NodePath, TrackCache *> track_cache;
+ Set<TrackCache *> playing_caches;
+
+ Ref<AnimationNode> root;
+
+ AnimationProcessMode process_mode;
+ bool active;
+ NodePath animation_player;
+
+ AnimationNode::State state;
+ bool cache_valid;
+ void _node_removed(Node *p_node);
+ void _caches_cleared();
+
+ void _clear_caches();
+ bool _update_caches(AnimationPlayer *player);
+ void _process_graph(float p_delta);
+
+ uint64_t setup_pass;
+ uint64_t process_pass;
+
+ bool started;
+
+ NodePath root_motion_track;
+ Transform root_motion_transform;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ 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;
+
+ void set_process_mode(AnimationProcessMode p_mode);
+ AnimationProcessMode get_process_mode() const;
+
+ void set_animation_player(const NodePath &p_player);
+ NodePath get_animation_player() const;
+
+ virtual String get_configuration_warning() const;
+
+ 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;
+ AnimationTree();
+ ~AnimationTree();
+};
+
+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 ce5b372d72..026215508b 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -1216,6 +1216,12 @@ String AnimationTreePlayer::animation_node_get_master_animation(const StringName
return n->from;
}
+float AnimationTreePlayer::animation_node_get_position(const StringName &p_node) const {
+
+ GET_NODE_V(NODE_ANIMATION, AnimationNode, 0);
+ return n->time;
+}
+
bool AnimationTreePlayer::animation_node_is_path_filtered(const StringName &p_node, const NodePath &p_path) const {
GET_NODE_V(NODE_ANIMATION, AnimationNode, 0);
@@ -1724,6 +1730,7 @@ void AnimationTreePlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("animation_node_set_master_animation", "id", "source"), &AnimationTreePlayer::animation_node_set_master_animation);
ClassDB::bind_method(D_METHOD("animation_node_get_master_animation", "id"), &AnimationTreePlayer::animation_node_get_master_animation);
+ ClassDB::bind_method(D_METHOD("animation_node_get_position", "id"), &AnimationTreePlayer::animation_node_get_position);
ClassDB::bind_method(D_METHOD("animation_node_set_filter_path", "id", "path", "enable"), &AnimationTreePlayer::animation_node_set_filter_path);
ClassDB::bind_method(D_METHOD("oneshot_node_set_fadein_time", "id", "time_sec"), &AnimationTreePlayer::oneshot_node_set_fadein_time);
@@ -1807,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/animation_tree_player.h b/scene/animation/animation_tree_player.h
index 09d6f6fcb4..d2d7b1c9ec 100644
--- a/scene/animation/animation_tree_player.h
+++ b/scene/animation/animation_tree_player.h
@@ -348,6 +348,7 @@ public:
Ref<Animation> animation_node_get_animation(const StringName &p_node) const;
void animation_node_set_master_animation(const StringName &p_node, const String &p_master_animation);
String animation_node_get_master_animation(const StringName &p_node) const;
+ float animation_node_get_position(const StringName &p_node) const;
void animation_node_set_filter_path(const StringName &p_node, const NodePath &p_track_path, bool p_filter);
void animation_node_set_get_filtered_paths(const StringName &p_node, List<NodePath> *r_paths) const;
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/animation/tween.cpp b/scene/animation/tween.cpp
index 49013b160a..9f7503577b 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -201,6 +201,7 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("reset_all"), &Tween::reset_all);
ClassDB::bind_method(D_METHOD("stop", "object", "key"), &Tween::stop, DEFVAL(""));
ClassDB::bind_method(D_METHOD("stop_all"), &Tween::stop_all);
+ ClassDB::bind_method(D_METHOD("is_stopped"), &Tween::is_stopped);
ClassDB::bind_method(D_METHOD("resume", "object", "key"), &Tween::resume, DEFVAL(""));
ClassDB::bind_method(D_METHOD("resume_all"), &Tween::resume_all);
ClassDB::bind_method(D_METHOD("remove", "object", "key"), &Tween::remove, DEFVAL(""));
@@ -743,6 +744,10 @@ bool Tween::stop(Object *p_object, StringName p_key) {
return true;
}
+bool Tween::is_stopped() const {
+ return tell() >= get_runtime();
+}
+
bool Tween::stop_all() {
set_active(false);
@@ -886,6 +891,10 @@ real_t Tween::tell() const {
real_t Tween::get_runtime() const {
+ if (speed_scale == 0) {
+ return INFINITY;
+ }
+
pending_update++;
real_t runtime = 0;
for (const List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
@@ -896,7 +905,8 @@ real_t Tween::get_runtime() const {
runtime = t;
}
pending_update--;
- return runtime;
+
+ return runtime / speed_scale;
}
bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final_val, Variant &p_delta_val) {
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 757d80e90a..36094bf294 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -162,6 +162,7 @@ public:
bool reset_all();
bool stop(Object *p_object, StringName p_key);
bool stop_all();
+ bool is_stopped() const;
bool resume(Object *p_object, StringName p_key);
bool resume_all();
bool remove(Object *p_object, StringName p_key);
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 6f34f3e49f..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;
@@ -656,8 +677,9 @@ ColorPicker::ColorPicker() :
void ColorPickerButton::_color_changed(const Color &p_color) {
+ color = p_color;
update();
- emit_signal("color_changed", p_color);
+ emit_signal("color_changed", color);
}
void ColorPickerButton::_modal_closed() {
@@ -667,6 +689,7 @@ void ColorPickerButton::_modal_closed() {
void ColorPickerButton::pressed() {
+ _update_picker();
popup->set_position(get_global_position() - picker->get_combined_minimum_size());
popup->popup();
picker->set_focus_on_line_edit();
@@ -679,45 +702,66 @@ void ColorPickerButton::_notification(int p_what) {
Ref<StyleBox> normal = get_stylebox("normal");
Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size());
draw_texture_rect(Control::get_icon("bg", "ColorPickerButton"), r, true);
- draw_rect(r, picker->get_pick_color());
+ draw_rect(r, color);
}
- if (p_what == MainLoop::NOTIFICATION_WM_QUIT_REQUEST) {
+ if (p_what == MainLoop::NOTIFICATION_WM_QUIT_REQUEST && popup) {
popup->hide();
}
}
void ColorPickerButton::set_pick_color(const Color &p_color) {
- picker->set_pick_color(p_color);
+ color = p_color;
+ if (picker) {
+ picker->set_pick_color(p_color);
+ }
+
update();
- emit_signal("color_changed", p_color);
}
Color ColorPickerButton::get_pick_color() const {
- return picker->get_pick_color();
+ return color;
}
void ColorPickerButton::set_edit_alpha(bool p_show) {
- picker->set_edit_alpha(p_show);
+ edit_alpha = p_show;
+ if (picker) {
+ picker->set_edit_alpha(p_show);
+ }
}
bool ColorPickerButton::is_editing_alpha() const {
- return picker->is_editing_alpha();
+ return edit_alpha;
}
-ColorPicker *ColorPickerButton::get_picker() const {
+ColorPicker *ColorPickerButton::get_picker() {
+ _update_picker();
return picker;
}
-PopupPanel *ColorPickerButton::get_popup() const {
+PopupPanel *ColorPickerButton::get_popup() {
+ _update_picker();
return popup;
}
+void ColorPickerButton::_update_picker() {
+ if (!picker) {
+ popup = memnew(PopupPanel);
+ picker = memnew(ColorPicker);
+ popup->add_child(picker);
+ add_child(popup);
+ picker->connect("color_changed", this, "_color_changed");
+ popup->connect("modal_closed", this, "_modal_closed");
+ picker->set_pick_color(color);
+ picker->set_edit_alpha(edit_alpha);
+ }
+}
+
void ColorPickerButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPickerButton::set_pick_color);
@@ -737,12 +781,10 @@ void ColorPickerButton::_bind_methods() {
ColorPickerButton::ColorPickerButton() {
- popup = memnew(PopupPanel);
- picker = memnew(ColorPicker);
- popup->add_child(picker);
-
- picker->connect("color_changed", this, "_color_changed");
- popup->connect("modal_closed", this, "_modal_closed");
-
- add_child(popup);
+ //Initialization is now done deferred
+ //this improves performance in the inspector as the color picker
+ //can be expensive to initialize
+ picker = NULL;
+ popup = NULL;
+ edit_alpha = true;
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 7d1a554ada..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();
@@ -118,12 +122,16 @@ class ColorPickerButton : public Button {
PopupPanel *popup;
ColorPicker *picker;
+ Color color;
+ bool edit_alpha;
void _color_changed(const Color &p_color);
void _modal_closed();
virtual void pressed();
+ void _update_picker();
+
protected:
void _notification(int);
static void _bind_methods();
@@ -135,8 +143,8 @@ public:
void set_edit_alpha(bool p_show);
bool is_editing_alpha() const;
- ColorPicker *get_picker() const;
- PopupPanel *get_popup() const;
+ ColorPicker *get_picker();
+ PopupPanel *get_popup();
ColorPickerButton();
};
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index 7cb0ad5707..7df03bf7c6 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -34,9 +34,9 @@
void Container::_child_minsize_changed() {
- Size2 ms = get_combined_minimum_size();
- if (ms.width > get_size().width || ms.height > get_size().height)
- minimum_size_changed();
+ //Size2 ms = get_combined_minimum_size();
+ //if (ms.width > get_size().width || ms.height > get_size().height) {
+ minimum_size_changed();
queue_sort();
}
@@ -51,6 +51,8 @@ void Container::add_child_notify(Node *p_child) {
control->connect("size_flags_changed", this, "queue_sort");
control->connect("minimum_size_changed", this, "_child_minsize_changed");
control->connect("visibility_changed", this, "_child_minsize_changed");
+
+ minimum_size_changed();
queue_sort();
}
@@ -61,6 +63,7 @@ void Container::move_child_notify(Node *p_child) {
if (!Object::cast_to<Control>(p_child))
return;
+ minimum_size_changed();
queue_sort();
}
@@ -75,6 +78,8 @@ void Container::remove_child_notify(Node *p_child) {
control->disconnect("size_flags_changed", this, "queue_sort");
control->disconnect("minimum_size_changed", this, "_child_minsize_changed");
control->disconnect("visibility_changed", this, "_child_minsize_changed");
+
+ minimum_size_changed();
queue_sort();
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index b7c1d35fd7..068af42260 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -155,12 +155,29 @@ Size2 Control::get_custom_minimum_size() const {
return data.custom_minimum_size;
}
-Size2 Control::get_combined_minimum_size() const {
+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);
- return minsize;
+
+ bool size_changed = false;
+ if (data.minimum_size_cache != minsize)
+ size_changed = true;
+
+ data.minimum_size_cache = minsize;
+ data.minimum_size_valid = true;
+
+ if (size_changed)
+ minimum_size_changed();
+}
+
+Size2 Control::get_combined_minimum_size() const {
+
+ if (!data.minimum_size_valid) {
+ const_cast<Control *>(this)->_update_minimum_size_cache();
+ }
+ return data.minimum_size_cache;
}
Size2 Control::_edit_get_minimum_size() const {
@@ -259,14 +276,18 @@ void Control::_update_minimum_size() {
if (!is_inside_tree())
return;
- data.pending_min_size_update = false;
Size2 minsize = get_combined_minimum_size();
if (minsize.x > data.size_cache.x ||
minsize.y > data.size_cache.y) {
_size_changed();
}
- emit_signal(SceneStringNames::get_singleton()->minimum_size_changed);
+ data.updating_last_minimum_size = false;
+
+ if (minsize != data.last_minimum_size) {
+ data.last_minimum_size = minsize;
+ emit_signal(SceneStringNames::get_singleton()->minimum_size_changed);
+ }
}
bool Control::_get(const StringName &p_name, Variant &r_ret) const {
@@ -437,8 +458,10 @@ void Control::_notification(int p_notification) {
case NOTIFICATION_ENTER_TREE: {
+ } break;
+ case NOTIFICATION_POST_ENTER_TREE: {
+ data.minimum_size_valid = false;
_size_changed();
-
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -620,13 +643,12 @@ void Control::_notification(int p_notification) {
if (is_inside_tree()) {
_modal_stack_remove();
- minimum_size_changed();
}
//remove key focus
//remove modalness
} else {
-
+ data.minimum_size_valid = false;
_size_changed();
}
@@ -1252,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;
+}
- Size2 parent_size = get_parent_area_size();
+void Control::_size_changed() {
+
+ 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);
}
@@ -1310,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;
@@ -1320,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 (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) {
+ 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();
+ }
- return data.parent_canvas_item->_edit_get_rect().size[p_idx & 1];
- } else {
- return get_viewport()->get_visible_rect().size[p_idx & 1];
+ if (pos_changed && !size_changed) {
+ _update_canvas_item_transform(); //move because it won't be updated
+ }
}
-
- 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;
@@ -1386,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()) {
@@ -1396,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) {
@@ -1542,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) {
@@ -1555,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;
}
@@ -1583,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;
}
@@ -1607,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:
@@ -1625,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;
}
@@ -1635,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:
@@ -1653,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;
}
@@ -1732,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();
}
@@ -1769,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();
}
@@ -1811,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());
@@ -2310,12 +2290,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),
@@ -2369,12 +2348,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;
@@ -2464,17 +2442,25 @@ void Control::minimum_size_changed() {
if (!is_inside_tree() || data.block_minimum_size_adjust)
return;
- if (data.pending_min_size_update)
+ Control *invalidate = this;
+
+ //invalidate cache upwards
+ while (invalidate && invalidate->data.minimum_size_valid) {
+ invalidate->data.minimum_size_valid = false;
+ if (invalidate->is_set_as_toplevel())
+ break; // do not go further up
+ invalidate = invalidate->data.parent;
+ }
+
+ if (!is_visible_in_tree())
return;
- data.pending_min_size_update = true;
- MessageQueue::get_singleton()->push_call(this, "_update_minimum_size");
+ if (data.updating_last_minimum_size)
+ return;
- if (!is_toplevel_control()) {
- Control *pc = get_parent_control();
- if (pc)
- pc->minimum_size_changed();
- }
+ data.updating_last_minimum_size = true;
+
+ MessageQueue::get_singleton()->push_call(this, "_update_minimum_size");
}
int Control::get_v_size_flags() const {
@@ -2865,12 +2851,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_");
@@ -2985,7 +2971,6 @@ Control::Control() {
data.h_size_flags = SIZE_FILL;
data.v_size_flags = SIZE_FILL;
data.expand = 1;
- data.pending_min_size_update = false;
data.rotation = 0;
data.parent_canvas_item = NULL;
data.scale = Vector2(1, 1);
@@ -2995,6 +2980,8 @@ Control::Control() {
data.disable_visibility_clip = false;
data.h_grow = GROW_DIRECTION_END;
data.v_grow = GROW_DIRECTION_END;
+ data.minimum_size_valid = false;
+ data.updating_last_minimum_size = false;
data.clip_contents = false;
for (int i = 0; i < 4; i++) {
diff --git a/scene/gui/control.h b/scene/gui/control.h
index b5453e60f5..fa5274d854 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -148,6 +148,11 @@ private:
Point2 pos_cache;
Size2 size_cache;
+ Size2 minimum_size_cache;
+ bool minimum_size_valid;
+
+ Size2 last_minimum_size;
+ bool updating_last_minimum_size;
float margin[4];
float anchor[4];
@@ -164,7 +169,6 @@ private:
int h_size_flags;
int v_size_flags;
float expand;
- bool pending_min_size_update;
Point2 custom_minimum_size;
bool pass_on_modal_close_click;
@@ -198,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;
@@ -216,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();
@@ -229,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;
@@ -244,6 +247,8 @@ private:
void _modal_stack_remove();
void _modal_set_prev_focus_owner(ObjectID p_prev);
+ void _update_minimum_size_cache();
+
protected:
virtual void add_child_notify(Node *p_child);
virtual void remove_child_notify(Node *p_child);
@@ -277,6 +282,7 @@ public:
};
+ /* EDITOR */
virtual Dictionary _edit_get_state() const;
virtual void _edit_set_state(const Dictionary &p_state);
@@ -352,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);
@@ -459,6 +466,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/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 4bd92d888d..25cb74a494 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -777,6 +777,7 @@ void FileDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mode", "mode"), &FileDialog::set_mode);
ClassDB::bind_method(D_METHOD("get_mode"), &FileDialog::get_mode);
ClassDB::bind_method(D_METHOD("get_vbox"), &FileDialog::get_vbox);
+ ClassDB::bind_method(D_METHOD("get_line_edit"), &FileDialog::get_line_edit);
ClassDB::bind_method(D_METHOD("set_access", "access"), &FileDialog::set_access);
ClassDB::bind_method(D_METHOD("get_access"), &FileDialog::get_access);
ClassDB::bind_method(D_METHOD("set_show_hidden_files", "show"), &FileDialog::set_show_hidden_files);
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 9fc8e98a7f..b5622604e2 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -147,6 +147,7 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) {
grabbed = _get_point_from_pos(x);
//grab or select
if (grabbed != -1) {
+ grabbed = false;
return;
}
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 38ce91a4df..e2c730a56e 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -58,6 +58,7 @@ Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const S
c.from_port = p_from_port;
c.to = p_to;
c.to_port = p_to_port;
+ c.activity = 0;
connections.push_back(c);
top_layer->update();
update();
@@ -624,6 +625,7 @@ void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const
void GraphEdit::_connections_layer_draw() {
+ Color activity_color = get_color("activity");
//draw connections
List<List<Connection>::Element *> to_erase;
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
@@ -661,6 +663,11 @@ void GraphEdit::_connections_layer_draw() {
Color color = gfrom->get_connection_output_color(E->get().from_port);
Vector2 topos = gto->get_connection_input_position(E->get().to_port) + gto->get_offset() * zoom;
Color tocolor = gto->get_connection_input_color(E->get().to_port);
+
+ if (E->get().activity > 0) {
+ color = color.linear_interpolate(activity_color, E->get().activity);
+ tocolor = tocolor.linear_interpolate(activity_color, E->get().activity);
+ }
_draw_cos_line(connections_layer, frompos, topos, color, tocolor);
}
@@ -980,6 +987,23 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
}
}
+void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) {
+
+ for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
+
+ if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) {
+
+ if (ABS(E->get().activity != p_activity)) {
+ //update only if changed
+ top_layer->update();
+ connections_layer->update();
+ }
+ E->get().activity = p_activity;
+ return;
+ }
+ }
+}
+
void GraphEdit::clear_connections() {
connections.clear();
@@ -1141,11 +1165,16 @@ void GraphEdit::_snap_value_changed(double) {
update();
}
+HBoxContainer *GraphEdit::get_zoom_hbox() {
+ return zoom_hb;
+}
+
void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("connect_node", "from", "from_port", "to", "to_port"), &GraphEdit::connect_node);
ClassDB::bind_method(D_METHOD("is_node_connected", "from", "from_port", "to", "to_port"), &GraphEdit::is_node_connected);
ClassDB::bind_method(D_METHOD("disconnect_node", "from", "from_port", "to", "to_port"), &GraphEdit::disconnect_node);
+ ClassDB::bind_method(D_METHOD("set_connection_activity", "from", "from_port", "to", "to_port", "amount"), &GraphEdit::set_connection_activity);
ClassDB::bind_method(D_METHOD("get_connection_list"), &GraphEdit::_get_connection_list);
ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections);
ClassDB::bind_method(D_METHOD("get_scroll_ofs"), &GraphEdit::get_scroll_ofs);
@@ -1187,6 +1216,8 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_scroll_offset"), &GraphEdit::_update_scroll_offset);
ClassDB::bind_method(D_METHOD("_connections_layer_draw"), &GraphEdit::_connections_layer_draw);
+ ClassDB::bind_method(D_METHOD("get_zoom_hbox"), &GraphEdit::get_zoom_hbox);
+
ClassDB::bind_method(D_METHOD("set_selected", "node"), &GraphEdit::set_selected);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "right_disconnects"), "set_right_disconnects", "is_right_disconnects_enabled");
@@ -1253,7 +1284,7 @@ GraphEdit::GraphEdit() {
zoom = 1;
- HBoxContainer *zoom_hb = memnew(HBoxContainer);
+ zoom_hb = memnew(HBoxContainer);
top_layer->add_child(zoom_hb);
zoom_hb->set_position(Vector2(10, 10));
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 3bfde44854..14789001e4 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -31,6 +31,7 @@
#ifndef GRAPH_EDIT_H
#define GRAPH_EDIT_H
+#include "scene/gui/box_container.h"
#include "scene/gui/graph_node.h"
#include "scene/gui/scroll_bar.h"
#include "scene/gui/slider.h"
@@ -62,6 +63,7 @@ public:
StringName to;
int from_port;
int to_port;
+ float activity;
};
private:
@@ -157,6 +159,8 @@ private:
Set<int> valid_left_disconnect_types;
Set<int> valid_right_disconnect_types;
+ HBoxContainer *zoom_hb;
+
friend class GraphEditFilter;
bool _filter_input(const Point2 &p_point);
void _snap_toggled();
@@ -175,6 +179,8 @@ public:
void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
void clear_connections();
+ void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity);
+
void add_valid_connection_type(int p_type, int p_with_type);
void remove_valid_connection_type(int p_type, int p_with_type);
bool is_valid_connection_type(int p_type, int p_with_type) const;
@@ -206,6 +212,8 @@ public:
int get_snap() const;
void set_snap(int p_snap);
+ HBoxContainer *get_zoom_hbox();
+
GraphEdit();
};
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 57b9a9a11b..72ed0e9b81 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -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 f1b0d36f32..9af479c1cc 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -103,7 +103,8 @@ void Label::_notification(int p_what) {
int lines_visible = (size.y + line_spacing) / font_h;
- int space_w = font->get_char_size(' ').width;
+ // ceiling to ensure autowrapping does not cut text
+ int space_w = Math::ceil(font->get_char_size(' ').width);
int chars_total = 0;
int vbegin = 0, vsep = 0;
@@ -331,7 +332,8 @@ int Label::get_longest_line_width() const {
}
} else {
- int char_width = font->get_char_size(current, xl_text[i + 1]).width;
+ // ceiling to ensure autowrapping does not cut text
+ int char_width = Math::ceil(font->get_char_size(current, xl_text[i + 1]).width);
line_width += char_width;
}
}
@@ -384,7 +386,8 @@ void Label::regenerate_word_cache() {
int word_pos = 0;
int line_width = 0;
int space_count = 0;
- int space_width = font->get_char_size(' ').width;
+ // ceiling to ensure autowrapping does not cut text
+ int space_width = Math::ceil(font->get_char_size(' ').width);
int line_spacing = get_constant("line_spacing");
line_count = 1;
total_char_cache = 0;
@@ -446,8 +449,8 @@ void Label::regenerate_word_cache() {
if (current_word_size == 0) {
word_pos = i;
}
-
- char_width = font->get_char_size(current, xl_text[i + 1]).width;
+ // ceiling to ensure autowrapping does not cut text
+ char_width = Math::ceil(font->get_char_size(current, xl_text[i + 1]).width);
current_word_size += char_width;
line_width += char_width;
total_char_cache++;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index e57af0a4c0..b71a4dd133 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -215,12 +215,14 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
case (KEY_A): { //Select All
select();
} break;
+#ifdef APPLE_STYLE_KEYS
case (KEY_LEFT): { // Go to start of text - like HOME key
set_cursor_position(0);
} break;
case (KEY_RIGHT): { // Go to end of text - like END key
set_cursor_position(text.length());
} break;
+#endif
default: { handled = false; }
}
@@ -563,6 +565,9 @@ void LineEdit::_notification(int p_what) {
#endif
case NOTIFICATION_RESIZED: {
+ if (expand_to_text_length) {
+ window_pos = 0; //force scroll back since it's expanding to text length
+ }
set_cursor_position(get_cursor_position());
} break;
@@ -608,7 +613,8 @@ void LineEdit::_notification(int p_what) {
}
int x_ofs = 0;
- int cached_text_width = text.empty() ? cached_placeholder_width : cached_width;
+ bool using_placeholder = text.empty();
+ int cached_text_width = using_placeholder ? cached_placeholder_width : cached_width;
switch (align) {
@@ -643,9 +649,9 @@ void LineEdit::_notification(int p_what) {
Color font_color_selected = get_color("font_color_selected");
Color cursor_color = get_color("cursor_color");
- const String &t = text.empty() ? placeholder : text;
+ const String &t = using_placeholder ? placeholder : text;
// draw placeholder color
- if (text.empty())
+ if (using_placeholder)
font_color.a *= placeholder_alpha;
font_color.a *= disabled_alpha;
@@ -705,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) {
@@ -754,7 +761,8 @@ void LineEdit::_notification(int p_what) {
if (has_focus()) {
- OS::get_singleton()->set_ime_position(get_global_position() + Point2(x_ofs, y_ofs + caret_height));
+ OS::get_singleton()->set_ime_active(true);
+ OS::get_singleton()->set_ime_position(get_global_position() + Point2(using_placeholder ? 0 : x_ofs, y_ofs + caret_height));
OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
}
} break;
@@ -764,6 +772,7 @@ void LineEdit::_notification(int p_what) {
draw_caret = true;
}
+ OS::get_singleton()->set_ime_active(true);
Point2 cursor_pos = Point2(get_cursor_position(), 1) * get_minimum_size().height;
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos);
OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
@@ -776,6 +785,7 @@ void LineEdit::_notification(int p_what) {
OS::get_singleton()->set_ime_position(Point2());
OS::get_singleton()->set_ime_intermediate_text_callback(NULL, NULL);
+ OS::get_singleton()->set_ime_active(false);
ime_text = "";
ime_selection = Point2();
@@ -1005,7 +1015,6 @@ void LineEdit::set_text(String p_text) {
update();
cursor_pos = 0;
window_pos = 0;
- _text_changed();
}
void LineEdit::clear() {
@@ -1093,11 +1102,12 @@ void LineEdit::set_cursor_position(int p_pos) {
for (int i = cursor_pos; i >= window_pos; i--) {
if (i >= text.length()) {
- accum_width = font->get_char_size(' ').width; //anything should do
+ //do not do this, because if the cursor is at the end, its just fine that it takes no space
+ //accum_width = font->get_char_size(' ').width; //anything should do
} else {
accum_width += font->get_char_size(text[i], i + 1 < text.length() ? text[i + 1] : 0).width; //anything should do
}
- if (accum_width >= window_width)
+ if (accum_width > window_width)
break;
wp = i;
@@ -1164,7 +1174,7 @@ Size2 LineEdit::get_minimum_size() const {
int mstext = get_constant("minimum_spaces") * space_size;
if (expand_to_text_length) {
- mstext = MAX(mstext, font->get_string_size(text).x + space_size); //add a spce because some fonts are too exact
+ mstext = MAX(mstext, font->get_string_size(text).x + space_size); //add a spce because some fonts are too exact, and because cursor needs a bit more when at the end
}
min.width += mstext;
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.cpp b/scene/gui/popup.cpp
index d18a3a3f2f..26d01ecc09 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -113,37 +113,9 @@ void Popup::set_as_minsize() {
void Popup::popup_centered_minsize(const Size2 &p_minsize) {
- Size2 total_minsize = p_minsize;
-
- for (int i = 0; i < get_child_count(); i++) {
-
- Control *c = Object::cast_to<Control>(get_child(i));
- if (!c)
- continue;
- if (!c->is_visible())
- continue;
-
- Size2 minsize = c->get_combined_minimum_size();
-
- for (int j = 0; j < 2; j++) {
-
- Margin m_beg = Margin(0 + j);
- Margin m_end = Margin(2 + j);
-
- float margin_begin = c->get_margin(m_beg);
- float margin_end = c->get_margin(m_end);
- float anchor_begin = c->get_anchor(m_beg);
- float anchor_end = c->get_anchor(m_end);
-
- minsize[j] += margin_begin * (ANCHOR_END - anchor_begin) + margin_end * anchor_end;
- }
-
- total_minsize.width = MAX(total_minsize.width, minsize.width);
- total_minsize.height = MAX(total_minsize.height, minsize.height);
- }
-
- popup_centered(total_minsize);
- popped_up = true;
+ set_custom_minimum_size(p_minsize);
+ _fix_size();
+ popup_centered();
}
void Popup::popup_centered(const Size2 &p_size) {
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index fd2466407e..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();
@@ -440,6 +451,8 @@ void PopupMenu::_notification(int p_what) {
float h;
Size2 icon_size;
+ Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1);
+
item_ofs.x += items[i].h_ofs;
if (!items[i].icon.is_null()) {
@@ -455,31 +468,51 @@ 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) {
Texture *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
- icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)));
+ icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color);
item_ofs.x += icon->get_width() + hseparation;
}
if (!items[i].icon.is_null()) {
- items[i].icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon_size.height) / 2.0)));
+ items[i].icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
item_ofs.x += items[i].icon->get_width();
item_ofs.x += hseparation;
}
if (items[i].submenu != "") {
- submenu->draw(ci, Point2(size.width - style->get_margin(MARGIN_RIGHT) - submenu->get_width(), item_ofs.y + Math::floor(h - submenu->get_height()) / 2));
+ submenu->draw(ci, Point2(size.width - style->get_margin(MARGIN_RIGHT) - submenu->get_width(), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
}
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));
}
@@ -533,6 +566,7 @@ void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label,
item.ID = p_ID;
items.push_back(item);
update();
+ minimum_size_changed();
}
void PopupMenu::add_item(const String &p_label, int p_ID, uint32_t p_accel) {
@@ -543,6 +577,7 @@ void PopupMenu::add_item(const String &p_label, int p_ID, uint32_t p_accel) {
item.ID = p_ID;
items.push_back(item);
update();
+ minimum_size_changed();
}
void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_ID) {
@@ -554,6 +589,7 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu,
item.submenu = p_submenu;
items.push_back(item);
update();
+ minimum_size_changed();
}
void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
@@ -567,6 +603,7 @@ void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
+ minimum_size_changed();
}
void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
@@ -579,6 +616,7 @@ void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
+ minimum_size_changed();
}
void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
@@ -586,6 +624,7 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p
add_check_item(p_label, p_ID, p_accel);
items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
+ minimum_size_changed();
}
void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
@@ -593,6 +632,7 @@ void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const Stri
add_icon_check_item(p_icon, p_label, p_ID, p_accel);
items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
+ minimum_size_changed();
}
void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
@@ -608,6 +648,7 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut
item.shortcut_is_global = p_global;
items.push_back(item);
update();
+ minimum_size_changed();
}
void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
@@ -622,6 +663,7 @@ void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_g
item.shortcut_is_global = p_global;
items.push_back(item);
update();
+ minimum_size_changed();
}
void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
@@ -638,6 +680,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<Sh
item.shortcut_is_global = p_global;
items.push_back(item);
update();
+ minimum_size_changed();
}
void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
@@ -653,6 +696,7 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
+ minimum_size_changed();
}
void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
@@ -660,6 +704,7 @@ void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_
add_check_shortcut(p_shortcut, p_ID, p_global);
items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
+ minimum_size_changed();
}
void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID, uint32_t p_accel) {
@@ -673,6 +718,7 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
item.state = p_default_state;
items.push_back(item);
update();
+ minimum_size_changed();
}
void PopupMenu::set_item_text(int p_idx, const String &p_text) {
@@ -682,6 +728,7 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) {
items[p_idx].xl_text = tr(p_text);
update();
+ minimum_size_changed();
}
void PopupMenu::set_item_icon(int p_idx, const Ref<Texture> &p_icon) {
@@ -689,6 +736,7 @@ void PopupMenu::set_item_icon(int p_idx, const Ref<Texture> &p_icon) {
items[p_idx].icon = p_icon;
update();
+ minimum_size_changed();
}
void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
@@ -697,6 +745,7 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
items[p_idx].checked = p_checked;
update();
+ minimum_size_changed();
}
void PopupMenu::set_item_id(int p_idx, int p_ID) {
@@ -704,6 +753,7 @@ void PopupMenu::set_item_id(int p_idx, int p_ID) {
items[p_idx].ID = p_ID;
update();
+ minimum_size_changed();
}
void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) {
@@ -712,6 +762,7 @@ void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) {
items[p_idx].accel = p_accel;
update();
+ minimum_size_changed();
}
void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) {
@@ -719,6 +770,7 @@ void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) {
ERR_FAIL_INDEX(p_idx, items.size());
items[p_idx].metadata = p_meta;
update();
+ minimum_size_changed();
}
void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) {
@@ -726,6 +778,7 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) {
ERR_FAIL_INDEX(p_idx, items.size());
items[p_idx].disabled = p_disabled;
update();
+ minimum_size_changed();
}
void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) {
@@ -733,6 +786,7 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) {
ERR_FAIL_INDEX(p_idx, items.size());
items[p_idx].submenu = p_submenu;
update();
+ minimum_size_changed();
}
void PopupMenu::toggle_item_checked(int p_idx) {
@@ -740,6 +794,7 @@ void PopupMenu::toggle_item_checked(int p_idx) {
ERR_FAIL_INDEX(p_idx, items.size());
items[p_idx].checked = !items[p_idx].checked;
update();
+ minimum_size_changed();
}
String PopupMenu::get_item_text(int p_idx) const {
@@ -881,6 +936,7 @@ void PopupMenu::set_item_h_offset(int p_idx, int p_offset) {
ERR_FAIL_INDEX(p_idx, items.size());
items[p_idx].h_ofs = p_offset;
update();
+ minimum_size_changed();
}
void PopupMenu::set_item_multistate(int p_idx, int p_state) {
@@ -890,6 +946,13 @@ void PopupMenu::set_item_multistate(int p_idx, int p_state) {
update();
}
+void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) {
+
+ ERR_FAIL_INDEX(p_idx, items.size());
+ items[p_idx].shortcut_is_disabled = p_disabled;
+ update();
+}
+
void PopupMenu::toggle_item_multistate(int p_idx) {
ERR_FAIL_INDEX(p_idx, items.size());
@@ -914,6 +977,12 @@ bool PopupMenu::is_item_radio_checkable(int p_idx) const {
return items[p_idx].checkable_type == Item::CHECKABLE_TYPE_RADIO_BUTTON;
}
+bool PopupMenu::is_item_shortcut_disabled(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx, items.size(), false);
+ return items[p_idx].shortcut_is_disabled;
+}
+
int PopupMenu::get_item_count() const {
return items.size();
@@ -940,7 +1009,7 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo
int il = items.size();
for (int i = 0; i < il; i++) {
- if (is_item_disabled(i))
+ if (is_item_disabled(i) || items[i].shortcut_is_disabled)
continue;
if (items[i].shortcut.is_valid() && items[i].shortcut->is_shortcut(p_event) && (items[i].shortcut_is_global || !p_for_global_only)) {
@@ -1026,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();
}
@@ -1045,6 +1118,7 @@ void PopupMenu::clear() {
items.clear();
mouse_over = -1;
update();
+ minimum_size_changed();
}
Array PopupMenu::_get_items() const {
@@ -1162,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);
@@ -1224,6 +1311,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &PopupMenu::set_item_tooltip);
ClassDB::bind_method(D_METHOD("set_item_shortcut", "idx", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_item_multistate", "idx", "state"), &PopupMenu::set_item_multistate);
+ ClassDB::bind_method(D_METHOD("set_item_shortcut_disabled", "idx", "disabled"), &PopupMenu::set_item_shortcut_disabled);
ClassDB::bind_method(D_METHOD("toggle_item_checked", "idx"), &PopupMenu::toggle_item_checked);
ClassDB::bind_method(D_METHOD("toggle_item_multistate", "idx"), &PopupMenu::toggle_item_multistate);
@@ -1240,6 +1328,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &PopupMenu::is_item_separator);
ClassDB::bind_method(D_METHOD("is_item_checkable", "idx"), &PopupMenu::is_item_checkable);
ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "idx"), &PopupMenu::is_item_radio_checkable);
+ ClassDB::bind_method(D_METHOD("is_item_shortcut_disabled", "idx"), &PopupMenu::is_item_shortcut_disabled);
ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &PopupMenu::get_item_tooltip);
ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut);
@@ -1247,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);
@@ -1262,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 fde91bd845..8ec51c7d3a 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -64,6 +64,7 @@ class PopupMenu : public Popup {
int h_ofs;
Ref<ShortCut> shortcut;
bool shortcut_is_global;
+ bool shortcut_is_disabled;
Item() {
checked = false;
@@ -76,6 +77,7 @@ class PopupMenu : public Popup {
_ofs_cache = 0;
h_ofs = 0;
shortcut_is_global = false;
+ shortcut_is_disabled = false;
}
};
@@ -149,6 +151,7 @@ public:
void set_item_h_offset(int p_idx, int p_offset);
void set_item_multistate(int p_idx, int p_state);
void toggle_item_multistate(int p_idx);
+ void set_item_shortcut_disabled(int p_idx, bool p_disabled);
void toggle_item_checked(int p_idx);
@@ -165,6 +168,7 @@ public:
bool is_item_separator(int p_idx) const;
bool is_item_checkable(int p_idx) const;
bool is_item_radio_checkable(int p_idx) const;
+ bool is_item_shortcut_disabled(int p_idx) const;
String get_item_tooltip(int p_idx) const;
Ref<ShortCut> get_item_shortcut(int p_idx) const;
int get_item_state(int p_idx) const;
@@ -176,7 +180,7 @@ public:
void remove_item(int p_idx);
- void add_separator();
+ void add_separator(const String &p_text = String());
void clear();
@@ -198,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/progress_bar.cpp b/scene/gui/progress_bar.cpp
index 37e519e375..fc5d56237a 100644
--- a/scene/gui/progress_bar.cpp
+++ b/scene/gui/progress_bar.cpp
@@ -39,9 +39,9 @@ Size2 ProgressBar::get_minimum_size() const {
Size2 minimum_size = bg->get_minimum_size();
minimum_size.height = MAX(minimum_size.height, fg->get_minimum_size().height);
minimum_size.width = MAX(minimum_size.width, fg->get_minimum_size().width);
- if (percent_visible) {
- minimum_size.height = MAX(minimum_size.height, bg->get_minimum_size().height + font->get_height());
- }
+ //if (percent_visible) { this is needed, else the progressbar will collapse
+ minimum_size.height = MAX(minimum_size.height, bg->get_minimum_size().height + font->get_height());
+ //}
return minimum_size;
}
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index cd6c6bb65c..4062e48640 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -71,10 +71,10 @@ void Range::set_value(double p_val) {
p_val = Math::round(p_val);
}
- if (p_val > shared->max - shared->page)
+ if (!shared->allow_greater && p_val > shared->max - shared->page)
p_val = shared->max - shared->page;
- if (p_val < shared->min)
+ if (!shared->allow_lesser && p_val < shared->min)
p_val = shared->min;
if (shared->val == p_val)
@@ -136,9 +136,9 @@ void Range::set_as_ratio(double p_value) {
double v;
- if (shared->exp_ratio && get_min() > 0) {
+ if (shared->exp_ratio && get_min() >= 0) {
- double exp_min = Math::log(get_min()) / Math::log((double)2);
+ double exp_min = get_min() == 0 ? 0.0 : Math::log(get_min()) / Math::log((double)2);
double exp_max = Math::log(get_max()) / Math::log((double)2);
v = Math::pow(2, exp_min + (exp_max - exp_min) * p_value);
} else {
@@ -151,21 +151,24 @@ void Range::set_as_ratio(double p_value) {
v = percent + get_min();
}
}
+ v = CLAMP(v, get_min(), get_max());
set_value(v);
}
double Range::get_as_ratio() const {
- if (shared->exp_ratio && get_min() > 0) {
+ if (shared->exp_ratio && get_min() >= 0) {
- double exp_min = Math::log(get_min()) / Math::log((double)2);
+ double exp_min = get_min() == 0 ? 0.0 : Math::log(get_min()) / Math::log((double)2);
double exp_max = Math::log(get_max()) / Math::log((double)2);
- double v = Math::log(get_value()) / Math::log((double)2);
+ float value = CLAMP(get_value(), shared->min, shared->max);
+ double v = Math::log(value) / Math::log((double)2);
return (v - exp_min) / (exp_max - exp_min);
} else {
- return (get_value() - get_min()) / (get_max() - get_min());
+ float value = CLAMP(get_value(), shared->min, shared->max);
+ return (value - get_min()) / (get_max() - get_min());
}
}
@@ -193,6 +196,8 @@ void Range::unshare() {
nshared->val = shared->val;
nshared->step = shared->step;
nshared->page = shared->page;
+ nshared->allow_greater = shared->allow_greater;
+ nshared->allow_lesser = shared->allow_lesser;
_unref_shared();
_ref_shared(nshared);
}
@@ -236,6 +241,10 @@ void Range::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_using_rounded_values"), &Range::is_using_rounded_values);
ClassDB::bind_method(D_METHOD("set_exp_ratio", "enabled"), &Range::set_exp_ratio);
ClassDB::bind_method(D_METHOD("is_ratio_exp"), &Range::is_ratio_exp);
+ ClassDB::bind_method(D_METHOD("set_allow_greater", "allow"), &Range::set_allow_greater);
+ ClassDB::bind_method(D_METHOD("is_greater_allowed"), &Range::is_greater_allowed);
+ ClassDB::bind_method(D_METHOD("set_allow_lesser", "allow"), &Range::set_allow_lesser);
+ ClassDB::bind_method(D_METHOD("is_lesser_allowed"), &Range::is_lesser_allowed);
ClassDB::bind_method(D_METHOD("share", "with"), &Range::_share);
ClassDB::bind_method(D_METHOD("unshare"), &Range::unshare);
@@ -251,6 +260,8 @@ void Range::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ratio", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_as_ratio", "get_as_ratio");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exp_edit"), "set_exp_ratio", "is_ratio_exp");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rounded"), "set_use_rounded_values", "is_using_rounded_values");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "allow_greater"), "set_allow_greater", "is_greater_allowed");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "allow_lesser"), "set_allow_lesser", "is_lesser_allowed");
}
void Range::set_use_rounded_values(bool p_enable) {
@@ -273,6 +284,22 @@ bool Range::is_ratio_exp() const {
return shared->exp_ratio;
}
+void Range::set_allow_greater(bool p_allow) {
+ shared->allow_greater = p_allow;
+}
+
+bool Range::is_greater_allowed() const {
+ return shared->allow_greater;
+}
+
+void Range::set_allow_lesser(bool p_allow) {
+ shared->allow_lesser = p_allow;
+}
+
+bool Range::is_lesser_allowed() const {
+ return shared->allow_lesser;
+}
+
Range::Range() {
shared = memnew(Shared);
shared->min = 0;
@@ -282,6 +309,8 @@ Range::Range() {
shared->page = 0;
shared->owners.insert(this);
shared->exp_ratio = false;
+ shared->allow_greater = false;
+ shared->allow_lesser = false;
_rounded_values = false;
}
diff --git a/scene/gui/range.h b/scene/gui/range.h
index de9383afd8..125f559248 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -43,6 +43,8 @@ class Range : public Control {
double val, min, max;
double step, page;
bool exp_ratio;
+ bool allow_greater;
+ bool allow_lesser;
Set<Range *> owners;
void emit_value_changed();
void emit_changed(const char *p_what = "");
@@ -86,6 +88,12 @@ public:
void set_exp_ratio(bool p_enable);
bool is_ratio_exp() const;
+ void set_allow_greater(bool p_allow);
+ bool is_greater_allowed() const;
+
+ void set_allow_lesser(bool p_allow);
+ bool is_lesser_allowed() const;
+
void share(Range *p_range);
void unshare();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 8fc626208b..ce2e3538da 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -86,6 +86,54 @@ RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) {
return NULL;
}
+RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) {
+ if (p_free) {
+
+ if (p_item->subitems.size()) {
+
+ return p_item->subitems.back()->get();
+ } else if (!p_item->parent) {
+ return NULL;
+ } else if (p_item->E->prev()) {
+
+ return p_item->E->prev()->get();
+ } else {
+ //go back until something with a prev is found
+ while (p_item->parent && !p_item->E->prev()) {
+ p_item = p_item->parent;
+ }
+
+ if (p_item->parent)
+ return p_item->E->prev()->get();
+ else
+ return NULL;
+ }
+
+ } else {
+ if (p_item->subitems.size() && p_item->type != ITEM_TABLE) {
+
+ return p_item->subitems.back()->get();
+ } else if (p_item->type == ITEM_FRAME) {
+ return NULL;
+ } else if (p_item->E->prev()) {
+
+ return p_item->E->prev()->get();
+ } else {
+ //go back until something with a prev is found
+ while (p_item->type != ITEM_FRAME && !p_item->E->prev()) {
+ p_item = p_item->parent;
+ }
+
+ if (p_item->type != ITEM_FRAME)
+ return p_item->E->prev()->get();
+ else
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
Rect2 RichTextLabel::_get_text_rect() {
Ref<StyleBox> style = get_stylebox("normal");
return Rect2(style->get_offset(), get_size() - style->get_minimum_size());
@@ -276,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;
}
@@ -1889,7 +1937,7 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) {
}
}
-bool RichTextLabel::search(const String &p_string, bool p_from_selection) {
+bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) {
ERR_FAIL_COND_V(!selection.enabled, false);
Item *it = main;
@@ -1938,7 +1986,10 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection) {
}
}
- it = _get_next_item(it, true);
+ if (p_search_previous)
+ it = _get_prev_item(it, true);
+ else
+ it = _get_next_item(it, true);
charidx = 0;
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index e054ce3935..af368af46a 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -190,7 +190,6 @@ private:
struct ItemNewline : public Item {
- int line; // FIXME: Overriding base's line ?
ItemNewline() { type = ITEM_NEWLINE; }
};
@@ -285,6 +284,7 @@ private:
void _gui_input(Ref<InputEvent> p_event);
Item *_get_next_item(Item *p_item, bool p_free = false);
+ Item *_get_prev_item(Item *p_item, bool p_free = false);
Rect2 _get_text_rect();
@@ -334,7 +334,7 @@ public:
void set_tab_size(int p_spaces);
int get_tab_size() const;
- bool search(const String &p_string, bool p_from_selection = false);
+ bool search(const String &p_string, bool p_from_selection = false, bool p_search_previous = false);
void scroll_to_line(int p_line);
int get_line_count() const;
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/scroll_container.cpp b/scene/gui/scroll_container.cpp
index a1dcf3b002..495d618930 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -37,6 +37,7 @@ bool ScrollContainer::clips_input() const {
Size2 ScrollContainer::get_minimum_size() const {
+ Ref<StyleBox> sb = get_stylebox("bg");
Size2 min_size;
for (int i = 0; i < get_child_count(); i++) {
@@ -64,8 +65,9 @@ Size2 ScrollContainer::get_minimum_size() const {
if (v_scroll->is_visible_in_tree()) {
min_size.x += v_scroll->get_minimum_size().x;
}
+ min_size += sb->get_minimum_size();
return min_size;
-};
+}
void ScrollContainer::_cancel_drag() {
set_physics_process_internal(false);
@@ -233,10 +235,16 @@ void ScrollContainer::_notification(int p_what) {
child_max_size = Size2(0, 0);
Size2 size = get_size();
- if (h_scroll->is_visible_in_tree())
+ Point2 ofs;
+
+ Ref<StyleBox> sb = get_stylebox("bg");
+ size -= sb->get_minimum_size();
+ ofs += sb->get_offset();
+
+ if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) //scrolls may have been moved out for reasons
size.y -= h_scroll->get_minimum_size().y;
- if (v_scroll->is_visible_in_tree())
+ if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) //scrolls may have been moved out for reasons
size.x -= h_scroll->get_minimum_size().x;
for (int i = 0; i < get_child_count(); i++) {
@@ -268,6 +276,7 @@ void ScrollContainer::_notification(int p_what) {
else
r.size.height = minsize.height;
}
+ r.position += ofs;
fit_child_in_rect(c, r);
}
update();
@@ -275,6 +284,9 @@ void ScrollContainer::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
+ Ref<StyleBox> sb = get_stylebox("bg");
+ draw_style_box(sb, Rect2(Vector2(), get_size()));
+
update_scrollbars();
}
@@ -353,6 +365,8 @@ void ScrollContainer::_notification(int p_what) {
void ScrollContainer::update_scrollbars() {
Size2 size = get_size();
+ Ref<StyleBox> sb = get_stylebox("bg");
+ size -= sb->get_minimum_size();
Size2 hmin = h_scroll->get_combined_minimum_size();
Size2 vmin = v_scroll->get_combined_minimum_size();
@@ -468,6 +482,16 @@ String ScrollContainer::get_configuration_warning() const {
return "";
}
+HScrollBar *ScrollContainer::get_h_scrollbar() {
+
+ return h_scroll;
+}
+
+VScrollBar *ScrollContainer::get_v_scrollbar() {
+
+ return v_scroll;
+}
+
void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_scroll_moved"), &ScrollContainer::_scroll_moved);
@@ -484,6 +508,9 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_deadzone", "deadzone"), &ScrollContainer::set_deadzone);
ClassDB::bind_method(D_METHOD("get_deadzone"), &ScrollContainer::get_deadzone);
+ ClassDB::bind_method(D_METHOD("get_h_scrollbar"), &ScrollContainer::get_h_scrollbar);
+ ClassDB::bind_method(D_METHOD("get_v_scrollbar"), &ScrollContainer::get_v_scrollbar);
+
ADD_SIGNAL(MethodInfo("scroll_started"));
ADD_SIGNAL(MethodInfo("scroll_ended"));
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 3fe1ed447a..abef80294a 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -92,6 +92,9 @@ public:
int get_deadzone() const;
void set_deadzone(int p_deadzone);
+ HScrollBar *get_h_scrollbar();
+ VScrollBar *get_v_scrollbar();
+
virtual bool clips_input() const;
virtual String get_configuration_warning() const;
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/split_container.cpp b/scene/gui/split_container.cpp
index bf7033e8ba..c38c411333 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -33,13 +33,6 @@
#include "label.h"
#include "margin_container.h"
-struct _MinSizeCache {
-
- int min_size;
- bool will_stretch;
- int final_size;
-};
-
Control *SplitContainer::_getch(int p_idx) const {
int idx = 0;
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 0363dd44c2..c30fa96327 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -143,6 +143,42 @@ void TabContainer::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_RESIZED: {
+
+ Vector<Control *> tabs = _get_tabs();
+ int side_margin = get_constant("side_margin");
+ Ref<Texture> menu = get_icon("menu");
+ Ref<Texture> increment = get_icon("increment");
+ Ref<Texture> decrement = get_icon("decrement");
+ int header_width = get_size().width - side_margin * 2;
+
+ // Find the width of the header area.
+ if (popup)
+ header_width -= menu->get_width();
+ if (buttons_visible_cache)
+ header_width -= increment->get_width() + decrement->get_width();
+ if (popup || buttons_visible_cache)
+ header_width += side_margin;
+
+ // Find the width of all tabs after first_tab_cache.
+ int all_tabs_width = 0;
+ for (int i = first_tab_cache; i < tabs.size(); i++) {
+ int tab_width = _get_tab_width(i);
+ all_tabs_width += tab_width;
+ }
+
+ // Check if tabs before first_tab_cache would fit into the header area.
+ for (int i = first_tab_cache - 1; i >= 0; i--) {
+ int tab_width = _get_tab_width(i);
+
+ if (all_tabs_width + tab_width > header_width)
+ break;
+
+ all_tabs_width += tab_width;
+ first_tab_cache--;
+ }
+ } break;
+
case NOTIFICATION_DRAW: {
RID canvas = get_canvas_item();
@@ -197,6 +233,10 @@ void TabContainer::_notification(int p_what) {
header_width += side_margin;
}
+ if (!buttons_visible_cache) {
+ first_tab_cache = 0;
+ }
+
// Go through the visible tabs to find the width they occupy.
all_tabs_width = 0;
Vector<int> tab_widths;
@@ -367,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)
@@ -475,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 4c9f515ced..218b5060a1 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -117,7 +117,6 @@ void TextEdit::Text::set_indent_size(int p_indent_size) {
void TextEdit::Text::_update_line_cache(int p_line) const {
int w = 0;
- int tab_w = font->get_char_size(' ').width * indent_size;
int len = text[p_line].data.length();
const CharType *str = text[p_line].data.c_str();
@@ -125,22 +124,13 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
//update width
for (int i = 0; i < len; i++) {
- if (str[i] == '\t') {
-
- int left = w % tab_w;
- if (left == 0)
- w += tab_w;
- else
- w += tab_w - w % tab_w; // is right...
-
- } else {
-
- w += font->get_char_size(str[i], str[i + 1]).width;
- }
+ w += get_char_width(str[i], str[i + 1], w);
}
text[p_line].width_cache = w;
+ text[p_line].wrap_amount_cache = -1;
+
//update regions
text[p_line].region_info.clear();
@@ -242,10 +232,32 @@ int TextEdit::Text::get_line_width(int p_line) const {
return text[p_line].width_cache;
}
-void TextEdit::Text::clear_caches() {
+void TextEdit::Text::set_line_wrap_amount(int p_line, int p_wrap_amount) const {
+
+ ERR_FAIL_INDEX(p_line, text.size());
+
+ text[p_line].wrap_amount_cache = p_wrap_amount;
+}
+
+int TextEdit::Text::get_line_wrap_amount(int p_line) const {
+
+ ERR_FAIL_INDEX_V(p_line, text.size(), -1);
+
+ return text[p_line].wrap_amount_cache;
+}
- for (int i = 0; i < text.size(); i++)
+void TextEdit::Text::clear_width_cache() {
+
+ for (int i = 0; i < text.size(); i++) {
text[i].width_cache = -1;
+ }
+}
+
+void TextEdit::Text::clear_wrap_cache() {
+
+ for (int i = 0; i < text.size(); i++) {
+ text[i].wrap_amount_cache = -1;
+ }
}
void TextEdit::Text::clear() {
@@ -270,6 +282,7 @@ void TextEdit::Text::set(int p_line, const String &p_text) {
ERR_FAIL_INDEX(p_line, text.size());
text[p_line].width_cache = -1;
+ text[p_line].wrap_amount_cache = -1;
text[p_line].data = p_text;
}
@@ -280,6 +293,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) {
line.breakpoint = false;
line.hidden = false;
line.width_cache = -1;
+ line.wrap_amount_cache = -1;
line.data = p_text;
text.insert(p_at, line);
}
@@ -288,6 +302,25 @@ void TextEdit::Text::remove(int p_at) {
text.remove(p_at);
}
+int TextEdit::Text::get_char_width(CharType c, CharType next_c, int px) const {
+
+ int tab_w = font->get_char_size(' ').width * indent_size;
+ int w = 0;
+
+ if (c == '\t') {
+
+ int left = px % tab_w;
+ if (left == 0)
+ w = tab_w;
+ else
+ w = tab_w - px % tab_w; // is right...
+ } else {
+
+ w = font->get_char_size(c, next_c).width;
+ }
+ return w;
+}
+
void TextEdit::_update_scrollbars() {
Size2 size = get_size();
@@ -302,9 +335,12 @@ void TextEdit::_update_scrollbars() {
int hscroll_rows = ((hmin.height - 1) / get_row_height()) + 1;
int visible_rows = get_visible_rows();
- int num_rows = MAX(visible_rows, num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), MIN(visible_rows, text.size() - 1 - cursor.line_ofs)));
- int total_rows = (is_hiding_enabled() ? get_total_unhidden_rows() : text.size());
+ int first_vis_line = get_first_visible_line();
+ int wi;
+ int num_rows = MAX(visible_rows, num_lines_from_rows(first_vis_line, cursor.wrap_ofs, visible_rows, wi));
+
+ int total_rows = get_total_visible_rows();
if (scroll_past_end_of_file_enabled) {
total_rows += visible_rows - 1;
}
@@ -350,28 +386,24 @@ void TextEdit::_update_scrollbars() {
if (use_vscroll) {
v_scroll->show();
- v_scroll->set_max(total_rows);
- v_scroll->set_page(visible_rows);
+ v_scroll->set_max(total_rows + get_visible_rows_offset());
+ v_scroll->set_page(visible_rows + get_visible_rows_offset());
if (smooth_scroll_enabled) {
v_scroll->set_step(0.25);
} else {
v_scroll->set_step(1);
}
-
- update_line_scroll_pos();
- if (fabs(v_scroll->get_value() - get_line_scroll_pos()) >= 1) {
- cursor.line_ofs += v_scroll->get_value() - get_line_scroll_pos();
- }
+ set_v_scroll(get_v_scroll());
} else {
cursor.line_ofs = 0;
- line_scroll_pos = 0;
+ cursor.wrap_ofs = 0;
v_scroll->set_value(0);
v_scroll->hide();
}
- if (use_hscroll) {
+ if (use_hscroll && !is_wrap_enabled()) {
h_scroll->show();
h_scroll->set_max(total_width);
@@ -394,6 +426,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: {
@@ -415,14 +450,14 @@ void TextEdit::_click_selection_held() {
}
void TextEdit::_update_selection_mode_pointer() {
- Point2 mp = Input::get_singleton()->get_mouse_position() - get_global_position();
+ Point2 mp = get_local_mouse_position();
int row, col;
_get_mouse_pos(Point2i(mp.x, mp.y), row, col);
select(selection.selecting_line, selection.selecting_column, row, col);
- cursor_set_line(row);
+ cursor_set_line(row, false);
cursor_set_column(col);
update();
@@ -430,7 +465,7 @@ void TextEdit::_update_selection_mode_pointer() {
}
void TextEdit::_update_selection_mode_word() {
- Point2 mp = Input::get_singleton()->get_mouse_position() - get_global_position();
+ Point2 mp = get_local_mouse_position();
int row, col;
_get_mouse_pos(Point2i(mp.x, mp.y), row, col);
@@ -464,26 +499,29 @@ 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);
update();
+
click_select_held->start();
}
void TextEdit::_update_selection_mode_line() {
- Point2 mp = Input::get_singleton()->get_mouse_position() - get_global_position();
+ Point2 mp = get_local_mouse_position();
int row, col;
_get_mouse_pos(Point2i(mp.x, mp.y), row, col);
@@ -491,11 +529,11 @@ void TextEdit::_update_selection_mode_line() {
col = 0;
if (row < selection.selecting_line) {
// cursor is above us
- cursor_set_line(row - 1);
+ cursor_set_line(row - 1, false);
selection.selecting_column = text[selection.selecting_line].length();
} else {
// cursor is below us
- cursor_set_line(row + 1);
+ cursor_set_line(row + 1, false);
selection.selecting_column = 0;
col = text[row].length();
}
@@ -517,13 +555,13 @@ void TextEdit::_notification(int p_what) {
MessageQueue::get_singleton()->push_call(this, "_cursor_changed_emit");
if (text_changed_dirty)
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
-
+ update_wrap_at();
} break;
case NOTIFICATION_RESIZED: {
cache.size = get_size();
- adjust_viewport_to_cursor();
-
+ _update_scrollbars();
+ update_wrap_at();
} break;
case NOTIFICATION_THEME_CHANGED: {
@@ -540,17 +578,17 @@ void TextEdit::_notification(int p_what) {
update();
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (scrolling && v_scroll->get_value() != target_v_scroll) {
- double target_y = target_v_scroll - v_scroll->get_value();
+ if (scrolling && get_v_scroll() != target_v_scroll) {
+ double target_y = target_v_scroll - get_v_scroll();
double dist = sqrt(target_y * target_y);
double vel = ((target_y / dist) * v_scroll_speed) * get_physics_process_delta_time();
if (Math::abs(vel) >= dist) {
- v_scroll->set_value(target_v_scroll);
+ set_v_scroll(target_v_scroll);
scrolling = false;
set_physics_process_internal(false);
} else {
- v_scroll->set_value(v_scroll->get_value() + vel);
+ set_v_scroll(get_v_scroll() + vel);
}
} else {
scrolling = false;
@@ -777,12 +815,14 @@ void TextEdit::_notification(int p_what) {
String highlighted_text = get_selection_text();
String line_num_padding = line_numbers_zero_padded ? "0" : " ";
- update_line_scroll_pos();
- int line = cursor.line_ofs - 1;
- // another row may be visible during smooth scrolling
- int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0);
+ int cursor_wrap_index = get_cursor_wrap_index();
+
FontDrawer drawer(cache.font, Color(1, 1, 1));
+
+ int line = get_first_visible_line() - 1;
+ int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0);
+ draw_amount += times_line_wraps(line + 1);
for (int i = 0; i < draw_amount; i++) {
line++;
@@ -800,269 +840,363 @@ void TextEdit::_notification(int p_what) {
if (line < 0 || line >= (int)text.size())
continue;
- const String &str = text[line];
-
- int char_margin = xmargin_beg - cursor.x_ofs;
- int char_ofs = 0;
+ const String &fullstr = text[line];
- int ofs_readonly = 0;
- int ofs_x = 0;
+ Map<int, HighlighterInfo> color_map;
+ if (syntax_coloring) {
+ color_map = _get_line_syntax_highlighting(line);
+ }
+ // ensure we at least use the font color
+ Color current_color = cache.font_color;
if (readonly) {
- ofs_readonly = cache.style_readonly->get_offset().y / 2;
- ofs_x = cache.style_readonly->get_offset().x / 2;
+ current_color.a *= readonly_alpha;
}
- int ofs_y = (i * get_row_height() + cache.line_spacing / 2) + ofs_readonly;
- if (smooth_scroll_enabled)
- ofs_y -= ((v_scroll->get_value() - get_line_scroll_pos()) * get_row_height());
bool underlined = false;
- // check if line contains highlighted word
- int highlighted_text_col = -1;
- int search_text_col = -1;
- int highlighted_word_col = -1;
+ int line_wrap_amount = times_line_wraps(line);
+ int last_wrap_column = 0;
+ Vector<String> wrap_rows = get_wrap_rows_text(line);
+
+ for (int line_wrap_index = 0; line_wrap_index < line_wrap_amount + 1; line_wrap_index++) {
+ if (line_wrap_index != 0) {
+ i++;
+ if (i >= draw_amount)
+ break;
+ }
+
+ const String &str = wrap_rows[line_wrap_index];
+ int indent_px = line_wrap_index != 0 ? get_indent_level(line) * cache.font->get_char_size(' ').width : 0;
- if (!search_text.empty())
- search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0);
+ if (line_wrap_index > 0)
+ last_wrap_column += wrap_rows[line_wrap_index - 1].length();
- if (highlighted_text.length() != 0 && highlighted_text != search_text)
- highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
+ int char_margin = xmargin_beg - cursor.x_ofs;
+ char_margin += indent_px;
+ int char_ofs = 0;
- if (select_identifiers_enabled && highlighted_word.length() != 0) {
- if (_is_char(highlighted_word[0])) {
- highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
+ int ofs_readonly = 0;
+ int ofs_x = 0;
+ if (readonly) {
+ ofs_readonly = cache.style_readonly->get_offset().y / 2;
+ ofs_x = cache.style_readonly->get_offset().x / 2;
}
- }
- if (text.is_marked(line)) {
+ int ofs_y = (i * get_row_height() + cache.line_spacing / 2) + ofs_readonly;
+ ofs_y -= cursor.wrap_ofs * get_row_height();
+ if (smooth_scroll_enabled)
+ ofs_y += (-get_v_scroll_offset()) * get_row_height();
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color);
- }
+ // check if line contains highlighted word
+ int highlighted_text_col = -1;
+ int search_text_col = -1;
+ int highlighted_word_col = -1;
+
+ if (!search_text.empty())
+ search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0);
- if (str.length() == 0) {
- // draw line background if empty as we won't loop at at all
- if (line == cursor.line && highlight_current_line) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, get_row_height()), cache.current_line_color);
+ if (highlighted_text.length() != 0 && highlighted_text != search_text)
+ highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
+
+ if (select_identifiers_enabled && highlighted_word.length() != 0) {
+ if (_is_char(highlighted_word[0])) {
+ highlighted_word_col = _get_column_pos_of_word(highlighted_word, fullstr, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
+ }
}
- // give visual indication of empty selected line
- if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) {
- int char_w = cache.font->get_char_size(' ').width;
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, get_row_height()), cache.selection_color);
+ if (text.is_marked(line)) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color);
}
- } else {
- // if it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later.
- if (line == cursor.line && highlight_current_line) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg + ofs_x, get_row_height()), cache.current_line_color);
+
+ if (str.length() == 0) {
+ // draw line background if empty as we won't loop at at all
+ if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, get_row_height()), cache.current_line_color);
+ }
+
+ // give visual indication of empty selected line
+ if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) {
+ int char_w = cache.font->get_char_size(' ').width;
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, get_row_height()), cache.selection_color);
+ }
+ } else {
+ // if it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later.
+ if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg, get_row_height()), cache.current_line_color);
+ }
}
- }
- if (text.is_breakpoint(line) && !draw_breakpoint_gutter) {
+ if (line_wrap_index == 0) {
+ // only do these if we are on the first wrapped part of a line
+
+ if (text.is_breakpoint(line) && !draw_breakpoint_gutter) {
#ifdef TOOLS_ENABLED
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.breakpoint_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.breakpoint_color);
#else
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.breakpoint_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.breakpoint_color);
#endif
- }
+ }
- // draw breakpoint marker
- if (text.is_breakpoint(line)) {
- if (draw_breakpoint_gutter) {
- int vertical_gap = (get_row_height() * 40) / 100;
- int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
- int marker_height = get_row_height() - (vertical_gap * 2);
- int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2);
- // no transparency on marker
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2, ofs_y + vertical_gap, marker_width, marker_height), Color(cache.breakpoint_color.r, cache.breakpoint_color.g, cache.breakpoint_color.b));
- }
- }
+ // draw breakpoint marker
+ if (text.is_breakpoint(line)) {
+ if (draw_breakpoint_gutter) {
+ int vertical_gap = (get_row_height() * 40) / 100;
+ int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
+ int marker_height = get_row_height() - (vertical_gap * 2);
+ int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2);
+ // no transparency on marker
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2, ofs_y + vertical_gap, marker_width, marker_height), Color(cache.breakpoint_color.r, cache.breakpoint_color.g, cache.breakpoint_color.b));
+ }
+ }
- // draw fold markers
- if (draw_fold_gutter) {
- int horizontal_gap = (cache.fold_gutter_width * 30) / 100;
- int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w;
- if (is_folded(line)) {
- int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2;
- int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2;
- cache.folded_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
- } else if (can_fold(line)) {
- int xofs = -cache.can_fold_icon->get_width() / 2 - horizontal_gap + 3;
- int yofs = (get_row_height() - cache.can_fold_icon->get_height()) / 2;
- cache.can_fold_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
- }
- }
+ // draw fold markers
+ if (draw_fold_gutter) {
+ int horizontal_gap = (cache.fold_gutter_width * 30) / 100;
+ int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w;
+ if (is_folded(line)) {
+ int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2;
+ int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2;
+ cache.folded_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
+ } else if (can_fold(line)) {
+ int xofs = -cache.can_fold_icon->get_width() / 2 - horizontal_gap + 3;
+ int yofs = (get_row_height() - cache.can_fold_icon->get_height()) / 2;
+ cache.can_fold_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
+ }
+ }
+
+ // 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;
+ }
- if (cache.line_number_w) {
- 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, yofs + 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, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
- }
+ //loop through characters in one line
+ for (int j = 0; j < str.length(); j++) {
- //loop through characters in one line
- Map<int, HighlighterInfo> color_map;
- if (syntax_coloring) {
- color_map = _get_line_syntax_highlighting(line);
- }
+ if (syntax_coloring) {
+ if (color_map.has(last_wrap_column + j)) {
+ current_color = color_map[last_wrap_column + j].color;
+ if (readonly) {
+ current_color.a *= readonly_alpha;
+ }
+ }
+ color = current_color;
+ }
- // ensure we at least use the font color
- Color current_color = cache.font_color;
- if (readonly) {
- current_color.a *= readonly_alpha;
- }
- for (int j = 0; j < str.length(); j++) {
+ int char_w;
+
+ //handle tabulator
+ char_w = text.get_char_width(str[j], str[j + 1], char_ofs);
+
+ if ((char_ofs + char_margin) < xmargin_beg) {
+ char_ofs += char_w;
+
+ // line highlighting handle horizontal clipping
+ if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
- if (syntax_coloring) {
- if (color_map.has(j)) {
- current_color = color_map[j].color;
- if (readonly) {
- current_color.a *= readonly_alpha;
+ if (j == str.length() - 1) {
+ // end of line when last char is skipped
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color);
+ } else if ((char_ofs + char_margin) > xmargin_beg) {
+ // char next to margin is skipped
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, (char_ofs + char_margin) - (xmargin_beg + ofs_x), get_row_height()), cache.current_line_color);
+ }
}
+ continue;
}
- color = current_color;
- }
- int char_w;
- //handle tabulator
+ if ((char_ofs + char_margin + char_w) >= xmargin_end) {
+ if (syntax_coloring)
+ continue;
+ else
+ break;
+ }
- if (str[j] == '\t') {
- int left = char_ofs % tab_w;
- if (left == 0)
- char_w = tab_w;
- else
- char_w = tab_w - char_ofs % tab_w; // is right...
+ bool in_search_result = false;
- } else {
- char_w = cache.font->get_char_size(str[j], str[j + 1]).width;
- }
+ if (search_text_col != -1) {
+ // if we are at the end check for new search result on same line
+ if (j >= search_text_col + search_text.length())
+ search_text_col = _get_column_pos_of_word(search_text, str, search_flags, j);
- if ((char_ofs + char_margin) < xmargin_beg) {
- char_ofs += char_w;
+ in_search_result = j >= search_text_col && j < search_text_col + search_text.length();
- // line highlighting handle horizontal clipping
- if (line == cursor.line && highlight_current_line) {
+ if (in_search_result) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.search_result_color);
+ }
+ }
- if (j == str.length() - 1) {
- // end of line when last char is skipped
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - (xmargin_beg + ofs_x), get_row_height()), cache.current_line_color);
+ //current line highlighting
+ bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || last_wrap_column + j >= selection.from_column) && (line < selection.to_line || last_wrap_column + j < selection.to_column));
- } else if ((char_ofs + char_margin) > xmargin_beg) {
- // char next to margin is skipped
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, (char_ofs + char_margin) - xmargin_beg, get_row_height()), cache.current_line_color);
+ if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
+ // draw the wrap indent offset highlight
+ if (line_wrap_index != 0 && j == 0) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, (char_ofs + char_margin), get_row_height()), cache.current_line_color);
+ }
+ // if its the last char draw to end of the line
+ if (j == str.length() - 1) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color);
+ }
+ // actual text
+ if (!in_selection) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color);
}
}
- continue;
- }
-
- if ((char_ofs + char_margin + char_w) >= xmargin_end) {
- if (syntax_coloring)
- continue;
- else
- break;
- }
- bool in_search_result = false;
+ if (in_selection) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color);
+ }
- if (search_text_col != -1) {
- // if we are at the end check for new search result on same line
- if (j >= search_text_col + search_text.length())
- search_text_col = _get_column_pos_of_word(search_text, str, search_flags, j);
+ if (in_search_result) {
+ Color border_color = (line == search_result_line && j >= search_result_col && j < search_result_col + search_text.length()) ? cache.font_color : cache.search_result_border_color;
- in_search_result = j >= search_text_col && j < search_text_col + search_text.length();
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, 1)), border_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y + get_row_height() - 1), Size2i(char_w, 1)), border_color);
- if (in_search_result) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.search_result_color);
+ if (j == search_text_col)
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(1, get_row_height())), border_color);
+ if (j == search_text_col + search_text.length() - 1)
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + char_w + ofs_x - 1, ofs_y), Size2i(1, get_row_height())), border_color);
}
- }
- //current line highlighting
- bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || j >= selection.from_column) && (line < selection.to_line || j < selection.to_column));
+ if (highlight_all_occurrences) {
+ if (highlighted_text_col != -1) {
+
+ // if we are at the end check for new word on same line
+ if (j > highlighted_text_col + highlighted_text.length()) {
+ highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j);
+ }
+
+ bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col + highlighted_text.length());
- if (line == cursor.line && highlight_current_line) {
- // if its the last char draw to end of the line
- if (j == str.length() - 1) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color);
+ // if this is the original highlighted text we don't want to highlight it again
+ if (cursor.line == line && cursor_wrap_index == line_wrap_index && (cursor.column >= highlighted_text_col && cursor.column <= highlighted_text_col + highlighted_text.length())) {
+ in_highlighted_word = false;
+ }
+
+ if (in_highlighted_word) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.word_highlighted_color);
+ }
+ }
}
- // actual text
- if (!in_selection) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color);
+
+ if (highlighted_word_col != -1) {
+ if (j + last_wrap_column > highlighted_word_col + highlighted_word.length()) {
+ highlighted_word_col = _get_column_pos_of_word(highlighted_word, fullstr, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j + last_wrap_column);
+ }
+ underlined = (j + last_wrap_column >= highlighted_word_col && j + last_wrap_column < highlighted_word_col + highlighted_word.length());
}
- }
- if (in_selection) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color);
- }
+ 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 (in_search_result) {
- Color border_color = (line == search_result_line && j >= search_result_col && j < search_result_col + search_text.length()) ? cache.font_color : cache.search_result_border_color;
+ if (brace_open_mismatch)
+ color = cache.brace_mismatch_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);
+ }
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, 1)), border_color);
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y + get_row_height() - 1), Size2i(char_w, 1)), border_color);
+ if ((brace_close_match_line == line && brace_close_match_column == last_wrap_column + j) ||
+ (cursor.column == last_wrap_column + j + 1 && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_close_matching || brace_close_mismatch))) {
- if (j == search_text_col)
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(1, get_row_height())), border_color);
- if (j == search_text_col + search_text.length() - 1)
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + char_w + ofs_x - 1, ofs_y), Size2i(1, get_row_height())), border_color);
- }
+ if (brace_close_mismatch)
+ color = cache.brace_mismatch_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 (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index) {
- if (highlight_all_occurrences) {
- if (highlighted_text_col != -1) {
+ cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y);
- // if we are at the end check for new word on same line
- if (j > highlighted_text_col + highlighted_text.length()) {
- highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j);
+ if (insert_mode) {
+ cursor_pos.y += (get_row_height() - 3);
}
- bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col + highlighted_text.length());
+ int caret_w = (str[j] == '\t') ? cache.font->get_char_size(' ').width : char_w;
+ if (ime_text.length() > 0) {
+ int ofs = 0;
+ while (true) {
+ if (ofs >= ime_text.length())
+ break;
- /* if this is the original highlighted text we don't want to highlight it again */
- if (cursor.line == line && (cursor.column >= highlighted_text_col && cursor.column <= highlighted_text_col + highlighted_text.length())) {
- in_highlighted_word = false;
- }
+ CharType cchar = ime_text[ofs];
+ CharType next = ime_text[ofs + 1];
+ int im_char_width = cache.font->get_char_size(cchar, next).width;
- if (in_highlighted_word) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.word_highlighted_color);
+ if ((char_ofs + char_margin + im_char_width) >= xmargin_end)
+ break;
+
+ bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y;
+ if (selected) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color);
+ } else {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
+ }
+
+ drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
+
+ char_ofs += im_char_width;
+ ofs++;
+ }
+ }
+ if (ime_text.length() == 0) {
+ if (draw_caret) {
+ if (insert_mode) {
+ int caret_h = (block_caret) ? 4 : 1;
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, caret_h)), cache.caret_color);
+ } else {
+ caret_w = (block_caret) ? caret_w : 1;
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color);
+ }
+ }
}
}
- }
- if (highlighted_word_col != -1) {
- if (j > highlighted_word_col + highlighted_word.length()) {
- highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j);
+ if (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && block_caret && draw_caret && !insert_mode) {
+ color = cache.caret_background_color;
+ } else if (!syntax_coloring && block_caret) {
+ color = cache.font_color;
+ color.a *= readonly_alpha;
}
- underlined = (j >= highlighted_word_col && j < highlighted_word_col + highlighted_word.length());
- }
- if (brace_matching_enabled) {
- if ((brace_open_match_line == line && brace_open_match_column == j) ||
- (cursor.column == j && cursor.line == line && (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);
+ if (str[j] >= 32) {
+ 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, 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;
+ cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
- if (
- (brace_close_match_line == line && brace_close_match_column == j) ||
- (cursor.column == j + 1 && cursor.line == line && (brace_close_matching || brace_close_mismatch))) {
+ char_ofs += char_w;
- 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);
+ if (line_wrap_index == line_wrap_amount && j == str.length() - 1 && is_folded(line)) {
+ int yofs = (get_row_height() - cache.folded_eol_icon->get_height()) / 2;
+ int xofs = cache.folded_eol_icon->get_width() / 2;
+ Color eol_color = cache.code_folding_color;
+ eol_color.a = 1;
+ cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs + ofs_x, ofs_y + yofs), eol_color);
}
}
- if (cursor.column == j && cursor.line == line) {
+ if (cursor.column == last_wrap_column + str.length() && cursor.line == line && cursor_wrap_index == line_wrap_index && (char_ofs + char_margin) >= xmargin_beg) {
cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y);
if (insert_mode) {
cursor_pos.y += (get_row_height() - 3);
}
-
- int caret_w = (str[j] == '\t') ? cache.font->get_char_size(' ').width : char_w;
if (ime_text.length() > 0) {
int ofs = 0;
while (true) {
@@ -1092,92 +1226,17 @@ void TextEdit::_notification(int p_what) {
if (ime_text.length() == 0) {
if (draw_caret) {
if (insert_mode) {
+ int char_w = cache.font->get_char_size(' ').width;
int caret_h = (block_caret) ? 4 : 1;
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, caret_h)), cache.caret_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(char_w, caret_h)), cache.caret_color);
} else {
- caret_w = (block_caret) ? caret_w : 1;
+ int char_w = cache.font->get_char_size(' ').width;
+ int caret_w = (block_caret) ? char_w : 1;
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color);
}
}
}
}
-
- if (cursor.column == j && cursor.line == line && block_caret && draw_caret && !insert_mode) {
- color = cache.caret_background_color;
- } else if (!syntax_coloring && block_caret) {
- color = cache.font_color;
- color.a *= readonly_alpha;
- }
-
- 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);
- 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);
- }
- }
-
- else if (draw_tabs && str[j] == '\t') {
- int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2;
- cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color);
- }
-
- char_ofs += char_w;
-
- if (j == str.length() - 1 && is_folded(line)) {
- int yofs = (get_row_height() - cache.folded_eol_icon->get_height()) / 2;
- int xofs = cache.folded_eol_icon->get_width() / 2;
- Color eol_color = cache.code_folding_color;
- eol_color.a = 1;
- cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs + ofs_x, ofs_y + yofs), eol_color);
- }
- }
-
- if (cursor.column == str.length() && cursor.line == line && (char_ofs + char_margin) >= xmargin_beg) {
-
- cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y);
-
- if (insert_mode) {
- cursor_pos.y += (get_row_height() - 3);
- }
- if (ime_text.length() > 0) {
- int ofs = 0;
- while (true) {
- if (ofs >= ime_text.length())
- break;
-
- CharType cchar = ime_text[ofs];
- CharType next = ime_text[ofs + 1];
- int im_char_width = cache.font->get_char_size(cchar, next).width;
-
- if ((char_ofs + char_margin + im_char_width) >= xmargin_end)
- break;
-
- bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y;
- if (selected) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color);
- } else {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
- }
-
- drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
-
- char_ofs += im_char_width;
- ofs++;
- }
- }
- if (ime_text.length() == 0) {
- if (draw_caret) {
- if (insert_mode) {
- int char_w = cache.font->get_char_size(' ').width;
- int caret_h = (block_caret) ? 4 : 1;
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(char_w, caret_h)), cache.caret_color);
- } else {
- int char_w = cache.font->get_char_size(' ').width;
- int caret_w = (block_caret) ? char_w : 1;
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color);
- }
- }
- }
}
}
@@ -1338,6 +1397,7 @@ void TextEdit::_notification(int p_what) {
}
if (has_focus()) {
+ OS::get_singleton()->set_ime_active(true);
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos + Point2(0, get_row_height()));
OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
}
@@ -1349,6 +1409,7 @@ void TextEdit::_notification(int p_what) {
draw_caret = true;
}
+ OS::get_singleton()->set_ime_active(true);
Point2 cursor_pos = Point2(cursor_get_column(), cursor_get_line()) * get_row_height();
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos);
OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
@@ -1363,6 +1424,7 @@ void TextEdit::_notification(int p_what) {
OS::get_singleton()->set_ime_position(Point2());
OS::get_singleton()->set_ime_intermediate_text_callback(NULL, NULL);
+ OS::get_singleton()->set_ime_active(false);
ime_text = "";
ime_selection = Point2();
@@ -1475,8 +1537,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 &&
@@ -1604,16 +1669,20 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
float rows = p_mouse.y;
rows -= cache.style_normal->get_margin(MARGIN_TOP);
- rows += (CLAMP(v_scroll->get_value() - get_line_scroll_pos(true), 0, 1) * get_row_height());
rows /= get_row_height();
- int first_vis_line = CLAMP(cursor.line_ofs, 0, text.size() - 1);
+ 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()) {
- if (is_hiding_enabled()) {
- // row will be offset by the hidden rows
- int f_ofs = num_lines_from(first_vis_line, rows + 1) - 1;
- row = first_vis_line + f_ofs;
- row = CLAMP(row, 0, text.size() - num_lines_from(text.size() - 1, -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)
@@ -1627,9 +1696,19 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
col = text[row].size();
} else {
- col = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width);
- col += cursor.x_ofs;
- col = get_char_pos_for(col, get_line(row));
+ int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width);
+ colx += cursor.x_ofs;
+ col = get_char_pos_for_line(colx, row, wrap_index);
+ if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) {
+ // move back one if we are at the end of the row
+ Vector<String> rows = get_wrap_rows_text(row);
+ int row_end_col = 0;
+ for (int i = 0; i < wrap_index + 1; i++) {
+ row_end_col += rows[i].length();
+ }
+ if (col >= row_end_col)
+ col -= 1;
+ }
}
r_row = row;
@@ -1704,7 +1783,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_reset_caret_blink_timer();
int row, col;
- update_line_scroll_pos();
_get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col);
if (mb->get_command() && highlighted_word != String()) {
@@ -1836,7 +1914,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_reset_caret_blink_timer();
int row, col;
- update_line_scroll_pos();
_get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col);
if (is_right_click_moving_caret()) {
@@ -2453,13 +2530,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
break;
} else if (k->get_command()) {
#endif
- bool prev_char = false;
int cc = cursor.column;
if (cc == 0 && cursor.line > 0) {
cursor_set_line(cursor.line - 1);
cursor_set_column(text[cursor.line].length());
} else {
+ bool prev_char = false;
+
while (cc > 0) {
bool ischar = _is_text_char(text[cursor.line][cc - 1]);
@@ -2514,13 +2592,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
break;
} else if (k->get_command()) {
#endif
- bool prev_char = false;
int cc = cursor.column;
if (cc == text[cursor.line].length() && cursor.line < text.size() - 1) {
cursor_set_line(cursor.line + 1);
cursor_set_column(0);
} else {
+ bool prev_char = false;
+
while (cc < text[cursor.line].length()) {
bool ischar = _is_text_char(text[cursor.line][cc]);
@@ -2572,11 +2651,25 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
break;
}
- if (k->get_command())
+ if (k->get_command()) {
cursor_set_line(0);
- else
+ } else
#endif
- cursor_set_line(cursor_get_line() - num_lines_from(CLAMP(cursor.line - 1, 0, text.size() - 1), -1));
+ {
+ int cur_wrap_index = get_cursor_wrap_index();
+ if (cur_wrap_index > 0) {
+ cursor_set_line(cursor.line, true, false, cur_wrap_index - 1);
+ } else if (cursor.line == 0) {
+ cursor_set_column(0);
+ } else {
+ int new_line = cursor.line - num_lines_from(cursor.line - 1, -1);
+ if (line_wraps(new_line)) {
+ cursor_set_line(new_line, true, false, times_line_wraps(new_line));
+ } else {
+ cursor_set_line(new_line, true, false);
+ }
+ }
+ }
if (k->get_shift())
_post_shift_selection();
@@ -2604,22 +2697,25 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
break;
}
- {
#else
if (k->get_command() && k->get_alt()) {
_scroll_lines_down();
break;
}
- if (k->get_command())
- cursor_set_line(text.size() - 1, true, false);
- else {
+ if (k->get_command()) {
+ cursor_set_line(get_last_unhidden_line(), true, false, 9999);
+ } else
#endif
- if (!is_last_visible_line(cursor.line)) {
- cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false);
+ {
+ int cur_wrap_index = get_cursor_wrap_index();
+ if (cur_wrap_index < times_line_wraps(cursor.line)) {
+ cursor_set_line(cursor.line, true, false, cur_wrap_index + 1);
+ } else if (cursor.line == get_last_unhidden_line()) {
+ cursor_set_column(text[cursor.line].length());
} else {
- cursor_set_line(text.size() - 1);
- cursor_set_column(get_line(cursor.line).length(), true);
+ int new_line = cursor.line + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1);
+ cursor_set_line(new_line, true, false, 0);
}
}
@@ -2628,7 +2724,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_cancel_code_hint();
} break;
-
case KEY_DELETE: {
if (readonly)
@@ -2735,19 +2830,31 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
cursor_set_line(0);
cursor_set_column(0);
} else {
- // compute whitespace symbols seq length
- int current_line_whitespace_len = 0;
- while (current_line_whitespace_len < text[cursor.line].length()) {
- CharType c = text[cursor.line][current_line_whitespace_len];
- if (c != '\t' && c != ' ')
- break;
- current_line_whitespace_len++;
+
+ // move cursor column to start of wrapped row and then to start of text
+ Vector<String> rows = get_wrap_rows_text(cursor.line);
+ int wi = get_cursor_wrap_index();
+ int row_start_col = 0;
+ for (int i = 0; i < wi; i++) {
+ row_start_col += rows[i].length();
}
+ if (cursor.column == row_start_col || wi == 0) {
+ // compute whitespace symbols seq length
+ int current_line_whitespace_len = 0;
+ while (current_line_whitespace_len < text[cursor.line].length()) {
+ CharType c = text[cursor.line][current_line_whitespace_len];
+ if (c != '\t' && c != ' ')
+ break;
+ current_line_whitespace_len++;
+ }
- if (cursor_get_column() == current_line_whitespace_len)
- cursor_set_column(0);
- else
- cursor_set_column(current_line_whitespace_len);
+ if (cursor_get_column() == current_line_whitespace_len)
+ cursor_set_column(0);
+ else
+ cursor_set_column(current_line_whitespace_len);
+ } else {
+ cursor_set_column(row_start_col);
+ }
}
if (k->get_shift())
@@ -2772,7 +2879,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->get_shift())
_pre_shift_selection();
- cursor_set_line(text.size() - 1, true, false);
+ cursor_set_line(get_last_unhidden_line(), true, false, 9999);
if (k->get_shift())
_post_shift_selection();
@@ -2787,8 +2894,20 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_pre_shift_selection();
if (k->get_command())
- cursor_set_line(text.size() - 1, true, false);
- cursor_set_column(text[cursor.line].length());
+ cursor_set_line(get_last_unhidden_line(), true, false, 9999);
+
+ // move cursor column to end of wrapped row and then to end of text
+ Vector<String> rows = get_wrap_rows_text(cursor.line);
+ int wi = get_cursor_wrap_index();
+ int row_end_col = -1;
+ for (int i = 0; i < wi + 1; i++) {
+ row_end_col += rows[i].length();
+ }
+ if (wi == rows.size() - 1 || cursor.column == row_end_col) {
+ cursor_set_column(text[cursor.line].length());
+ } else {
+ cursor_set_column(row_end_col);
+ }
if (k->get_shift())
_post_shift_selection();
@@ -2812,7 +2931,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->get_shift())
_pre_shift_selection();
- cursor_set_line(cursor_get_line() - num_lines_from(cursor.line, -get_visible_rows()), true, false);
+ int wi;
+ int n_line = cursor.line - num_lines_from_rows(cursor.line, get_cursor_wrap_index(), -get_visible_rows(), wi) + 1;
+ cursor_set_line(n_line, true, false, wi);
if (k->get_shift())
_post_shift_selection();
@@ -2826,14 +2947,16 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
scancode_handled = false;
break;
}
- // numlock disabled. fallthrough to key_pageup
+ // numlock disabled. fallthrough to key_pagedown
}
case KEY_PAGEDOWN: {
if (k->get_shift())
_pre_shift_selection();
- cursor_set_line(cursor_get_line() + num_lines_from(cursor.line, get_visible_rows()), true, false);
+ int wi;
+ int n_line = cursor.line + num_lines_from_rows(cursor.line, get_cursor_wrap_index(), get_visible_rows(), wi) - 1;
+ cursor_set_line(n_line, true, false, wi);
if (k->get_shift())
_post_shift_selection();
@@ -2845,13 +2968,13 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
case KEY_A: {
#ifndef APPLE_STYLE_KEYS
- if (!k->get_command() || k->get_shift() || k->get_alt()) {
+ if (!k->get_control() || k->get_shift() || k->get_alt()) {
scancode_handled = false;
break;
}
select_all();
#else
- if (k->get_alt()) {
+ if ((!k->get_command() && !k->get_control())) {
scancode_handled = false;
break;
}
@@ -3078,7 +3201,7 @@ void TextEdit::_scroll_up(real_t p_delta) {
if (scrolling) {
target_v_scroll = (target_v_scroll - p_delta);
} else {
- target_v_scroll = (v_scroll->get_value() - p_delta);
+ target_v_scroll = (get_v_scroll() - p_delta);
}
if (smooth_scroll_enabled) {
@@ -3092,7 +3215,7 @@ void TextEdit::_scroll_up(real_t p_delta) {
set_physics_process_internal(true);
}
} else {
- v_scroll->set_value(target_v_scroll);
+ set_v_scroll(target_v_scroll);
}
}
@@ -3104,20 +3227,15 @@ void TextEdit::_scroll_down(real_t p_delta) {
if (scrolling) {
target_v_scroll = (target_v_scroll + p_delta);
} else {
- target_v_scroll = (v_scroll->get_value() + p_delta);
+ target_v_scroll = (get_v_scroll() + p_delta);
}
if (smooth_scroll_enabled) {
- int max_v_scroll = get_total_unhidden_rows();
- if (!scroll_past_end_of_file_enabled) {
- max_v_scroll -= get_visible_rows();
- max_v_scroll = CLAMP(max_v_scroll, 0, get_total_unhidden_rows());
- }
-
+ int max_v_scroll = v_scroll->get_max() - v_scroll->get_page();
if (target_v_scroll > max_v_scroll) {
target_v_scroll = max_v_scroll;
+ v_scroll->set_value(target_v_scroll);
}
-
if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) {
v_scroll->set_value(target_v_scroll);
} else {
@@ -3125,7 +3243,7 @@ void TextEdit::_scroll_down(real_t p_delta) {
set_physics_process_internal(true);
}
} else {
- v_scroll->set_value(target_v_scroll);
+ set_v_scroll(target_v_scroll);
}
}
@@ -3156,35 +3274,37 @@ void TextEdit::_scroll_lines_up() {
scrolling = false;
// adjust the vertical scroll
- if (get_v_scroll() >= 0) {
- set_v_scroll(get_v_scroll() - 1);
- }
+ set_v_scroll(get_v_scroll() - 1);
+
+ // adjust the cursor to viewport
+ if (!selection.active) {
+ int cur_line = cursor.line;
+ int cur_wrap = get_cursor_wrap_index();
+ int last_vis_line = get_last_visible_line();
+ int last_vis_wrap = get_last_visible_line_wrap_index();
- // adjust the cursor
- int num_lines = num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), get_visible_rows());
- if (cursor.line >= cursor.line_ofs + num_lines && !selection.active) {
- cursor_set_line(cursor.line_ofs + num_lines, false, false);
+ if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) {
+ cursor_set_line(last_vis_line, false, false, last_vis_wrap);
+ }
}
}
void TextEdit::_scroll_lines_down() {
scrolling = false;
- // calculate the maximum vertical scroll position
- int max_v_scroll = get_total_unhidden_rows();
- if (!scroll_past_end_of_file_enabled) {
- max_v_scroll -= get_visible_rows();
- max_v_scroll = CLAMP(max_v_scroll, 0, get_total_unhidden_rows());
- }
-
// adjust the vertical scroll
- if (get_v_scroll() < max_v_scroll) {
- set_v_scroll(get_v_scroll() + 1);
- }
+ set_v_scroll(get_v_scroll() + 1);
+
+ // adjust the cursor to viewport
+ if (!selection.active) {
+ int cur_line = cursor.line;
+ int cur_wrap = get_cursor_wrap_index();
+ int first_vis_line = get_first_visible_line();
+ int first_vis_wrap = cursor.wrap_ofs;
- // adjust the cursor
- if (cursor.line <= cursor.line_ofs - 1 && !selection.active) {
- cursor_set_line(cursor.line_ofs, false, false);
+ if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) {
+ cursor_set_line(first_vis_line, false, false, first_vis_wrap);
+ }
}
}
@@ -3196,22 +3316,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
@@ -3229,15 +3364,15 @@ 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);
text.set_hidden(p_line, false);
}
+ text.set_line_wrap_amount(p_line, -1);
+
r_end_line = p_line + substrings.size() - 1;
r_end_column = text[r_end_line].length() - postinsert_text.length();
@@ -3285,13 +3420,24 @@ 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);
+
if (!text_changed_dirty && !setting_text) {
if (is_inside_tree())
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
@@ -3450,61 +3596,63 @@ int TextEdit::get_visible_rows() const {
int total = cache.size.height;
total -= cache.style_normal->get_minimum_size().height;
+ if (h_scroll->is_visible_in_tree())
+ total -= h_scroll->get_size().height;
total /= get_row_height();
return total;
}
-int TextEdit::get_total_unhidden_rows() const {
- if (!is_hiding_enabled())
+int TextEdit::get_total_visible_rows() const {
+
+ // returns the total amount of rows we need in the editor.
+ // This skips hidden lines and counts each wrapping of a line.
+ if (!is_hiding_enabled() && !is_wrap_enabled())
return text.size();
- int total_unhidden = 0;
+ int total_rows = 0;
for (int i = 0; i < text.size(); i++) {
- if (!text.is_hidden(i))
- total_unhidden++;
+ if (!text.is_hidden(i)) {
+ total_rows++;
+ total_rows += times_line_wraps(i);
+ }
}
- return total_unhidden;
+ return total_rows;
}
-double TextEdit::get_line_scroll_pos(bool p_recalculate) const {
+void TextEdit::update_wrap_at() {
- if (!is_hiding_enabled())
- return cursor.line_ofs;
- if (!p_recalculate)
- return line_scroll_pos;
+ wrap_at = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - wrap_right_offset;
+ update_cursor_wrap_offset();
+ text.clear_wrap_cache();
- // count num unhidden lines to the cursor line ofs
- double new_line_scroll_pos = 0;
- int to = CLAMP(cursor.line_ofs, 0, text.size() - 1);
- for (int i = 0; i < to; i++) {
- if (!text.is_hidden(i))
- new_line_scroll_pos++;
+ for (int i = 0; i < text.size(); i++) {
+ // update all values that wrap
+ if (!line_wraps(i))
+ continue;
+ Vector<String> rows = get_wrap_rows_text(i);
+ text.set_line_wrap_amount(i, rows.size() - 1);
}
- return new_line_scroll_pos;
}
-void TextEdit::update_line_scroll_pos() {
+void TextEdit::adjust_viewport_to_cursor() {
- if (!is_hiding_enabled()) {
- line_scroll_pos = cursor.line_ofs;
- return;
- }
+ // make sure cursor is visible on the screen
+ scrolling = false;
- // count num unhidden lines to the cursor line ofs
- double new_line_scroll_pos = 0;
- int to = CLAMP(cursor.line_ofs, 0, text.size() - 1);
- for (int i = 0; i < to; i++) {
- if (!text.is_hidden(i))
- new_line_scroll_pos++;
- }
- line_scroll_pos = new_line_scroll_pos;
-}
+ int cur_line = cursor.line;
+ int cur_wrap = get_cursor_wrap_index();
-void TextEdit::adjust_viewport_to_cursor() {
- scrolling = false;
+ int first_vis_line = get_first_visible_line();
+ int first_vis_wrap = cursor.wrap_ofs;
+ int last_vis_line = get_last_visible_line();
+ int last_vis_wrap = get_last_visible_line_wrap_index();
- if (cursor.line_ofs > cursor.line) {
- cursor.line_ofs = cursor.line;
+ if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) {
+ // cursor is above screen
+ set_line_as_first_visible(cur_line, cur_wrap);
+ } else if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) {
+ // cursor is below screen
+ set_line_as_last_visible(cur_line, cur_wrap);
}
int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width;
@@ -3512,91 +3660,174 @@ void TextEdit::adjust_viewport_to_cursor() {
visible_width -= v_scroll->get_combined_minimum_size().width;
visible_width -= 20; // give it a little more space
- int visible_rows = get_visible_rows();
- if (h_scroll->is_visible_in_tree() && !scroll_past_end_of_file_enabled)
- visible_rows -= ((h_scroll->get_combined_minimum_size().height - 1) / get_row_height());
- int num_rows = num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), MIN(visible_rows, text.size() - 1 - cursor.line_ofs));
+ if (!is_wrap_enabled()) {
+ // adjust x offset
+ int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]);
- // make sure the cursor is on the screen
- // above the caret
- if (cursor.line > (cursor.line_ofs + MAX(num_rows, visible_rows))) {
- cursor.line_ofs = cursor.line - num_lines_from(cursor.line, -visible_rows) + 1;
- }
- // below the caret
- if (cursor.line_ofs == cursor.line) {
- cursor.line_ofs = cursor.line - 2;
- }
- int line_ofs_max = text.size() - 1;
- if (!scroll_past_end_of_file_enabled) {
- line_ofs_max -= num_lines_from(text.size() - 1, -visible_rows) - 1;
- line_ofs_max += (h_scroll->is_visible_in_tree() ? 1 : 0);
- line_ofs_max += (cursor.line == text.size() - 1 ? 1 : 0);
- }
- line_ofs_max = MAX(line_ofs_max, 0);
- cursor.line_ofs = CLAMP(cursor.line_ofs, 0, line_ofs_max);
+ if (cursor_x > (cursor.x_ofs + visible_width))
+ cursor.x_ofs = cursor_x - visible_width + 1;
- // adjust x offset
- int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]);
-
- if (cursor_x > (cursor.x_ofs + visible_width))
- cursor.x_ofs = cursor_x - visible_width + 1;
-
- if (cursor_x < cursor.x_ofs)
- cursor.x_ofs = cursor_x;
-
- updating_scrolls = true;
- h_scroll->set_value(cursor.x_ofs);
- update_line_scroll_pos();
- double new_v_scroll = get_line_scroll_pos();
- // keep offset if smooth scroll is enabled
- if (smooth_scroll_enabled) {
- new_v_scroll += fmod(v_scroll->get_value(), 1.0);
+ if (cursor_x < cursor.x_ofs)
+ cursor.x_ofs = cursor_x;
+ } else {
+ cursor.x_ofs = 0;
}
- v_scroll->set_value(new_v_scroll);
- updating_scrolls = false;
+ h_scroll->set_value(cursor.x_ofs);
+
update();
}
void TextEdit::center_viewport_to_cursor() {
- scrolling = false;
- if (cursor.line_ofs > cursor.line)
- cursor.line_ofs = cursor.line;
+ // move viewport so the cursor is in the center of the screen
+ scrolling = false;
if (is_line_hidden(cursor.line))
unfold_line(cursor.line);
+ set_line_as_center_visible(cursor.line, get_cursor_wrap_index());
int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width;
if (v_scroll->is_visible_in_tree())
visible_width -= v_scroll->get_combined_minimum_size().width;
visible_width -= 20; // give it a little more space
- int visible_rows = get_visible_rows();
- if (h_scroll->is_visible_in_tree())
- visible_rows -= ((h_scroll->get_combined_minimum_size().height - 1) / get_row_height());
- if (text.size() >= visible_rows) {
- int max_ofs = text.size() - (scroll_past_end_of_file_enabled ? 1 : MAX(num_lines_from(text.size() - 1, -visible_rows), 0));
- cursor.line_ofs = CLAMP(cursor.line - num_lines_from(MAX(cursor.line - visible_rows / 2, 0), -visible_rows / 2), 0, max_ofs);
+ if (is_wrap_enabled()) {
+ // center x offset
+ int cursor_x = get_column_x_offset_for_line(cursor.column, cursor.line);
+
+ if (cursor_x > (cursor.x_ofs + visible_width))
+ cursor.x_ofs = cursor_x - visible_width + 1;
+
+ if (cursor_x < cursor.x_ofs)
+ cursor.x_ofs = cursor_x;
+ } else {
+ cursor.x_ofs = 0;
}
- int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]);
+ h_scroll->set_value(cursor.x_ofs);
- if (cursor_x > (cursor.x_ofs + visible_width))
- cursor.x_ofs = cursor_x - visible_width + 1;
+ update();
+}
- if (cursor_x < cursor.x_ofs)
- cursor.x_ofs = cursor_x;
+void TextEdit::update_cursor_wrap_offset() {
+ int first_vis_line = get_first_visible_line();
+ if (line_wraps(first_vis_line)) {
+ cursor.wrap_ofs = MIN(cursor.wrap_ofs, times_line_wraps(first_vis_line));
+ } else {
+ cursor.wrap_ofs = 0;
+ }
+ set_line_as_first_visible(cursor.line_ofs, cursor.wrap_ofs);
+}
- updating_scrolls = true;
- h_scroll->set_value(cursor.x_ofs);
- update_line_scroll_pos();
- double new_v_scroll = get_line_scroll_pos();
- // keep offset if smooth scroll is enabled
- if (smooth_scroll_enabled) {
- new_v_scroll += fmod(v_scroll->get_value(), 1.0);
+bool TextEdit::line_wraps(int line) const {
+
+ ERR_FAIL_INDEX_V(line, text.size(), 0);
+ if (!is_wrap_enabled())
+ return false;
+ return text.get_line_width(line) > wrap_at;
+}
+
+int TextEdit::times_line_wraps(int line) const {
+
+ ERR_FAIL_INDEX_V(line, text.size(), 0);
+ if (!line_wraps(line))
+ return 0;
+
+ int wrap_amount = text.get_line_wrap_amount(line);
+ if (wrap_amount == -1) {
+ // update the value
+ Vector<String> rows = get_wrap_rows_text(line);
+ wrap_amount = rows.size() - 1;
+ text.set_line_wrap_amount(line, wrap_amount);
}
- v_scroll->set_value(new_v_scroll);
- updating_scrolls = false;
- update();
+
+ return wrap_amount;
+}
+
+Vector<String> TextEdit::get_wrap_rows_text(int p_line) const {
+
+ ERR_FAIL_INDEX_V(p_line, text.size(), Vector<String>());
+
+ Vector<String> lines;
+ if (!line_wraps(p_line)) {
+ lines.push_back(text[p_line]);
+ return lines;
+ }
+
+ int px = 0;
+ int col = 0;
+ String line_text = text[p_line];
+ String wrap_substring = "";
+
+ int word_px = 0;
+ String word_str = "";
+ int cur_wrap_index = 0;
+
+ int tab_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width;
+
+ while (col < line_text.length()) {
+ char c = line_text[col];
+ int w = text.get_char_width(c, line_text[col + 1], px + word_px);
+
+ int indent_ofs = (cur_wrap_index != 0 ? tab_offset_px : 0);
+
+ word_str += c;
+ word_px += w;
+ if (c == ' ') {
+ // end of a word; add this word to the substring
+ wrap_substring += word_str;
+ px += word_px;
+ word_str = "";
+ word_px = 0;
+ }
+
+ if ((indent_ofs + px + word_px) > wrap_at) {
+ // do not want to add this word
+ if (indent_ofs + word_px > wrap_at) {
+ // not enough space; add it anyway
+ wrap_substring += word_str;
+ px += word_px;
+ word_str = "";
+ word_px = 0;
+ }
+ lines.push_back(wrap_substring);
+ // reset for next wrap
+ cur_wrap_index++;
+ wrap_substring = "";
+ px = 0;
+ }
+ col++;
+ }
+ // line ends before hit wrap_at; add this word to the substring
+ wrap_substring += word_str;
+ px += word_px;
+ lines.push_back(wrap_substring);
+ return lines;
+}
+
+int TextEdit::get_cursor_wrap_index() const {
+
+ return get_line_wrap_index_at_col(cursor.line, cursor.column);
+}
+
+int TextEdit::get_line_wrap_index_at_col(int p_line, int p_column) const {
+
+ ERR_FAIL_INDEX_V(p_line, text.size(), 0);
+
+ if (!line_wraps(p_line))
+ return 0;
+
+ // loop through wraps in the line text until we get to the column
+ int wrap_index = 0;
+ int col = 0;
+ Vector<String> rows = get_wrap_rows_text(p_line);
+ for (int i = 0; i < rows.size(); i++) {
+ wrap_index = i;
+ String s = rows[wrap_index];
+ col += s.length();
+ if (col > p_column)
+ break;
+ }
+ return wrap_index;
}
void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) {
@@ -3608,7 +3839,7 @@ void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) {
if (cursor.column > get_line(cursor.line).length())
cursor.column = get_line(cursor.line).length();
- cursor.last_fit_x = get_column_x_offset(cursor.column, get_line(cursor.line));
+ cursor.last_fit_x = get_column_x_offset_for_line(cursor.column, cursor.line);
if (p_adjust_viewport)
adjust_viewport_to_cursor();
@@ -3620,7 +3851,7 @@ void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) {
}
}
-void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_hidden) {
+void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_hidden, int p_wrap_index) {
if (setting_row)
return;
@@ -3629,8 +3860,8 @@ void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_
if (p_row < 0)
p_row = 0;
- if (p_row >= (int)text.size())
- p_row = (int)text.size() - 1;
+ if (p_row >= text.size())
+ p_row = text.size() - 1;
if (!p_can_be_hidden) {
if (is_line_hidden(CLAMP(p_row, 0, text.size() - 1))) {
@@ -3648,7 +3879,18 @@ void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_
}
}
cursor.line = p_row;
- cursor.column = get_char_pos_for(cursor.last_fit_x, get_line(cursor.line));
+
+ int n_col = get_char_pos_for_line(cursor.last_fit_x, p_row, p_wrap_index);
+ if (is_wrap_enabled() && p_wrap_index < times_line_wraps(p_row)) {
+ Vector<String> rows = get_wrap_rows_text(p_row);
+ int row_end_col = 0;
+ for (int i = 0; i < p_wrap_index + 1; i++) {
+ row_end_col += rows[i].length();
+ }
+ if (n_col >= row_end_col)
+ n_col -= 1;
+ }
+ cursor.column = n_col;
if (p_adjust_viewport)
adjust_viewport_to_cursor();
@@ -3725,9 +3967,25 @@ void TextEdit::_scroll_moved(double p_to_val) {
if (h_scroll->is_visible_in_tree())
cursor.x_ofs = h_scroll->get_value();
if (v_scroll->is_visible_in_tree()) {
- double val = v_scroll->get_value();
- cursor.line_ofs = num_lines_from(0, (int)floor(val));
- line_scroll_pos = (int)floor(val);
+
+ // set line ofs and wrap ofs
+ int v_scroll_i = floor(get_v_scroll());
+ int sc = 0;
+ int n_line;
+ for (n_line = 0; n_line < text.size(); n_line++) {
+ if (!is_line_hidden(n_line)) {
+ sc++;
+ sc += times_line_wraps(n_line);
+ if (sc > v_scroll_i)
+ break;
+ }
+ }
+ int line_wrap_amount = times_line_wraps(n_line);
+ int wi = line_wrap_amount - (sc - v_scroll_i - 1);
+ wi = CLAMP(wi, 0, line_wrap_amount);
+
+ cursor.line_ofs = n_line;
+ cursor.wrap_ofs = wi;
}
update();
}
@@ -3737,29 +3995,73 @@ int TextEdit::get_row_height() const {
return cache.font->get_height() + cache.line_spacing;
}
-int TextEdit::get_char_pos_for(int p_px, String p_str) const {
+int TextEdit::get_char_pos_for_line(int p_px, int p_line, int p_wrap_index) const {
- int px = 0;
- int c = 0;
+ ERR_FAIL_INDEX_V(p_line, text.size(), 0);
- int tab_w = cache.font->get_char_size(' ').width * indent_size;
+ if (line_wraps(p_line)) {
- while (c < p_str.length()) {
+ int line_wrap_amount = times_line_wraps(p_line);
+ int wrap_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width;
+ if (p_wrap_index > line_wrap_amount)
+ p_wrap_index = line_wrap_amount;
+ if (p_wrap_index > 0)
+ p_px -= wrap_offset_px;
+ else
+ p_wrap_index = 0;
+ Vector<String> rows = get_wrap_rows_text(p_line);
+ int c_pos = get_char_pos_for(p_px, rows[p_wrap_index]);
+ for (int i = 0; i < p_wrap_index; i++) {
+ String s = rows[i];
+ c_pos += s.length();
+ }
- int w = 0;
+ return c_pos;
+ } else {
- if (p_str[c] == '\t') {
+ return get_char_pos_for(p_px, text[p_line]);
+ }
+}
- int left = px % tab_w;
- if (left == 0)
- w = tab_w;
- else
- w = tab_w - px % tab_w; // is right...
+int TextEdit::get_column_x_offset_for_line(int p_char, int p_line) const {
- } else {
+ ERR_FAIL_INDEX_V(p_line, text.size(), 0);
- w = cache.font->get_char_size(p_str[c], p_str[c + 1]).width;
+ if (line_wraps(p_line)) {
+
+ int n_char = p_char;
+ int col = 0;
+ Vector<String> rows = get_wrap_rows_text(p_line);
+ int wrap_index = 0;
+ for (int i = 0; i < rows.size(); i++) {
+ wrap_index = i;
+ String s = rows[wrap_index];
+ col += s.length();
+ if (col > p_char)
+ break;
+ n_char -= s.length();
}
+ int px = get_column_x_offset(n_char, rows[wrap_index]);
+
+ int wrap_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width;
+ if (wrap_index != 0)
+ px += wrap_offset_px;
+
+ return px;
+ } else {
+
+ return get_column_x_offset(p_char, text[p_line]);
+ }
+}
+
+int TextEdit::get_char_pos_for(int p_px, String p_str) const {
+
+ int px = 0;
+ int c = 0;
+
+ while (c < p_str.length()) {
+
+ int w = text.get_char_width(p_str[c], p_str[c + 1], px);
if (p_px < (px + w / 2))
break;
@@ -3770,28 +4072,16 @@ int TextEdit::get_char_pos_for(int p_px, String p_str) const {
return c;
}
-int TextEdit::get_column_x_offset(int p_char, String p_str) {
+int TextEdit::get_column_x_offset(int p_char, String p_str) const {
int px = 0;
- int tab_w = cache.font->get_char_size(' ').width * indent_size;
-
for (int i = 0; i < p_char; i++) {
if (i >= p_str.length())
break;
- if (p_str[i] == '\t') {
-
- int left = px % tab_w;
- if (left == 0)
- px += tab_w;
- else
- px += tab_w - px % tab_w; // is right...
-
- } else {
- px += cache.font->get_char_size(p_str[i], p_str[i + 1]).width;
- }
+ px += text.get_char_width(p_str[i], p_str[i + 1], px);
}
return px;
@@ -3860,20 +4150,20 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
void TextEdit::set_text(String p_text) {
setting_text = true;
- clear();
+ _clear();
_insert_text_at_cursor(p_text);
clear_undo_history();
cursor.column = 0;
cursor.line = 0;
cursor.x_ofs = 0;
cursor.line_ofs = 0;
- line_scroll_pos = 0;
+ cursor.wrap_ofs = 0;
cursor.last_fit_x = 0;
cursor_set_line(0);
cursor_set_column(0);
update();
setting_text = false;
- _text_changed_emit();
+
//get_range()->set(0);
};
@@ -3953,7 +4243,7 @@ void TextEdit::_clear() {
cursor.line = 0;
cursor.x_ofs = 0;
cursor.line_ofs = 0;
- line_scroll_pos = 0;
+ cursor.wrap_ofs = 0;
cursor.last_fit_x = 0;
}
@@ -3975,14 +4265,14 @@ bool TextEdit::is_readonly() const {
return readonly;
}
-void TextEdit::set_wrap(bool p_wrap) {
+void TextEdit::set_wrap_enabled(bool p_wrap_enabled) {
- wrap = p_wrap;
+ wrap_enabled = p_wrap_enabled;
}
-bool TextEdit::is_wrapping() const {
+bool TextEdit::is_wrap_enabled() const {
- return wrap;
+ return wrap_enabled;
}
void TextEdit::set_max_chars(int p_max_chars) {
@@ -4131,7 +4421,7 @@ void TextEdit::clear_colors() {
keywords.clear();
color_regions.clear();
color_region_cache.clear();
- text.clear_caches();
+ text.clear_width_cache();
}
void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color) {
@@ -4151,7 +4441,7 @@ Color TextEdit::get_keyword_color(String p_keyword) const {
void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only));
- text.clear_caches();
+ text.clear_width_cache();
update();
}
@@ -4236,6 +4526,7 @@ void TextEdit::paste() {
String clipboard = OS::get_singleton()->get_clipboard();
+ begin_complex_operation();
if (selection.active) {
selection.active = false;
@@ -4252,6 +4543,8 @@ void TextEdit::paste() {
}
_insert_text_at_cursor(clipboard);
+ end_complex_operation();
+
update();
}
@@ -4613,6 +4906,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());
@@ -4645,52 +4956,99 @@ void TextEdit::unhide_all_lines() {
update();
}
-int TextEdit::num_lines_from(int p_line_from, int unhidden_amount) const {
+int TextEdit::num_lines_from(int p_line_from, int visible_amount) const {
- // returns the number of hidden and unhidden lines from p_line_from to p_line_from + amount of visible lines
- ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(unhidden_amount));
+ // returns the number of lines (hidden and unhidden) from p_line_from to (p_line_from + visible_amount of unhidden lines)
+ ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(visible_amount));
if (!is_hiding_enabled())
- return ABS(unhidden_amount);
+ return ABS(visible_amount);
+
int num_visible = 0;
int num_total = 0;
- if (unhidden_amount >= 0) {
+ if (visible_amount >= 0) {
for (int i = p_line_from; i < text.size(); i++) {
num_total++;
- if (!is_line_hidden(i))
+ if (!is_line_hidden(i)) {
num_visible++;
- if (num_visible >= unhidden_amount)
+ }
+ if (num_visible >= visible_amount)
break;
}
} else {
- unhidden_amount = ABS(unhidden_amount);
+ visible_amount = ABS(visible_amount);
for (int i = p_line_from; i >= 0; i--) {
num_total++;
- if (!is_line_hidden(i))
+ if (!is_line_hidden(i)) {
num_visible++;
- if (num_visible >= unhidden_amount)
+ }
+ if (num_visible >= visible_amount)
break;
}
}
return num_total;
}
-bool TextEdit::is_last_visible_line(int p_line) const {
+int TextEdit::num_lines_from_rows(int p_line_from, int p_wrap_index_from, int visible_amount, int &wrap_index) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), false);
+ // returns the number of lines (hidden and unhidden) from (p_line_from + p_wrap_index_from) row to (p_line_from + visible_amount of unhidden and wrapped rows)
+ // wrap index is set to the wrap index of the last line
+ wrap_index = 0;
+ ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(visible_amount));
- if (p_line == text.size() - 1)
- return true;
+ if (!is_hiding_enabled() && !is_wrap_enabled())
+ return ABS(visible_amount);
+
+ int num_visible = 0;
+ int num_total = 0;
+ if (visible_amount == 0) {
+ num_total = 0;
+ wrap_index = 0;
+ } else if (visible_amount > 0) {
+ int i;
+ num_visible -= p_wrap_index_from;
+ for (i = p_line_from; i < text.size(); i++) {
+ num_total++;
+ if (!is_line_hidden(i)) {
+ num_visible++;
+ num_visible += times_line_wraps(i);
+ }
+ if (num_visible >= visible_amount)
+ break;
+ }
+ wrap_index = times_line_wraps(MIN(i, text.size() - 1)) - (num_visible - visible_amount);
+ } else {
+ visible_amount = ABS(visible_amount);
+ int i;
+ num_visible -= times_line_wraps(p_line_from) - p_wrap_index_from;
+ for (i = p_line_from; i >= 0; i--) {
+ num_total++;
+ if (!is_line_hidden(i)) {
+ num_visible++;
+ num_visible += times_line_wraps(i);
+ }
+ if (num_visible >= visible_amount)
+ break;
+ }
+ wrap_index = (num_visible - visible_amount);
+ }
+ wrap_index = MAX(wrap_index, 0);
+ return num_total;
+}
+int TextEdit::get_last_unhidden_line() const {
+
+ // returns the last line in the text that is not hidden
if (!is_hiding_enabled())
- return false;
+ return text.size() - 1;
- for (int i = p_line + 1; i < text.size(); i++) {
- if (!is_line_hidden(i))
- return false;
+ int last_line;
+ for (last_line = text.size() - 1; last_line > 0; last_line--) {
+ if (!is_line_hidden(last_line)) {
+ break;
+ }
}
-
- return true;
+ return last_line;
}
int TextEdit::get_indent_level(int p_line) const {
@@ -4710,7 +5068,7 @@ int TextEdit::get_indent_level(int p_line) const {
break;
}
}
- return tab_count + whitespace_count / indent_size;
+ return tab_count * indent_size + whitespace_count;
}
bool TextEdit::is_line_comment(int p_line) const {
@@ -5058,6 +5416,7 @@ bool TextEdit::is_drawing_tabs() const {
void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) {
override_selected_font_color = p_override_selected_font_color;
}
+
bool TextEdit::is_overriding_selected_font_color() const {
return override_selected_font_color;
}
@@ -5078,58 +5437,143 @@ bool TextEdit::is_insert_text_operation() {
uint32_t TextEdit::get_version() const {
return current_op.version;
}
+
uint32_t TextEdit::get_saved_version() const {
return saved_version;
}
+
void TextEdit::tag_saved_version() {
saved_version = get_version();
}
-int TextEdit::get_v_scroll() const {
+double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const {
- return v_scroll->get_value();
-}
-void TextEdit::set_v_scroll(int p_scroll) {
+ if (!is_wrap_enabled() && !is_hiding_enabled())
+ return p_line;
- if (p_scroll < 0) {
- p_scroll = 0;
- }
- if (!scroll_past_end_of_file_enabled) {
- if (p_scroll + get_visible_rows() > get_total_unhidden_rows()) {
- int num_rows = num_lines_from(CLAMP(p_scroll, 0, text.size() - 1), MIN(get_visible_rows(), text.size() - 1 - p_scroll));
- p_scroll = text.size() - num_rows;
+ // count the number of visible lines up to this line
+ double new_line_scroll_pos = 0;
+ int to = CLAMP(p_line, 0, text.size() - 1);
+ for (int i = 0; i < to; i++) {
+ if (!text.is_hidden(i)) {
+ new_line_scroll_pos++;
+ new_line_scroll_pos += times_line_wraps(i);
}
}
+ new_line_scroll_pos += p_wrap_index;
+ return new_line_scroll_pos;
+}
+
+void TextEdit::set_line_as_first_visible(int p_line, int p_wrap_index) {
+
+ set_v_scroll(get_scroll_pos_for_line(p_line, p_wrap_index));
+}
+
+void TextEdit::set_line_as_center_visible(int p_line, int p_wrap_index) {
+
+ int visible_rows = get_visible_rows();
+ int wi;
+ int first_line = p_line - num_lines_from_rows(p_line, p_wrap_index, -visible_rows / 2, wi) + 1;
+
+ set_v_scroll(get_scroll_pos_for_line(first_line, wi));
+}
+
+void TextEdit::set_line_as_last_visible(int p_line, int p_wrap_index) {
+
+ int wi;
+ int first_line = p_line - num_lines_from_rows(p_line, p_wrap_index, -get_visible_rows() - 1, wi) + 1;
+
+ set_v_scroll(get_scroll_pos_for_line(first_line, wi) + get_visible_rows_offset());
+}
+
+int TextEdit::get_first_visible_line() const {
+
+ return CLAMP(cursor.line_ofs, 0, text.size() - 1);
+}
+
+int TextEdit::get_last_visible_line() const {
+
+ int first_vis_line = get_first_visible_line();
+ int last_vis_line = 0;
+ int wi;
+ last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1;
+ last_vis_line = CLAMP(last_vis_line, 0, text.size() - 1);
+ return last_vis_line;
+}
+
+int TextEdit::get_last_visible_line_wrap_index() const {
+
+ int first_vis_line = get_first_visible_line();
+ int last_vis_line = 0;
+ int wi;
+ last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1;
+ return wi;
+}
+
+double TextEdit::get_visible_rows_offset() const {
+
+ double total = cache.size.height;
+ total -= cache.style_normal->get_minimum_size().height;
+ if (h_scroll->is_visible_in_tree())
+ total -= h_scroll->get_size().height;
+ total /= (double)get_row_height();
+ total = total - floor(total);
+ total = -CLAMP(total, 0.001, 1) + 1;
+ return total;
+}
+
+double TextEdit::get_v_scroll_offset() const {
+
+ double val = get_v_scroll() - floor(get_v_scroll());
+ return CLAMP(val, 0, 1);
+}
+
+double TextEdit::get_v_scroll() const {
+
+ return v_scroll->get_value();
+}
+
+void TextEdit::set_v_scroll(double p_scroll) {
+
v_scroll->set_value(p_scroll);
- cursor.line_ofs = num_lines_from(0, p_scroll);
- line_scroll_pos = p_scroll;
+ int max_v_scroll = v_scroll->get_max() - v_scroll->get_page();
+ if (p_scroll >= max_v_scroll - 1.0)
+ _scroll_moved(v_scroll->get_value());
}
int TextEdit::get_h_scroll() const {
return h_scroll->get_value();
}
+
void TextEdit::set_h_scroll(int p_scroll) {
+ if (p_scroll < 0) {
+ p_scroll = 0;
+ }
h_scroll->set_value(p_scroll);
}
void TextEdit::set_smooth_scroll_enabled(bool p_enable) {
+
v_scroll->set_smooth_scroll_enabled(p_enable);
smooth_scroll_enabled = p_enable;
}
bool TextEdit::is_smooth_scroll_enabled() const {
+
return smooth_scroll_enabled;
}
void TextEdit::set_v_scroll_speed(float p_speed) {
+
v_scroll_speed = p_speed;
}
float TextEdit::get_v_scroll_speed() const {
+
return v_scroll_speed;
}
@@ -5149,7 +5593,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--;
}
@@ -5376,18 +5830,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;
}
}
}
@@ -5468,12 +5927,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;
}
@@ -5609,7 +6068,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line);
ClassDB::bind_method(D_METHOD("cursor_set_column", "column", "adjust_viewport"), &TextEdit::cursor_set_column, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport", "can_be_hidden"), &TextEdit::cursor_set_line, DEFVAL(true), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport", "can_be_hidden", "wrap_index"), &TextEdit::cursor_set_line, DEFVAL(true), DEFVAL(true), DEFVAL(0));
ClassDB::bind_method(D_METHOD("cursor_get_column"), &TextEdit::cursor_get_column);
ClassDB::bind_method(D_METHOD("cursor_get_line"), &TextEdit::cursor_get_line);
@@ -5626,8 +6085,8 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_readonly", "enable"), &TextEdit::set_readonly);
ClassDB::bind_method(D_METHOD("is_readonly"), &TextEdit::is_readonly);
- ClassDB::bind_method(D_METHOD("set_wrap", "enable"), &TextEdit::set_wrap);
- ClassDB::bind_method(D_METHOD("is_wrapping"), &TextEdit::is_wrapping);
+ ClassDB::bind_method(D_METHOD("set_wrap_enabled", "enable"), &TextEdit::set_wrap_enabled);
+ ClassDB::bind_method(D_METHOD("is_wrap_enabled"), &TextEdit::is_wrap_enabled);
// ClassDB::bind_method(D_METHOD("set_max_chars", "amount"), &TextEdit::set_max_chars);
// ClassDB::bind_method(D_METHOD("get_max_char"), &TextEdit::get_max_chars);
ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &TextEdit::set_context_menu_enabled);
@@ -5656,6 +6115,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);
@@ -5694,18 +6155,22 @@ 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");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hiding_enabled"), "set_hiding_enabled", "is_hiding_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_lines"), "set_wrap", "is_wrapping");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_enabled"), "set_wrap_enabled", "is_wrap_enabled");
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "max_chars"), "set_max_chars", "get_max_chars");
ADD_GROUP("Caret", "caret_");
@@ -5740,7 +6205,8 @@ TextEdit::TextEdit() {
draw_caret = true;
max_chars = 0;
clear();
- wrap = false;
+ wrap_enabled = false;
+ wrap_right_offset = 10;
set_focus_mode(FOCUS_ALL);
syntax_highlighter = NULL;
_update_caches();
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 60c6ab4929..586f4c8e93 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;
+ int wrap_amount_cache : 24;
Map<int, ColorRegionInfo> region_info;
String data;
};
@@ -94,6 +95,9 @@ public:
void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; }
int get_line_width(int p_line) const;
int get_max_width(bool p_exclude_hidden = false) const;
+ int get_char_width(CharType c, CharType next_c, int px) const;
+ void set_line_wrap_amount(int p_line, int p_wrap_amount) const;
+ int get_line_wrap_amount(int p_line) const;
const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const;
void set(int p_line, const String &p_text);
void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; }
@@ -106,7 +110,8 @@ public:
void remove(int p_at);
int size() const { return text.size(); }
void clear();
- void clear_caches();
+ void clear_width_cache();
+ void clear_wrap_cache();
_FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
Text() { indent_size = 4; }
};
@@ -115,7 +120,7 @@ private:
struct Cursor {
int last_fit_x;
int line, column; ///< cursor
- int x_ofs, line_ofs;
+ int x_ofs, line_ofs, wrap_ofs;
} cursor;
struct Selection {
@@ -263,8 +268,11 @@ private:
bool block_caret;
bool right_click_moves_caret;
+ bool wrap_enabled;
+ int wrap_at;
+ int wrap_right_offset;
+
bool setting_row;
- bool wrap;
bool draw_tabs;
bool override_selected_font_color;
bool cursor_changed_dirty;
@@ -321,19 +329,34 @@ private:
int search_result_line;
int search_result_col;
- double line_scroll_pos;
-
bool context_menu_enabled;
int get_visible_rows() const;
- int get_total_unhidden_rows() const;
- double get_line_scroll_pos(bool p_recalculate = false) const;
- void update_line_scroll_pos();
-
+ int get_total_visible_rows() const;
+
+ void update_cursor_wrap_offset();
+ void update_wrap_at();
+ bool line_wraps(int line) const;
+ int times_line_wraps(int line) const;
+ Vector<String> get_wrap_rows_text(int p_line) const;
+ int get_cursor_wrap_index() const;
+ int get_line_wrap_index_at_col(int p_line, int p_column) const;
int get_char_count();
+ double get_scroll_pos_for_line(int p_line, int p_wrap_index = 0) const;
+ void set_line_as_first_visible(int p_line, int p_wrap_index = 0);
+ void set_line_as_center_visible(int p_line, int p_wrap_index = 0);
+ void set_line_as_last_visible(int p_line, int p_wrap_index = 0);
+ int get_first_visible_line() const;
+ int get_last_visible_line() const;
+ int get_last_visible_line_wrap_index() const;
+ double get_visible_rows_offset() const;
+ double get_v_scroll_offset() const;
+
+ int get_char_pos_for_line(int p_px, int p_line, int p_wrap_index = 0) const;
+ int get_column_x_offset_for_line(int p_char, int p_line) const;
int get_char_pos_for(int p_px, String p_str) const;
- int get_column_x_offset(int p_char, String p_str);
+ int get_column_x_offset(int p_char, String p_str) const;
void adjust_viewport_to_cursor();
double get_scroll_line_diff() const;
@@ -450,13 +473,17 @@ public:
void set_line_as_breakpoint(int p_line, bool p_breakpoint);
bool is_line_set_as_breakpoint(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;
void fold_all_lines();
void unhide_all_lines();
- int num_lines_from(int p_line_from, int unhidden_amount) const;
- bool is_last_visible_line(int p_line) const;
+ int num_lines_from(int p_line_from, int visible_amount) const;
+ int num_lines_from_rows(int p_line_from, int p_wrap_index_from, int visible_amount, int &wrap_index) const;
+ int get_last_unhidden_line() const;
+
bool can_fold(int p_line) const;
bool is_folded(int p_line) const;
void fold_line(int p_line);
@@ -493,7 +520,7 @@ public:
void center_viewport_to_cursor();
void cursor_set_column(int p_col, bool p_adjust_viewport = true);
- void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true);
+ void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true, int p_wrap_index = 0);
int cursor_get_column() const;
int cursor_get_line() const;
@@ -516,8 +543,8 @@ public:
void set_max_chars(int p_max_chars);
int get_max_chars() const;
- void set_wrap(bool p_wrap);
- bool is_wrapping() const;
+ void set_wrap_enabled(bool p_wrap_enabled);
+ bool is_wrap_enabled() const;
void clear();
@@ -578,8 +605,8 @@ public:
Color get_member_color(String p_member) const;
void clear_member_keywords();
- int get_v_scroll() const;
- void set_v_scroll(int p_scroll);
+ double get_v_scroll() const;
+ void set_v_scroll(double p_scroll);
int get_h_scroll() const;
void set_h_scroll(int p_scroll);
@@ -607,8 +634,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 67ee246252..6d18cce21d 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -40,7 +40,6 @@
#include "viewport.h"
VARIANT_ENUM_CAST(Node::PauseMode);
-VARIANT_ENUM_CAST(Node::RPCMode);
void Node::_notification(int p_notification) {
@@ -176,6 +175,9 @@ void Node::_propagate_ready() {
data.children[i]->_propagate_ready();
}
data.blocked--;
+
+ notification(NOTIFICATION_POST_ENTER_TREE);
+
if (data.ready_first) {
data.ready_first = false;
notification(NOTIFICATION_READY);
@@ -477,23 +479,23 @@ bool Node::is_network_master() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
- return get_multiplayer_api()->get_network_unique_id() == data.network_master;
+ return get_multiplayer()->get_network_unique_id() == data.network_master;
}
/***** RPC CONFIG ********/
-void Node::rpc_config(const StringName &p_method, RPCMode p_mode) {
+void Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_mode) {
- if (p_mode == RPC_MODE_DISABLED) {
+ if (p_mode == MultiplayerAPI::RPC_MODE_DISABLED) {
data.rpc_methods.erase(p_method);
} else {
data.rpc_methods[p_method] = p_mode;
};
}
-void Node::rset_config(const StringName &p_property, RPCMode p_mode) {
+void Node::rset_config(const StringName &p_property, MultiplayerAPI::RPCMode p_mode) {
- if (p_mode == RPC_MODE_DISABLED) {
+ if (p_mode == MultiplayerAPI::RPC_MODE_DISABLED) {
data.rpc_properties.erase(p_property);
} else {
data.rpc_properties[p_property] = p_mode;
@@ -668,12 +670,12 @@ Variant Node::_rpc_unreliable_id_bind(const Variant **p_args, int p_argcount, Va
void Node::rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer_api()->rpcp(this, p_peer_id, p_unreliable, p_method, p_arg, p_argcount);
+ get_multiplayer()->rpcp(this, p_peer_id, p_unreliable, p_method, p_arg, p_argcount);
}
void Node::rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) {
ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer_api()->rsetp(this, p_peer_id, p_unreliable, p_property, p_value);
+ get_multiplayer()->rsetp(this, p_peer_id, p_unreliable, p_property, p_value);
}
/******** RSET *********/
@@ -698,138 +700,31 @@ void Node::rset_unreliable_id(int p_peer_id, const StringName &p_property, const
}
//////////// end of rpc
-Ref<MultiplayerAPI> Node::get_multiplayer_api() const {
- if (multiplayer_api.is_valid())
- return multiplayer_api;
+Ref<MultiplayerAPI> Node::get_multiplayer() const {
+ if (multiplayer.is_valid())
+ return multiplayer;
if (!is_inside_tree())
return Ref<MultiplayerAPI>();
- return get_tree()->get_multiplayer_api();
+ return get_tree()->get_multiplayer();
}
-Ref<MultiplayerAPI> Node::get_custom_multiplayer_api() const {
- return multiplayer_api;
+Ref<MultiplayerAPI> Node::get_custom_multiplayer() const {
+ return multiplayer;
}
-void Node::set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) {
+void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
- multiplayer_api = p_multiplayer_api;
+ multiplayer = p_multiplayer;
}
-const Map<StringName, Node::RPCMode>::Element *Node::get_node_rpc_mode(const StringName &p_method) {
+const Map<StringName, MultiplayerAPI::RPCMode>::Element *Node::get_node_rpc_mode(const StringName &p_method) {
return data.rpc_methods.find(p_method);
}
-const Map<StringName, Node::RPCMode>::Element *Node::get_node_rset_mode(const StringName &p_property) {
+const Map<StringName, MultiplayerAPI::RPCMode>::Element *Node::get_node_rset_mode(const StringName &p_property) {
return data.rpc_properties.find(p_property);
}
-bool Node::can_call_rpc(const StringName &p_method, int p_from) const {
-
- const Map<StringName, RPCMode>::Element *E = data.rpc_methods.find(p_method);
- if (E) {
-
- switch (E->get()) {
-
- case RPC_MODE_DISABLED: {
- return false;
- } break;
- case RPC_MODE_REMOTE: {
- return true;
- } break;
- case RPC_MODE_SYNC: {
- return true;
- } break;
- case RPC_MODE_MASTER: {
- return is_network_master();
- } break;
- case RPC_MODE_SLAVE: {
- return !is_network_master() && p_from == get_network_master();
- } break;
- }
- }
-
- if (get_script_instance()) {
- //attempt with script
- ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rpc_mode(p_method);
-
- switch (rpc_mode) {
-
- case ScriptInstance::RPC_MODE_DISABLED: {
- return false;
- } break;
- case ScriptInstance::RPC_MODE_REMOTE: {
- return true;
- } break;
- case ScriptInstance::RPC_MODE_SYNC: {
- return true;
- } break;
- case ScriptInstance::RPC_MODE_MASTER: {
- return is_network_master();
- } break;
- case ScriptInstance::RPC_MODE_SLAVE: {
- return !is_network_master() && p_from == get_network_master();
- } break;
- }
- }
-
- ERR_PRINTS("RPC from " + itos(p_from) + " on unauthorized method attempted: " + String(p_method) + " on base: " + String(Variant(this)));
- return false;
-}
-
-bool Node::can_call_rset(const StringName &p_property, int p_from) const {
-
- const Map<StringName, RPCMode>::Element *E = data.rpc_properties.find(p_property);
- if (E) {
-
- switch (E->get()) {
-
- case RPC_MODE_DISABLED: {
- return false;
- } break;
- case RPC_MODE_REMOTE: {
- return true;
- } break;
- case RPC_MODE_SYNC: {
- return true;
- } break;
- case RPC_MODE_MASTER: {
- return is_network_master();
- } break;
- case RPC_MODE_SLAVE: {
- return !is_network_master() && p_from == get_network_master();
- } break;
- }
- }
-
- if (get_script_instance()) {
- //attempt with script
- ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rset_mode(p_property);
-
- switch (rpc_mode) {
-
- case ScriptInstance::RPC_MODE_DISABLED: {
- return false;
- } break;
- case ScriptInstance::RPC_MODE_REMOTE: {
- return true;
- } break;
- case ScriptInstance::RPC_MODE_SYNC: {
- return true;
- } break;
- case ScriptInstance::RPC_MODE_MASTER: {
- return is_network_master();
- } break;
- case ScriptInstance::RPC_MODE_SLAVE: {
- return !is_network_master() && p_from == get_network_master();
- } break;
- }
- }
-
- ERR_PRINTS("RSET from " + itos(p_from) + " on unauthorized property attempted: " + String(p_property) + " on base: " + String(Variant(this)));
-
- return false;
-}
-
bool Node::can_process() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
@@ -1200,6 +1095,10 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) {
}
void Node::add_child_below_node(Node *p_node, Node *p_child, bool p_legible_unique_name) {
+
+ ERR_FAIL_NULL(p_node);
+ ERR_FAIL_NULL(p_child);
+
add_child(p_child, p_legible_unique_name);
if (is_a_parent_of(p_node)) {
@@ -1996,7 +1895,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;
}
@@ -2035,8 +1934,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 {
@@ -2748,9 +2648,9 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_network_master"), &Node::is_network_master);
- ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &Node::get_multiplayer_api);
- ClassDB::bind_method(D_METHOD("get_custom_multiplayer_api"), &Node::get_custom_multiplayer_api);
- ClassDB::bind_method(D_METHOD("set_custom_multiplayer_api", "api"), &Node::set_custom_multiplayer_api);
+ ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer);
+ ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer);
+ ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer);
ClassDB::bind_method(D_METHOD("rpc_config", "method", "mode"), &Node::rpc_config);
ClassDB::bind_method(D_METHOD("rset_config", "property", "mode"), &Node::rset_config);
@@ -2799,12 +2699,6 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_INTERNAL_PROCESS);
BIND_CONSTANT(NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
- BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
- BIND_ENUM_CONSTANT(RPC_MODE_REMOTE);
- BIND_ENUM_CONSTANT(RPC_MODE_SYNC);
- BIND_ENUM_CONSTANT(RPC_MODE_MASTER);
- BIND_ENUM_CONSTANT(RPC_MODE_SLAVE);
-
BIND_ENUM_CONSTANT(PAUSE_MODE_INHERIT);
BIND_ENUM_CONSTANT(PAUSE_MODE_STOP);
BIND_ENUM_CONSTANT(PAUSE_MODE_PROCESS);
@@ -2830,8 +2724,8 @@ void Node::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name");
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer_api");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "custom_multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer_api", "get_custom_multiplayer_api");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer", "get_custom_multiplayer");
BIND_VMETHOD(MethodInfo("_process", PropertyInfo(Variant::REAL, "delta")));
BIND_VMETHOD(MethodInfo("_physics_process", PropertyInfo(Variant::REAL, "delta")));
diff --git a/scene/main/node.h b/scene/main/node.h
index b5a956116d..341349de79 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -65,15 +65,6 @@ public:
#endif
};
- enum RPCMode {
-
- RPC_MODE_DISABLED, //no rpc for this method, calls to this will be blocked (default)
- RPC_MODE_REMOTE, // using rpc() on it will call method / set property in all other peers
- RPC_MODE_SYNC, // using rpc() on it will call method / set property in all other peers and locally
- RPC_MODE_MASTER, // usinc rpc() on it will call method on wherever the master is, be it local or remote
- RPC_MODE_SLAVE, // usinc rpc() on it will call method for all slaves, be it local or remote
- };
-
struct Comparator {
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); }
@@ -120,8 +111,8 @@ private:
Node *pause_owner;
int network_master;
- Map<StringName, RPCMode> rpc_methods;
- Map<StringName, RPCMode> rpc_properties;
+ Map<StringName, MultiplayerAPI::RPCMode> rpc_methods;
+ Map<StringName, MultiplayerAPI::RPCMode> rpc_properties;
// variables used to properly sort the node when processing, ignored otherwise
//should move all the stuff below to bits
@@ -151,7 +142,7 @@ private:
NAME_CASING_SNAKE_CASE
};
- Ref<MultiplayerAPI> multiplayer_api;
+ Ref<MultiplayerAPI> multiplayer;
void _print_tree_pretty(const String prefix, const bool last);
void _print_tree(const Node *p_node);
@@ -237,6 +228,7 @@ public:
NOTIFICATION_TRANSLATION_CHANGED = 24,
NOTIFICATION_INTERNAL_PROCESS = 25,
NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26,
+ NOTIFICATION_POST_ENTER_TREE = 27,
};
@@ -403,8 +395,8 @@ public:
int get_network_master() const;
bool is_network_master() const;
- void rpc_config(const StringName &p_method, RPCMode p_mode); // config a local method for RPC
- void rset_config(const StringName &p_property, RPCMode p_mode); // config a local property for RPC
+ void rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_mode); // config a local method for RPC
+ void rset_config(const StringName &p_property, MultiplayerAPI::RPCMode p_mode); // config a local property for RPC
void rpc(const StringName &p_method, VARIANT_ARG_LIST); //rpc call, honors RPCMode
void rpc_unreliable(const StringName &p_method, VARIANT_ARG_LIST); //rpc call, honors RPCMode
@@ -419,14 +411,11 @@ public:
void rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
void rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value);
- Ref<MultiplayerAPI> get_multiplayer_api() const;
- Ref<MultiplayerAPI> get_custom_multiplayer_api() const;
- void set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api);
- const Map<StringName, RPCMode>::Element *get_node_rpc_mode(const StringName &p_method);
- const Map<StringName, RPCMode>::Element *get_node_rset_mode(const StringName &p_property);
-
- bool can_call_rpc(const StringName &p_method, int p_from) const;
- bool can_call_rset(const StringName &p_property, int p_from) const;
+ Ref<MultiplayerAPI> get_multiplayer() const;
+ Ref<MultiplayerAPI> get_custom_multiplayer() const;
+ void set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
+ const Map<StringName, MultiplayerAPI::RPCMode>::Element *get_node_rpc_mode(const StringName &p_method);
+ const Map<StringName, MultiplayerAPI::RPCMode>::Element *get_node_rset_mode(const StringName &p_property);
Node();
~Node();
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 011087b487..6438616cf2 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -485,7 +485,9 @@ bool SceneTree::idle(float p_time) {
idle_process_time = p_time;
- multiplayer_api->poll();
+ if (multiplayer_poll) {
+ multiplayer->poll();
+ }
emit_signal("idle_frame");
@@ -496,7 +498,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;
@@ -654,6 +657,11 @@ void SceneTree::_notification(int p_notification) {
#endif
} break;
+ case NOTIFICATION_CRASH: {
+
+ get_root()->propagate_notification(p_notification);
+ } break;
+
default:
break;
};
@@ -1110,7 +1118,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;
@@ -1668,70 +1676,78 @@ void SceneTree::_server_disconnected() {
emit_signal("server_disconnected");
}
-Ref<MultiplayerAPI> SceneTree::get_multiplayer_api() const {
- return multiplayer_api;
+Ref<MultiplayerAPI> SceneTree::get_multiplayer() const {
+ return multiplayer;
+}
+
+void SceneTree::set_multiplayer_poll_enabled(bool p_enabled) {
+ multiplayer_poll = p_enabled;
+}
+
+bool SceneTree::is_multiplayer_poll_enabled() const {
+ return multiplayer_poll;
}
-void SceneTree::set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) {
- ERR_FAIL_COND(!p_multiplayer_api.is_valid());
+void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
+ ERR_FAIL_COND(!p_multiplayer.is_valid());
- if (multiplayer_api.is_valid()) {
- multiplayer_api->disconnect("network_peer_connected", this, "_network_peer_connected");
- multiplayer_api->disconnect("network_peer_disconnected", this, "_network_peer_disconnected");
- multiplayer_api->disconnect("connected_to_server", this, "_connected_to_server");
- multiplayer_api->disconnect("connection_failed", this, "_connection_failed");
- multiplayer_api->disconnect("server_disconnected", this, "_server_disconnected");
+ if (multiplayer.is_valid()) {
+ multiplayer->disconnect("network_peer_connected", this, "_network_peer_connected");
+ multiplayer->disconnect("network_peer_disconnected", this, "_network_peer_disconnected");
+ multiplayer->disconnect("connected_to_server", this, "_connected_to_server");
+ multiplayer->disconnect("connection_failed", this, "_connection_failed");
+ multiplayer->disconnect("server_disconnected", this, "_server_disconnected");
}
- multiplayer_api = p_multiplayer_api;
- multiplayer_api->set_root_node(root);
+ multiplayer = p_multiplayer;
+ multiplayer->set_root_node(root);
- multiplayer_api->connect("network_peer_connected", this, "_network_peer_connected");
- multiplayer_api->connect("network_peer_disconnected", this, "_network_peer_disconnected");
- multiplayer_api->connect("connected_to_server", this, "_connected_to_server");
- multiplayer_api->connect("connection_failed", this, "_connection_failed");
- multiplayer_api->connect("server_disconnected", this, "_server_disconnected");
+ multiplayer->connect("network_peer_connected", this, "_network_peer_connected");
+ multiplayer->connect("network_peer_disconnected", this, "_network_peer_disconnected");
+ multiplayer->connect("connected_to_server", this, "_connected_to_server");
+ multiplayer->connect("connection_failed", this, "_connection_failed");
+ multiplayer->connect("server_disconnected", this, "_server_disconnected");
}
void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer) {
- multiplayer_api->set_network_peer(p_network_peer);
+ multiplayer->set_network_peer(p_network_peer);
}
Ref<NetworkedMultiplayerPeer> SceneTree::get_network_peer() const {
- return multiplayer_api->get_network_peer();
+ return multiplayer->get_network_peer();
}
bool SceneTree::is_network_server() const {
- return multiplayer_api->is_network_server();
+ return multiplayer->is_network_server();
}
bool SceneTree::has_network_peer() const {
- return multiplayer_api->has_network_peer();
+ return multiplayer->has_network_peer();
}
int SceneTree::get_network_unique_id() const {
- return multiplayer_api->get_network_unique_id();
+ return multiplayer->get_network_unique_id();
}
Vector<int> SceneTree::get_network_connected_peers() const {
- return multiplayer_api->get_network_connected_peers();
+ return multiplayer->get_network_connected_peers();
}
int SceneTree::get_rpc_sender_id() const {
- return multiplayer_api->get_rpc_sender_id();
+ return multiplayer->get_rpc_sender_id();
}
void SceneTree::set_refuse_new_network_connections(bool p_refuse) {
- multiplayer_api->set_refuse_new_network_connections(p_refuse);
+ multiplayer->set_refuse_new_network_connections(p_refuse);
}
bool SceneTree::is_refusing_new_network_connections() const {
- return multiplayer_api->is_refusing_new_network_connections();
+ return multiplayer->is_refusing_new_network_connections();
}
void SceneTree::_bind_methods() {
@@ -1800,8 +1816,10 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene);
- ClassDB::bind_method(D_METHOD("set_multiplayer_api", "multiplayer_api"), &SceneTree::set_multiplayer_api);
- ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &SceneTree::get_multiplayer_api);
+ ClassDB::bind_method(D_METHOD("set_multiplayer", "multiplayer"), &SceneTree::set_multiplayer);
+ ClassDB::bind_method(D_METHOD("get_multiplayer"), &SceneTree::get_multiplayer);
+ ClassDB::bind_method(D_METHOD("set_multiplayer_poll_enabled", "enabled"), &SceneTree::set_multiplayer_poll_enabled);
+ ClassDB::bind_method(D_METHOD("is_multiplayer_poll_enabled"), &SceneTree::is_multiplayer_poll_enabled);
ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &SceneTree::set_network_peer);
ClassDB::bind_method(D_METHOD("get_network_peer"), &SceneTree::get_network_peer);
ClassDB::bind_method(D_METHOD("is_network_server"), &SceneTree::is_network_server);
@@ -1829,7 +1847,8 @@ void SceneTree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "current_scene", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_current_scene", "get_current_scene");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "", "get_root");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_multiplayer_api", "get_multiplayer_api");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_multiplayer", "get_multiplayer");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multiplayer_poll"), "set_multiplayer_poll_enabled", "is_multiplayer_poll_enabled");
ADD_SIGNAL(MethodInfo("tree_changed"));
ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node")));
@@ -1934,7 +1953,8 @@ SceneTree::SceneTree() {
root->set_world(Ref<World>(memnew(World)));
// Initialize network state
- set_multiplayer_api(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
+ multiplayer_poll = true;
+ set_multiplayer(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
//root->set_world_2d( Ref<World2D>( memnew( World2D )));
root->set_as_audio_listener(true);
@@ -1990,7 +2010,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 9c06e4ded3..aa8d78b1e1 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -185,7 +185,8 @@ private:
///network///
- Ref<MultiplayerAPI> multiplayer_api;
+ Ref<MultiplayerAPI> multiplayer;
+ bool multiplayer_poll;
void _network_peer_connected(int p_id);
void _network_peer_disconnected(int p_id);
@@ -410,8 +411,10 @@ public:
//network API
- Ref<MultiplayerAPI> get_multiplayer_api() const;
- void set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api);
+ Ref<MultiplayerAPI> get_multiplayer() const;
+ void set_multiplayer_poll_enabled(bool p_enabled);
+ bool is_multiplayer_poll_enabled() const;
+ void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer);
Ref<NetworkedMultiplayerPeer> get_network_peer() const;
bool is_network_server() const;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 295f131db3..01302d4214 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -72,6 +72,9 @@ void ViewportTexture::setup_local_to_scene() {
vp->viewport_textures.insert(this);
VS::get_singleton()->texture_set_proxy(proxy, vp->texture_rid);
+
+ vp->texture_flags = flags;
+ VS::get_singleton()->texture_set_flags(vp->texture_rid, flags);
}
void ViewportTexture::set_viewport_path_in_scene(const NodePath &p_path) {
@@ -122,20 +125,18 @@ Ref<Image> ViewportTexture::get_data() const {
return VS::get_singleton()->texture_get_data(vp->texture_rid);
}
void ViewportTexture::set_flags(uint32_t p_flags) {
+ flags = p_flags;
if (!vp)
return;
- vp->texture_flags = p_flags;
- VS::get_singleton()->texture_set_flags(vp->texture_rid, p_flags);
+ vp->texture_flags = flags;
+ VS::get_singleton()->texture_set_flags(vp->texture_rid, flags);
}
uint32_t ViewportTexture::get_flags() const {
- if (!vp)
- return 0;
-
- return vp->texture_flags;
+ return flags;
}
void ViewportTexture::_bind_methods() {
@@ -143,12 +144,13 @@ 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() {
vp = NULL;
+ flags = 0;
set_local_to_scene(true);
proxy = VS::get_singleton()->texture_create();
}
@@ -624,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);
@@ -1306,13 +1308,37 @@ void Viewport::_gui_cancel_tooltip() {
}
}
+String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos) {
+
+ Vector2 pos = p_pos;
+ String tooltip;
+
+ while (p_control) {
+
+ tooltip = p_control->get_tooltip(pos);
+
+ if (tooltip != String())
+ break;
+ pos = p_control->get_transform().xform(pos);
+
+ if (p_control->data.mouse_filter == Control::MOUSE_FILTER_STOP)
+ break;
+ if (p_control->is_set_as_toplevel())
+ break;
+
+ p_control = p_control->get_parent_control();
+ }
+
+ return tooltip;
+}
+
void Viewport::_gui_show_tooltip() {
if (!gui.tooltip) {
return;
}
- String tooltip = gui.tooltip->get_tooltip(gui.tooltip->get_global_transform().xform_inv(gui.tooltip_pos));
+ String tooltip = _gui_get_tooltip(gui.tooltip, gui.tooltip->get_global_transform().xform_inv(gui.tooltip_pos));
if (tooltip.length() == 0)
return; // bye
@@ -1345,7 +1371,7 @@ void Viewport::_gui_show_tooltip() {
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_combined_minimum_size() + ttp->get_minimum_size());
+ 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();
if (r.size.x + r.position.x > vr.size.x)
r.position.x = vr.size.x - r.size.x;
@@ -1378,6 +1404,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;
@@ -1386,12 +1414,14 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
Control *control = Object::cast_to<Control>(ci);
if (control) {
- control->call_multilevel(SceneStringNames::get_singleton()->_gui_input, ev);
+
+ control->emit_signal(SceneStringNames::get_singleton()->gui_input, ev); //signal should be first, so it's possible to override an event (and then accept it)
if (gui.key_event_accepted)
break;
if (!control->is_inside_tree())
break;
- control->emit_signal(SceneStringNames::get_singleton()->gui_input, ev);
+ control->call_multilevel(SceneStringNames::get_singleton()->_gui_input, ev);
+
if (!control->is_inside_tree() || control->is_set_as_toplevel())
break;
if (gui.key_event_accepted)
@@ -1560,7 +1590,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (mb->is_pressed()) {
Size2 pos = mpos;
- if (gui.mouse_focus && mb->get_button_index() != gui.mouse_focus_button && mb->get_button_index() == BUTTON_LEFT) {
+ if (gui.mouse_focus && mb->get_button_index() != gui.mouse_focus_button) {
//do not steal mouse focus and stuff
@@ -1862,7 +1892,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (gui.tooltip_popup) {
if (can_tooltip) {
- String tooltip = over->get_tooltip(gui.tooltip->get_global_transform().xform_inv(mpos));
+ String tooltip = _gui_get_tooltip(over, gui.tooltip->get_global_transform().xform_inv(mpos));
if (tooltip.length() == 0)
_gui_cancel_tooltip();
@@ -1884,7 +1914,23 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
mm->set_position(pos);
- Control::CursorShape cursor_shape = over->get_cursor_shape(pos);
+ Control::CursorShape cursor_shape = Control::CURSOR_ARROW;
+ {
+ Control *c = over;
+ Vector2 cpos = pos;
+ while (c) {
+ cursor_shape = c->get_cursor_shape(cpos);
+ cpos = c->get_transform().xform(cpos);
+ if (cursor_shape != Control::CURSOR_ARROW)
+ break;
+ if (c->data.mouse_filter == Control::MOUSE_FILTER_STOP)
+ break;
+ if (c->is_set_as_toplevel())
+ break;
+ c = c->get_parent_control();
+ }
+ }
+
OS::get_singleton()->set_cursor_shape((OS::CursorShape)cursor_shape);
if (over->can_process()) {
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 363414bbad..3000398540 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -58,6 +58,7 @@ class ViewportTexture : public Texture {
friend class Viewport;
Viewport *vp;
+ uint32_t flags;
RID proxy;
@@ -311,6 +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);
void _gui_cancel_tooltip();
void _gui_show_tooltip();
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 7533fa5f6c..8f78e137b4 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,8 +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_1d.h"
+#include "scene/animation/animation_blend_space_2d.h"
+#include "scene/animation/animation_blend_tree.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"
@@ -128,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"
@@ -151,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"
@@ -377,11 +388,34 @@ 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<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<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<AnimationNodeAdd2>();
+ ClassDB::register_class<AnimationNodeAdd3>();
+ ClassDB::register_class<AnimationNodeBlend2>();
+ ClassDB::register_class<AnimationNodeBlend3>();
+ ClassDB::register_class<AnimationNodeTimeScale>();
+ ClassDB::register_class<AnimationNodeTimeSeek>();
+ ClassDB::register_class<AnimationNodeTransition>();
+
OS::get_singleton()->yield(); //may take time to init
ClassDB::register_virtual_class<CollisionObject>();
@@ -404,6 +438,7 @@ void register_scene_types() {
ClassDB::register_class<Curve3D>();
ClassDB::register_class<Path>();
ClassDB::register_class<PathFollow>();
+ ClassDB::register_class<OrientedPathFollow>();
ClassDB::register_class<VisibilityNotifier>();
ClassDB::register_class<VisibilityEnabler>();
ClassDB::register_class<WorldEnvironment>();
@@ -423,6 +458,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>();
@@ -509,6 +577,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>();
@@ -534,6 +603,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 7a1fffaa26..8acfdc482a 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -32,6 +32,8 @@
#include "geometry.h"
+#define ANIM_MIN_LENGTH 0.001
+
bool Animation::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
@@ -54,6 +56,15 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
} else if (type == "method") {
add_track(TYPE_METHOD);
+ } else if (type == "bezier") {
+
+ add_track(TYPE_BEZIER);
+ } else if (type == "audio") {
+
+ add_track(TYPE_AUDIO);
+ } else if (type == "animation") {
+
+ add_track(TYPE_ANIMATION);
} else {
return false;
@@ -123,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);
}
@@ -163,7 +174,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
return true;
- } else {
+ } else if (track_get_type(track) == TYPE_METHOD) {
while (track_get_key_count(track))
track_remove_key(track, 0); //well shouldn't be set anyway
@@ -201,6 +212,114 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
}
}
}
+ } else if (track_get_type(track) == TYPE_BEZIER) {
+
+ BezierTrack *bt = static_cast<BezierTrack *>(tracks[track]);
+ Dictionary d = p_value;
+ ERR_FAIL_COND_V(!d.has("times"), false);
+ ERR_FAIL_COND_V(!d.has("points"), false);
+
+ PoolVector<float> times = d["times"];
+ PoolRealArray values = d["points"];
+
+ ERR_FAIL_COND_V(times.size() * 5 != values.size(), false);
+
+ if (times.size()) {
+
+ int valcount = times.size();
+
+ PoolVector<float>::Read rt = times.read();
+ PoolVector<float>::Read rv = values.read();
+
+ bt->values.resize(valcount);
+
+ for (int i = 0; i < valcount; i++) {
+
+ bt->values[i].time = rt[i];
+ bt->values[i].transition = 0; //unused in bezier
+ bt->values[i].value.value = rv[i * 5 + 0];
+ bt->values[i].value.in_handle.x = rv[i * 5 + 1];
+ bt->values[i].value.in_handle.y = rv[i * 5 + 2];
+ bt->values[i].value.out_handle.x = rv[i * 5 + 3];
+ bt->values[i].value.out_handle.y = rv[i * 5 + 4];
+ }
+ }
+
+ return true;
+ } else if (track_get_type(track) == TYPE_AUDIO) {
+
+ AudioTrack *ad = static_cast<AudioTrack *>(tracks[track]);
+ Dictionary d = p_value;
+ ERR_FAIL_COND_V(!d.has("times"), false);
+ ERR_FAIL_COND_V(!d.has("clips"), false);
+
+ PoolVector<float> times = d["times"];
+ Array clips = d["clips"];
+
+ ERR_FAIL_COND_V(clips.size() != times.size(), false);
+
+ if (times.size()) {
+
+ int valcount = times.size();
+
+ PoolVector<float>::Read rt = times.read();
+
+ ad->values.clear();
+
+ for (int i = 0; i < valcount; i++) {
+
+ Dictionary d = clips[i];
+ if (!d.has("start_offset"))
+ continue;
+ if (!d.has("end_offset"))
+ continue;
+ if (!d.has("stream"))
+ continue;
+
+ TKey<AudioKey> ak;
+ ak.time = rt[i];
+ ak.value.start_offset = d["start_offset"];
+ ak.value.end_offset = d["end_offset"];
+ ak.value.stream = d["stream"];
+
+ ad->values.push_back(ak);
+ }
+ }
+
+ return true;
+ } else if (track_get_type(track) == TYPE_ANIMATION) {
+
+ AnimationTrack *an = static_cast<AnimationTrack *>(tracks[track]);
+ Dictionary d = p_value;
+ ERR_FAIL_COND_V(!d.has("times"), false);
+ ERR_FAIL_COND_V(!d.has("clips"), false);
+
+ PoolVector<float> times = d["times"];
+ PoolVector<String> clips = d["clips"];
+
+ ERR_FAIL_COND_V(clips.size() != times.size(), false);
+
+ if (times.size()) {
+
+ int valcount = times.size();
+
+ PoolVector<float>::Read rt = times.read();
+ PoolVector<String>::Read rc = clips.read();
+
+ an->values.resize(valcount);
+
+ for (int i = 0; i < valcount; i++) {
+
+ TKey<StringName> ak;
+ ak.time = rt[i];
+ ak.value = rc[i];
+ an->values[i] = ak;
+ }
+ }
+
+ return true;
+ } else {
+ return false;
}
} else
return false;
@@ -232,6 +351,9 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
case TYPE_TRANSFORM: r_ret = "transform"; break;
case TYPE_VALUE: r_ret = "value"; break;
case TYPE_METHOD: r_ret = "method"; break;
+ case TYPE_BEZIER: r_ret = "bezier"; break;
+ case TYPE_AUDIO: r_ret = "audio"; break;
+ case TYPE_ANIMATION: r_ret = "animation"; break;
}
return true;
@@ -329,7 +451,7 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
return true;
- } else {
+ } else if (track_get_type(track) == TYPE_METHOD) {
Dictionary d;
@@ -368,6 +490,119 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = d;
return true;
+ } else if (track_get_type(track) == TYPE_BEZIER) {
+
+ const BezierTrack *bt = static_cast<const BezierTrack *>(tracks[track]);
+
+ Dictionary d;
+
+ PoolVector<float> key_times;
+ PoolVector<float> key_points;
+
+ int kk = bt->values.size();
+
+ key_times.resize(kk);
+ key_points.resize(kk * 5);
+
+ PoolVector<float>::Write wti = key_times.write();
+ PoolVector<float>::Write wpo = key_points.write();
+
+ int idx = 0;
+
+ const TKey<BezierKey> *vls = bt->values.ptr();
+
+ for (int i = 0; i < kk; i++) {
+
+ wti[idx] = vls[i].time;
+ wpo[idx * 5 + 0] = vls[i].value.value;
+ wpo[idx * 5 + 1] = vls[i].value.in_handle.x;
+ wpo[idx * 5 + 2] = vls[i].value.in_handle.y;
+ wpo[idx * 5 + 3] = vls[i].value.out_handle.x;
+ wpo[idx * 5 + 4] = vls[i].value.out_handle.y;
+ idx++;
+ }
+
+ wti = PoolVector<float>::Write();
+ wpo = PoolVector<float>::Write();
+
+ d["times"] = key_times;
+ d["points"] = key_points;
+
+ r_ret = d;
+
+ return true;
+ } else if (track_get_type(track) == TYPE_AUDIO) {
+
+ const AudioTrack *ad = static_cast<const AudioTrack *>(tracks[track]);
+
+ Dictionary d;
+
+ PoolVector<float> key_times;
+ Array clips;
+
+ int kk = ad->values.size();
+
+ key_times.resize(kk);
+
+ PoolVector<float>::Write wti = key_times.write();
+
+ int idx = 0;
+
+ const TKey<AudioKey> *vls = ad->values.ptr();
+
+ for (int i = 0; i < kk; i++) {
+
+ wti[idx] = vls[i].time;
+ Dictionary clip;
+ clip["start_offset"] = vls[i].value.start_offset;
+ clip["end_offset"] = vls[i].value.end_offset;
+ clip["stream"] = vls[i].value.stream;
+ clips.push_back(clip);
+ idx++;
+ }
+
+ wti = PoolVector<float>::Write();
+
+ d["times"] = key_times;
+ d["clips"] = clips;
+
+ r_ret = d;
+
+ return true;
+ } else if (track_get_type(track) == TYPE_ANIMATION) {
+
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(tracks[track]);
+
+ Dictionary d;
+
+ PoolVector<float> key_times;
+ PoolVector<String> clips;
+
+ int kk = an->values.size();
+
+ key_times.resize(kk);
+ clips.resize(kk);
+
+ PoolVector<float>::Write wti = key_times.write();
+ PoolVector<String>::Write wcl = clips.write();
+
+ const TKey<StringName> *vls = an->values.ptr();
+
+ for (int i = 0; i < kk; i++) {
+
+ wti[i] = vls[i].time;
+ wcl[i] = vls[i].value;
+ }
+
+ wti = PoolVector<float>::Write();
+ wcl = PoolVector<String>::Write();
+
+ d["times"] = key_times;
+ d["clips"] = clips;
+
+ r_ret = d;
+
+ return true;
}
} else
return false;
@@ -412,6 +647,21 @@ int Animation::add_track(TrackType p_type, int p_at_pos) {
tracks.insert(p_at_pos, memnew(MethodTrack));
} break;
+ case TYPE_BEZIER: {
+
+ tracks.insert(p_at_pos, memnew(BezierTrack));
+
+ } break;
+ case TYPE_AUDIO: {
+
+ tracks.insert(p_at_pos, memnew(AudioTrack));
+
+ } break;
+ case TYPE_ANIMATION: {
+
+ tracks.insert(p_at_pos, memnew(AnimationTrack));
+
+ } break;
default: {
ERR_PRINT("Unknown track type");
@@ -446,6 +696,24 @@ void Animation::remove_track(int p_track) {
_clear(mt->methods);
} break;
+ case TYPE_BEZIER: {
+
+ BezierTrack *bz = static_cast<BezierTrack *>(t);
+ _clear(bz->values);
+
+ } break;
+ case TYPE_AUDIO: {
+
+ AudioTrack *ad = static_cast<AudioTrack *>(t);
+ _clear(ad->values);
+
+ } break;
+ case TYPE_ANIMATION: {
+
+ AnimationTrack *an = static_cast<AnimationTrack *>(t);
+ _clear(an->values);
+
+ } break;
}
memdelete(t);
@@ -642,6 +910,27 @@ void Animation::track_remove_key(int p_track, int p_idx) {
mt->methods.remove(p_idx);
} break;
+ case TYPE_BEZIER: {
+
+ BezierTrack *bz = static_cast<BezierTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, bz->values.size());
+ bz->values.remove(p_idx);
+
+ } break;
+ case TYPE_AUDIO: {
+
+ AudioTrack *ad = static_cast<AudioTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, ad->values.size());
+ ad->values.remove(p_idx);
+
+ } break;
+ case TYPE_ANIMATION: {
+
+ AnimationTrack *an = static_cast<AnimationTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, an->values.size());
+ an->values.remove(p_idx);
+
+ } break;
}
emit_changed();
@@ -686,6 +975,39 @@ int Animation::track_find_key(int p_track, float p_time, bool p_exact) const {
return k;
} break;
+ case TYPE_BEZIER: {
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+ int k = _find(bt->values, p_time);
+ if (k < 0 || k >= bt->values.size())
+ return -1;
+ if (bt->values[k].time != p_time && p_exact)
+ return -1;
+ return k;
+
+ } break;
+ case TYPE_AUDIO: {
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+ int k = _find(at->values, p_time);
+ if (k < 0 || k >= at->values.size())
+ return -1;
+ if (at->values[k].time != p_time && p_exact)
+ return -1;
+ return k;
+
+ } break;
+ case TYPE_ANIMATION: {
+
+ AnimationTrack *at = static_cast<AnimationTrack *>(t);
+ int k = _find(at->values, p_time);
+ if (k < 0 || k >= at->values.size())
+ return -1;
+ if (at->values[k].time != p_time && p_exact)
+ return -1;
+ return k;
+
+ } break;
}
return -1;
@@ -748,6 +1070,51 @@ void Animation::track_insert_key(int p_track, float p_time, const Variant &p_key
_insert(p_time, mt->methods, k);
} break;
+ case TYPE_BEZIER: {
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+
+ Array arr = p_key;
+ ERR_FAIL_COND(arr.size() != 5);
+
+ TKey<BezierKey> k;
+ k.time = p_time;
+ k.value.value = arr[0];
+ k.value.in_handle.x = arr[1];
+ k.value.in_handle.y = arr[2];
+ k.value.out_handle.x = arr[3];
+ k.value.out_handle.y = arr[4];
+ _insert(p_time, bt->values, k);
+
+ } break;
+ case TYPE_AUDIO: {
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+
+ Dictionary k = p_key;
+ ERR_FAIL_COND(!k.has("start_offset"));
+ ERR_FAIL_COND(!k.has("end_offset"));
+ ERR_FAIL_COND(!k.has("stream"));
+
+ TKey<AudioKey> ak;
+ ak.time = p_time;
+ ak.value.start_offset = k["start_offset"];
+ ak.value.end_offset = k["end_offset"];
+ ak.value.stream = k["stream"];
+ _insert(p_time, at->values, ak);
+
+ } break;
+ case TYPE_ANIMATION: {
+
+ AnimationTrack *at = static_cast<AnimationTrack *>(t);
+
+ TKey<StringName> ak;
+ ak.time = p_time;
+ ak.value = p_key;
+
+ _insert(p_time, at->values, ak);
+
+ } break;
}
emit_changed();
@@ -776,6 +1143,21 @@ int Animation::track_get_key_count(int p_track) const {
MethodTrack *mt = static_cast<MethodTrack *>(t);
return mt->methods.size();
} break;
+ case TYPE_BEZIER: {
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+ return bt->values.size();
+ } break;
+ case TYPE_AUDIO: {
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+ return at->values.size();
+ } break;
+ case TYPE_ANIMATION: {
+
+ AnimationTrack *at = static_cast<AnimationTrack *>(t);
+ return at->values.size();
+ } break;
}
ERR_FAIL_V(-1);
@@ -817,6 +1199,41 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const {
return d;
} break;
+ case TYPE_BEZIER: {
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, bt->values.size(), Variant());
+
+ Array arr;
+ arr.resize(5);
+ arr[0] = bt->values[p_key_idx].value.value;
+ arr[1] = bt->values[p_key_idx].value.in_handle.x;
+ arr[2] = bt->values[p_key_idx].value.in_handle.y;
+ arr[3] = bt->values[p_key_idx].value.out_handle.x;
+ arr[4] = bt->values[p_key_idx].value.out_handle.y;
+ return arr;
+
+ } break;
+ case TYPE_AUDIO: {
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, at->values.size(), Variant());
+
+ Dictionary k;
+ k["start_offset"] = at->values[p_key_idx].value.start_offset;
+ k["end_offset"] = at->values[p_key_idx].value.end_offset;
+ k["stream"] = at->values[p_key_idx].value.stream;
+ return k;
+
+ } break;
+ case TYPE_ANIMATION: {
+
+ AnimationTrack *at = static_cast<AnimationTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, at->values.size(), Variant());
+
+ return at->values[p_key_idx].value;
+
+ } break;
}
ERR_FAIL_V(Variant());
@@ -849,6 +1266,27 @@ float Animation::track_get_key_time(int p_track, int p_key_idx) const {
return mt->methods[p_key_idx].time;
} break;
+ case TYPE_BEZIER: {
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, bt->values.size(), -1);
+ return bt->values[p_key_idx].time;
+
+ } break;
+ case TYPE_AUDIO: {
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, at->values.size(), -1);
+ return at->values[p_key_idx].time;
+
+ } break;
+ case TYPE_ANIMATION: {
+
+ AnimationTrack *at = static_cast<AnimationTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, at->values.size(), -1);
+ return at->values[p_key_idx].time;
+
+ } break;
}
ERR_FAIL_V(-1);
@@ -881,6 +1319,18 @@ float Animation::track_get_key_transition(int p_track, int p_key_idx) const {
return mt->methods[p_key_idx].transition;
} break;
+ case TYPE_BEZIER: {
+
+ return 1; //bezier does not really use transitions
+ } break;
+ case TYPE_AUDIO: {
+
+ return 1; //audio does not really use transitions
+ } break;
+ case TYPE_ANIMATION: {
+
+ return 1; //animation does not really use transitions
+ } break;
}
ERR_FAIL_V(0);
@@ -923,6 +1373,42 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
if (d.has("args"))
mt->methods[p_key_idx].params = d["args"];
} break;
+ case TYPE_BEZIER: {
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, bt->values.size());
+
+ Array arr = p_value;
+ ERR_FAIL_COND(arr.size() != 5);
+
+ bt->values[p_key_idx].value.value = arr[0];
+ bt->values[p_key_idx].value.in_handle.x = arr[1];
+ bt->values[p_key_idx].value.in_handle.y = arr[2];
+ bt->values[p_key_idx].value.out_handle.x = arr[3];
+ bt->values[p_key_idx].value.out_handle.y = arr[4];
+
+ } break;
+ case TYPE_AUDIO: {
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+
+ Dictionary k = p_value;
+ ERR_FAIL_COND(!k.has("start_offset"));
+ ERR_FAIL_COND(!k.has("end_offset"));
+ ERR_FAIL_COND(!k.has("stream"));
+
+ at->values[p_key_idx].value.start_offset = k["start_offset"];
+ at->values[p_key_idx].value.end_offset = k["end_offset"];
+ at->values[p_key_idx].value.stream = k["stream"];
+
+ } break;
+ case TYPE_ANIMATION: {
+
+ AnimationTrack *at = static_cast<AnimationTrack *>(t);
+
+ at->values[p_key_idx].value = p_value;
+
+ } break;
}
}
@@ -953,6 +1439,11 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, float p_tra
mt->methods[p_key_idx].transition = p_transition;
} break;
+ case TYPE_BEZIER:
+ case TYPE_AUDIO:
+ case TYPE_ANIMATION: {
+ // they dont use transition
+ } break;
}
}
@@ -1410,7 +1901,7 @@ void Animation::value_track_set_update_mode(int p_track, UpdateMode p_mode) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_VALUE);
- ERR_FAIL_INDEX(p_mode, 3);
+ ERR_FAIL_INDEX(p_mode, 4);
ValueTrack *vt = static_cast<ValueTrack *>(t);
vt->update_mode = p_mode;
@@ -1426,6 +1917,161 @@ Animation::UpdateMode Animation::value_track_get_update_mode(int p_track) const
return vt->update_mode;
}
+template <class T>
+void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, float from_time, float to_time, List<int> *p_indices) const {
+
+ if (from_time != length && to_time == length)
+ to_time = length * 1.01; //include a little more if at the end
+
+ int to = _find(p_array, to_time);
+
+ // can't really send the events == time, will be sent in the next frame.
+ // if event>=len then it will probably never be requested by the anim player.
+
+ if (to >= 0 && p_array[to].time >= to_time)
+ to--;
+
+ if (to < 0)
+ return; // not bother
+
+ int from = _find(p_array, from_time);
+
+ // position in the right first event.+
+ if (from < 0 || p_array[from].time < from_time)
+ from++;
+
+ int max = p_array.size();
+
+ for (int i = from; i <= to; i++) {
+
+ ERR_CONTINUE(i < 0 || i >= max); // shouldn't happen
+ p_indices->push_back(i);
+ }
+}
+
+void Animation::track_get_key_indices_in_range(int p_track, float p_time, float p_delta, List<int> *p_indices) const {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ const Track *t = tracks[p_track];
+
+ float from_time = p_time - p_delta;
+ float to_time = p_time;
+
+ if (from_time > to_time)
+ SWAP(from_time, to_time);
+
+ if (loop) {
+
+ if (from_time > length || from_time < 0)
+ from_time = Math::fposmod(from_time, length);
+
+ if (to_time > length || to_time < 0)
+ to_time = Math::fposmod(to_time, length);
+
+ if (from_time > to_time) {
+ // handle loop by splitting
+
+ switch (t->type) {
+
+ case TYPE_TRANSFORM: {
+
+ const TransformTrack *tt = static_cast<const TransformTrack *>(t);
+ _track_get_key_indices_in_range(tt->transforms, from_time, length, p_indices);
+ _track_get_key_indices_in_range(tt->transforms, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_VALUE: {
+
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_METHOD: {
+
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
+ _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_BEZIER: {
+
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_AUDIO: {
+
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_ANIMATION: {
+
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
+
+ } break;
+ }
+ return;
+ }
+ } else {
+
+ if (from_time < 0)
+ from_time = 0;
+ if (from_time > length)
+ from_time = length;
+
+ if (to_time < 0)
+ to_time = 0;
+ if (to_time > length)
+ to_time = length;
+ }
+
+ switch (t->type) {
+
+ case TYPE_TRANSFORM: {
+
+ const TransformTrack *tt = static_cast<const TransformTrack *>(t);
+ _track_get_key_indices_in_range(tt->transforms, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_VALUE: {
+
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_METHOD: {
+
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_BEZIER: {
+
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_AUDIO: {
+
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_ANIMATION: {
+
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, from_time, to_time, p_indices);
+
+ } break;
+ }
+}
+
void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, float from_time, float to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length)
@@ -1527,9 +2173,361 @@ StringName Animation::method_track_get_name(int p_track, int p_key_idx) const {
return pm->methods[p_key_idx].method;
}
+int Animation::bezier_track_insert_key(int p_track, float p_time, float p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle) {
+
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_BEZIER, -1);
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+
+ TKey<BezierKey> k;
+ k.time = p_time;
+ k.value.value = p_value;
+ k.value.in_handle = p_in_handle;
+ if (k.value.in_handle.x > 0) {
+ k.value.in_handle.x = 0;
+ }
+ k.value.out_handle = p_out_handle;
+ if (k.value.out_handle.x < 0) {
+ k.value.out_handle.x = 0;
+ }
+
+ int key = _insert(p_time, bt->values, k);
+
+ emit_changed();
+
+ return key;
+}
+
+void Animation::bezier_track_set_key_value(int p_track, int p_index, float p_value) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND(t->type != TYPE_BEZIER);
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+
+ ERR_FAIL_INDEX(p_index, bt->values.size());
+
+ bt->values[p_index].value.value = p_value;
+ emit_changed();
+}
+
+void Animation::bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND(t->type != TYPE_BEZIER);
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+
+ ERR_FAIL_INDEX(p_index, bt->values.size());
+
+ bt->values[p_index].value.in_handle = p_handle;
+ if (bt->values[p_index].value.in_handle.x > 0) {
+ bt->values[p_index].value.in_handle.x = 0;
+ }
+ emit_changed();
+}
+void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND(t->type != TYPE_BEZIER);
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+
+ ERR_FAIL_INDEX(p_index, bt->values.size());
+
+ bt->values[p_index].value.out_handle = p_handle;
+ if (bt->values[p_index].value.out_handle.x < 0) {
+ bt->values[p_index].value.out_handle.x = 0;
+ }
+ emit_changed();
+}
+float Animation::bezier_track_get_key_value(int p_track, int p_index) const {
+
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_BEZIER, 0);
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+
+ ERR_FAIL_INDEX_V(p_index, bt->values.size(), 0);
+
+ return bt->values[p_index].value.value;
+}
+Vector2 Animation::bezier_track_get_key_in_handle(int p_track, int p_index) const {
+
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), Vector2());
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_BEZIER, Vector2());
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+
+ ERR_FAIL_INDEX_V(p_index, bt->values.size(), Vector2());
+
+ return bt->values[p_index].value.in_handle;
+}
+Vector2 Animation::bezier_track_get_key_out_handle(int p_track, int p_index) const {
+
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), Vector2());
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_BEZIER, Vector2());
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+
+ ERR_FAIL_INDEX_V(p_index, bt->values.size(), Vector2());
+
+ return bt->values[p_index].value.out_handle;
+}
+
+static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, const Vector2 &control_1, const Vector2 &control_2, const Vector2 &end) {
+ /* Formula from Wikipedia article on Bezier curves. */
+ real_t omt = (1.0 - t);
+ real_t omt2 = omt * omt;
+ real_t omt3 = omt2 * omt;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
+
+ return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
+}
+
+float Animation::bezier_track_interpolate(int p_track, float p_time) const {
+ //this uses a different interpolation scheme
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
+ Track *track = tracks[p_track];
+ ERR_FAIL_COND_V(track->type != TYPE_BEZIER, 0);
+
+ BezierTrack *bt = static_cast<BezierTrack *>(track);
+
+ int len = _find(bt->values, length) + 1; // try to find last key (there may be more past the end)
+
+ if (len <= 0) {
+ // (-1 or -2 returned originally) (plus one above)
+ return 0;
+ } else if (len == 1) { // one key found (0+1), return it
+ return bt->values[0].value.value;
+ }
+
+ int idx = _find(bt->values, p_time);
+
+ ERR_FAIL_COND_V(idx == -2, 0);
+
+ //there really is no looping interpolation on bezier
+
+ if (idx < 0) {
+ return bt->values[0].value.value;
+ }
+
+ if (idx >= bt->values.size() - 1) {
+ return bt->values[bt->values.size() - 1].value.value;
+ }
+
+ float t = p_time - bt->values[idx].time;
+
+ int iterations = 10;
+
+ 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(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
+ for (int i = 0; i < iterations; i++) {
+
+ middle = (low + high) / 2;
+
+ Vector2 interp = _bezier_interp(middle, start, start_out, end_in, end);
+
+ if (interp.x < t) {
+ low = middle;
+ } else {
+ high = middle;
+ }
+ }
+
+ //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;
+}
+
+int Animation::audio_track_insert_key(int p_track, float p_time, const RES &p_stream, float p_start_offset, float p_end_offset) {
+
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_AUDIO, -1);
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+
+ TKey<AudioKey> k;
+ k.time = p_time;
+ k.value.stream = p_stream;
+ k.value.start_offset = p_start_offset;
+ if (k.value.start_offset < 0)
+ k.value.start_offset = 0;
+ k.value.end_offset = p_end_offset;
+ if (k.value.end_offset < 0)
+ k.value.end_offset = 0;
+
+ int key = _insert(p_time, at->values, k);
+
+ emit_changed();
+
+ return key;
+}
+
+void Animation::audio_track_set_key_stream(int p_track, int p_key, const RES &p_stream) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND(t->type != TYPE_AUDIO);
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+
+ ERR_FAIL_INDEX(p_key, at->values.size());
+
+ at->values[p_key].value.stream = p_stream;
+
+ emit_changed();
+}
+
+void Animation::audio_track_set_key_start_offset(int p_track, int p_key, float p_offset) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND(t->type != TYPE_AUDIO);
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+
+ ERR_FAIL_INDEX(p_key, at->values.size());
+
+ if (p_offset < 0)
+ p_offset = 0;
+
+ at->values[p_key].value.start_offset = p_offset;
+
+ emit_changed();
+}
+
+void Animation::audio_track_set_key_end_offset(int p_track, int p_key, float p_offset) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND(t->type != TYPE_AUDIO);
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+
+ ERR_FAIL_INDEX(p_key, at->values.size());
+
+ if (p_offset < 0)
+ p_offset = 0;
+
+ at->values[p_key].value.end_offset = p_offset;
+
+ emit_changed();
+}
+
+RES Animation::audio_track_get_key_stream(int p_track, int p_key) const {
+
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), RES());
+ const Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_AUDIO, RES());
+
+ const AudioTrack *at = static_cast<const AudioTrack *>(t);
+
+ ERR_FAIL_INDEX_V(p_key, at->values.size(), RES());
+
+ return at->values[p_key].value.stream;
+}
+float Animation::audio_track_get_key_start_offset(int p_track, int p_key) const {
+
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
+ const Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_AUDIO, 0);
+
+ const AudioTrack *at = static_cast<const AudioTrack *>(t);
+
+ ERR_FAIL_INDEX_V(p_key, at->values.size(), 0);
+
+ return at->values[p_key].value.start_offset;
+}
+float Animation::audio_track_get_key_end_offset(int p_track, int p_key) const {
+
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
+ const Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_AUDIO, 0);
+
+ const AudioTrack *at = static_cast<const AudioTrack *>(t);
+
+ ERR_FAIL_INDEX_V(p_key, at->values.size(), 0);
+
+ return at->values[p_key].value.end_offset;
+}
+
+//
+
+int Animation::animation_track_insert_key(int p_track, float p_time, const StringName &p_animation) {
+
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_ANIMATION, -1);
+
+ AnimationTrack *at = static_cast<AnimationTrack *>(t);
+
+ TKey<StringName> k;
+ k.time = p_time;
+ k.value = p_animation;
+
+ int key = _insert(p_time, at->values, k);
+
+ emit_changed();
+
+ return key;
+}
+
+void Animation::animation_track_set_key_animation(int p_track, int p_key, const StringName &p_animation) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND(t->type != TYPE_ANIMATION);
+
+ AnimationTrack *at = static_cast<AnimationTrack *>(t);
+
+ ERR_FAIL_INDEX(p_key, at->values.size());
+
+ at->values[p_key].value = p_animation;
+
+ emit_changed();
+}
+
+StringName Animation::animation_track_get_key_animation(int p_track, int p_key) const {
+
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), StringName());
+ const Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_ANIMATION, StringName());
+
+ const AnimationTrack *at = static_cast<const AnimationTrack *>(t);
+
+ ERR_FAIL_INDEX_V(p_key, at->values.size(), StringName());
+
+ return at->values[p_key].value;
+}
+
void Animation::set_length(float p_length) {
- ERR_FAIL_COND(length < 0);
+ if (p_length < ANIM_MIN_LENGTH) {
+ p_length = ANIM_MIN_LENGTH;
+ }
length = p_length;
emit_changed();
}
@@ -1592,6 +2590,16 @@ void Animation::track_move_down(int p_track) {
emit_changed();
}
+void Animation::track_swap(int p_track, int p_with_track) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ ERR_FAIL_INDEX(p_with_track, tracks.size());
+ if (p_track == p_with_track)
+ return;
+ SWAP(tracks[p_track], tracks[p_with_track]);
+ emit_changed();
+}
+
void Animation::set_step(float p_step) {
step = p_step;
@@ -1631,6 +2639,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_move_up", "idx"), &Animation::track_move_up);
ClassDB::bind_method(D_METHOD("track_move_down", "idx"), &Animation::track_move_down);
+ ClassDB::bind_method(D_METHOD("track_swap", "idx", "with_idx"), &Animation::track_swap);
ClassDB::bind_method(D_METHOD("track_set_imported", "idx", "imported"), &Animation::track_set_imported);
ClassDB::bind_method(D_METHOD("track_is_imported", "idx"), &Animation::track_is_imported);
@@ -1667,6 +2676,30 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("method_track_get_name", "idx", "key_idx"), &Animation::method_track_get_name);
ClassDB::bind_method(D_METHOD("method_track_get_params", "idx", "key_idx"), &Animation::method_track_get_params);
+ ClassDB::bind_method(D_METHOD("bezier_track_insert_key", "track", "time", "value", "in_handle", "out_handle"), &Animation::bezier_track_insert_key, DEFVAL(Vector2()), DEFVAL(Vector2()));
+
+ ClassDB::bind_method(D_METHOD("bezier_track_set_key_value", "idx", "key_idx", "value"), &Animation::bezier_track_set_key_value);
+ ClassDB::bind_method(D_METHOD("bezier_track_set_key_in_handle", "idx", "key_idx", "in_handle"), &Animation::bezier_track_set_key_in_handle);
+ ClassDB::bind_method(D_METHOD("bezier_track_set_key_out_handle", "idx", "key_idx", "out_handle"), &Animation::bezier_track_set_key_out_handle);
+
+ ClassDB::bind_method(D_METHOD("bezier_track_get_key_value", "idx", "key_idx"), &Animation::bezier_track_get_key_value);
+ ClassDB::bind_method(D_METHOD("bezier_track_get_key_in_handle", "idx", "key_idx"), &Animation::bezier_track_get_key_in_handle);
+ ClassDB::bind_method(D_METHOD("bezier_track_get_key_out_handle", "idx", "key_idx"), &Animation::bezier_track_get_key_out_handle);
+
+ ClassDB::bind_method(D_METHOD("bezier_track_interpolate", "track", "time"), &Animation::bezier_track_interpolate);
+
+ ClassDB::bind_method(D_METHOD("audio_track_insert_key", "track", "time", "stream", "start_offset", "end_offset"), &Animation::audio_track_insert_key, DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("audio_track_set_key_stream", "idx", "key_idx", "stream"), &Animation::audio_track_set_key_stream);
+ ClassDB::bind_method(D_METHOD("audio_track_set_key_start_offset", "idx", "key_idx", "offset"), &Animation::audio_track_set_key_start_offset);
+ ClassDB::bind_method(D_METHOD("audio_track_set_key_end_offset", "idx", "key_idx", "offset"), &Animation::audio_track_set_key_end_offset);
+ ClassDB::bind_method(D_METHOD("audio_track_get_key_stream", "idx", "key_idx"), &Animation::audio_track_get_key_stream);
+ ClassDB::bind_method(D_METHOD("audio_track_get_key_start_offset", "idx", "key_idx"), &Animation::audio_track_get_key_start_offset);
+ ClassDB::bind_method(D_METHOD("audio_track_get_key_end_offset", "idx", "key_idx"), &Animation::audio_track_get_key_end_offset);
+
+ ClassDB::bind_method(D_METHOD("animation_track_insert_key", "track", "time", "animation"), &Animation::animation_track_insert_key);
+ ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation);
+ ClassDB::bind_method(D_METHOD("animation_track_get_key_animation", "idx", "key_idx"), &Animation::animation_track_get_key_animation);
+
ClassDB::bind_method(D_METHOD("set_length", "time_sec"), &Animation::set_length);
ClassDB::bind_method(D_METHOD("get_length"), &Animation::get_length);
@@ -1686,6 +2719,9 @@ void Animation::_bind_methods() {
BIND_ENUM_CONSTANT(TYPE_VALUE);
BIND_ENUM_CONSTANT(TYPE_TRANSFORM);
BIND_ENUM_CONSTANT(TYPE_METHOD);
+ BIND_ENUM_CONSTANT(TYPE_BEZIER);
+ BIND_ENUM_CONSTANT(TYPE_AUDIO);
+ BIND_ENUM_CONSTANT(TYPE_ANIMATION);
BIND_ENUM_CONSTANT(INTERPOLATION_NEAREST);
BIND_ENUM_CONSTANT(INTERPOLATION_LINEAR);
@@ -1694,6 +2730,7 @@ void Animation::_bind_methods() {
BIND_ENUM_CONSTANT(UPDATE_CONTINUOUS);
BIND_ENUM_CONSTANT(UPDATE_DISCRETE);
BIND_ENUM_CONSTANT(UPDATE_TRIGGER);
+ BIND_ENUM_CONSTANT(UPDATE_CAPTURE);
}
void Animation::clear() {
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 73691a69f2..a41e6ea5d7 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -45,6 +45,9 @@ public:
TYPE_VALUE, ///< Set a value in a property, can be interpolated.
TYPE_TRANSFORM, ///< Transform a node or a bone.
TYPE_METHOD, ///< Call any method on a specific node.
+ TYPE_BEZIER, ///< Bezier curve
+ TYPE_AUDIO,
+ TYPE_ANIMATION,
};
enum InterpolationType {
@@ -57,6 +60,7 @@ public:
UPDATE_CONTINUOUS,
UPDATE_DISCRETE,
UPDATE_TRIGGER,
+ UPDATE_CAPTURE,
};
@@ -137,6 +141,55 @@ private:
MethodTrack() { type = TYPE_METHOD; }
};
+ /* BEZIER TRACK */
+
+ struct BezierKey {
+ Vector2 in_handle; //relative (x always <0)
+ Vector2 out_handle; //relative (x always >0)
+ float value;
+ };
+
+ struct BezierTrack : public Track {
+
+ Vector<TKey<BezierKey> > values;
+
+ BezierTrack() {
+ type = TYPE_BEZIER;
+ }
+ };
+
+ /* AUDIO TRACK */
+
+ struct AudioKey {
+ RES stream;
+ float start_offset; //offset from start
+ float end_offset; //offset from end, if 0 then full length or infinite
+ AudioKey() {
+ start_offset = 0;
+ end_offset = 0;
+ }
+ };
+
+ struct AudioTrack : public Track {
+
+ Vector<TKey<AudioKey> > values;
+
+ AudioTrack() {
+ type = TYPE_AUDIO;
+ }
+ };
+
+ /* AUDIO TRACK */
+
+ struct AnimationTrack : public Track {
+
+ Vector<TKey<StringName> > values;
+
+ AnimationTrack() {
+ type = TYPE_ANIMATION;
+ }
+ };
+
Vector<Track *> tracks;
/*
@@ -168,6 +221,9 @@ private:
template <class T>
_FORCE_INLINE_ T _interpolate(const Vector<TKey<T> > &p_keys, float p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const;
+ template <class T>
+ _FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, float from_time, float to_time, List<int> *p_indices) const;
+
_FORCE_INLINE_ void _value_track_get_key_indices_in_range(const ValueTrack *vt, float from_time, float to_time, List<int> *p_indices) const;
_FORCE_INLINE_ void _method_track_get_key_indices_in_range(const MethodTrack *mt, float from_time, float to_time, List<int> *p_indices) const;
@@ -238,6 +294,7 @@ public:
void track_move_up(int p_track);
void track_move_down(int p_track);
+ void track_swap(int p_track, int p_with_track);
void track_set_imported(int p_track, bool p_imported);
bool track_is_imported(int p_track) const;
@@ -245,7 +302,6 @@ public:
void track_set_enabled(int p_track, bool p_enabled);
bool track_is_enabled(int p_track) const;
- int transform_track_insert_key(int p_track, float p_time, const Vector3 p_loc, const Quat &p_rot = Quat(), const Vector3 &p_scale = Vector3());
void track_insert_key(int p_track, float p_time, const Variant &p_key, float p_transition = 1);
void track_set_key_transition(int p_track, int p_key_idx, float p_transition);
void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value);
@@ -257,10 +313,33 @@ public:
float track_get_key_time(int p_track, int p_key_idx) const;
float track_get_key_transition(int p_track, int p_key_idx) const;
+ int transform_track_insert_key(int p_track, float p_time, const Vector3 p_loc, const Quat &p_rot = Quat(), const Vector3 &p_scale = Vector3());
Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quat *r_rot, Vector3 *r_scale) const;
void track_set_interpolation_type(int p_track, InterpolationType p_interp);
InterpolationType track_get_interpolation_type(int p_track) const;
+ int bezier_track_insert_key(int p_track, float p_time, float p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle);
+ void bezier_track_set_key_value(int p_track, int p_index, float p_value);
+ void bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle);
+ void bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle);
+ float bezier_track_get_key_value(int p_track, int p_index) const;
+ Vector2 bezier_track_get_key_in_handle(int p_track, int p_index) const;
+ Vector2 bezier_track_get_key_out_handle(int p_track, int p_index) const;
+
+ float bezier_track_interpolate(int p_track, float p_time) const;
+
+ int audio_track_insert_key(int p_track, float p_time, const RES &p_stream, float p_start_offset = 0, float p_end_offset = 0);
+ void audio_track_set_key_stream(int p_track, int p_key, const RES &p_stream);
+ void audio_track_set_key_start_offset(int p_track, int p_key, float p_offset);
+ void audio_track_set_key_end_offset(int p_track, int p_key, float p_offset);
+ RES audio_track_get_key_stream(int p_track, int p_key) const;
+ float audio_track_get_key_start_offset(int p_track, int p_key) const;
+ float audio_track_get_key_end_offset(int p_track, int p_key) const;
+
+ int animation_track_insert_key(int p_track, float p_time, const StringName &p_animation);
+ void animation_track_set_key_animation(int p_track, int p_key, const StringName &p_animation);
+ StringName animation_track_get_key_animation(int p_track, int p_key) const;
+
void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
bool track_get_interpolation_loop_wrap(int p_track) const;
@@ -277,6 +356,8 @@ public:
void copy_track(int p_track, Ref<Animation> p_to_animation);
+ void track_get_key_indices_in_range(int p_track, float p_time, float p_delta, List<int> *p_indices) const;
+
void set_length(float p_length);
float get_length() const;
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index b77143cd9d..02a9e4d69b 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -524,6 +524,9 @@ String AudioStreamSample::get_stream_name() const {
void AudioStreamSample::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamSample::set_data);
+ ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamSample::get_data);
+
ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioStreamSample::set_format);
ClassDB::bind_method(D_METHOD("get_format"), &AudioStreamSample::get_format);
@@ -542,16 +545,13 @@ void AudioStreamSample::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamSample::set_stereo);
ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamSample::is_stereo);
- ClassDB::bind_method(D_METHOD("_set_data", "data"), &AudioStreamSample::set_data);
- ClassDB::bind_method(D_METHOD("_get_data"), &AudioStreamSample::get_data);
-
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong"), "set_loop_mode", "get_loop_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_begin"), "set_loop_begin", "get_loop_begin");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_end"), "set_loop_end", "get_loop_end");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_rate"), "set_mix_rate", "get_mix_rate");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stereo"), "set_stereo", "is_stereo");
- ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
BIND_ENUM_CONSTANT(FORMAT_8_BITS);
BIND_ENUM_CONSTANT(FORMAT_16_BITS);
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 4ec1e8973d..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));
@@ -1169,6 +1179,7 @@ void Curve3D::_bake() const {
if (points.size() == 0) {
baked_point_cache.resize(0);
baked_tilt_cache.resize(0);
+ baked_up_vector_cache.resize(0);
return;
}
@@ -1178,6 +1189,14 @@ void Curve3D::_bake() const {
baked_point_cache.set(0, points[0].pos);
baked_tilt_cache.resize(1);
baked_tilt_cache.set(0, points[0].tilt);
+
+ if (up_vector_enabled) {
+
+ baked_up_vector_cache.resize(1);
+ baked_up_vector_cache.set(0, Vector3(0, 1, 0));
+ } else
+ baked_up_vector_cache.resize(0);
+
return;
}
@@ -1247,10 +1266,51 @@ void Curve3D::_bake() const {
baked_tilt_cache.resize(pointlist.size());
PoolRealArray::Write wt = baked_tilt_cache.write();
+ baked_up_vector_cache.resize(up_vector_enabled ? pointlist.size() : 0);
+ PoolVector3Array::Write up_write = baked_up_vector_cache.write();
+
+ Vector3 sideways;
+ Vector3 up;
+ Vector3 forward;
+
+ Vector3 prev_sideways = Vector3(1, 0, 0);
+ Vector3 prev_up = Vector3(0, 1, 0);
+ Vector3 prev_forward = Vector3(0, 0, 1);
+
for (List<Plane>::Element *E = pointlist.front(); E; E = E->next()) {
w[idx] = E->get().normal;
wt[idx] = E->get().d;
+
+ if (!up_vector_enabled) {
+ idx++;
+ continue;
+ }
+
+ forward = idx > 0 ? (w[idx] - w[idx - 1]).normalized() : prev_forward;
+
+ float y_dot = prev_up.dot(forward);
+
+ if (y_dot > (1.0f - CMP_EPSILON)) {
+ sideways = prev_sideways;
+ up = -prev_forward;
+ } else if (y_dot < -(1.0f - CMP_EPSILON)) {
+ sideways = prev_sideways;
+ up = prev_forward;
+ } else {
+ sideways = prev_up.cross(forward).normalized();
+ up = forward.cross(sideways).normalized();
+ }
+
+ if (idx == 1)
+ up_write[0] = up;
+
+ up_write[idx] = up;
+
+ prev_sideways = sideways;
+ prev_up = up;
+ prev_forward = forward;
+
idx++;
}
}
@@ -1343,6 +1403,53 @@ float Curve3D::interpolate_baked_tilt(float p_offset) const {
return Math::lerp(r[idx], r[idx + 1], frac);
}
+Vector3 Curve3D::interpolate_baked_up_vector(float p_offset, bool p_apply_tilt) const {
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ // curve may not have baked up vectors
+ int count = baked_up_vector_cache.size();
+ if (count == 0) {
+ ERR_EXPLAIN("No up vectors in Curve3D");
+ ERR_FAIL_COND_V(count == 0, Vector3(0, 1, 0));
+ }
+
+ if (count == 1)
+ return baked_up_vector_cache.get(0);
+
+ PoolVector3Array::Read r = baked_up_vector_cache.read();
+ PoolVector3Array::Read rp = baked_point_cache.read();
+ PoolRealArray::Read rt = baked_tilt_cache.read();
+
+ float offset = CLAMP(p_offset, 0.0f, baked_max_ofs);
+
+ int idx = Math::floor((double)offset / (double)bake_interval);
+ float frac = Math::fmod(offset, bake_interval) / bake_interval;
+
+ if (idx == count - 1)
+ return p_apply_tilt ? r[idx].rotated((rp[idx] - rp[idx - 1]).normalized(), rt[idx]) : r[idx];
+
+ Vector3 forward = (rp[idx + 1] - rp[idx]).normalized();
+ Vector3 up = r[idx];
+ Vector3 up1 = r[idx + 1];
+
+ if (p_apply_tilt) {
+ up.rotate(forward, rt[idx]);
+ up1.rotate(idx + 2 >= count ? forward : (rp[idx + 2] - rp[idx + 1]).normalized(), rt[idx + 1]);
+ }
+
+ Vector3 axis = up.cross(up1);
+
+ if (axis.length_squared() < CMP_EPSILON2)
+ axis = forward;
+ else
+ axis.normalize();
+
+ return up.rotated(axis, up.angle_to(up1) * frac);
+}
+
PoolVector3Array Curve3D::get_baked_points() const {
if (baked_cache_dirty)
@@ -1359,6 +1466,14 @@ PoolRealArray Curve3D::get_baked_tilts() const {
return baked_tilt_cache;
}
+PoolVector3Array Curve3D::get_baked_up_vectors() const {
+
+ if (baked_cache_dirty)
+ _bake();
+
+ return baked_up_vector_cache;
+}
+
Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
// Brute force method
@@ -1452,6 +1567,18 @@ float Curve3D::get_bake_interval() const {
return bake_interval;
}
+void Curve3D::set_up_vector_enabled(bool p_enable) {
+
+ up_vector_enabled = p_enable;
+ baked_cache_dirty = true;
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+bool Curve3D::is_up_vector_enabled() const {
+
+ return up_vector_enabled;
+}
+
Dictionary Curve3D::_get_data() const {
Dictionary dc;
@@ -1563,11 +1690,15 @@ void Curve3D::_bind_methods() {
//ClassDB::bind_method(D_METHOD("bake","subdivs"),&Curve3D::bake,DEFVAL(10));
ClassDB::bind_method(D_METHOD("set_bake_interval", "distance"), &Curve3D::set_bake_interval);
ClassDB::bind_method(D_METHOD("get_bake_interval"), &Curve3D::get_bake_interval);
+ ClassDB::bind_method(D_METHOD("set_up_vector_enabled", "enable"), &Curve3D::set_up_vector_enabled);
+ ClassDB::bind_method(D_METHOD("is_up_vector_enabled"), &Curve3D::is_up_vector_enabled);
ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve3D::get_baked_length);
ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve3D::interpolate_baked, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("interpolate_baked_up_vector", "offset", "apply_tilt"), &Curve3D::interpolate_baked_up_vector, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve3D::get_baked_points);
ClassDB::bind_method(D_METHOD("get_baked_tilts"), &Curve3D::get_baked_tilts);
+ ClassDB::bind_method(D_METHOD("get_baked_up_vectors"), &Curve3D::get_baked_up_vectors);
ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve3D::get_closest_point);
ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve3D::get_closest_offset);
ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve3D::tessellate, DEFVAL(5), DEFVAL(4));
@@ -1577,6 +1708,9 @@ void Curve3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval");
ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+
+ ADD_GROUP("Up Vector", "up_vector_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "up_vector_enabled"), "set_up_vector_enabled", "is_up_vector_enabled");
}
Curve3D::Curve3D() {
@@ -1586,4 +1720,5 @@ Curve3D::Curve3D() {
add_point(Vector3(0,2,0));
add_point(Vector3(0,3,5));*/
bake_interval = 0.2;
+ up_vector_enabled = true;
}
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 492eb05d1e..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();
@@ -232,11 +234,13 @@ class Curve3D : public Resource {
mutable bool baked_cache_dirty;
mutable PoolVector3Array baked_point_cache;
mutable PoolRealArray baked_tilt_cache;
+ mutable PoolVector3Array baked_up_vector_cache;
mutable float baked_max_ofs;
void _bake() const;
float bake_interval;
+ bool up_vector_enabled;
void _bake_segment3d(Map<float, Vector3> &r_bake, float p_begin, float p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, float p_tol) const;
Dictionary _get_data() const;
@@ -264,12 +268,16 @@ public:
void set_bake_interval(float p_tolerance);
float get_bake_interval() const;
+ void set_up_vector_enabled(bool p_enable);
+ bool is_up_vector_enabled() const;
float get_baked_length() const;
Vector3 interpolate_baked(float p_offset, bool p_cubic = false) const;
float interpolate_baked_tilt(float p_offset) const;
+ Vector3 interpolate_baked_up_vector(float p_offset, bool p_apply_tilt = false) const;
PoolVector3Array get_baked_points() const; //useful for going through
PoolRealArray get_baked_tilts() const; //useful for going through
+ PoolVector3Array get_baked_up_vectors() const;
Vector3 get_closest_point(const Vector3 &p_to_point) const;
float get_closest_offset(const Vector3 &p_to_point) const;
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 5ac9344f31..d4432b969f 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -544,6 +544,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("updown", "SpinBox", make_icon(spinbox_updown_png));
+ //scroll container
+ Ref<StyleBoxEmpty> empty;
+ empty.instance();
+ theme->set_stylebox("bg", "ScrollContainer", empty);
+
// WindowDialog
theme->set_stylebox("panel", "WindowDialog", sb_expand(make_stylebox(popup_window_png, 10, 26, 10, 8), 8, 24, 8, 6));
@@ -580,6 +585,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));
@@ -839,7 +846,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);
@@ -869,6 +876,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("bg", "GraphEdit", make_stylebox(tree_bg_png, 4, 4, 4, 5));
theme->set_color("grid_minor", "GraphEdit", Color(1, 1, 1, 0.05));
theme->set_color("grid_major", "GraphEdit", Color(1, 1, 1, 0.2));
+ theme->set_color("activity", "GraphEdit", Color(1, 1, 1));
theme->set_constant("bezier_len_pos", "GraphEdit", 80 * scale);
theme->set_constant("bezier_len_neg", "GraphEdit", 160 * 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 f41a26a680..e5d463d391 100644
--- a/scene/resources/dynamic_font.cpp
+++ b/scene/resources/dynamic_font.cpp
@@ -307,8 +307,6 @@ Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const V
}
ret.x += _get_kerning_advance(font, p_char, p_next);
- // ensures oversampled glyphs will have enough space when this value is used by clipping/wrapping algorithms
- ret.x = Math::ceil(ret.x);
return ret;
}
@@ -627,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;
@@ -1080,6 +1078,11 @@ void DynamicFont::update_oversampling() {
if (E->self()->data_at_size.is_valid()) {
E->self()->data_at_size->update_oversampling();
+
+ if (E->self()->outline_data_at_size.is_valid()) {
+ E->self()->outline_data_at_size->update_oversampling();
+ }
+
changed.push_back(Ref<DynamicFont>(E->self()));
}
E = E->next();
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 1282ce767a..90e103c96b 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,9 @@ void SpatialMaterial::_update_shader() {
if (flags[FLAG_DONT_RECEIVE_SHADOWS]) {
code += ",shadows_disabled";
}
+ if (flags[FLAG_ENSURE_CORRECT_NORMALS]) {
+ code += ",ensure_correct_normals";
+ }
code += ";\n";
code += "uniform vec4 albedo : hint_color;\n";
@@ -552,7 +566,7 @@ void SpatialMaterial::_update_shader() {
//handle animation
code += "\tint particle_total_frames = particles_anim_h_frames * particles_anim_v_frames;\n";
- code += "\tint particle_frame = int(INSTANCE_CUSTOM.y * float(particle_total_frames));\n";
+ code += "\tint particle_frame = int(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";
code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0,particle_total_frames-1); else particle_frame=abs(particle_frame)%particle_total_frames;\n";
code += "\tUV /= vec2(float(particles_anim_h_frames),float(particles_anim_v_frames));\n";
code += "\tUV += vec2(float(particle_frame % particles_anim_h_frames) / float(particles_anim_h_frames),float(particle_frame / particles_anim_h_frames) / float(particles_anim_v_frames));\n";
@@ -1852,6 +1866,7 @@ 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_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 +2057,7 @@ 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_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..3b9d1be260 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,7 @@ public:
FLAG_USE_ALPHA_SCISSOR,
FLAG_ALBEDO_TEXTURE_FORCE_SRGB,
FLAG_DONT_RECEIVE_SHADOWS,
+ FLAG_ENSURE_CORRECT_NORMALS,
FLAG_MAX
};
@@ -237,7 +240,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 : 16;
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 d87644381c..b0620d3363 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -222,7 +222,6 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
continue;
Array a = surface_get_arrays(i);
- int vcount = 0;
if (i == 0) {
arrays = a;
@@ -230,6 +229,7 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
index_accum += v.size();
} else {
+ int vcount = 0;
for (int j = 0; j < arrays.size(); j++) {
if (arrays[j].get_type() == Variant::NIL || a[j].get_type() == Variant::NIL) {
@@ -1194,8 +1194,6 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe
for (int j = 0; j < 3; j++) {
- int vertex_idx = gen_vertices[gen_indices[i + j]];
-
SurfaceTool::Vertex v = surfaces[surface].vertices[uv_index[gen_vertices[gen_indices[i + j]]].second];
if (surfaces[surface].format & ARRAY_FORMAT_COLOR) {
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index e8b7ecaf9a..a3fb068569 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
};
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index e0562d9e4a..28aa6f1aa7 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -419,10 +419,10 @@ void CapsuleMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CapsuleMesh::set_rings);
ClassDB::bind_method(D_METHOD("get_rings"), &CapsuleMesh::get_rings);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "mid_height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_mid_height", "get_mid_height");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "mid_height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_mid_height", "get_mid_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
}
void CapsuleMesh::set_radius(const float p_radius) {
@@ -676,10 +676,10 @@ void CubeMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_subdivide_depth", "divisions"), &CubeMesh::set_subdivide_depth);
ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &CubeMesh::get_subdivide_depth);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1"), "set_subdivide_width", "get_subdivide_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1"), "set_subdivide_height", "get_subdivide_height");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1"), "set_subdivide_depth", "get_subdivide_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");
}
void CubeMesh::set_size(const Vector3 &p_size) {
@@ -881,11 +881,11 @@ void CylinderMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CylinderMesh::set_rings);
ClassDB::bind_method(D_METHOD("get_rings"), &CylinderMesh::get_rings);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "top_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_top_radius", "get_top_radius");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "bottom_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_bottom_radius", "get_bottom_radius");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_height", "get_height");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "top_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_top_radius", "get_top_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "bottom_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_bottom_radius", "get_bottom_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
}
void CylinderMesh::set_top_radius(const float p_radius) {
@@ -1017,8 +1017,8 @@ void PlaneMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PlaneMesh::get_subdivide_depth);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1"), "set_subdivide_width", "get_subdivide_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1"), "set_subdivide_depth", "get_subdivide_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");
}
void PlaneMesh::set_size(const Size2 &p_size) {
@@ -1283,9 +1283,9 @@ void PrismMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "left_to_right", PROPERTY_HINT_RANGE, "-2.0,2.0,0.1"), "set_left_to_right", "get_left_to_right");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1"), "set_subdivide_width", "get_subdivide_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1"), "set_subdivide_height", "get_subdivide_height");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1"), "set_subdivide_depth", "get_subdivide_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");
}
void PrismMesh::set_left_to_right(const float p_left_to_right) {
@@ -1352,10 +1352,10 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const {
PoolVector<float> tangents;
PoolVector<Vector2> uvs;
- faces.resize(4);
- normals.resize(4);
- tangents.resize(4 * 4);
- uvs.resize(4);
+ faces.resize(6);
+ normals.resize(6);
+ tangents.resize(6 * 4);
+ uvs.resize(6);
Vector2 _size = Vector2(size.x / 2.0f, size.y / 2.0f);
@@ -1366,9 +1366,15 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const {
Vector3(_size.x, -_size.y, 0),
};
- for (int i = 0; i < 4; i++) {
+ static const int indices[6] = {
+ 0, 1, 2,
+ 0, 2, 3
+ };
+
+ for (int i = 0; i < 6; i++) {
- faces.set(i, quad_faces[i]);
+ int j = indices[i];
+ faces.set(i, quad_faces[j]);
normals.set(i, Vector3(0, 0, 1));
tangents.set(i * 4 + 0, 1.0);
tangents.set(i * 4 + 1, 0.0);
@@ -1382,14 +1388,14 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const {
Vector2(1, 1),
};
- uvs.set(i, quad_uv[i]);
+ uvs.set(i, quad_uv[j]);
}
p_arr[VS::ARRAY_VERTEX] = faces;
p_arr[VS::ARRAY_NORMAL] = normals;
p_arr[VS::ARRAY_TANGENT] = tangents;
p_arr[VS::ARRAY_TEX_UV] = uvs;
-};
+}
void QuadMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_size", "size"), &QuadMesh::set_size);
@@ -1398,7 +1404,7 @@ void QuadMesh::_bind_methods() {
}
QuadMesh::QuadMesh() {
- primitive_type = PRIMITIVE_TRIANGLE_FAN;
+ primitive_type = PRIMITIVE_TRIANGLES;
size = Size2(1.0, 1.0);
}
@@ -1499,10 +1505,10 @@ void SphereMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_is_hemisphere", "is_hemisphere"), &SphereMesh::set_is_hemisphere);
ClassDB::bind_method(D_METHOD("get_is_hemisphere"), &SphereMesh::get_is_hemisphere);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_height", "get_height");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_hemisphere"), "set_is_hemisphere", "get_is_hemisphere");
}
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 7da65ac984..fb81375b0a 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -136,8 +136,17 @@ Ref<Texture> StyleBoxTexture::get_normal_map() const {
void StyleBoxTexture::set_margin_size(Margin p_margin, float p_size) {
+ ERR_FAIL_INDEX(p_margin, 4);
+
margin[p_margin] = p_size;
emit_changed();
+ static const char *margin_prop[4] = {
+ "content_margin_left",
+ "content_margin_top",
+ "content_margin_right",
+ "content_margin_bottom",
+ };
+ _change_notify(margin_prop[p_margin]);
}
float StyleBoxTexture::get_margin_size(Margin p_margin) const {
@@ -896,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_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() const {
- return grow;
+float StyleBoxLine::get_grow_begin() const {
+ return grow_begin;
}
void StyleBoxLine::_bind_methods() {
@@ -910,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");
}
@@ -932,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;
}
@@ -945,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/texture.cpp b/scene/resources/texture.cpp
index c0f6756fd1..2baad555c0 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -76,7 +76,9 @@ void Texture::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_rect_region", "canvas_item", "rect", "src_rect", "modulate", "transpose", "normal_map", "clip_uv"), &Texture::draw_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_data"), &Texture::get_data);
+ ADD_GROUP("Flags", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic Linear,Convert to Linear,Mirrored Repeat,Video Surface"), "set_flags", "get_flags");
+ ADD_GROUP("", "");
BIND_ENUM_CONSTANT(FLAGS_DEFAULT);
BIND_ENUM_CONSTANT(FLAG_MIPMAPS);
@@ -220,12 +222,15 @@ Image::Format ImageTexture::get_format() const {
return format;
}
-void ImageTexture::load(const String &p_path) {
+Error ImageTexture::load(const String &p_path) {
Ref<Image> img;
img.instance();
- img->load(p_path);
- create_from_image(img);
+ Error err = img->load(p_path);
+ if (err == OK) {
+ create_from_image(img);
+ }
+ return err;
}
void ImageTexture::set_data(const Ref<Image> &p_image) {
@@ -1664,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 93d7ec4ef9..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>
*/
@@ -124,7 +125,7 @@ public:
void set_flags(uint32_t p_flags);
uint32_t get_flags() const;
Image::Format get_format() const;
- void load(const String &p_path);
+ Error load(const String &p_path);
void set_data(const Ref<Image> &p_image);
Ref<Image> get_data() const;
@@ -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/tile_set.cpp b/scene/resources/tile_set.cpp
index 42d64376f5..58057cda0c 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -264,7 +264,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::RECT2, pre + "region"));
p_list->push_back(PropertyInfo(Variant::INT, pre + "tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE"));
if (tile_get_tile_mode(id) == AUTO_TILE) {
- p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3 (minimal),3X3", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/bitmask_flags", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/icon_coordinate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
@@ -960,6 +960,7 @@ void TileSet::_bind_methods() {
BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_subtile_selection", PropertyInfo(Variant::INT, "autotile_id"), PropertyInfo(Variant::INT, "bitmask"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location")));
BIND_ENUM_CONSTANT(BITMASK_2X2);
+ BIND_ENUM_CONSTANT(BITMASK_3X3_MINIMAL);
BIND_ENUM_CONSTANT(BITMASK_3X3);
BIND_ENUM_CONSTANT(BIND_TOPLEFT);
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index d5704ac9a0..ec635ee5cc 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -56,6 +56,7 @@ public:
enum BitmaskMode {
BITMASK_2X2,
+ BITMASK_3X3_MINIMAL,
BITMASK_3X3
};
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..e850aaa44c
--- /dev/null
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -0,0 +1,1879 @@
+#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");
+}
+
+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");
+}
+
+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");
+}
+
+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/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index 2dc32b893d..bf765385d0 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -187,6 +187,8 @@ SceneStringNames::SceneStringNames() {
node_configuration_warning_changed = StaticCString::create("node_configuration_warning_changed");
+ output = StaticCString::create("output");
+
path_pp = NodePath("..");
_default = StaticCString::create("default");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index 2e6da26d68..b88cf7d8d7 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -199,6 +199,8 @@ public:
StringName node_configuration_warning_changed;
+ StringName output;
+
enum {
MAX_MATERIALS = 32
};
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 0ad30987e7..113f23f8f2 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -89,6 +89,7 @@ void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale,
}
}
}
+
////////////////////////////////
void AudioStream::_bind_methods() {
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index fda4fc2ccc..3312ce1ff6 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -31,6 +31,7 @@
#ifndef AUDIO_STREAM_H
#define AUDIO_STREAM_H
+#include "image.h"
#include "resource.h"
#include "servers/audio/audio_filter_sw.h"
#include "servers/audio_server.h"
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index b08e41301a..69c1318a48 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -235,28 +235,12 @@ 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;
}
-#endif
}
void AudioServer::_mix_step() {
@@ -939,9 +923,6 @@ void AudioServer::finish() {
buses.clear();
}
-void AudioServer::update() {
-}
-
/* MISC config */
void AudioServer::lock() {
@@ -1204,6 +1185,8 @@ AudioServer::AudioServer() {
mix_frames = 0;
channel_count = 0;
to_mix = 0;
+ output_latency = 0;
+ output_latency_ticks = 0;
}
AudioServer::~AudioServer() {
diff --git a/servers/audio_server.h b/servers/audio_server.h
index af2668b69e..f928acc7b5 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -190,6 +190,9 @@ private:
Mutex *audio_data_lock;
+ float output_latency;
+ uint64_t output_latency_ticks;
+
void init_channels_and_buffers();
void _mix_step();
@@ -273,7 +276,6 @@ public:
virtual void init();
virtual void finish();
- virtual void update();
virtual void load_default_bus_layout();
/* MISC config */
@@ -307,6 +309,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/collision_solver_sat.cpp b/servers/physics/collision_solver_sat.cpp
index eefb0f0396..e587485fcb 100644
--- a/servers/physics/collision_solver_sat.cpp
+++ b/servers/physics/collision_solver_sat.cpp
@@ -341,26 +341,26 @@ public:
min_B -= (max_A - min_A) * 0.5;
max_B += (max_A - min_A) * 0.5;
- real_t dmin = min_B - (min_A + max_A) * 0.5;
- real_t dmax = max_B - (min_A + max_A) * 0.5;
+ min_B -= (min_A + max_A) * 0.5;
+ max_B -= (min_A + max_A) * 0.5;
- if (dmin > 0.0 || dmax < 0.0) {
+ if (min_B > 0.0 || max_B < 0.0) {
separator_axis = axis;
return false; // doesn't contain 0
}
//use the smallest depth
- dmin = Math::abs(dmin);
+ min_B = -min_B;
- if (dmax < dmin) {
- if (dmax < best_depth) {
- best_depth = dmax;
+ if (max_B < min_B) {
+ if (max_B < best_depth) {
+ best_depth = max_B;
best_axis = axis;
}
} else {
- if (dmin < best_depth) {
- best_depth = dmin;
+ if (min_B < best_depth) {
+ best_depth = min_B;
best_axis = -axis; // keep it as A axis
}
}
diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp
index f2dbb635f8..6c25ad43f9 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);
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..a473e0beb2 100644
--- a/servers/physics_2d/physics_2d_server_sw.cpp
+++ b/servers/physics_2d/physics_2d_server_sw.cpp
@@ -962,22 +962,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);
+ 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_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..e5961b9011 100644
--- a/servers/physics_2d/physics_2d_server_sw.h
+++ b/servers/physics_2d/physics_2d_server_sw.h
@@ -232,7 +232,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..3119b930d7 100644
--- a/servers/physics_2d/physics_2d_server_wrap_mt.h
+++ b/servers/physics_2d/physics_2d_server_wrap_mt.h
@@ -245,10 +245,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);
+ 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_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 503ca0af4a..6e45951f42 100644
--- a/servers/physics_2d/space_2d_sw.cpp
+++ b/servers/physics_2d/space_2d_sw.cpp
@@ -181,12 +181,15 @@ int Physics2DDirectSpaceStateSW::intersect_shape(const RID &p_shape, const Trans
Rect2 aabb = p_xform.xform(shape->get_aabb());
aabb = aabb.grow(p_margin);
- int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, p_result_max, space->intersection_query_subindex_results);
+ int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, Space2DSW::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);
int cc = 0;
for (int i = 0; i < amount; i++) {
+ if (cc >= p_result_max)
+ break;
+
if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
continue;
@@ -484,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
@@ -544,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];
@@ -632,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.cpp b/servers/physics_2d_server.cpp
index fafc239c7f..cb7669ec24 100644
--- a/servers/physics_2d_server.cpp
+++ b/servers/physics_2d_server.cpp
@@ -540,6 +540,8 @@ void Physics2DServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("area_get_object_instance_id", "area"), &Physics2DServer::area_get_object_instance_id);
ClassDB::bind_method(D_METHOD("area_set_monitor_callback", "area", "receiver", "method"), &Physics2DServer::area_set_monitor_callback);
+ ClassDB::bind_method(D_METHOD("area_set_area_monitor_callback", "area", "receiver", "method"), &Physics2DServer::area_set_area_monitor_callback);
+ ClassDB::bind_method(D_METHOD("area_set_monitorable", "area", "monitorable"), &Physics2DServer::area_set_monitorable);
ClassDB::bind_method(D_METHOD("body_create"), &Physics2DServer::body_create);
diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h
index ba5232f7fe..1d04fbc5c6 100644
--- a/servers/physics_2d_server.h
+++ b/servers/physics_2d_server.h
@@ -479,7 +479,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 08af26c24b..de173491b2 100644
--- a/servers/physics_server.cpp
+++ b/servers/physics_server.cpp
@@ -444,6 +444,8 @@ void PhysicsServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("area_get_object_instance_id", "area"), &PhysicsServer::area_get_object_instance_id);
ClassDB::bind_method(D_METHOD("area_set_monitor_callback", "area", "receiver", "method"), &PhysicsServer::area_set_monitor_callback);
+ ClassDB::bind_method(D_METHOD("area_set_area_monitor_callback", "area", "receiver", "method"), &PhysicsServer::area_set_area_monitor_callback);
+ ClassDB::bind_method(D_METHOD("area_set_monitorable", "area", "monitorable"), &PhysicsServer::area_set_monitorable);
ClassDB::bind_method(D_METHOD("area_set_ray_pickable", "area", "enable"), &PhysicsServer::area_set_ray_pickable);
ClassDB::bind_method(D_METHOD("area_is_ray_pickable", "area"), &PhysicsServer::area_is_ray_pickable);
@@ -662,6 +664,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);
diff --git a/servers/physics_server.h b/servers/physics_server.h
index 6712bee8dc..8ecf17c0e6 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"
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..0b37d266e7 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;
@@ -201,6 +201,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 */
@@ -282,19 +283,23 @@ public:
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 d399c548f3..fd1eb77143 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -1625,14 +1625,14 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
{ "smoothstep", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC4, TYPE_VOID } },
{ "isnan", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID } },
- { "isnan", TYPE_BOOL, { TYPE_VEC2, TYPE_VOID } },
- { "isnan", TYPE_BOOL, { TYPE_VEC3, TYPE_VOID } },
- { "isnan", TYPE_BOOL, { TYPE_VEC4, TYPE_VOID } },
+ { "isnan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID } },
+ { "isnan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID } },
+ { "isnan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID } },
{ "isinf", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID } },
- { "isinf", TYPE_BOOL, { TYPE_VEC2, TYPE_VOID } },
- { "isinf", TYPE_BOOL, { TYPE_VEC3, TYPE_VOID } },
- { "isinf", TYPE_BOOL, { TYPE_VEC4, TYPE_VOID } },
+ { "isinf", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID } },
+ { "isinf", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID } },
+ { "isinf", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID } },
{ "floatBitsToInt", TYPE_INT, { TYPE_FLOAT, TYPE_VOID } },
{ "floatBitsToInt", TYPE_IVEC2, { TYPE_VEC2, TYPE_VOID } },
@@ -2300,7 +2300,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltI
if (p_node->type == Node::TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(p_node);
- if (op->type == OP_INDEX) {
+ if (op->op == OP_INDEX) {
return _validate_assign(op->arguments[0], p_builtin_types);
}
}
@@ -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..92786ea740 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,44 @@ 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.insert("vertex_lighting");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("vertex_lighting");
/************ CANVAS ITEM **************************/
@@ -226,17 +228,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 +258,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 04dcde1365..697c890c9a 100644
--- a/servers/visual/visual_server_scene.cpp
+++ b/servers/visual/visual_server_scene.cpp
@@ -1257,6 +1257,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 +1362,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 +1472,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 +1527,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 +1549,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 +1580,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 +1605,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 +1619,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 +1636,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;
@@ -1674,7 +1677,8 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_view
} break;
}
- _render_scene(camera->transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), -1);
+ _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);
}
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) {
@@ -1684,7 +1688,6 @@ void VisualServerScene::render_camera(Ref<ARVRInterface> &p_interface, ARVRInter
ERR_FAIL_COND(!camera);
/* SETUP CAMERA, we are ignoring type and FOV here */
- bool ortho = false;
float aspect = p_viewport_size.width / (float)p_viewport_size.height;
CameraMatrix camera_matrix = p_interface->get_projection_for_eye(p_eye, aspect, camera->znear, camera->zfar);
@@ -1693,10 +1696,79 @@ void VisualServerScene::render_camera(Ref<ARVRInterface> &p_interface, ARVRInter
Transform world_origin = ARVRServer::get_singleton()->get_world_origin();
Transform cam_transform = p_interface->get_transform_for_eye(p_eye, world_origin);
- _render_scene(cam_transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), -1);
+ // For stereo render we only prepare for our left eye and then reuse the outcome for our right eye
+ if (p_eye == ARVRInterface::EYE_LEFT) {
+ ///@TODO possibly move responsibility for this into our ARVRServer or ARVRInterface?
+
+ // Center our transform, we assume basis is equal.
+ Transform mono_transform = cam_transform;
+ Transform right_transform = p_interface->get_transform_for_eye(ARVRInterface::EYE_RIGHT, world_origin);
+ mono_transform.origin += right_transform.origin;
+ mono_transform.origin *= 0.5;
+
+ // We need to combine our projection frustums for culling.
+ // Ideally we should use our clipping planes for this and combine them,
+ // however our shadow map logic uses our projection matrix.
+ // Note: as our left and right frustums should be mirrored, we don't need our right projection matrix.
+
+ // - get some base values we need
+ float eye_dist = (mono_transform.origin - cam_transform.origin).length();
+ float z_near = camera_matrix.get_z_near(); // get our near plane
+ float z_far = camera_matrix.get_z_far(); // get our far plane
+ 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 = 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);
+
+ // - calculate our near plane size (horizontal only, right_near is mirrored)
+ float left_near = -eye_dist - ((width - x_shift) * 0.5);
+
+ // - calculate our far plane size (horizontal only, right_far is mirrored)
+ float left_far = -eye_dist - (z_far * (width - x_shift) * 0.5 / z_near);
+ float left_far_right_eye = eye_dist - (z_far * (width + x_shift) * 0.5 / z_near);
+ if (left_far > left_far_right_eye) {
+ // on displays smaller then double our iod, the right eye far frustrum can overtake the left eyes.
+ left_far = left_far_right_eye;
+ }
+
+ // - figure out required z-shift
+ float slope = (left_far - left_near) / (z_far - z_near);
+ 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 += (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);
+
+ // - generate our frustum
+ CameraMatrix combined_matrix;
+ combined_matrix.set_frustum(left_near, -left_near, bottom_near, top_near, z_near + z_shift, z_far + z_shift);
+
+ // and finally move our camera back
+ Transform apply_z_shift;
+ apply_z_shift.origin = Vector3(0.0, 0.0, z_shift); // z negative is forward so this moves it backwards
+ mono_transform *= apply_z_shift;
+
+ // now prepare our scene with our adjusted transform projection matrix
+ _prepare_scene(mono_transform, combined_matrix, false, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
+ } else if (p_eye == ARVRInterface::EYE_MONO) {
+ // For mono render, prepare as per usual
+ _prepare_scene(cam_transform, camera_matrix, false, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
+ }
+
+ // And render our scene...
+ _render_scene(cam_transform, camera_matrix, false, camera->env, p_scenario, p_shadow_atlas, RID(), -1);
};
-void VisualServerScene::_render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
+void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe) {
+ // Note, in stereo rendering:
+ // - p_cam_transform will be a transform in the middle of our two eyes
+ // - p_cam_projection is a wider frustrum that encompasses both eyes
Scenario *scenario = scenario_owner.getornull(p_scenario);
@@ -1713,7 +1785,7 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform, const Cam
float z_far = p_cam_projection.get_z_far();
/* STEP 2 - CULL */
- int cull_count = scenario->octree.cull_convex(planes, instance_cull_result, MAX_INSTANCE_CULL);
+ instance_cull_count = scenario->octree.cull_convex(planes, instance_cull_result, MAX_INSTANCE_CULL);
light_cull_count = 0;
reflection_probe_cull_count = 0;
@@ -1731,7 +1803,7 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform, const Cam
/* STEP 4 - REMOVE FURTHER CULLED OBJECTS, ADD LIGHTS */
- for (int i = 0; i < cull_count; i++) {
+ for (int i = 0; i < instance_cull_count; i++) {
Instance *ins = instance_cull_result[i];
@@ -1857,8 +1929,8 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform, const Cam
if (!keep) {
// remove, no reason to keep
- cull_count--;
- SWAP(instance_cull_result[i], instance_cull_result[cull_count]);
+ instance_cull_count--;
+ SWAP(instance_cull_result[i], instance_cull_result[instance_cull_count]);
i--;
ins->last_render_pass = 0; // make invalid
} else {
@@ -1870,7 +1942,7 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform, const Cam
/* STEP 5 - PROCESS LIGHTS */
RID *directional_light_ptr = &light_instance_cull_result[light_cull_count];
- int directional_light_count = 0;
+ directional_light_count = 0;
// directional lights
{
@@ -2007,6 +2079,11 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform, const Cam
}
}
}
+}
+
+void VisualServerScene::_render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
+
+ Scenario *scenario = scenario_owner.getornull(p_scenario);
/* ENVIRONMENT */
@@ -2018,9 +2095,9 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform, const Cam
else
environment = scenario->fallback_environment;
- /* STEP 6 - PROCESS GEOMETRY AND DRAW SCENE*/
+ /* PROCESS GEOMETRY AND DRAW SCENE */
- VSG::scene_render->render_scene(p_cam_transform, p_cam_projection, p_cam_orthogonal, (RasterizerScene::InstanceBase **)instance_cull_result, cull_count, light_instance_cull_result, light_cull_count + directional_light_count, reflection_probe_instance_cull_result, reflection_probe_cull_count, environment, p_shadow_atlas, scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass);
+ VSG::scene_render->render_scene(p_cam_transform, p_cam_projection, p_cam_orthogonal, (RasterizerScene::InstanceBase **)instance_cull_result, instance_cull_count, light_instance_cull_result, light_cull_count + directional_light_count, reflection_probe_instance_cull_result, reflection_probe_cull_count, environment, p_shadow_atlas, scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass);
}
void VisualServerScene::render_empty_scene(RID p_scenario, RID p_shadow_atlas) {
@@ -2093,7 +2170,8 @@ bool VisualServerScene::_render_reflection_probe_step(Instance *p_instance, int
shadow_atlas = scenario->reflection_probe_shadow_atlas;
}
- _render_scene(xform, cm, false, RID(), VSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, p_step);
+ _prepare_scene(xform, cm, false, RID(), VSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, shadow_atlas, reflection_probe->instance);
+ _render_scene(xform, cm, false, RID(), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, p_step);
} else {
//do roughness postprocess step until it believes it's done
diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h
index 109cdf711c..12d732724a 100644
--- a/servers/visual/visual_server_scene.h
+++ b/servers/visual/visual_server_scene.h
@@ -434,11 +434,13 @@ public:
}
};
+ int instance_cull_count;
Instance *instance_cull_result[MAX_INSTANCE_CULL];
Instance *instance_shadow_cull_result[MAX_INSTANCE_CULL]; //used for generating shadowmaps
Instance *light_cull_result[MAX_LIGHTS_CULLED];
RID light_instance_cull_result[MAX_LIGHTS_CULLED];
int light_cull_count;
+ int directional_light_count;
RID reflection_probe_instance_cull_result[MAX_REFLECTION_PROBES_CULLED];
int reflection_probe_cull_count;
@@ -483,7 +485,8 @@ public:
_FORCE_INLINE_ void _light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario);
- void _render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
+ void _prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe);
+ void _render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
void render_empty_scene(RID p_scenario, RID p_shadow_atlas);
void render_camera(RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas);
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 f8b34f7df9..dffaccc0d4 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -1587,19 +1587,22 @@ 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);
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()));
@@ -1828,7 +1831,7 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("canvas_item_add_nine_patch", "item", "rect", "source", "texture", "topleft", "bottomright", "x_axis_mode", "y_axis_mode", "draw_center", "modulate", "normal_map"), &VisualServer::canvas_item_add_nine_patch, DEFVAL(NINE_PATCH_STRETCH), DEFVAL(NINE_PATCH_STRETCH), DEFVAL(true), DEFVAL(Color(1, 1, 1)), DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("canvas_item_add_primitive", "item", "points", "colors", "uvs", "texture", "width", "normal_map"), &VisualServer::canvas_item_add_primitive, DEFVAL(1.0), DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("canvas_item_add_polygon", "item", "points", "colors", "uvs", "texture", "normal_map", "antialiased"), &VisualServer::canvas_item_add_polygon, DEFVAL(Vector<Point2>()), DEFVAL(RID()), DEFVAL(RID()), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("canvas_item_add_triangle_array", "item", "indices", "points", "colors", "uvs", "texture", "count", "normal_map"), &VisualServer::canvas_item_add_triangle_array, DEFVAL(Vector<Point2>()), DEFVAL(RID()), DEFVAL(-1), DEFVAL(RID()));
+ ClassDB::bind_method(D_METHOD("canvas_item_add_triangle_array", "item", "indices", "points", "colors", "uvs", "bones", "weights", "texture", "count", "normal_map"), &VisualServer::canvas_item_add_triangle_array, DEFVAL(Vector<Point2>()), DEFVAL(Vector<int>()), DEFVAL(Vector<float>()), DEFVAL(RID()), DEFVAL(-1), DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("canvas_item_add_mesh", "item", "mesh", "texture", "normal_map"), &VisualServer::canvas_item_add_mesh, DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("canvas_item_add_multimesh", "item", "mesh", "texture", "normal_map"), &VisualServer::canvas_item_add_multimesh, DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("canvas_item_add_particles", "item", "particles", "texture", "normal_map", "h_frames", "v_frames"), &VisualServer::canvas_item_add_particles);
@@ -2143,7 +2146,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..968cb852ed 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
};
@@ -309,13 +310,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 +331,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 +730,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;
@@ -1023,6 +1034,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 06084641cc..ea2a996812 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -15,6 +15,7 @@ Important: Some files have Godot-made changes.
They are marked with `// -- GODOT start --` and `// -- GODOT end --`
comments.
+
## bullet
- Upstream: https://github.com/bulletphysics/bullet3
@@ -76,6 +77,7 @@ Important: Some files have Godot-made changes.
They are marked with `// -- GODOT start --` and `// -- GODOT end --`
comments.
+
## fonts
### Noto Sans
@@ -102,7 +104,7 @@ Use UI font variant if available, because it has tight vertical metrics and good
## freetype
- Upstream: https://www.freetype.org
-- Version: 2.8.1
+- Version: 2.9.1
- License: FreeType License (BSD-like)
Files extracted from upstream source:
@@ -175,6 +177,7 @@ Important: Some files have Godot-made changes.
They are marked with `// -- GODOT start --` and `// -- GODOT end --`
comments.
+
## libtheora
- Upstream: https://www.theora.org
@@ -191,7 +194,7 @@ Files extracted from upstream source:
## libvorbis
- Upstream: https://www.xiph.org/vorbis
-- Version: 1.3.5
+- Version: 1.3.6
- License: BSD-3-Clause
Files extracted from upstream source:
@@ -218,7 +221,7 @@ Godot-made change marked with `// -- GODOT --` comments.
## libwebp
- Upstream: https://chromium.googlesource.com/webm/libwebp/
-- Version: 0.6.1
+- Version: 1.0.0
- License: BSD-3-Clause
Files extracted from upstream source:
@@ -234,24 +237,29 @@ changes are marked with `// -- GODOT --` comments.
## libwebsockets
- Upstream: https://github.com/warmcat/libwebsockets
-- Version: 2.4.2
+- Version: 3.0.0
- License: LGPLv2.1 + static linking exception
File extracted from upstream source:
-- Everything in `lib/` except `minilex.c`, `http2/`, `event-libs/`.
- - From `misc/` exclude `lws-genhash.c`, `lws-ring.c`, `romfs.{c,h}`, `smtp.c`.
- - From `plat/` exclude `lws-plat-{esp*,optee}.c`.
- - From `server/` exclude `access-log.c`, `cgi.c`, `daemonize.c`, `lws-spa.c`,
-`peer-limits.c`, `rewrite.c`
-- Also copy `win32helpers/` from `win32port/`
-- `mbedtls_wrapper/include/platform/ssl_port.h` has a small change to check for OSX and FreeBSD (missing `malloc.h`).
- The bug is fixed in upstream master via `LWS_HAVE_MALLOC_H`, but not in the 2.4.1 branch (as the file structure has changed).
-- You might need to apply the patch in `thirdparty/lws/mbedtls_verify.diff` (port of PR 1215) to future `2.4.x` releases if it does not get cherry picked.
+- From `lib/` into `thirdparty/libwebsockets`:
+ - Everything from `core`
+ - From `event-libs` only the `poll` subfolder
+ - From `misc` only `base64-decode.c`, `getifaddrs.c`, `getifaddrs.h`, `lejp.c`, and `sha-1.c`
+ - From `plat` only `lws-plat-unix.c` and `lws-plat-win.c`
+ - From `roles` only `private.h`, `h1`, `http`, `listen`, `pipe`, `raw`, `ws`
+ - From `roles/http` exclude `minilex.c`
+ - From `roles/http/server` exclude `access-log.c`, `lws-spa.c`, `ranges.c`, and `rewrite.c`
+ - From `roles/ws` exclude `ext` folder.
+ - From `tls` exclude `openssl` folder.
+- Also copy `win32helpers/` from `win32port/` inside `thirdparty/libwebsockets`
+- A small fix has been added in `libwebsockets/libwebsockets.h` to `#include <sys/socket.h>` for the BSD family.
+ This change has been PRed upstream, and should be merged before the next update. Remember to check and remove this line.
Important: `lws_config.h` and `lws_config_private.h` contains custom
Godot build configurations, check them out when updating.
-## mbedTLS
+
+## mbedtls
- Upstream: https://tls.mbed.org/
- Version: 2.8.0
@@ -264,10 +272,20 @@ File extracted from upstream release tarball `mbedtls-2.8.0-apache.tgz`:
Be sure to check the Godot addition to only redfine it when undefined or `< 0x0501` (PRed upstream).
- Applied the patch in `thirdparty/mbedtls/1453.diff` (PR 1453). Soon to be merged upstream. Check it out at next update.
+
+## miniupnpc
+
+- Upstream: https://github.com/miniupnp/miniupnp/tree/master/miniupnpc
+- Version: 2.1 (git 25615e0, 2018-05-08) with modifications
+- License: BSD-3-Clause
+
+The only modified file is miniupnpcstrings.h, which was created for Godot (is usually autogenerated by cmake).
+
+
## minizip
- Upstream: http://www.zlib.net
-- Version: 1.2.4 (zlib contrib)
+- Version: 1.2.11 (zlib contrib)
- License: zlib
Files extracted from the upstream source:
@@ -293,6 +311,10 @@ Collection of single-file libraries used in Godot components.
* Upstream: http://episec.com/people/edelkind/c.html
* Version: latest, as of April 2017
* License: Public Domain
+ - `clipper.{cpp,hpp}`
+ * Upstream: https://sourceforge.net/projects/polyclipping
+ * Version: 6.4.2
+ * License: BSL-1.0
- `fastlz.{c,h}`
* Upstream: https://github.com/ariya/FastLZ
* Version: git (f121734, 2007)
@@ -342,11 +364,11 @@ Collection of single-file libraries used in Godot components.
* License: zlib
- `stb_truetype.h`
* Upstream: https://github.com/nothings/stb
- * Version: 1.17
+ * Version: 1.19
* License: Public Domain (Unlicense) or MIT
- `stb_vorbis.c`
* Upstream: https://github.com/nothings/stb
- * Version: 1.11
+ * Version: 1.14
* License: Public Domain (Unlicense) or MIT
@@ -361,6 +383,7 @@ Files extracted from the upstream source:
- All .h files in `src/`
- LICENSE.txt
+
## opus
- Upstream: https://opus-codec.org
@@ -379,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
@@ -480,7 +506,7 @@ Files extracted from upstream source:
## zstd
- Upstream: https://github.com/facebook/zstd
-- Version: 1.3.3
+- Version: 1.3.4
- License: BSD-3-Clause
Files extracted from upstream source:
diff --git a/thirdparty/freetype/FTL.TXT b/thirdparty/freetype/FTL.TXT
index 433ab060e3..c406d150fa 100644
--- a/thirdparty/freetype/FTL.TXT
+++ b/thirdparty/freetype/FTL.TXT
@@ -163,7 +163,7 @@ Legal Terms
Our home page can be found at
- http://www.freetype.org
+ https://www.freetype.org
--- end of FTL.TXT ---
diff --git a/thirdparty/freetype/include/freetype/config/ftconfig.h b/thirdparty/freetype/include/freetype/config/ftconfig.h
index 889aebf5ab..eedebf4082 100644
--- a/thirdparty/freetype/include/freetype/config/ftconfig.h
+++ b/thirdparty/freetype/include/freetype/config/ftconfig.h
@@ -4,7 +4,7 @@
/* */
/* ANSI-specific configuration file (specification only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -73,11 +73,11 @@ FT_BEGIN_HEADER
/* The size of an `int' type. */
#if FT_UINT_MAX == 0xFFFFUL
-#define FT_SIZEOF_INT (16 / FT_CHAR_BIT)
+#define FT_SIZEOF_INT ( 16 / FT_CHAR_BIT )
#elif FT_UINT_MAX == 0xFFFFFFFFUL
-#define FT_SIZEOF_INT (32 / FT_CHAR_BIT)
+#define FT_SIZEOF_INT ( 32 / FT_CHAR_BIT )
#elif FT_UINT_MAX > 0xFFFFFFFFUL && FT_UINT_MAX == 0xFFFFFFFFFFFFFFFFUL
-#define FT_SIZEOF_INT (64 / FT_CHAR_BIT)
+#define FT_SIZEOF_INT ( 64 / FT_CHAR_BIT )
#else
#error "Unsupported size of `int' type!"
#endif
@@ -85,11 +85,11 @@ FT_BEGIN_HEADER
/* The size of a `long' type. A five-byte `long' (as used e.g. on the */
/* DM642) is recognized but avoided. */
#if FT_ULONG_MAX == 0xFFFFFFFFUL
-#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT)
+#define FT_SIZEOF_LONG ( 32 / FT_CHAR_BIT )
#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFUL
-#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT)
+#define FT_SIZEOF_LONG ( 32 / FT_CHAR_BIT )
#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFFFFFFFUL
-#define FT_SIZEOF_LONG (64 / FT_CHAR_BIT)
+#define FT_SIZEOF_LONG ( 64 / FT_CHAR_BIT )
#else
#error "Unsupported size of `long' type!"
#endif
@@ -236,12 +236,12 @@ FT_BEGIN_HEADER
#endif
-#if FT_SIZEOF_INT == (32 / FT_CHAR_BIT)
+#if FT_SIZEOF_INT == ( 32 / FT_CHAR_BIT )
typedef signed int FT_Int32;
typedef unsigned int FT_UInt32;
-#elif FT_SIZEOF_LONG == (32 / FT_CHAR_BIT)
+#elif FT_SIZEOF_LONG == ( 32 / FT_CHAR_BIT )
typedef signed long FT_Int32;
typedef unsigned long FT_UInt32;
@@ -252,12 +252,12 @@ FT_BEGIN_HEADER
/* look up an integer type that is at least 32 bits */
-#if FT_SIZEOF_INT >= (32 / FT_CHAR_BIT)
+#if FT_SIZEOF_INT >= ( 32 / FT_CHAR_BIT )
typedef int FT_Fast;
typedef unsigned int FT_UFast;
-#elif FT_SIZEOF_LONG >= (32 / FT_CHAR_BIT)
+#elif FT_SIZEOF_LONG >= ( 32 / FT_CHAR_BIT )
typedef long FT_Fast;
typedef unsigned long FT_UFast;
@@ -267,7 +267,7 @@ FT_BEGIN_HEADER
/* determine whether we have a 64-bit int type for platforms without */
/* Autoconf */
-#if FT_SIZEOF_LONG == (64 / FT_CHAR_BIT)
+#if FT_SIZEOF_LONG == ( 64 / FT_CHAR_BIT )
/* FT_LONG64 must be defined if a 64-bit type is available */
#define FT_LONG64
@@ -365,6 +365,14 @@ FT_BEGIN_HEADER
#endif
+ /* Use FT_LOCAL and FT_LOCAL_DEF to declare and define, respectively, */
+ /* a function that gets used only within the scope of a module. */
+ /* Normally, both the header and source code files for such a */
+ /* function are within a single module directory. */
+ /* */
+ /* Intra-module arrays should be tagged with FT_LOCAL_ARRAY and */
+ /* FT_LOCAL_ARRAY_DEF. */
+ /* */
#ifdef FT_MAKE_OPTION_SINGLE_OBJECT
#define FT_LOCAL( x ) static x
@@ -386,6 +394,12 @@ FT_BEGIN_HEADER
#define FT_LOCAL_ARRAY_DEF( x ) const x
+ /* Use FT_BASE and FT_BASE_DEF to declare and define, respectively, */
+ /* functions that are used in more than a single module. In the */
+ /* current setup this implies that the declaration is in a header */
+ /* file in the `include/freetype/internal' directory, and the */
+ /* function body is in a file in `src/base'. */
+ /* */
#ifndef FT_BASE
#ifdef __cplusplus
@@ -408,14 +422,63 @@ FT_BEGIN_HEADER
#endif /* !FT_BASE_DEF */
+ /* When compiling FreeType as a DLL or DSO with hidden visibility */
+ /* some systems/compilers need a special attribute in front OR after */
+ /* the return type of function declarations. */
+ /* */
+ /* Two macros are used within the FreeType source code to define */
+ /* exported library functions: FT_EXPORT and FT_EXPORT_DEF. */
+ /* */
+ /* FT_EXPORT( return_type ) */
+ /* */
+ /* is used in a function declaration, as in */
+ /* */
+ /* FT_EXPORT( FT_Error ) */
+ /* FT_Init_FreeType( FT_Library* alibrary ); */
+ /* */
+ /* */
+ /* FT_EXPORT_DEF( return_type ) */
+ /* */
+ /* is used in a function definition, as in */
+ /* */
+ /* FT_EXPORT_DEF( FT_Error ) */
+ /* FT_Init_FreeType( FT_Library* alibrary ) */
+ /* { */
+ /* ... some code ... */
+ /* return FT_Err_Ok; */
+ /* } */
+ /* */
+ /* You can provide your own implementation of FT_EXPORT and */
+ /* FT_EXPORT_DEF here if you want. */
+ /* */
+ /* To export a variable, use FT_EXPORT_VAR. */
+ /* */
#ifndef FT_EXPORT
-#ifdef __cplusplus
+#ifdef FT2_BUILD_LIBRARY
+
+#if defined( _WIN32 ) && ( defined( _DLL ) || defined( DLL_EXPORT ) )
+#define FT_EXPORT( x ) __declspec( dllexport ) x
+#elif defined( __GNUC__ ) && __GNUC__ >= 4
+#define FT_EXPORT( x ) __attribute__(( visibility( "default" ) )) x
+#elif defined( __cplusplus )
+#define FT_EXPORT( x ) extern "C" x
+#else
+#define FT_EXPORT( x ) extern x
+#endif
+
+#else
+
+#if defined( FT2_DLLIMPORT )
+#define FT_EXPORT( x ) __declspec( dllimport ) x
+#elif defined( __cplusplus )
#define FT_EXPORT( x ) extern "C" x
#else
#define FT_EXPORT( x ) extern x
#endif
+#endif
+
#endif /* !FT_EXPORT */
@@ -440,6 +503,7 @@ FT_BEGIN_HEADER
#endif /* !FT_EXPORT_VAR */
+
/* The following macros are needed to compile the library with a */
/* C++ compiler and with 16bit compilers. */
/* */
@@ -451,7 +515,13 @@ FT_BEGIN_HEADER
/* functions which are accessed by (global) function pointers. */
/* */
/* */
- /* FT_CALLBACK_DEF is used to _define_ a callback function. */
+ /* FT_CALLBACK_DEF is used to _define_ a callback function, */
+ /* located in the same source code file as the structure that uses */
+ /* it. */
+ /* */
+ /* FT_BASE_CALLBACK and FT_BASE_CALLBACK_DEF are used to declare */
+ /* and define a callback function, respectively, in a similar way */
+ /* as FT_BASE and FT_BASE_DEF work. */
/* */
/* FT_CALLBACK_TABLE is used to _declare_ a constant variable that */
/* contains pointers to callback functions. */
@@ -471,6 +541,16 @@ FT_BEGIN_HEADER
#endif
#endif /* FT_CALLBACK_DEF */
+#ifndef FT_BASE_CALLBACK
+#ifdef __cplusplus
+#define FT_BASE_CALLBACK( x ) extern "C" x
+#define FT_BASE_CALLBACK_DEF( x ) extern "C" x
+#else
+#define FT_BASE_CALLBACK( x ) extern x
+#define FT_BASE_CALLBACK_DEF( x ) x
+#endif
+#endif /* FT_BASE_CALLBACK */
+
#ifndef FT_CALLBACK_TABLE
#ifdef __cplusplus
#define FT_CALLBACK_TABLE extern "C"
diff --git a/thirdparty/freetype/include/freetype/config/ftheader.h b/thirdparty/freetype/include/freetype/config/ftheader.h
index d491af57c3..702f77cc42 100644
--- a/thirdparty/freetype/include/freetype/config/ftheader.h
+++ b/thirdparty/freetype/include/freetype/config/ftheader.h
@@ -4,7 +4,7 @@
/* */
/* Build macros of the FreeType 2 library. */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -318,14 +318,29 @@
/*************************************************************************
*
* @macro:
+ * FT_DRIVER_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing
+ * structures and macros related to the driver modules.
+ *
+ */
+#define FT_DRIVER_H <freetype/ftdriver.h>
+
+
+ /*************************************************************************
+ *
+ * @macro:
* FT_AUTOHINTER_H
*
* @description:
* A macro used in #include statements to name the file containing
* structures and macros related to the auto-hinting module.
*
+ * Deprecated since version 2.9; use @FT_DRIVER_H instead.
+ *
*/
-#define FT_AUTOHINTER_H <freetype/ftautoh.h>
+#define FT_AUTOHINTER_H FT_DRIVER_H
/*************************************************************************
@@ -337,8 +352,10 @@
* A macro used in #include statements to name the file containing
* structures and macros related to the CFF driver module.
*
+ * Deprecated since version 2.9; use @FT_DRIVER_H instead.
+ *
*/
-#define FT_CFF_DRIVER_H <freetype/ftcffdrv.h>
+#define FT_CFF_DRIVER_H FT_DRIVER_H
/*************************************************************************
@@ -350,8 +367,10 @@
* A macro used in #include statements to name the file containing
* structures and macros related to the TrueType driver module.
*
+ * Deprecated since version 2.9; use @FT_DRIVER_H instead.
+ *
*/
-#define FT_TRUETYPE_DRIVER_H <freetype/ftttdrv.h>
+#define FT_TRUETYPE_DRIVER_H FT_DRIVER_H
/*************************************************************************
@@ -363,8 +382,10 @@
* A macro used in #include statements to name the file containing
* structures and macros related to the PCF driver module.
*
+ * Deprecated since version 2.9; use @FT_DRIVER_H instead.
+ *
*/
-#define FT_PCF_DRIVER_H <freetype/ftpcfdrv.h>
+#define FT_PCF_DRIVER_H FT_DRIVER_H
/*************************************************************************
@@ -557,63 +578,6 @@
/*************************************************************************
*
* @macro:
- * FT_CACHE_IMAGE_H
- *
- * @description:
- * A macro used in #include statements to name the file containing the
- * `glyph image' API of the FreeType~2 cache sub-system.
- *
- * It is used to define a cache for @FT_Glyph elements. You can also
- * use the API defined in @FT_CACHE_SMALL_BITMAPS_H if you only need to
- * store small glyph bitmaps, as it will use less memory.
- *
- * This macro is deprecated. Simply include @FT_CACHE_H to have all
- * glyph image-related cache declarations.
- *
- */
-#define FT_CACHE_IMAGE_H FT_CACHE_H
-
-
- /*************************************************************************
- *
- * @macro:
- * FT_CACHE_SMALL_BITMAPS_H
- *
- * @description:
- * A macro used in #include statements to name the file containing the
- * `small bitmaps' API of the FreeType~2 cache sub-system.
- *
- * It is used to define a cache for small glyph bitmaps in a relatively
- * memory-efficient way. You can also use the API defined in
- * @FT_CACHE_IMAGE_H if you want to cache arbitrary glyph images,
- * including scalable outlines.
- *
- * This macro is deprecated. Simply include @FT_CACHE_H to have all
- * small bitmaps-related cache declarations.
- *
- */
-#define FT_CACHE_SMALL_BITMAPS_H FT_CACHE_H
-
-
- /*************************************************************************
- *
- * @macro:
- * FT_CACHE_CHARMAP_H
- *
- * @description:
- * A macro used in #include statements to name the file containing the
- * `charmap' API of the FreeType~2 cache sub-system.
- *
- * This macro is deprecated. Simply include @FT_CACHE_H to have all
- * charmap-based cache declarations.
- *
- */
-#define FT_CACHE_CHARMAP_H FT_CACHE_H
-
-
- /*************************************************************************
- *
- * @macro:
* FT_MAC_H
*
* @description:
@@ -763,17 +727,6 @@
/*************************************************************************
*
* @macro:
- * FT_UNPATENTED_HINTING_H
- *
- * @description:
- * Deprecated.
- */
-#define FT_UNPATENTED_HINTING_H <freetype/ttunpat.h>
-
-
- /*************************************************************************
- *
- * @macro:
* FT_INCREMENTAL_H
*
* @description:
@@ -809,25 +762,30 @@
/* */
+ /* These header files don't need to be included by the user. */
#define FT_ERROR_DEFINITIONS_H <freetype/fterrdef.h>
+#define FT_PARAMETER_TAGS_H <freetype/ftparams.h>
+ /* Deprecated macros. */
+#define FT_UNPATENTED_HINTING_H <freetype/ftparams.h>
+#define FT_TRUETYPE_UNPATENTED_H <freetype/ftparams.h>
+
+ /* FT_CACHE_H is the only header file needed for the cache subsystem. */
+#define FT_CACHE_IMAGE_H FT_CACHE_H
+#define FT_CACHE_SMALL_BITMAPS_H FT_CACHE_H
+#define FT_CACHE_CHARMAP_H FT_CACHE_H
/* The internals of the cache sub-system are no longer exposed. We */
/* default to FT_CACHE_H at the moment just in case, but we know of */
/* no rogue client that uses them. */
/* */
-#define FT_CACHE_MANAGER_H <freetype/ftcache.h>
-#define FT_CACHE_INTERNAL_MRU_H <freetype/ftcache.h>
-#define FT_CACHE_INTERNAL_MANAGER_H <freetype/ftcache.h>
-#define FT_CACHE_INTERNAL_CACHE_H <freetype/ftcache.h>
-#define FT_CACHE_INTERNAL_GLYPH_H <freetype/ftcache.h>
-#define FT_CACHE_INTERNAL_IMAGE_H <freetype/ftcache.h>
-#define FT_CACHE_INTERNAL_SBITS_H <freetype/ftcache.h>
-
-
-#define FT_INCREMENTAL_H <freetype/ftincrem.h>
-
-#define FT_TRUETYPE_UNPATENTED_H <freetype/ttunpat.h>
+#define FT_CACHE_MANAGER_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_MRU_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_MANAGER_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_CACHE_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_GLYPH_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_IMAGE_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_SBITS_H FT_CACHE_H
/*
diff --git a/thirdparty/freetype/include/freetype/config/ftoption.h b/thirdparty/freetype/include/freetype/config/ftoption.h
index 2fbe80b9b4..4bcab2af5c 100644
--- a/thirdparty/freetype/include/freetype/config/ftoption.h
+++ b/thirdparty/freetype/include/freetype/config/ftoption.h
@@ -4,7 +4,7 @@
/* */
/* User-selectable configuration macros (specification only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -75,19 +75,21 @@ FT_BEGIN_HEADER
/*************************************************************************/
- /*************************************************************************/
+ /*#***********************************************************************/
/* */
/* If you enable this configuration option, FreeType recognizes an */
/* environment variable called `FREETYPE_PROPERTIES', which can be used */
/* to control the various font drivers and modules. The controllable */
- /* properties are listed in the section `Controlling FreeType Modules' */
- /* in the reference's table of contents; currently there are properties */
- /* for the auto-hinter (file `ftautoh.h'), CFF (file `ftcffdrv.h'), */
- /* TrueType (file `ftttdrv.h'), and PCF (file `ftpcfdrv.h'). */
+ /* properties are listed in the section @properties. */
+ /* */
+ /* You have to undefine this configuration option on platforms that lack */
+ /* the concept of environment variables (and thus don't have the */
+ /* `getenv' function), for example Windows CE. */
/* */
/* `FREETYPE_PROPERTIES' has the following syntax form (broken here into */
/* multiple lines for better readability). */
/* */
+ /* { */
/* <optional whitespace> */
/* <module-name1> ':' */
/* <property-name1> '=' <property-value1> */
@@ -95,6 +97,7 @@ FT_BEGIN_HEADER
/* <module-name2> ':' */
/* <property-name2> '=' <property-value2> */
/* ... */
+ /* } */
/* */
/* Example: */
/* */
@@ -211,6 +214,10 @@ FT_BEGIN_HEADER
/* Do not #undef this macro here since the build system might define */
/* it for certain configurations only. */
/* */
+ /* If you use a build system like cmake or the `configure' script, */
+ /* options set by those programs have precendence, overwriting the */
+ /* value here with the configured one. */
+ /* */
/* #define FT_CONFIG_OPTION_SYSTEM_ZLIB */
@@ -227,6 +234,10 @@ FT_BEGIN_HEADER
/* */
/* Define this macro if you want to enable this `feature'. */
/* */
+ /* If you use a build system like cmake or the `configure' script, */
+ /* options set by those programs have precendence, overwriting the */
+ /* value here with the configured one. */
+ /* */
/* #define FT_CONFIG_OPTION_USE_BZIP2 */
@@ -252,6 +263,10 @@ FT_BEGIN_HEADER
/* */
/* Define this macro if you want to enable this `feature'. */
/* */
+ /* If you use a build system like cmake or the `configure' script, */
+ /* options set by those programs have precendence, overwriting the */
+ /* value here with the configured one. */
+ /* */
/* #define FT_CONFIG_OPTION_USE_PNG */
@@ -265,49 +280,11 @@ FT_BEGIN_HEADER
/* */
/* Define this macro if you want to enable this `feature'. */
/* */
-/* #define FT_CONFIG_OPTION_USE_HARFBUZZ */
-
-
- /*************************************************************************/
- /* */
- /* DLL export compilation */
- /* */
- /* When compiling FreeType as a DLL, some systems/compilers need a */
- /* special keyword in front OR after the return type of function */
- /* declarations. */
- /* */
- /* Two macros are used within the FreeType source code to define */
- /* exported library functions: FT_EXPORT and FT_EXPORT_DEF. */
- /* */
- /* FT_EXPORT( return_type ) */
- /* */
- /* is used in a function declaration, as in */
- /* */
- /* FT_EXPORT( FT_Error ) */
- /* FT_Init_FreeType( FT_Library* alibrary ); */
- /* */
+ /* If you use a build system like cmake or the `configure' script, */
+ /* options set by those programs have precendence, overwriting the */
+ /* value here with the configured one. */
/* */
- /* FT_EXPORT_DEF( return_type ) */
- /* */
- /* is used in a function definition, as in */
- /* */
- /* FT_EXPORT_DEF( FT_Error ) */
- /* FT_Init_FreeType( FT_Library* alibrary ) */
- /* { */
- /* ... some code ... */
- /* return FT_Err_Ok; */
- /* } */
- /* */
- /* You can provide your own implementation of FT_EXPORT and */
- /* FT_EXPORT_DEF here if you want. If you leave them undefined, they */
- /* will be later automatically defined as `extern return_type' to */
- /* allow normal compilation. */
- /* */
- /* Do not #undef these macros here since the build system might define */
- /* them for certain configurations only. */
- /* */
-/* #define FT_EXPORT(x) extern x */
-/* #define FT_EXPORT_DEF(x) x */
+/* #define FT_CONFIG_OPTION_USE_HARFBUZZ */
/*************************************************************************/
@@ -678,7 +655,7 @@ FT_BEGIN_HEADER
/* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */
/* defined. */
/* */
- /* [1] http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */
+ /* [1] https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */
/* */
/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING 1 */
#define TT_CONFIG_OPTION_SUBPIXEL_HINTING 2
@@ -698,7 +675,7 @@ FT_BEGIN_HEADER
/* composite flags array which can be used to disambiguate, but old */
/* fonts will not have them. */
/* */
- /* http://www.microsoft.com/typography/otspec/glyf.htm */
+ /* https://www.microsoft.com/typography/otspec/glyf.htm */
/* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html */
/* */
#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED
@@ -796,6 +773,16 @@ FT_BEGIN_HEADER
/*************************************************************************/
+ /* */
+ /* T1_CONFIG_OPTION_OLD_ENGINE controls whether the pre-Adobe Type 1 */
+ /* engine gets compiled into FreeType. If defined, it is possible to */
+ /* switch between the two engines using the `hinting-engine' property of */
+ /* the type1 driver module. */
+ /* */
+/* #define T1_CONFIG_OPTION_OLD_ENGINE */
+
+
+ /*************************************************************************/
/*************************************************************************/
/**** ****/
/**** C F F D R I V E R C O N F I G U R A T I O N ****/
@@ -810,8 +797,8 @@ FT_BEGIN_HEADER
/* possible to set up the default values of the four control points that */
/* define the stem darkening behaviour of the (new) CFF engine. For */
/* more details please read the documentation of the */
- /* `darkening-parameters' property of the cff driver module (file */
- /* `ftcffdrv.h'), which allows the control at run-time. */
+ /* `darkening-parameters' property (file `ftdriver.h'), which allows the */
+ /* control at run-time. */
/* */
/* Do *not* undefine these macros! */
/* */
@@ -899,7 +886,7 @@ FT_BEGIN_HEADER
/* */
/* This experimental option is active only if the rendering mode is */
/* FT_RENDER_MODE_LIGHT; you can switch warping on and off with the */
- /* `warping' property of the auto-hinter (see file `ftautoh.h' for more */
+ /* `warping' property of the auto-hinter (see file `ftdriver.h' for more */
/* information; by default it is switched off). */
/* */
#define AF_CONFIG_OPTION_USE_WARPER
diff --git a/thirdparty/freetype/include/freetype/config/ftstdlib.h b/thirdparty/freetype/include/freetype/config/ftstdlib.h
index 05a4845fd5..42f9a06e43 100644
--- a/thirdparty/freetype/include/freetype/config/ftstdlib.h
+++ b/thirdparty/freetype/include/freetype/config/ftstdlib.h
@@ -5,7 +5,7 @@
/* ANSI-specific library and header configuration file (specification */
/* only). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/freetype.h b/thirdparty/freetype/include/freetype/freetype.h
index b0c261799a..96644046e4 100644
--- a/thirdparty/freetype/include/freetype/freetype.h
+++ b/thirdparty/freetype/include/freetype/freetype.h
@@ -4,7 +4,7 @@
/* */
/* FreeType high-level API and common types (specification only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -138,6 +138,7 @@ FT_BEGIN_HEADER
/* FT_FACE_FLAG_TRICKY */
/* FT_FACE_FLAG_KERNING */
/* FT_FACE_FLAG_MULTIPLE_MASTERS */
+ /* FT_FACE_FLAG_VARIATION */
/* FT_FACE_FLAG_GLYPH_NAMES */
/* FT_FACE_FLAG_EXTERNAL_STREAM */
/* FT_FACE_FLAG_HINTER */
@@ -147,14 +148,16 @@ FT_BEGIN_HEADER
/* FT_HAS_KERNING */
/* FT_HAS_FIXED_SIZES */
/* FT_HAS_GLYPH_NAMES */
- /* FT_HAS_MULTIPLE_MASTERS */
/* FT_HAS_COLOR */
+ /* FT_HAS_MULTIPLE_MASTERS */
/* */
/* FT_IS_SFNT */
/* FT_IS_SCALABLE */
/* FT_IS_FIXED_WIDTH */
/* FT_IS_CID_KEYED */
/* FT_IS_TRICKY */
+ /* FT_IS_NAMED_INSTANCE */
+ /* FT_IS_VARIATION */
/* */
/* FT_STYLE_FLAG_BOLD */
/* FT_STYLE_FLAG_ITALIC */
@@ -648,7 +651,7 @@ FT_BEGIN_HEADER
/* FT_ENCODING_MS_SYMBOL :: */
/* Microsoft Symbol encoding, used to encode mathematical symbols */
/* and wingdings. For more information, see */
- /* `http://www.microsoft.com/typography/otspec/recom.htm', */
+ /* `https://www.microsoft.com/typography/otspec/recom.htm', */
/* `http://www.kostis.net/charsets/symbol.htm', and */
/* `http://www.kostis.net/charsets/wingding.htm'. */
/* */
@@ -657,7 +660,7 @@ FT_BEGIN_HEADER
/* */
/* FT_ENCODING_SJIS :: */
/* Shift JIS encoding for Japanese. More info at */
- /* `http://en.wikipedia.org/wiki/Shift_JIS'. See note on */
+ /* `https://en.wikipedia.org/wiki/Shift_JIS'. See note on */
/* multi-byte encodings below. */
/* */
/* FT_ENCODING_PRC :: */
@@ -673,7 +676,7 @@ FT_BEGIN_HEADER
/* Corresponds to the Korean encoding system known as Extended */
/* Wansung (MS Windows code page 949). */
/* For more information see */
- /* `http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WindowsBestFit/bestfit949.txt'. */
+ /* `https://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WindowsBestFit/bestfit949.txt'. */
/* */
/* FT_ENCODING_JOHAB :: */
/* The Korean standard character set (KS~C 5601-1992), which */
@@ -721,11 +724,12 @@ FT_BEGIN_HEADER
/* Same as FT_ENCODING_JOHAB. Deprecated. */
/* */
/* <Note> */
- /* By default, FreeType automatically synthesizes a Unicode charmap */
- /* for PostScript fonts, using their glyph name dictionaries. */
- /* However, it also reports the encodings defined explicitly in the */
- /* font file, for the cases when they are needed, with the Adobe */
- /* values as well. */
+ /* By default, FreeType enables a Unicode charmap and tags it with */
+ /* FT_ENCODING_UNICODE when it is either provided or can be generated */
+ /* from PostScript glyph name dictionaries in the font file. */
+ /* All other encodings are considered legacy and tagged only if */
+ /* explicitly defined in the font file. Otherwise, FT_ENCODING_NONE */
+ /* is used. */
/* */
/* FT_ENCODING_NONE is set by the BDF and PCF drivers if the charmap */
/* is neither Unicode nor ISO-8859-1 (otherwise it is set to */
@@ -749,7 +753,7 @@ FT_BEGIN_HEADER
/* @FT_Get_CMap_Language_ID to query the Mac language ID that may */
/* be needed to be able to distinguish Apple encoding variants. See */
/* */
- /* http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/Readme.txt */
+ /* https://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/Readme.txt */
/* */
/* to get an idea how to do that. Basically, if the language ID */
/* is~0, don't use it, otherwise subtract 1 from the language ID. */
@@ -888,34 +892,41 @@ FT_BEGIN_HEADER
/* are set to~0 if there is only one face in */
/* the font file. */
/* */
- /* Bits 16-30 are relevant to GX and OpenType */
- /* variation fonts only, holding the named */
- /* instance index for the current face index */
- /* (starting with value~1; value~0 indicates */
- /* font access without a named instance). For */
- /* non-variation fonts, bits 16-30 are */
- /* ignored. If we have the third named */
- /* instance of face~4, say, `face_index' is */
- /* set to 0x00030004. */
+ /* [Since 2.6.1] Bits 16-30 are relevant to GX */
+ /* and OpenType variation fonts only, holding */
+ /* the named instance index for the current */
+ /* face index (starting with value~1; value~0 */
+ /* indicates font access without a named */
+ /* instance). For non-variation fonts, bits */
+ /* 16-30 are ignored. If we have the third */
+ /* named instance of face~4, say, `face_index' */
+ /* is set to 0x00030004. */
/* */
/* Bit 31 is always zero (this is, */
/* `face_index' is always a positive value). */
/* */
+ /* [Since 2.9] Changing the design coordinates */
+ /* with @FT_Set_Var_Design_Coordinates or */
+ /* @FT_Set_Var_Blend_Coordinates does not */
+ /* influence the named instance index value */
+ /* (only @FT_Set_Named_Instance does that). */
+ /* */
/* face_flags :: A set of bit flags that give important */
/* information about the face; see */
/* @FT_FACE_FLAG_XXX for the details. */
/* */
/* style_flags :: The lower 16~bits contain a set of bit */
/* flags indicating the style of the face; see */
- /* @FT_STYLE_FLAG_XXX for the details. Bits */
- /* 16-30 hold the number of named instances */
- /* available for the current face if we have a */
- /* GX or OpenType variation (sub)font. Bit 31 */
- /* is always zero (this is, `style_flags' is */
- /* always a positive value). Note that a */
- /* variation font has always at least one */
- /* named instance, namely the default */
- /* instance. */
+ /* @FT_STYLE_FLAG_XXX for the details. */
+ /* */
+ /* [Since 2.6.1] Bits 16-30 hold the number */
+ /* of named instances available for the */
+ /* current face if we have a GX or OpenType */
+ /* variation (sub)font. Bit 31 is always zero */
+ /* (this is, `style_flags' is always a */
+ /* positive value). Note that a variation */
+ /* font has always at least one named */
+ /* instance, namely the default instance. */
/* */
/* num_glyphs :: The number of glyphs in the face. If the */
/* face is scalable and has sbits (see */
@@ -1052,6 +1063,9 @@ FT_BEGIN_HEADER
/* `descender', `height', `underline_position', and */
/* `underline_thickness'. */
/* */
+ /* Especially for TrueType fonts see also the documentation for */
+ /* @FT_Size_Metrics. */
+ /* */
typedef struct FT_FaceRec_
{
FT_Long num_faces;
@@ -1162,7 +1176,7 @@ FT_BEGIN_HEADER
/* interpolating between them. Supported formats are Adobe MM, */
/* TrueType GX, and OpenType variation fonts. */
/* */
- /* See the multiple-masters specific API for details. */
+ /* See section @multiple_masters for API details. */
/* */
/* FT_FACE_FLAG_GLYPH_NAMES :: */
/* The face contains glyph names, which can be retrieved using */
@@ -1212,8 +1226,15 @@ FT_BEGIN_HEADER
/* tricky fonts; they are hard-coded in file `ttobjs.c'. */
/* */
/* FT_FACE_FLAG_COLOR :: */
- /* The face has color glyph tables. To access color glyphs use */
- /* @FT_LOAD_COLOR. */
+ /* [Since 2.5.1] The face has color glyph tables. To access color */
+ /* glyphs use @FT_LOAD_COLOR. */
+ /* */
+ /* FT_FACE_FLAG_VARIATION :: */
+ /* [Since 2.9] Set if the current face (or named instance) has been */
+ /* altered with @FT_Set_MM_Design_Coordinates, */
+ /* @FT_Set_Var_Design_Coordinates, or */
+ /* @FT_Set_Var_Blend_Coordinates. This flag is unset by a call to */
+ /* @FT_Set_Named_Instance. */
/* */
#define FT_FACE_FLAG_SCALABLE ( 1L << 0 )
#define FT_FACE_FLAG_FIXED_SIZES ( 1L << 1 )
@@ -1230,6 +1251,7 @@ FT_BEGIN_HEADER
#define FT_FACE_FLAG_CID_KEYED ( 1L << 12 )
#define FT_FACE_FLAG_TRICKY ( 1L << 13 )
#define FT_FACE_FLAG_COLOR ( 1L << 14 )
+#define FT_FACE_FLAG_VARIATION ( 1L << 15 )
/*************************************************************************
@@ -1391,6 +1413,14 @@ FT_BEGIN_HEADER
* A macro that returns true whenever a face object is a named instance
* of a GX or OpenType variation font.
*
+ * [Since 2.9] Changing the design coordinates with
+ * @FT_Set_Var_Design_Coordinates or @FT_Set_Var_Blend_Coordinates does
+ * not influence the return value of this macro (only
+ * @FT_Set_Named_Instance does that).
+ *
+ * @since:
+ * 2.7
+ *
*/
#define FT_IS_NAMED_INSTANCE( face ) \
( (face)->face_index & 0x7FFF0000L )
@@ -1399,6 +1429,24 @@ FT_BEGIN_HEADER
/*************************************************************************
*
* @macro:
+ * FT_IS_VARIATION( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object has been altered
+ * by @FT_Set_MM_Design_Coordinates, @FT_Set_Var_Design_Coordinates, or
+ * @FT_Set_Var_Blend_Coordinates.
+ *
+ * @since:
+ * 2.9
+ *
+ */
+#define FT_IS_VARIATION( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_VARIATION )
+
+
+ /*************************************************************************
+ *
+ * @macro:
* FT_IS_CID_KEYED( face )
*
* @description:
@@ -1437,6 +1485,9 @@ FT_BEGIN_HEADER
* A macro that returns true whenever a face object contains
* tables for color glyphs.
*
+ * @since:
+ * 2.5.1
+ *
*/
#define FT_HAS_COLOR( face ) \
( (face)->face_flags & FT_FACE_FLAG_COLOR )
@@ -1534,7 +1585,7 @@ FT_BEGIN_HEADER
/* to the following. */
/* */
/* { */
- /* scaled_ascender = FT_MulFix( face->root.ascender, */
+ /* scaled_ascender = FT_MulFix( face->ascender, */
/* size_metrics->y_scale ); */
/* } */
/* */
@@ -1548,6 +1599,43 @@ FT_BEGIN_HEADER
/* */
/* The `FT_Size_Metrics' structure is valid for bitmap fonts also. */
/* */
+ /* */
+ /* *TrueType* *fonts* *with* *native* *bytecode* *hinting* */
+ /* */
+ /* All applications that handle TrueType fonts with native hinting */
+ /* must be aware that TTFs expect different rounding of vertical font */
+ /* dimensions. The application has to cater for this, especially if */
+ /* it wants to rely on a TTF's vertical data (for example, to */
+ /* properly align box characters vertically). */
+ /* */
+ /* Only the application knows _in_ _advance_ that it is going to use */
+ /* native hinting for TTFs! FreeType, on the other hand, selects the */
+ /* hinting mode not at the time of creating an @FT_Size object but */
+ /* much later, namely while calling @FT_Load_Glyph. */
+ /* */
+ /* Here is some pseudo code that illustrates a possible solution. */
+ /* */
+ /* { */
+ /* font_format = FT_Get_Font_Format( face ); */
+ /* */
+ /* if ( !strcmp( font_format, "TrueType" ) && */
+ /* do_native_bytecode_hinting ) */
+ /* { */
+ /* ascender = ROUND( FT_MulFix( face->ascender, */
+ /* size_metrics->y_scale ) ); */
+ /* descender = ROUND( FT_MulFix( face->descender, */
+ /* size_metrics->y_scale ) ); */
+ /* } */
+ /* else */
+ /* { */
+ /* ascender = size_metrics->ascender; */
+ /* descender = size_metrics->descender; */
+ /* } */
+ /* */
+ /* height = size_metrics->height; */
+ /* max_advance = size_metrics->max_advance; */
+ /* } */
+ /* */
typedef struct FT_Size_Metrics_
{
FT_UShort x_ppem; /* horizontal pixels per EM */
@@ -1689,17 +1777,13 @@ FT_BEGIN_HEADER
/* @FT_GLYPH_FORMAT_COMPOSITE, but other values */
/* are possible. */
/* */
- /* bitmap :: This field is used as a bitmap descriptor */
- /* when the slot format is */
- /* @FT_GLYPH_FORMAT_BITMAP. Note that the */
- /* address and content of the bitmap buffer can */
- /* change between calls of @FT_Load_Glyph and a */
- /* few other functions. */
+ /* bitmap :: This field is used as a bitmap descriptor. */
+ /* Note that the address and content of the */
+ /* bitmap buffer can change between calls of */
+ /* @FT_Load_Glyph and a few other functions. */
/* */
/* bitmap_left :: The bitmap's left bearing expressed in */
- /* integer pixels. Only valid if the format is */
- /* @FT_GLYPH_FORMAT_BITMAP, this is, if the */
- /* glyph slot contains a bitmap. */
+ /* integer pixels. */
/* */
/* bitmap_top :: The bitmap's top bearing expressed in integer */
/* pixels. This is the distance from the */
@@ -1746,7 +1830,9 @@ FT_BEGIN_HEADER
/* If @FT_Load_Glyph is called with default flags (see */
/* @FT_LOAD_DEFAULT) the glyph image is loaded in the glyph slot in */
/* its native format (e.g., an outline glyph for TrueType and Type~1 */
- /* formats). */
+ /* formats). [Since 2.9] The prospective bitmap metrics are */
+ /* calculated according to @FT_LOAD_TARGET_XXX and other flags even */
+ /* for the outline glyph, even if @FT_LOAD_RENDER is not set. */
/* */
/* This image can later be converted into a bitmap by calling */
/* @FT_Render_Glyph. This function searches the current renderer for */
@@ -1895,8 +1981,8 @@ FT_BEGIN_HEADER
/* */
/* If compilation option FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES is */
/* set, this function reads the `FREETYPE_PROPERTIES' environment */
- /* variable to control driver properties. See sections @auto_hinter, */
- /* @cff_driver, @pcf_driver, and @tt_driver for more. */
+ /* variable to control driver properties. See section @properties */
+ /* for more. */
/* */
FT_EXPORT( FT_Error )
FT_Init_FreeType( FT_Library *alibrary );
@@ -1977,8 +2063,8 @@ FT_BEGIN_HEADER
/* data :: A pointer to the parameter data. */
/* */
/* <Note> */
- /* The ID and function of parameters are driver-specific. See the */
- /* various FT_PARAM_TAG_XXX flags for more information. */
+ /* The ID and function of parameters are driver-specific. See */
+ /* section @parameter_tags for more information. */
/* */
typedef struct FT_Parameter_
{
@@ -2155,14 +2241,14 @@ FT_BEGIN_HEADER
/* with value~0). Set it to~0 if there is only one */
/* face in the font file. */
/* */
- /* Bits 16-30 are relevant to GX and OpenType variation */
- /* fonts only, specifying the named instance index for */
- /* the current face index (starting with value~1; */
- /* value~0 makes FreeType ignore named instances). For */
- /* non-variation fonts, bits 16-30 are ignored. */
- /* Assuming that you want to access the third named */
- /* instance in face~4, `face_index' should be set to */
- /* 0x00030004. If you want to access face~4 without */
+ /* [Since 2.6.1] Bits 16-30 are relevant to GX and */
+ /* OpenType variation fonts only, specifying the named */
+ /* instance index for the current face index (starting */
+ /* with value~1; value~0 makes FreeType ignore named */
+ /* instances). For non-variation fonts, bits 16-30 are */
+ /* ignored. Assuming that you want to access the third */
+ /* named instance in face~4, `face_index' should be set */
+ /* to 0x00030004. If you want to access face~4 without */
/* variation handling, simply set `face_index' to */
/* value~4. */
/* */
@@ -2748,6 +2834,10 @@ FT_BEGIN_HEADER
/* since its glyph indices are not listed in any of the font's */
/* charmaps. */
/* */
+ /* If no active cmap is set up (i.e., `face->charmap' is zero), the */
+ /* call to @FT_Get_Char_Index is omitted, and the function behaves */
+ /* identically to @FT_Load_Glyph. */
+ /* */
FT_EXPORT( FT_Error )
FT_Load_Char( FT_Face face,
FT_ULong char_code,
@@ -2869,26 +2959,26 @@ FT_BEGIN_HEADER
* Disable the auto-hinter. See also the note below.
*
* FT_LOAD_COLOR ::
- * Load embedded color bitmap images. The resulting color bitmaps,
- * if available, will have the @FT_PIXEL_MODE_BGRA format. If the
- * flag is not set and color bitmaps are found, they are converted
- * to 256-level gray bitmaps transparently, using the
+ * [Since 2.5] Load embedded color bitmap images. The resulting color
+ * bitmaps, if available, will have the @FT_PIXEL_MODE_BGRA format.
+ * If the flag is not set and color bitmaps are found, they are
+ * converted to 256-level gray bitmaps transparently, using the
* @FT_PIXEL_MODE_GRAY format.
*
* FT_LOAD_COMPUTE_METRICS ::
- * Compute glyph metrics from the glyph data, without the use of
- * bundled metrics tables (for example, the `hdmx' table in TrueType
- * fonts). This flag is mainly used by font validating or font
- * editing applications, which need to ignore, verify, or edit those
- * tables.
+ * [Since 2.6.1] Compute glyph metrics from the glyph data, without
+ * the use of bundled metrics tables (for example, the `hdmx' table in
+ * TrueType fonts). This flag is mainly used by font validating or
+ * font editing applications, which need to ignore, verify, or edit
+ * those tables.
*
* Currently, this flag is only implemented for TrueType fonts.
*
* FT_LOAD_BITMAP_METRICS_ONLY ::
- * Request loading of the metrics and bitmap image information of a
- * (possibly embedded) bitmap glyph without allocating or copying
- * the bitmap image data itself. No effect if the target glyph is
- * not a bitmap image.
+ * [Since 2.7.1] Request loading of the metrics and bitmap image
+ * information of a (possibly embedded) bitmap glyph without
+ * allocating or copying the bitmap image data itself. No effect if
+ * the target glyph is not a bitmap image.
*
* This flag unsets @FT_LOAD_RENDER.
*
@@ -2980,7 +3070,7 @@ FT_BEGIN_HEADER
*
* Advance widths are rounded to integer values; however, using the
* `lsb_delta' and `rsb_delta' fields of @FT_GlyphSlotRec, it is
- * possible to get fractional advance widths for sub-pixel positioning
+ * possible to get fractional advance widths for subpixel positioning
* (which is recommended to use).
*
* If configuration option AF_CONFIG_OPTION_TT_SIZE_METRICS is active,
@@ -3119,13 +3209,13 @@ FT_BEGIN_HEADER
/* opacity). */
/* */
/* FT_RENDER_MODE_LCD :: */
- /* This mode corresponds to horizontal RGB and BGR sub-pixel */
+ /* This mode corresponds to horizontal RGB and BGR subpixel */
/* displays like LCD screens. It produces 8-bit bitmaps that are */
/* 3~times the width of the original glyph outline in pixels, and */
/* which use the @FT_PIXEL_MODE_LCD mode. */
/* */
/* FT_RENDER_MODE_LCD_V :: */
- /* This mode corresponds to vertical RGB and BGR sub-pixel displays */
+ /* This mode corresponds to vertical RGB and BGR subpixel displays */
/* (like PDA screens, rotated LCD displays, etc.). It produces */
/* 8-bit bitmaps that are 3~times the height of the original */
/* glyph outline in pixels and use the @FT_PIXEL_MODE_LCD_V mode. */
@@ -3471,7 +3561,14 @@ FT_BEGIN_HEADER
/* retrieve it. FreeType follows Adobe TechNote #5902, `Generating */
/* PostScript Names for Fonts Using OpenType Font Variations'. */
/* */
- /* http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5902.AdobePSNameGeneration.html */
+ /* https://download.macromedia.com/pub/developer/opentype/tech-notes/5902.AdobePSNameGeneration.html */
+ /* */
+ /* [Since 2.9] Special PostScript names for named instances are only */
+ /* returned if the named instance is set with @FT_Set_Named_Instance */
+ /* (and the font has corresponding entries in its `fvar' table). If */
+ /* @FT_IS_VARIATION returns true, the algorithmically derived */
+ /* PostScript name is provided, not looking up special entries for */
+ /* named instances. */
/* */
FT_EXPORT( const char* )
FT_Get_Postscript_Name( FT_Face face );
@@ -3702,17 +3799,17 @@ FT_BEGIN_HEADER
* Note that only a subset of the available properties can be
* controlled.
*
- * * Stem darkening (@FT_PARAM_TAG_STEM_DARKENING, corresponding to the
- * property `no-stem-darkening' provided by the `autofit' and `cff'
- * modules; see @no-stem-darkening[autofit] and
- * @no-stem-darkening[cff]).
+ * * @FT_PARAM_TAG_STEM_DARKENING (stem darkening, corresponding to the
+ * property `no-stem-darkening' provided by the `autofit', `cff',
+ * `type1', and `t1cid' modules; see @no-stem-darkening).
*
- * * LCD filter weights (@FT_PARAM_TAG_LCD_FILTER_WEIGHTS, corresponding
+ * * @FT_PARAM_TAG_LCD_FILTER_WEIGHTS (LCD filter weights, corresponding
* to function @FT_Library_SetLcdFilterWeights).
*
- * * Seed value for the CFF `random' operator
- * (@FT_PARAM_TAG_RANDOM_SEED, corresponding to the `random-seed'
- * property provided by the `cff' module; see @random-seed).
+ * * @FT_PARAM_TAG_RANDOM_SEED (seed value for the CFF, Type~1, and CID
+ * `random' operator, corresponding to the `random-seed' property
+ * provided by the `cff', `type1', and `t1cid' modules; see
+ * @random-seed).
*
* Pass NULL as `data' in @FT_Parameter for a given tag to reset the
* option and use the library or module default again.
@@ -3775,6 +3872,9 @@ FT_BEGIN_HEADER
* FT_Face_Properties( face, 1, &property );
* }
*
+ * @since:
+ * 2.8
+ *
*/
FT_EXPORT( FT_Error )
FT_Face_Properties( FT_Face face,
@@ -3899,7 +3999,7 @@ FT_BEGIN_HEADER
/* and subsetting restrictions associated with a font. */
/* */
/* See */
- /* http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/FontPolicies.pdf */
+ /* https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/FontPolicies.pdf */
/* for more details. */
/* */
/* <Values> */
@@ -3999,9 +4099,9 @@ FT_BEGIN_HEADER
/* Sequences' (IVS), collected in the `Ideographic Variation */
/* Database' (IVD). */
/* */
- /* http://unicode.org/Public/UCD/latest/ucd/StandardizedVariants.txt */
- /* http://unicode.org/reports/tr37/ */
- /* http://unicode.org/ivd/ */
+ /* https://unicode.org/Public/UCD/latest/ucd/StandardizedVariants.txt */
+ /* https://unicode.org/reports/tr37/ */
+ /* https://unicode.org/ivd/ */
/* */
/* To date (January 2017), the character with the most ideographic */
/* variations is U+9089, having 32 such IVS. */
@@ -4456,7 +4556,7 @@ FT_BEGIN_HEADER
*
*/
#define FREETYPE_MAJOR 2
-#define FREETYPE_MINOR 8
+#define FREETYPE_MINOR 9
#define FREETYPE_PATCH 1
diff --git a/thirdparty/freetype/include/freetype/ftadvanc.h b/thirdparty/freetype/include/freetype/ftadvanc.h
index dea96e0db4..f78e8b1a9d 100644
--- a/thirdparty/freetype/include/freetype/ftadvanc.h
+++ b/thirdparty/freetype/include/freetype/ftadvanc.h
@@ -4,7 +4,7 @@
/* */
/* Quick computation of advance widths (specification only). */
/* */
-/* Copyright 2008-2017 by */
+/* Copyright 2008-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftbbox.h b/thirdparty/freetype/include/freetype/ftbbox.h
index f03bdc1e1e..f9eb70b137 100644
--- a/thirdparty/freetype/include/freetype/ftbbox.h
+++ b/thirdparty/freetype/include/freetype/ftbbox.h
@@ -4,7 +4,7 @@
/* */
/* FreeType exact bbox computation (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -61,7 +61,7 @@ FT_BEGIN_HEADER
/* Compute the exact bounding box of an outline. This is slower */
/* than computing the control box. However, it uses an advanced */
/* algorithm that returns _very_ quickly when the two boxes */
- /* coincide. Otherwise, the outline Bézier arcs are traversed to */
+ /* coincide. Otherwise, the outline Bezier arcs are traversed to */
/* extract their extrema. */
/* */
/* <Input> */
diff --git a/thirdparty/freetype/include/freetype/ftbdf.h b/thirdparty/freetype/include/freetype/ftbdf.h
index 3d3106bad0..1b6dea6586 100644
--- a/thirdparty/freetype/include/freetype/ftbdf.h
+++ b/thirdparty/freetype/include/freetype/ftbdf.h
@@ -4,7 +4,7 @@
/* */
/* FreeType API for accessing BDF-specific strings (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftbitmap.h b/thirdparty/freetype/include/freetype/ftbitmap.h
index 04b2402ad0..a43187cad4 100644
--- a/thirdparty/freetype/include/freetype/ftbitmap.h
+++ b/thirdparty/freetype/include/freetype/ftbitmap.h
@@ -4,7 +4,7 @@
/* */
/* FreeType utility functions for bitmaps (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -97,7 +97,7 @@ FT_BEGIN_HEADER
FT_EXPORT( FT_Error )
FT_Bitmap_Copy( FT_Library library,
const FT_Bitmap *source,
- FT_Bitmap *target);
+ FT_Bitmap *target );
/*************************************************************************/
diff --git a/thirdparty/freetype/include/freetype/ftbzip2.h b/thirdparty/freetype/include/freetype/ftbzip2.h
index 9147a790a4..6edfa031b5 100644
--- a/thirdparty/freetype/include/freetype/ftbzip2.h
+++ b/thirdparty/freetype/include/freetype/ftbzip2.h
@@ -4,7 +4,7 @@
/* */
/* Bzip2-compressed stream support. */
/* */
-/* Copyright 2010-2017 by */
+/* Copyright 2010-2018 by */
/* Joel Klinghed. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftcache.h b/thirdparty/freetype/include/freetype/ftcache.h
index 5ff3ccf404..52d5f00e06 100644
--- a/thirdparty/freetype/include/freetype/ftcache.h
+++ b/thirdparty/freetype/include/freetype/ftcache.h
@@ -4,7 +4,7 @@
/* */
/* FreeType Cache subsystem (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -540,13 +540,6 @@ FT_BEGIN_HEADER
FTC_FaceID face_id );
- /*************************************************************************/
- /* */
- /* <Section> */
- /* cache_subsystem */
- /* */
- /*************************************************************************/
-
/*************************************************************************
*
* @type:
@@ -624,14 +617,6 @@ FT_BEGIN_HEADER
/*************************************************************************/
- /* */
- /* <Section> */
- /* cache_subsystem */
- /* */
- /*************************************************************************/
-
-
- /*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/***** *****/
diff --git a/thirdparty/freetype/include/freetype/ftchapters.h b/thirdparty/freetype/include/freetype/ftchapters.h
index a0a121b0a1..51257bb7ca 100644
--- a/thirdparty/freetype/include/freetype/ftchapters.h
+++ b/thirdparty/freetype/include/freetype/ftchapters.h
@@ -76,8 +76,11 @@
/* <Sections> */
/* auto_hinter */
/* cff_driver */
+/* t1_cid_driver */
/* tt_driver */
/* pcf_driver */
+/* properties */
+/* parameter_tags */
/* */
/***************************************************************************/
diff --git a/thirdparty/freetype/include/freetype/ftcid.h b/thirdparty/freetype/include/freetype/ftcid.h
index 4adcbeeda9..5e9100a67c 100644
--- a/thirdparty/freetype/include/freetype/ftcid.h
+++ b/thirdparty/freetype/include/freetype/ftcid.h
@@ -4,7 +4,7 @@
/* */
/* FreeType API for accessing CID font information (specification). */
/* */
-/* Copyright 2007-2017 by */
+/* Copyright 2007-2018 by */
/* Dereg Clegg and Michael Toftdal. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -87,7 +87,7 @@ FT_BEGIN_HEADER
FT_Get_CID_Registry_Ordering_Supplement( FT_Face face,
const char* *registry,
const char* *ordering,
- FT_Int *supplement);
+ FT_Int *supplement );
/**********************************************************************
diff --git a/thirdparty/freetype/include/freetype/ftdriver.h b/thirdparty/freetype/include/freetype/ftdriver.h
new file mode 100644
index 0000000000..e90475b2af
--- /dev/null
+++ b/thirdparty/freetype/include/freetype/ftdriver.h
@@ -0,0 +1,1225 @@
+/***************************************************************************/
+/* */
+/* ftdriver.h */
+/* */
+/* FreeType API for controlling driver modules (specification only). */
+/* */
+/* Copyright 2017-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef FTDRIVER_H_
+#define FTDRIVER_H_
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_PARAMETER_TAGS_H
+
+#ifdef FREETYPE_H
+#error "freetype.h of FreeType 1 has been loaded!"
+#error "Please fix the directory search order for header files"
+#error "so that freetype.h of FreeType 2 is found first."
+#endif
+
+
+FT_BEGIN_HEADER
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * auto_hinter
+ *
+ * @title:
+ * The auto-hinter
+ *
+ * @abstract:
+ * Controlling the auto-hinting module.
+ *
+ * @description:
+ * While FreeType's auto-hinter doesn't expose API functions by itself,
+ * it is possible to control its behaviour with @FT_Property_Set and
+ * @FT_Property_Get. The following lists the available properties
+ * together with the necessary macros and structures.
+ *
+ * Note that the auto-hinter's module name is `autofitter' for
+ * historical reasons.
+ *
+ * Available properties are @increase-x-height, @no-stem-darkening
+ * (experimental), @darkening-parameters (experimental), @warping
+ * (experimental), @glyph-to-script-map (experimental), @fallback-script
+ * (experimental), and @default-script (experimental), as documented in
+ * the @properties section.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * cff_driver
+ *
+ * @title:
+ * The CFF driver
+ *
+ * @abstract:
+ * Controlling the CFF driver module.
+ *
+ * @description:
+ * While FreeType's CFF driver doesn't expose API functions by itself,
+ * it is possible to control its behaviour with @FT_Property_Set and
+ * @FT_Property_Get.
+ *
+ * The CFF driver's module name is `cff'.
+ *
+ * Available properties are @hinting-engine, @no-stem-darkening,
+ * @darkening-parameters, and @random-seed, as documented in the
+ * @properties section.
+ *
+ *
+ * *Hinting* *and* *antialiasing* *principles* *of* *the* *new* *engine*
+ *
+ * The rasterizer is positioning horizontal features (e.g., ascender
+ * height & x-height, or crossbars) on the pixel grid and minimizing the
+ * amount of antialiasing applied to them, while placing vertical
+ * features (vertical stems) on the pixel grid without hinting, thus
+ * representing the stem position and weight accurately. Sometimes the
+ * vertical stems may be only partially black. In this context,
+ * `antialiasing' means that stems are not positioned exactly on pixel
+ * borders, causing a fuzzy appearance.
+ *
+ * There are two principles behind this approach.
+ *
+ * 1) No hinting in the horizontal direction: Unlike `superhinted'
+ * TrueType, which changes glyph widths to accommodate regular
+ * inter-glyph spacing, Adobe's approach is `faithful to the design' in
+ * representing both the glyph width and the inter-glyph spacing
+ * designed for the font. This makes the screen display as close as it
+ * can be to the result one would get with infinite resolution, while
+ * preserving what is considered the key characteristics of each glyph.
+ * Note that the distances between unhinted and grid-fitted positions at
+ * small sizes are comparable to kerning values and thus would be
+ * noticeable (and distracting) while reading if hinting were applied.
+ *
+ * One of the reasons to not hint horizontally is antialiasing for LCD
+ * screens: The pixel geometry of modern displays supplies three
+ * vertical subpixels as the eye moves horizontally across each visible
+ * pixel. On devices where we can be certain this characteristic is
+ * present a rasterizer can take advantage of the subpixels to add
+ * increments of weight. In Western writing systems this turns out to
+ * be the more critical direction anyway; the weights and spacing of
+ * vertical stems (see above) are central to Armenian, Cyrillic, Greek,
+ * and Latin type designs. Even when the rasterizer uses greyscale
+ * antialiasing instead of color (a necessary compromise when one
+ * doesn't know the screen characteristics), the unhinted vertical
+ * features preserve the design's weight and spacing much better than
+ * aliased type would.
+ *
+ * 2) Alignment in the vertical direction: Weights and spacing along the
+ * y~axis are less critical; what is much more important is the visual
+ * alignment of related features (like cap-height and x-height). The
+ * sense of alignment for these is enhanced by the sharpness of grid-fit
+ * edges, while the cruder vertical resolution (full pixels instead of
+ * 1/3 pixels) is less of a problem.
+ *
+ * On the technical side, horizontal alignment zones for ascender,
+ * x-height, and other important height values (traditionally called
+ * `blue zones') as defined in the font are positioned independently,
+ * each being rounded to the nearest pixel edge, taking care of
+ * overshoot suppression at small sizes, stem darkening, and scaling.
+ *
+ * Hstems (this is, hint values defined in the font to help align
+ * horizontal features) that fall within a blue zone are said to be
+ * `captured' and are aligned to that zone. Uncaptured stems are moved
+ * in one of four ways, top edge up or down, bottom edge up or down.
+ * Unless there are conflicting hstems, the smallest movement is taken
+ * to minimize distortion.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * pcf_driver
+ *
+ * @title:
+ * The PCF driver
+ *
+ * @abstract:
+ * Controlling the PCF driver module.
+ *
+ * @description:
+ * While FreeType's PCF driver doesn't expose API functions by itself,
+ * it is possible to control its behaviour with @FT_Property_Set and
+ * @FT_Property_Get. Right now, there is a single property
+ * @no-long-family-names available if FreeType is compiled with
+ * PCF_CONFIG_OPTION_LONG_FAMILY_NAMES.
+ *
+ * The PCF driver's module name is `pcf'.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * t1_cid_driver
+ *
+ * @title:
+ * The Type 1 and CID drivers
+ *
+ * @abstract:
+ * Controlling the Type~1 and CID driver modules.
+ *
+ * @description:
+ * It is possible to control the behaviour of FreeType's Type~1 and
+ * Type~1 CID drivers with @FT_Property_Set and @FT_Property_Get.
+ *
+ * Behind the scenes, both drivers use the Adobe CFF engine for hinting;
+ * however, the used properties must be specified separately.
+ *
+ * The Type~1 driver's module name is `type1'; the CID driver's module
+ * name is `t1cid'.
+ *
+ * Available properties are @hinting-engine, @no-stem-darkening,
+ * @darkening-parameters, and @random-seed, as documented in the
+ * @properties section.
+ *
+ * Please see the @cff_driver section for more details on the new
+ * hinting engine.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * tt_driver
+ *
+ * @title:
+ * The TrueType driver
+ *
+ * @abstract:
+ * Controlling the TrueType driver module.
+ *
+ * @description:
+ * While FreeType's TrueType driver doesn't expose API functions by
+ * itself, it is possible to control its behaviour with @FT_Property_Set
+ * and @FT_Property_Get. The following lists the available properties
+ * together with the necessary macros and structures.
+ *
+ * The TrueType driver's module name is `truetype'.
+ *
+ * A single property @interpreter-version is available, as documented in
+ * the @properties section.
+ *
+ * We start with a list of definitions, kindly provided by Greg
+ * Hitchcock.
+ *
+ * _Bi-Level_ _Rendering_
+ *
+ * Monochromatic rendering, exclusively used in the early days of
+ * TrueType by both Apple and Microsoft. Microsoft's GDI interface
+ * supported hinting of the right-side bearing point, such that the
+ * advance width could be non-linear. Most often this was done to
+ * achieve some level of glyph symmetry. To enable reasonable
+ * performance (e.g., not having to run hinting on all glyphs just to
+ * get the widths) there was a bit in the head table indicating if the
+ * side bearing was hinted, and additional tables, `hdmx' and `LTSH', to
+ * cache hinting widths across multiple sizes and device aspect ratios.
+ *
+ * _Font_ _Smoothing_
+ *
+ * Microsoft's GDI implementation of anti-aliasing. Not traditional
+ * anti-aliasing as the outlines were hinted before the sampling. The
+ * widths matched the bi-level rendering.
+ *
+ * _ClearType_ _Rendering_
+ *
+ * Technique that uses physical subpixels to improve rendering on LCD
+ * (and other) displays. Because of the higher resolution, many methods
+ * of improving symmetry in glyphs through hinting the right-side
+ * bearing were no longer necessary. This lead to what GDI calls
+ * `natural widths' ClearType, see
+ * http://www.beatstamm.com/typography/RTRCh4.htm#Sec21. Since hinting
+ * has extra resolution, most non-linearity went away, but it is still
+ * possible for hints to change the advance widths in this mode.
+ *
+ * _ClearType_ _Compatible_ _Widths_
+ *
+ * One of the earliest challenges with ClearType was allowing the
+ * implementation in GDI to be selected without requiring all UI and
+ * documents to reflow. To address this, a compatible method of
+ * rendering ClearType was added where the font hints are executed once
+ * to determine the width in bi-level rendering, and then re-run in
+ * ClearType, with the difference in widths being absorbed in the font
+ * hints for ClearType (mostly in the white space of hints); see
+ * http://www.beatstamm.com/typography/RTRCh4.htm#Sec20. Somewhat by
+ * definition, compatible width ClearType allows for non-linear widths,
+ * but only when the bi-level version has non-linear widths.
+ *
+ * _ClearType_ _Subpixel_ _Positioning_
+ *
+ * One of the nice benefits of ClearType is the ability to more crisply
+ * display fractional widths; unfortunately, the GDI model of integer
+ * bitmaps did not support this. However, the WPF and Direct Write
+ * frameworks do support fractional widths. DWrite calls this `natural
+ * mode', not to be confused with GDI's `natural widths'. Subpixel
+ * positioning, in the current implementation of Direct Write,
+ * unfortunately does not support hinted advance widths, see
+ * http://www.beatstamm.com/typography/RTRCh4.htm#Sec22. Note that the
+ * TrueType interpreter fully allows the advance width to be adjusted in
+ * this mode, just the DWrite client will ignore those changes.
+ *
+ * _ClearType_ _Backward_ _Compatibility_
+ *
+ * This is a set of exceptions made in the TrueType interpreter to
+ * minimize hinting techniques that were problematic with the extra
+ * resolution of ClearType; see
+ * http://www.beatstamm.com/typography/RTRCh4.htm#Sec1 and
+ * https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx.
+ * This technique is not to be confused with ClearType compatible
+ * widths. ClearType backward compatibility has no direct impact on
+ * changing advance widths, but there might be an indirect impact on
+ * disabling some deltas. This could be worked around in backward
+ * compatibility mode.
+ *
+ * _Native_ _ClearType_ _Mode_
+ *
+ * (Not to be confused with `natural widths'.) This mode removes all
+ * the exceptions in the TrueType interpreter when running with
+ * ClearType. Any issues on widths would still apply, though.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * properties
+ *
+ * @title:
+ * Driver properties
+ *
+ * @abstract:
+ * Controlling driver modules.
+ *
+ * @description:
+ * Driver modules can be controlled by setting and unsetting properties,
+ * using the functions @FT_Property_Set and @FT_Property_Get. This
+ * section documents the available properties, together with auxiliary
+ * macros and structures.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @enum:
+ * FT_HINTING_XXX
+ *
+ * @description:
+ * A list of constants used for the @hinting-engine property to
+ * select the hinting engine for CFF, Type~1, and CID fonts.
+ *
+ * @values:
+ * FT_HINTING_FREETYPE ::
+ * Use the old FreeType hinting engine.
+ *
+ * FT_HINTING_ADOBE ::
+ * Use the hinting engine contributed by Adobe.
+ *
+ * @since:
+ * 2.9
+ *
+ */
+#define FT_HINTING_FREETYPE 0
+#define FT_HINTING_ADOBE 1
+
+ /* these constants (introduced in 2.4.12) are deprecated */
+#define FT_CFF_HINTING_FREETYPE FT_HINTING_FREETYPE
+#define FT_CFF_HINTING_ADOBE FT_HINTING_ADOBE
+
+
+ /**************************************************************************
+ *
+ * @property:
+ * hinting-engine
+ *
+ * @description:
+ * Thanks to Adobe, which contributed a new hinting (and parsing)
+ * engine, an application can select between `freetype' and `adobe' if
+ * compiled with CFF_CONFIG_OPTION_OLD_ENGINE. If this configuration
+ * macro isn't defined, `hinting-engine' does nothing.
+ *
+ * The same holds for the Type~1 and CID modules if compiled with
+ * T1_CONFIG_OPTION_OLD_ENGINE.
+ *
+ * For the `cff' module, the default engine is `freetype' if
+ * CFF_CONFIG_OPTION_OLD_ENGINE is defined, and `adobe' otherwise.
+ *
+ * For both the `type1' and `t1cid' modules, the default engine is
+ * `freetype' if T1_CONFIG_OPTION_OLD_ENGINE is defined, and `adobe'
+ * otherwise.
+ *
+ * The following example code demonstrates how to select Adobe's hinting
+ * engine for the `cff' module (omitting the error handling).
+ *
+ * {
+ * FT_Library library;
+ * FT_UInt hinting_engine = FT_CFF_HINTING_ADOBE;
+ *
+ *
+ * FT_Init_FreeType( &library );
+ *
+ * FT_Property_Set( library, "cff",
+ * "hinting-engine", &hinting_engine );
+ * }
+ *
+ * @note:
+ * This property can be used with @FT_Property_Get also.
+ *
+ * This property can be set via the `FREETYPE_PROPERTIES' environment
+ * variable (using values `adobe' or `freetype').
+ *
+ * @since:
+ * 2.4.12 (for `cff' module)
+ *
+ * 2.9 (for `type1' and `t1cid' modules)
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @property:
+ * no-stem-darkening
+ *
+ * @description:
+ * All glyphs that pass through the auto-hinter will be emboldened
+ * unless this property is set to TRUE. The same is true for the CFF,
+ * Type~1, and CID font modules if the `Adobe' engine is selected (which
+ * is the default).
+ *
+ * Stem darkening emboldens glyphs at smaller sizes to make them more
+ * readable on common low-DPI screens when using linear alpha blending
+ * and gamma correction, see @FT_Render_Glyph. When not using linear
+ * alpha blending and gamma correction, glyphs will appear heavy and
+ * fuzzy!
+ *
+ * Gamma correction essentially lightens fonts since shades of grey are
+ * shifted to higher pixel values (=~higher brightness) to match the
+ * original intention to the reality of our screens. The side-effect is
+ * that glyphs `thin out'. Mac OS~X and Adobe's proprietary font
+ * rendering library implement a counter-measure: stem darkening at
+ * smaller sizes where shades of gray dominate. By emboldening a glyph
+ * slightly in relation to its pixel size, individual pixels get higher
+ * coverage of filled-in outlines and are therefore `blacker'. This
+ * counteracts the `thinning out' of glyphs, making text remain readable
+ * at smaller sizes.
+ *
+ * By default, the Adobe engines for CFF, Type~1, and CID fonts darken
+ * stems at smaller sizes, regardless of hinting, to enhance contrast.
+ * Setting this property, stem darkening gets switched off.
+ *
+ * For the auto-hinter, stem-darkening is experimental currently and
+ * thus switched off by default (this is, `no-stem-darkening' is set to
+ * TRUE by default). Total consistency with the CFF driver is not
+ * achieved right now because the emboldening method differs and glyphs
+ * must be scaled down on the Y-axis to keep outline points inside their
+ * precomputed blue zones. The smaller the size (especially 9ppem and
+ * down), the higher the loss of emboldening versus the CFF driver.
+ *
+ * Note that stem darkening is never applied if @FT_LOAD_NO_SCALE is
+ * set.
+ *
+ * {
+ * FT_Library library;
+ * FT_Bool no_stem_darkening = TRUE;
+ *
+ *
+ * FT_Init_FreeType( &library );
+ *
+ * FT_Property_Set( library, "cff",
+ * "no-stem-darkening", &no_stem_darkening );
+ * }
+ *
+ * @note:
+ * This property can be used with @FT_Property_Get also.
+ *
+ * This property can be set via the `FREETYPE_PROPERTIES' environment
+ * variable (using values 1 and 0 for `on' and `off', respectively).
+ * It can also be set per face using @FT_Face_Properties with
+ * @FT_PARAM_TAG_STEM_DARKENING.
+ *
+ * @since:
+ * 2.4.12 (for `cff' module)
+ *
+ * 2.6.2 (for `autofitter' module)
+ *
+ * 2.9 (for `type1' and `t1cid' modules)
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @property:
+ * darkening-parameters
+ *
+ * @description:
+ * By default, the Adobe hinting engine, as used by the CFF, Type~1, and
+ * CID font drivers, darkens stems as follows (if the
+ * `no-stem-darkening' property isn't set):
+ *
+ * {
+ * stem width <= 0.5px: darkening amount = 0.4px
+ * stem width = 1px: darkening amount = 0.275px
+ * stem width = 1.667px: darkening amount = 0.275px
+ * stem width >= 2.333px: darkening amount = 0px
+ * }
+ *
+ * and piecewise linear in-between. At configuration time, these four
+ * control points can be set with the macro
+ * `CFF_CONFIG_OPTION_DARKENING_PARAMETERS'; the CFF, Type~1, and CID
+ * drivers share these values. At runtime, the control points can be
+ * changed using the `darkening-parameters' property, as the following
+ * example demonstrates for the Type~1 driver.
+ *
+ * {
+ * FT_Library library;
+ * FT_Int darken_params[8] = { 500, 300, // x1, y1
+ * 1000, 200, // x2, y2
+ * 1500, 100, // x3, y3
+ * 2000, 0 }; // x4, y4
+ *
+ *
+ * FT_Init_FreeType( &library );
+ *
+ * FT_Property_Set( library, "type1",
+ * "darkening-parameters", darken_params );
+ * }
+ *
+ * The x~values give the stem width, and the y~values the darkening
+ * amount. The unit is 1000th of pixels. All coordinate values must be
+ * positive; the x~values must be monotonically increasing; the
+ * y~values must be monotonically decreasing and smaller than or
+ * equal to 500 (corresponding to half a pixel); the slope of each
+ * linear piece must be shallower than -1 (e.g., -.4).
+ *
+ * The auto-hinter provides this property, too, as an experimental
+ * feature. See @no-stem-darkening for more.
+ *
+ * @note:
+ * This property can be used with @FT_Property_Get also.
+ *
+ * This property can be set via the `FREETYPE_PROPERTIES' environment
+ * variable, using eight comma-separated integers without spaces. Here
+ * the above example, using `\' to break the line for readability.
+ *
+ * {
+ * FREETYPE_PROPERTIES=\
+ * type1:darkening-parameters=500,300,1000,200,1500,100,2000,0
+ * }
+ *
+ * @since:
+ * 2.5.1 (for `cff' module)
+ *
+ * 2.6.2 (for `autofitter' module)
+ *
+ * 2.9 (for `type1' and `t1cid' modules)
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @property:
+ * random-seed
+ *
+ * @description:
+ * By default, the seed value for the CFF `random' operator and the
+ * similar `0 28 callothersubr pop' command for the Type~1 and CID
+ * drivers is set to a random value. However, mainly for debugging
+ * purposes, it is often necessary to use a known value as a seed so
+ * that the pseudo-random number sequences generated by `random' are
+ * repeatable.
+ *
+ * The `random-seed' property does that. Its argument is a signed 32bit
+ * integer; if the value is zero or negative, the seed given by the
+ * `intitialRandomSeed' private DICT operator in a CFF file gets used
+ * (or a default value if there is no such operator). If the value is
+ * positive, use it instead of `initialRandomSeed', which is
+ * consequently ignored.
+ *
+ * @note:
+ * This property can be set via the `FREETYPE_PROPERTIES' environment
+ * variable. It can also be set per face using @FT_Face_Properties with
+ * @FT_PARAM_TAG_RANDOM_SEED.
+ *
+ * @since:
+ * 2.8 (for `cff' module)
+ *
+ * 2.9 (for `type1' and `t1cid' modules)
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @property:
+ * no-long-family-names
+ *
+ * @description:
+ * If PCF_CONFIG_OPTION_LONG_FAMILY_NAMES is active while compiling
+ * FreeType, the PCF driver constructs long family names.
+ *
+ * There are many PCF fonts just called `Fixed' which look completely
+ * different, and which have nothing to do with each other. When
+ * selecting `Fixed' in KDE or Gnome one gets results that appear rather
+ * random, the style changes often if one changes the size and one
+ * cannot select some fonts at all. The improve this situation, the PCF
+ * module prepends the foundry name (plus a space) to the family name.
+ * It also checks whether there are `wide' characters; all put together,
+ * family names like `Sony Fixed' or `Misc Fixed Wide' are constructed.
+ *
+ * If `no-long-family-names' is set, this feature gets switched off.
+ *
+ * {
+ * FT_Library library;
+ * FT_Bool no_long_family_names = TRUE;
+ *
+ *
+ * FT_Init_FreeType( &library );
+ *
+ * FT_Property_Set( library, "pcf",
+ * "no-long-family-names",
+ * &no_long_family_names );
+ * }
+ *
+ * @note:
+ * This property can be used with @FT_Property_Get also.
+ *
+ * This property can be set via the `FREETYPE_PROPERTIES' environment
+ * variable (using values 1 and 0 for `on' and `off', respectively).
+ *
+ * @since:
+ * 2.8
+ */
+
+
+ /**************************************************************************
+ *
+ * @enum:
+ * TT_INTERPRETER_VERSION_XXX
+ *
+ * @description:
+ * A list of constants used for the @interpreter-version property to
+ * select the hinting engine for Truetype fonts.
+ *
+ * The numeric value in the constant names represents the version
+ * number as returned by the `GETINFO' bytecode instruction.
+ *
+ * @values:
+ * TT_INTERPRETER_VERSION_35 ::
+ * Version~35 corresponds to MS rasterizer v.1.7 as used e.g. in
+ * Windows~98; only grayscale and B/W rasterizing is supported.
+ *
+ * TT_INTERPRETER_VERSION_38 ::
+ * Version~38 corresponds to MS rasterizer v.1.9; it is roughly
+ * equivalent to the hinting provided by DirectWrite ClearType (as can
+ * be found, for example, in the Internet Explorer~9 running on
+ * Windows~7). It is used in FreeType to select the `Infinality'
+ * subpixel hinting code. The code may be removed in a future
+ * version.
+ *
+ * TT_INTERPRETER_VERSION_40 ::
+ * Version~40 corresponds to MS rasterizer v.2.1; it is roughly
+ * equivalent to the hinting provided by DirectWrite ClearType (as can
+ * be found, for example, in Microsoft's Edge Browser on Windows~10).
+ * It is used in FreeType to select the `minimal' subpixel hinting
+ * code, a stripped-down and higher performance version of the
+ * `Infinality' code.
+ *
+ * @note:
+ * This property controls the behaviour of the bytecode interpreter
+ * and thus how outlines get hinted. It does *not* control how glyph
+ * get rasterized! In particular, it does not control subpixel color
+ * filtering.
+ *
+ * If FreeType has not been compiled with the configuration option
+ * TT_CONFIG_OPTION_SUBPIXEL_HINTING, selecting version~38 or~40 causes
+ * an `FT_Err_Unimplemented_Feature' error.
+ *
+ * Depending on the graphics framework, Microsoft uses different
+ * bytecode and rendering engines. As a consequence, the version
+ * numbers returned by a call to the `GETINFO' bytecode instruction are
+ * more convoluted than desired.
+ *
+ * Here are two tables that try to shed some light on the possible
+ * values for the MS rasterizer engine, together with the additional
+ * features introduced by it.
+ *
+ * {
+ * GETINFO framework version feature
+ * -------------------------------------------------------------------
+ * 3 GDI (Win 3.1), v1.0 16-bit, first version
+ * TrueImage
+ * 33 GDI (Win NT 3.1), v1.5 32-bit
+ * HP Laserjet
+ * 34 GDI (Win 95) v1.6 font smoothing,
+ * new SCANTYPE opcode
+ * 35 GDI (Win 98/2000) v1.7 (UN)SCALED_COMPONENT_OFFSET
+ * bits in composite glyphs
+ * 36 MGDI (Win CE 2) v1.6+ classic ClearType
+ * 37 GDI (XP and later), v1.8 ClearType
+ * GDI+ old (before Vista)
+ * 38 GDI+ old (Vista, Win 7), v1.9 subpixel ClearType,
+ * WPF Y-direction ClearType,
+ * additional error checking
+ * 39 DWrite (before Win 8) v2.0 subpixel ClearType flags
+ * in GETINFO opcode,
+ * bug fixes
+ * 40 GDI+ (after Win 7), v2.1 Y-direction ClearType flag
+ * DWrite (Win 8) in GETINFO opcode,
+ * Gray ClearType
+ * }
+ *
+ * The `version' field gives a rough orientation only, since some
+ * applications provided certain features much earlier (as an example,
+ * Microsoft Reader used subpixel and Y-direction ClearType already in
+ * Windows 2000). Similarly, updates to a given framework might include
+ * improved hinting support.
+ *
+ * {
+ * version sampling rendering comment
+ * x y x y
+ * --------------------------------------------------------------
+ * v1.0 normal normal B/W B/W bi-level
+ * v1.6 high high gray gray grayscale
+ * v1.8 high normal color-filter B/W (GDI) ClearType
+ * v1.9 high high color-filter gray Color ClearType
+ * v2.1 high normal gray B/W Gray ClearType
+ * v2.1 high high gray gray Gray ClearType
+ * }
+ *
+ * Color and Gray ClearType are the two available variants of
+ * `Y-direction ClearType', meaning grayscale rasterization along the
+ * Y-direction; the name used in the TrueType specification for this
+ * feature is `symmetric smoothing'. `Classic ClearType' is the
+ * original algorithm used before introducing a modified version in
+ * Win~XP. Another name for v1.6's grayscale rendering is `font
+ * smoothing', and `Color ClearType' is sometimes also called `DWrite
+ * ClearType'. To differentiate between today's Color ClearType and the
+ * earlier ClearType variant with B/W rendering along the vertical axis,
+ * the latter is sometimes called `GDI ClearType'.
+ *
+ * `Normal' and `high' sampling describe the (virtual) resolution to
+ * access the rasterized outline after the hinting process. `Normal'
+ * means 1 sample per grid line (i.e., B/W). In the current Microsoft
+ * implementation, `high' means an extra virtual resolution of 16x16 (or
+ * 16x1) grid lines per pixel for bytecode instructions like `MIRP'.
+ * After hinting, these 16 grid lines are mapped to 6x5 (or 6x1) grid
+ * lines for color filtering if Color ClearType is activated.
+ *
+ * Note that `Gray ClearType' is essentially the same as v1.6's
+ * grayscale rendering. However, the GETINFO instruction handles it
+ * differently: v1.6 returns bit~12 (hinting for grayscale), while v2.1
+ * returns bits~13 (hinting for ClearType), 18 (symmetrical smoothing),
+ * and~19 (Gray ClearType). Also, this mode respects bits 2 and~3 for
+ * the version~1 gasp table exclusively (like Color ClearType), while
+ * v1.6 only respects the values of version~0 (bits 0 and~1).
+ *
+ * Keep in mind that the features of the above interpreter versions
+ * might not map exactly to FreeType features or behavior because it is
+ * a fundamentally different library with different internals.
+ *
+ */
+#define TT_INTERPRETER_VERSION_35 35
+#define TT_INTERPRETER_VERSION_38 38
+#define TT_INTERPRETER_VERSION_40 40
+
+
+ /**************************************************************************
+ *
+ * @property:
+ * interpreter-version
+ *
+ * @description:
+ * Currently, three versions are available, two representing the
+ * bytecode interpreter with subpixel hinting support (old `Infinality'
+ * code and new stripped-down and higher performance `minimal' code) and
+ * one without, respectively. The default is subpixel support if
+ * TT_CONFIG_OPTION_SUBPIXEL_HINTING is defined, and no subpixel support
+ * otherwise (since it isn't available then).
+ *
+ * If subpixel hinting is on, many TrueType bytecode instructions behave
+ * differently compared to B/W or grayscale rendering (except if `native
+ * ClearType' is selected by the font). Microsoft's main idea is to
+ * render at a much increased horizontal resolution, then sampling down
+ * the created output to subpixel precision. However, many older fonts
+ * are not suited to this and must be specially taken care of by
+ * applying (hardcoded) tweaks in Microsoft's interpreter.
+ *
+ * Details on subpixel hinting and some of the necessary tweaks can be
+ * found in Greg Hitchcock's whitepaper at
+ * `https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx'.
+ * Note that FreeType currently doesn't really `subpixel hint' (6x1, 6x2,
+ * or 6x5 supersampling) like discussed in the paper. Depending on the
+ * chosen interpreter, it simply ignores instructions on vertical stems
+ * to arrive at very similar results.
+ *
+ * The following example code demonstrates how to deactivate subpixel
+ * hinting (omitting the error handling).
+ *
+ * {
+ * FT_Library library;
+ * FT_Face face;
+ * FT_UInt interpreter_version = TT_INTERPRETER_VERSION_35;
+ *
+ *
+ * FT_Init_FreeType( &library );
+ *
+ * FT_Property_Set( library, "truetype",
+ * "interpreter-version",
+ * &interpreter_version );
+ * }
+ *
+ * @note:
+ * This property can be used with @FT_Property_Get also.
+ *
+ * This property can be set via the `FREETYPE_PROPERTIES' environment
+ * variable (using values `35', `38', or `40').
+ *
+ * @since:
+ * 2.5
+ */
+
+
+ /**************************************************************************
+ *
+ * @property:
+ * glyph-to-script-map
+ *
+ * @description:
+ * *Experimental* *only*
+ *
+ * The auto-hinter provides various script modules to hint glyphs.
+ * Examples of supported scripts are Latin or CJK. Before a glyph is
+ * auto-hinted, the Unicode character map of the font gets examined, and
+ * the script is then determined based on Unicode character ranges, see
+ * below.
+ *
+ * OpenType fonts, however, often provide much more glyphs than
+ * character codes (small caps, superscripts, ligatures, swashes, etc.),
+ * to be controlled by so-called `features'. Handling OpenType features
+ * can be quite complicated and thus needs a separate library on top of
+ * FreeType.
+ *
+ * The mapping between glyph indices and scripts (in the auto-hinter
+ * sense, see the @FT_AUTOHINTER_SCRIPT_XXX values) is stored as an
+ * array with `num_glyphs' elements, as found in the font's @FT_Face
+ * structure. The `glyph-to-script-map' property returns a pointer to
+ * this array, which can be modified as needed. Note that the
+ * modification should happen before the first glyph gets processed by
+ * the auto-hinter so that the global analysis of the font shapes
+ * actually uses the modified mapping.
+ *
+ * The following example code demonstrates how to access it (omitting
+ * the error handling).
+ *
+ * {
+ * FT_Library library;
+ * FT_Face face;
+ * FT_Prop_GlyphToScriptMap prop;
+ *
+ *
+ * FT_Init_FreeType( &library );
+ * FT_New_Face( library, "foo.ttf", 0, &face );
+ *
+ * prop.face = face;
+ *
+ * FT_Property_Get( library, "autofitter",
+ * "glyph-to-script-map", &prop );
+ *
+ * // adjust `prop.map' as needed right here
+ *
+ * FT_Load_Glyph( face, ..., FT_LOAD_FORCE_AUTOHINT );
+ * }
+ *
+ * @since:
+ * 2.4.11
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @enum:
+ * FT_AUTOHINTER_SCRIPT_XXX
+ *
+ * @description:
+ * *Experimental* *only*
+ *
+ * A list of constants used for the @glyph-to-script-map property to
+ * specify the script submodule the auto-hinter should use for hinting a
+ * particular glyph.
+ *
+ * @values:
+ * FT_AUTOHINTER_SCRIPT_NONE ::
+ * Don't auto-hint this glyph.
+ *
+ * FT_AUTOHINTER_SCRIPT_LATIN ::
+ * Apply the latin auto-hinter. For the auto-hinter, `latin' is a
+ * very broad term, including Cyrillic and Greek also since characters
+ * from those scripts share the same design constraints.
+ *
+ * By default, characters from the following Unicode ranges are
+ * assigned to this submodule.
+ *
+ * {
+ * U+0020 - U+007F // Basic Latin (no control characters)
+ * U+00A0 - U+00FF // Latin-1 Supplement (no control characters)
+ * U+0100 - U+017F // Latin Extended-A
+ * U+0180 - U+024F // Latin Extended-B
+ * U+0250 - U+02AF // IPA Extensions
+ * U+02B0 - U+02FF // Spacing Modifier Letters
+ * U+0300 - U+036F // Combining Diacritical Marks
+ * U+0370 - U+03FF // Greek and Coptic
+ * U+0400 - U+04FF // Cyrillic
+ * U+0500 - U+052F // Cyrillic Supplement
+ * U+1D00 - U+1D7F // Phonetic Extensions
+ * U+1D80 - U+1DBF // Phonetic Extensions Supplement
+ * U+1DC0 - U+1DFF // Combining Diacritical Marks Supplement
+ * U+1E00 - U+1EFF // Latin Extended Additional
+ * U+1F00 - U+1FFF // Greek Extended
+ * U+2000 - U+206F // General Punctuation
+ * U+2070 - U+209F // Superscripts and Subscripts
+ * U+20A0 - U+20CF // Currency Symbols
+ * U+2150 - U+218F // Number Forms
+ * U+2460 - U+24FF // Enclosed Alphanumerics
+ * U+2C60 - U+2C7F // Latin Extended-C
+ * U+2DE0 - U+2DFF // Cyrillic Extended-A
+ * U+2E00 - U+2E7F // Supplemental Punctuation
+ * U+A640 - U+A69F // Cyrillic Extended-B
+ * U+A720 - U+A7FF // Latin Extended-D
+ * U+FB00 - U+FB06 // Alphab. Present. Forms (Latin Ligatures)
+ * U+1D400 - U+1D7FF // Mathematical Alphanumeric Symbols
+ * U+1F100 - U+1F1FF // Enclosed Alphanumeric Supplement
+ * }
+ *
+ * FT_AUTOHINTER_SCRIPT_CJK ::
+ * Apply the CJK auto-hinter, covering Chinese, Japanese, Korean, old
+ * Vietnamese, and some other scripts.
+ *
+ * By default, characters from the following Unicode ranges are
+ * assigned to this submodule.
+ *
+ * {
+ * U+1100 - U+11FF // Hangul Jamo
+ * U+2E80 - U+2EFF // CJK Radicals Supplement
+ * U+2F00 - U+2FDF // Kangxi Radicals
+ * U+2FF0 - U+2FFF // Ideographic Description Characters
+ * U+3000 - U+303F // CJK Symbols and Punctuation
+ * U+3040 - U+309F // Hiragana
+ * U+30A0 - U+30FF // Katakana
+ * U+3100 - U+312F // Bopomofo
+ * U+3130 - U+318F // Hangul Compatibility Jamo
+ * U+3190 - U+319F // Kanbun
+ * U+31A0 - U+31BF // Bopomofo Extended
+ * U+31C0 - U+31EF // CJK Strokes
+ * U+31F0 - U+31FF // Katakana Phonetic Extensions
+ * U+3200 - U+32FF // Enclosed CJK Letters and Months
+ * U+3300 - U+33FF // CJK Compatibility
+ * U+3400 - U+4DBF // CJK Unified Ideographs Extension A
+ * U+4DC0 - U+4DFF // Yijing Hexagram Symbols
+ * U+4E00 - U+9FFF // CJK Unified Ideographs
+ * U+A960 - U+A97F // Hangul Jamo Extended-A
+ * U+AC00 - U+D7AF // Hangul Syllables
+ * U+D7B0 - U+D7FF // Hangul Jamo Extended-B
+ * U+F900 - U+FAFF // CJK Compatibility Ideographs
+ * U+FE10 - U+FE1F // Vertical forms
+ * U+FE30 - U+FE4F // CJK Compatibility Forms
+ * U+FF00 - U+FFEF // Halfwidth and Fullwidth Forms
+ * U+1B000 - U+1B0FF // Kana Supplement
+ * U+1D300 - U+1D35F // Tai Xuan Hing Symbols
+ * U+1F200 - U+1F2FF // Enclosed Ideographic Supplement
+ * U+20000 - U+2A6DF // CJK Unified Ideographs Extension B
+ * U+2A700 - U+2B73F // CJK Unified Ideographs Extension C
+ * U+2B740 - U+2B81F // CJK Unified Ideographs Extension D
+ * U+2F800 - U+2FA1F // CJK Compatibility Ideographs Supplement
+ * }
+ *
+ * FT_AUTOHINTER_SCRIPT_INDIC ::
+ * Apply the indic auto-hinter, covering all major scripts from the
+ * Indian sub-continent and some other related scripts like Thai, Lao,
+ * or Tibetan.
+ *
+ * By default, characters from the following Unicode ranges are
+ * assigned to this submodule.
+ *
+ * {
+ * U+0900 - U+0DFF // Indic Range
+ * U+0F00 - U+0FFF // Tibetan
+ * U+1900 - U+194F // Limbu
+ * U+1B80 - U+1BBF // Sundanese
+ * U+A800 - U+A82F // Syloti Nagri
+ * U+ABC0 - U+ABFF // Meetei Mayek
+ * U+11800 - U+118DF // Sharada
+ * }
+ *
+ * Note that currently Indic support is rudimentary only, missing blue
+ * zone support.
+ *
+ * @since:
+ * 2.4.11
+ *
+ */
+#define FT_AUTOHINTER_SCRIPT_NONE 0
+#define FT_AUTOHINTER_SCRIPT_LATIN 1
+#define FT_AUTOHINTER_SCRIPT_CJK 2
+#define FT_AUTOHINTER_SCRIPT_INDIC 3
+
+
+ /**************************************************************************
+ *
+ * @struct:
+ * FT_Prop_GlyphToScriptMap
+ *
+ * @description:
+ * *Experimental* *only*
+ *
+ * The data exchange structure for the @glyph-to-script-map property.
+ *
+ * @since:
+ * 2.4.11
+ *
+ */
+ typedef struct FT_Prop_GlyphToScriptMap_
+ {
+ FT_Face face;
+ FT_UShort* map;
+
+ } FT_Prop_GlyphToScriptMap;
+
+
+ /**************************************************************************
+ *
+ * @property:
+ * fallback-script
+ *
+ * @description:
+ * *Experimental* *only*
+ *
+ * If no auto-hinter script module can be assigned to a glyph, a
+ * fallback script gets assigned to it (see also the
+ * @glyph-to-script-map property). By default, this is
+ * @FT_AUTOHINTER_SCRIPT_CJK. Using the `fallback-script' property,
+ * this fallback value can be changed.
+ *
+ * {
+ * FT_Library library;
+ * FT_UInt fallback_script = FT_AUTOHINTER_SCRIPT_NONE;
+ *
+ *
+ * FT_Init_FreeType( &library );
+ *
+ * FT_Property_Set( library, "autofitter",
+ * "fallback-script", &fallback_script );
+ * }
+ *
+ * @note:
+ * This property can be used with @FT_Property_Get also.
+ *
+ * It's important to use the right timing for changing this value: The
+ * creation of the glyph-to-script map that eventually uses the
+ * fallback script value gets triggered either by setting or reading a
+ * face-specific property like @glyph-to-script-map, or by auto-hinting
+ * any glyph from that face. In particular, if you have already created
+ * an @FT_Face structure but not loaded any glyph (using the
+ * auto-hinter), a change of the fallback script will affect this face.
+ *
+ * @since:
+ * 2.4.11
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @property:
+ * default-script
+ *
+ * @description:
+ * *Experimental* *only*
+ *
+ * If FreeType gets compiled with FT_CONFIG_OPTION_USE_HARFBUZZ to make
+ * the HarfBuzz library access OpenType features for getting better
+ * glyph coverages, this property sets the (auto-fitter) script to be
+ * used for the default (OpenType) script data of a font's GSUB table.
+ * Features for the default script are intended for all scripts not
+ * explicitly handled in GSUB; an example is a `dlig' feature,
+ * containing the combination of the characters `T', `E', and `L' to
+ * form a `TEL' ligature.
+ *
+ * By default, this is @FT_AUTOHINTER_SCRIPT_LATIN. Using the
+ * `default-script' property, this default value can be changed.
+ *
+ * {
+ * FT_Library library;
+ * FT_UInt default_script = FT_AUTOHINTER_SCRIPT_NONE;
+ *
+ *
+ * FT_Init_FreeType( &library );
+ *
+ * FT_Property_Set( library, "autofitter",
+ * "default-script", &default_script );
+ * }
+ *
+ * @note:
+ * This property can be used with @FT_Property_Get also.
+ *
+ * It's important to use the right timing for changing this value: The
+ * creation of the glyph-to-script map that eventually uses the
+ * default script value gets triggered either by setting or reading a
+ * face-specific property like @glyph-to-script-map, or by auto-hinting
+ * any glyph from that face. In particular, if you have already created
+ * an @FT_Face structure but not loaded any glyph (using the
+ * auto-hinter), a change of the default script will affect this face.
+ *
+ * @since:
+ * 2.5.3
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @property:
+ * increase-x-height
+ *
+ * @description:
+ * For ppem values in the range 6~<= ppem <= `increase-x-height', round
+ * up the font's x~height much more often than normally. If the value
+ * is set to~0, which is the default, this feature is switched off. Use
+ * this property to improve the legibility of small font sizes if
+ * necessary.
+ *
+ * {
+ * FT_Library library;
+ * FT_Face face;
+ * FT_Prop_IncreaseXHeight prop;
+ *
+ *
+ * FT_Init_FreeType( &library );
+ * FT_New_Face( library, "foo.ttf", 0, &face );
+ * FT_Set_Char_Size( face, 10 * 64, 0, 72, 0 );
+ *
+ * prop.face = face;
+ * prop.limit = 14;
+ *
+ * FT_Property_Set( library, "autofitter",
+ * "increase-x-height", &prop );
+ * }
+ *
+ * @note:
+ * This property can be used with @FT_Property_Get also.
+ *
+ * Set this value right after calling @FT_Set_Char_Size, but before
+ * loading any glyph (using the auto-hinter).
+ *
+ * @since:
+ * 2.4.11
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @struct:
+ * FT_Prop_IncreaseXHeight
+ *
+ * @description:
+ * The data exchange structure for the @increase-x-height property.
+ *
+ */
+ typedef struct FT_Prop_IncreaseXHeight_
+ {
+ FT_Face face;
+ FT_UInt limit;
+
+ } FT_Prop_IncreaseXHeight;
+
+
+ /**************************************************************************
+ *
+ * @property:
+ * warping
+ *
+ * @description:
+ * *Experimental* *only*
+ *
+ * If FreeType gets compiled with option AF_CONFIG_OPTION_USE_WARPER to
+ * activate the warp hinting code in the auto-hinter, this property
+ * switches warping on and off.
+ *
+ * Warping only works in `normal' auto-hinting mode replacing it.
+ * The idea of the code is to slightly scale and shift a glyph along
+ * the non-hinted dimension (which is usually the horizontal axis) so
+ * that as much of its segments are aligned (more or less) to the grid.
+ * To find out a glyph's optimal scaling and shifting value, various
+ * parameter combinations are tried and scored.
+ *
+ * By default, warping is off. The example below shows how to switch on
+ * warping (omitting the error handling).
+ *
+ * {
+ * FT_Library library;
+ * FT_Bool warping = 1;
+ *
+ *
+ * FT_Init_FreeType( &library );
+ *
+ * FT_Property_Set( library, "autofitter",
+ * "warping", &warping );
+ * }
+ *
+ * @note:
+ * This property can be used with @FT_Property_Get also.
+ *
+ * This property can be set via the `FREETYPE_PROPERTIES' environment
+ * variable (using values 1 and 0 for `on' and `off', respectively).
+ *
+ * The warping code can also change advance widths. Have a look at the
+ * `lsb_delta' and `rsb_delta' fields in the @FT_GlyphSlotRec structure
+ * for details on improving inter-glyph distances while rendering.
+ *
+ * Since warping is a global property of the auto-hinter it is best to
+ * change its value before rendering any face. Otherwise, you should
+ * reload all faces that get auto-hinted in `normal' hinting mode.
+ *
+ * @since:
+ * 2.6
+ *
+ */
+
+
+ /* */
+
+
+FT_END_HEADER
+
+
+#endif /* FTDRIVER_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/include/freetype/fterrdef.h b/thirdparty/freetype/include/freetype/fterrdef.h
index 6a6dc85b87..8ffd346ca8 100644
--- a/thirdparty/freetype/include/freetype/fterrdef.h
+++ b/thirdparty/freetype/include/freetype/fterrdef.h
@@ -4,7 +4,7 @@
/* */
/* FreeType error codes (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/fterrors.h b/thirdparty/freetype/include/freetype/fterrors.h
index ae382c419f..f6ee5c24e2 100644
--- a/thirdparty/freetype/include/freetype/fterrors.h
+++ b/thirdparty/freetype/include/freetype/fterrors.h
@@ -4,7 +4,7 @@
/* */
/* FreeType error code handling (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftfntfmt.h b/thirdparty/freetype/include/freetype/ftfntfmt.h
index 337758328a..cc86efac23 100644
--- a/thirdparty/freetype/include/freetype/ftfntfmt.h
+++ b/thirdparty/freetype/include/freetype/ftfntfmt.h
@@ -4,7 +4,7 @@
/* */
/* Support functions for font formats. */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftgasp.h b/thirdparty/freetype/include/freetype/ftgasp.h
index ce18d64784..fc1248ff48 100644
--- a/thirdparty/freetype/include/freetype/ftgasp.h
+++ b/thirdparty/freetype/include/freetype/ftgasp.h
@@ -4,7 +4,7 @@
/* */
/* Access of TrueType's `gasp' table (specification). */
/* */
-/* Copyright 2007-2017 by */
+/* Copyright 2007-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -29,6 +29,9 @@
#endif
+FT_BEGIN_HEADER
+
+
/***************************************************************************
*
* @section:
@@ -131,6 +134,8 @@
/* */
+FT_END_HEADER
+
#endif /* FTGASP_H_ */
diff --git a/thirdparty/freetype/include/freetype/ftglyph.h b/thirdparty/freetype/include/freetype/ftglyph.h
index db1a2c8ba5..5f3fc009cd 100644
--- a/thirdparty/freetype/include/freetype/ftglyph.h
+++ b/thirdparty/freetype/include/freetype/ftglyph.h
@@ -4,7 +4,7 @@
/* */
/* FreeType convenience functions to handle glyphs (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -347,10 +347,10 @@ FT_BEGIN_HEADER
/* */
/* <Description> */
/* Return a glyph's `control box'. The control box encloses all the */
- /* outline's points, including Bézier control points. Though it */
+ /* outline's points, including Bezier control points. Though it */
/* coincides with the exact bounding box for most glyphs, it can be */
/* slightly larger in some situations (like when rotating an outline */
- /* that contains Bézier outside arcs). */
+ /* that contains Bezier outside arcs). */
/* */
/* Computing the control box is very fast, while getting the bounding */
/* box can take much more time as it needs to walk over all segments */
diff --git a/thirdparty/freetype/include/freetype/ftgxval.h b/thirdparty/freetype/include/freetype/ftgxval.h
index f239c71eb1..8382d59954 100644
--- a/thirdparty/freetype/include/freetype/ftgxval.h
+++ b/thirdparty/freetype/include/freetype/ftgxval.h
@@ -4,7 +4,7 @@
/* */
/* FreeType API for validating TrueTypeGX/AAT tables (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* Masatake YAMATO, Redhat K.K, */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
@@ -101,15 +101,15 @@ FT_BEGIN_HEADER
* The number of tables checked in this module. Use it as a parameter
* for the `table-length' argument of function @FT_TrueTypeGX_Validate.
*/
-#define FT_VALIDATE_GX_LENGTH (FT_VALIDATE_GX_LAST_INDEX + 1)
+#define FT_VALIDATE_GX_LENGTH ( FT_VALIDATE_GX_LAST_INDEX + 1 )
/* */
/* Up to 0x1000 is used by otvalid.
Ox2xxx is reserved for feature OT extension. */
-#define FT_VALIDATE_GX_START 0x4000
-#define FT_VALIDATE_GX_BITFIELD( tag ) \
- ( FT_VALIDATE_GX_START << FT_VALIDATE_##tag##_INDEX )
+#define FT_VALIDATE_GX_START 0x4000
+#define FT_VALIDATE_GX_BITFIELD( tag ) \
+ ( FT_VALIDATE_GX_START << FT_VALIDATE_##tag##_INDEX )
/**********************************************************************
diff --git a/thirdparty/freetype/include/freetype/ftgzip.h b/thirdparty/freetype/include/freetype/ftgzip.h
index bd5ceaab9f..db033da0ed 100644
--- a/thirdparty/freetype/include/freetype/ftgzip.h
+++ b/thirdparty/freetype/include/freetype/ftgzip.h
@@ -4,7 +4,7 @@
/* */
/* Gzip-compressed stream support. */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -129,6 +129,9 @@ FT_BEGIN_HEADER
* @note:
* This function may return `FT_Err_Unimplemented_Feature' if your build
* of FreeType was not compiled with zlib support.
+ *
+ * @since:
+ * 2.5.1
*/
FT_EXPORT( FT_Error )
FT_Gzip_Uncompress( FT_Memory memory,
diff --git a/thirdparty/freetype/include/freetype/ftimage.h b/thirdparty/freetype/include/freetype/ftimage.h
index 1c789e5a44..79ede1959d 100644
--- a/thirdparty/freetype/include/freetype/ftimage.h
+++ b/thirdparty/freetype/include/freetype/ftimage.h
@@ -5,7 +5,7 @@
/* FreeType glyph image formats and default raster interface */
/* (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -169,13 +169,13 @@ FT_BEGIN_HEADER
/* @FT_RENDER_MODE_LCD_V. */
/* */
/* FT_PIXEL_MODE_BGRA :: */
- /* An image with four 8-bit channels per pixel, representing a */
- /* color image (such as emoticons) with alpha channel. For each */
- /* pixel, the format is BGRA, which means, the blue channel comes */
- /* first in memory. The color channels are pre-multiplied and in */
- /* the sRGB colorspace. For example, full red at half-translucent */
- /* opacity will be represented as `00,00,80,80', not `00,00,FF,80'. */
- /* See also @FT_LOAD_COLOR. */
+ /* [Since 2.5] An image with four 8-bit channels per pixel, */
+ /* representing a color image (such as emoticons) with alpha */
+ /* channel. For each pixel, the format is BGRA, which means, the */
+ /* blue channel comes first in memory. The color channels are */
+ /* pre-multiplied and in the sRGB colorspace. For example, full */
+ /* red at half-translucent opacity will be represented as */
+ /* `00,00,80,80', not `00,00,FF,80'. See also @FT_LOAD_COLOR. */
/* */
typedef enum FT_Pixel_Mode_
{
@@ -301,11 +301,11 @@ FT_BEGIN_HEADER
/* each outline point's type. */
/* */
/* If bit~0 is unset, the point is `off' the curve, */
- /* i.e., a Bézier control point, while it is `on' if */
+ /* i.e., a Bezier control point, while it is `on' if */
/* set. */
/* */
/* Bit~1 is meaningful for `off' points only. If set, */
- /* it indicates a third-order Bézier arc control point; */
+ /* it indicates a third-order Bezier arc control point; */
/* and a second-order control point if unset. */
/* */
/* If bit~2 is set, bits 5-7 contain the drop-out mode */
@@ -532,7 +532,7 @@ FT_BEGIN_HEADER
/* A function pointer type used to describe the signature of a `conic */
/* to' function during outline walking or decomposition. */
/* */
- /* A `conic to' is emitted to indicate a second-order Bézier arc in */
+ /* A `conic to' is emitted to indicate a second-order Bezier arc in */
/* the outline. */
/* */
/* <Input> */
@@ -564,12 +564,12 @@ FT_BEGIN_HEADER
/* A function pointer type used to describe the signature of a `cubic */
/* to' function during outline walking or decomposition. */
/* */
- /* A `cubic to' is emitted to indicate a third-order Bézier arc. */
+ /* A `cubic to' is emitted to indicate a third-order Bezier arc. */
/* */
/* <Input> */
- /* control1 :: A pointer to the first Bézier control point. */
+ /* control1 :: A pointer to the first Bezier control point. */
/* */
- /* control2 :: A pointer to the second Bézier control point. */
+ /* control2 :: A pointer to the second Bezier control point. */
/* */
/* to :: A pointer to the target end point. */
/* */
@@ -595,16 +595,16 @@ FT_BEGIN_HEADER
/* */
/* <Description> */
/* A structure to hold various function pointers used during outline */
- /* decomposition in order to emit segments, conic, and cubic Béziers. */
+ /* decomposition in order to emit segments, conic, and cubic Beziers. */
/* */
/* <Fields> */
/* move_to :: The `move to' emitter. */
/* */
/* line_to :: The segment emitter. */
/* */
- /* conic_to :: The second-order Bézier arc emitter. */
+ /* conic_to :: The second-order Bezier arc emitter. */
/* */
- /* cubic_to :: The third-order Bézier arc emitter. */
+ /* cubic_to :: The third-order Bezier arc emitter. */
/* */
/* shift :: The shift that is applied to coordinates before they */
/* are sent to the emitter. */
@@ -701,7 +701,7 @@ FT_BEGIN_HEADER
/* */
/* FT_GLYPH_FORMAT_OUTLINE :: */
/* The glyph image is a vectorial outline made of line segments */
- /* and Bézier arcs; it can be described as an @FT_Outline; you */
+ /* and Bezier arcs; it can be described as an @FT_Outline; you */
/* generally want to access the `outline' field of the */
/* @FT_GlyphSlotRec structure to read it. */
/* */
diff --git a/thirdparty/freetype/include/freetype/ftincrem.h b/thirdparty/freetype/include/freetype/ftincrem.h
index f6ae2baed6..44619f941e 100644
--- a/thirdparty/freetype/include/freetype/ftincrem.h
+++ b/thirdparty/freetype/include/freetype/ftincrem.h
@@ -4,7 +4,7 @@
/* */
/* FreeType incremental loading (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -21,6 +21,7 @@
#include <ft2build.h>
#include FT_FREETYPE_H
+#include FT_PARAMETER_TAGS_H
#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
@@ -331,18 +332,6 @@ FT_BEGIN_HEADER
typedef FT_Incremental_InterfaceRec* FT_Incremental_Interface;
- /***************************************************************************
- *
- * @constant:
- * FT_PARAM_TAG_INCREMENTAL
- *
- * @description:
- * A constant used as the tag of @FT_Parameter structures to indicate
- * an incremental loading object to be used by FreeType.
- *
- */
-#define FT_PARAM_TAG_INCREMENTAL FT_MAKE_TAG( 'i', 'n', 'c', 'r' )
-
/* */
diff --git a/thirdparty/freetype/include/freetype/ftlcdfil.h b/thirdparty/freetype/include/freetype/ftlcdfil.h
index bdaf9af906..2a27196cbb 100644
--- a/thirdparty/freetype/include/freetype/ftlcdfil.h
+++ b/thirdparty/freetype/include/freetype/ftlcdfil.h
@@ -5,7 +5,7 @@
/* FreeType API for color filtering of subpixel bitmap glyphs */
/* (specification). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -22,6 +22,7 @@
#include <ft2build.h>
#include FT_FREETYPE_H
+#include FT_PARAMETER_TAGS_H
#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
@@ -280,23 +281,6 @@ FT_BEGIN_HEADER
unsigned char *weights );
- /**************************************************************************
- *
- * @constant:
- * FT_PARAM_TAG_LCD_FILTER_WEIGHTS
- *
- * @description:
- * An @FT_Parameter tag to be used with @FT_Face_Properties. The
- * corresponding argument specifies the five LCD filter weights for a
- * given face (if using @FT_LOAD_TARGET_LCD, for example), overriding
- * the global default values or the values set up with
- * @FT_Library_SetLcdFilterWeights.
- *
- */
-#define FT_PARAM_TAG_LCD_FILTER_WEIGHTS \
- FT_MAKE_TAG( 'l', 'c', 'd', 'f' )
-
-
/*
* @type:
* FT_LcdFiveTapFilter
@@ -305,6 +289,9 @@ FT_BEGIN_HEADER
* A typedef for passing the five LCD filter weights to
* @FT_Face_Properties within an @FT_Parameter structure.
*
+ * @since:
+ * 2.8
+ *
*/
#define FT_LCD_FILTER_FIVE_TAPS 5
diff --git a/thirdparty/freetype/include/freetype/ftlist.h b/thirdparty/freetype/include/freetype/ftlist.h
index 5309cb18ba..117473b96a 100644
--- a/thirdparty/freetype/include/freetype/ftlist.h
+++ b/thirdparty/freetype/include/freetype/ftlist.h
@@ -4,7 +4,7 @@
/* */
/* Generic list support for FreeType (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftlzw.h b/thirdparty/freetype/include/freetype/ftlzw.h
index a82c95e7c9..1615912d62 100644
--- a/thirdparty/freetype/include/freetype/ftlzw.h
+++ b/thirdparty/freetype/include/freetype/ftlzw.h
@@ -4,7 +4,7 @@
/* */
/* LZW-compressed stream support. */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftmac.h b/thirdparty/freetype/include/freetype/ftmac.h
index a1656eec0e..c1e497ca2d 100644
--- a/thirdparty/freetype/include/freetype/ftmac.h
+++ b/thirdparty/freetype/include/freetype/ftmac.h
@@ -4,7 +4,7 @@
/* */
/* Additional Mac-specific API. */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftmm.h b/thirdparty/freetype/include/freetype/ftmm.h
index 80ac98d612..9948102c14 100644
--- a/thirdparty/freetype/include/freetype/ftmm.h
+++ b/thirdparty/freetype/include/freetype/ftmm.h
@@ -4,7 +4,7 @@
/* */
/* FreeType Multiple Master font interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -286,7 +286,7 @@ FT_BEGIN_HEADER
/* <Output> */
/* amaster :: The variation descriptor. */
/* Allocates a data structure, which the user must */
- /* deallocate with `free' after use. */
+ /* deallocate with a call to @FT_Done_MM_Var after use. */
/* */
/* <Return> */
/* FreeType error code. 0~means success. */
@@ -299,6 +299,26 @@ FT_BEGIN_HEADER
/*************************************************************************/
/* */
/* <Function> */
+ /* FT_Done_MM_Var */
+ /* */
+ /* <Description> */
+ /* Free the memory allocated by @FT_Get_MM_Var. */
+ /* */
+ /* <Input> */
+ /* library :: A handle of the face's parent library object that was */
+ /* used in the call to @FT_Get_MM_Var to create `amaster'. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ FT_EXPORT( FT_Error )
+ FT_Done_MM_Var( FT_Library library,
+ FT_MM_Var *amaster );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
/* FT_Set_MM_Design_Coordinates */
/* */
/* <Description> */
@@ -323,9 +343,13 @@ FT_BEGIN_HEADER
/* FreeType error code. 0~means success. */
/* */
/* <Note> */
- /* To reset all axes to the default values, call the function with */
- /* `num_coords' set to zero and `coords' set to NULL (new feature in */
- /* FreeType version 2.8.1). */
+ /* [Since 2.8.1] To reset all axes to the default values, call the */
+ /* function with `num_coords' set to zero and `coords' set to NULL. */
+ /* */
+ /* [Since 2.9] If `num_coords' is larger than zero, this function */
+ /* sets the @FT_FACE_FLAG_VARIATION bit in @FT_Face's `face_flags' */
+ /* field (i.e., @FT_IS_VARIATION will return true). If `num_coords' */
+ /* is zero, this bit flag gets unset. */
/* */
FT_EXPORT( FT_Error )
FT_Set_MM_Design_Coordinates( FT_Face face,
@@ -358,9 +382,15 @@ FT_BEGIN_HEADER
/* FreeType error code. 0~means success. */
/* */
/* <Note> */
- /* To reset all axes to the default values, call the function with */
- /* `num_coords' set to zero and `coords' set to NULL (new feature in */
- /* FreeType version 2.8.1). */
+ /* [Since 2.8.1] To reset all axes to the default values, call the */
+ /* function with `num_coords' set to zero and `coords' set to NULL. */
+ /* [Since 2.9] `Default values' means the currently selected named */
+ /* instance (or the base font if no named instance is selected). */
+ /* */
+ /* [Since 2.9] If `num_coords' is larger than zero, this function */
+ /* sets the @FT_FACE_FLAG_VARIATION bit in @FT_Face's `face_flags' */
+ /* field (i.e., @FT_IS_VARIATION will return true). If `num_coords' */
+ /* is zero, this bit flag gets unset. */
/* */
FT_EXPORT( FT_Error )
FT_Set_Var_Design_Coordinates( FT_Face face,
@@ -392,6 +422,9 @@ FT_BEGIN_HEADER
/* <Return> */
/* FreeType error code. 0~means success. */
/* */
+ /* <Since> */
+ /* 2.7.1 */
+ /* */
FT_EXPORT( FT_Error )
FT_Get_Var_Design_Coordinates( FT_Face face,
FT_UInt num_coords,
@@ -427,9 +460,15 @@ FT_BEGIN_HEADER
/* FreeType error code. 0~means success. */
/* */
/* <Note> */
- /* To reset all axes to the default values, call the function with */
- /* `num_coords' set to zero and `coords' set to NULL (new feature in */
- /* FreeType version 2.8.1). */
+ /* [Since 2.8.1] To reset all axes to the default values, call the */
+ /* function with `num_coords' set to zero and `coords' set to NULL. */
+ /* [Since 2.9] `Default values' means the currently selected named */
+ /* instance (or the base font if no named instance is selected). */
+ /* */
+ /* [Since 2.9] If `num_coords' is larger than zero, this function */
+ /* sets the @FT_FACE_FLAG_VARIATION bit in @FT_Face's `face_flags' */
+ /* field (i.e., @FT_IS_VARIATION will return true). If `num_coords' */
+ /* is zero, this bit flag gets unset. */
/* */
FT_EXPORT( FT_Error )
FT_Set_MM_Blend_Coordinates( FT_Face face,
@@ -462,6 +501,9 @@ FT_BEGIN_HEADER
/* <Return> */
/* FreeType error code. 0~means success. */
/* */
+ /* <Since> */
+ /* 2.7.1 */
+ /* */
FT_EXPORT( FT_Error )
FT_Get_MM_Blend_Coordinates( FT_Face face,
FT_UInt num_coords,
@@ -490,6 +532,9 @@ FT_BEGIN_HEADER
/* <Description> */
/* This is another name of @FT_Get_MM_Blend_Coordinates. */
/* */
+ /* <Since> */
+ /* 2.7.1 */
+ /* */
FT_EXPORT( FT_Error )
FT_Get_Var_Blend_Coordinates( FT_Face face,
FT_UInt num_coords,
@@ -509,6 +554,9 @@ FT_BEGIN_HEADER
/* FT_VAR_AXIS_FLAG_HIDDEN :: */
/* The variation axis should not be exposed to user interfaces. */
/* */
+ /* <Since> */
+ /* 2.8.1 */
+ /* */
#define FT_VAR_AXIS_FLAG_HIDDEN 1
@@ -534,11 +582,51 @@ FT_BEGIN_HEADER
/* <Return> */
/* FreeType error code. 0~means success. */
/* */
+ /* <Since> */
+ /* 2.8.1 */
+ /* */
FT_EXPORT( FT_Error )
FT_Get_Var_Axis_Flags( FT_MM_Var* master,
FT_UInt axis_index,
FT_UInt* flags );
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FT_Set_Named_Instance */
+ /* */
+ /* <Description> */
+ /* Set or change the current named instance. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the source face. */
+ /* */
+ /* instance_index :: The index of the requested instance, starting */
+ /* with value 1. If set to value 0, FreeType */
+ /* switches to font access without a named */
+ /* instance. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ /* <Note> */
+ /* The function uses the value of `instance_index' to set bits 16-30 */
+ /* of the face's `face_index' field. It also resets any variation */
+ /* applied to the font, and the @FT_FACE_FLAG_VARIATION bit of the */
+ /* face's `face_flags' field gets reset to zero (i.e., */
+ /* @FT_IS_VARIATION will return false). */
+ /* */
+ /* For Adobe MM fonts (which don't have named instances) this */
+ /* function simply resets the current face to the default instance. */
+ /* */
+ /* <Since> */
+ /* 2.9 */
+ /* */
+ FT_EXPORT( FT_Error )
+ FT_Set_Named_Instance( FT_Face face,
+ FT_UInt instance_index );
+
/* */
diff --git a/thirdparty/freetype/include/freetype/ftmodapi.h b/thirdparty/freetype/include/freetype/ftmodapi.h
index 4147aadf8b..a6eb876ebe 100644
--- a/thirdparty/freetype/include/freetype/ftmodapi.h
+++ b/thirdparty/freetype/include/freetype/ftmodapi.h
@@ -4,7 +4,7 @@
/* */
/* FreeType modules public interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -323,16 +323,15 @@ FT_BEGIN_HEADER
* The module name.
*
* property_name ::
- * The property name. Properties are described in the `Synopsis'
- * subsection of the module's documentation.
+ * The property name. Properties are described in section
+ * @properties.
*
* Note that only a few modules have properties.
*
* value ::
* A generic pointer to a variable or structure that gives the new
* value of the property. The exact definition of `value' is
- * dependent on the property; see the `Synopsis' subsection of the
- * module's documentation.
+ * dependent on the property; see section @properties.
*
* @return:
* FreeType error code. 0~means success.
@@ -390,15 +389,14 @@ FT_BEGIN_HEADER
* The module name.
*
* property_name ::
- * The property name. Properties are described in the `Synopsis'
- * subsection of the module's documentation.
+ * The property name. Properties are described in section
+ * @properties.
*
* @inout:
* value ::
* A generic pointer to a variable or structure that gives the
* value of the property. The exact definition of `value' is
- * dependent on the property; see the `Synopsis' subsection of the
- * module's documentation.
+ * dependent on the property; see section @properties.
*
* @return:
* FreeType error code. 0~means success.
@@ -446,8 +444,8 @@ FT_BEGIN_HEADER
/* <Description> */
/* If compilation option FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES is */
/* set, this function reads the `FREETYPE_PROPERTIES' environment */
- /* variable to control driver properties. See sections @auto_hinter, */
- /* @cff_driver, @pcf_driver, and @tt_driver for more. */
+ /* variable to control driver properties. See section @properties */
+ /* for more. */
/* */
/* If the compilation option is not set, this function does nothing. */
/* */
@@ -475,6 +473,9 @@ FT_BEGIN_HEADER
/* <InOut> */
/* library :: A handle to a new library object. */
/* */
+ /* <Since> */
+ /* 2.8 */
+ /* */
FT_EXPORT( void )
FT_Set_Default_Properties( FT_Library library );
diff --git a/thirdparty/freetype/include/freetype/ftmoderr.h b/thirdparty/freetype/include/freetype/ftmoderr.h
index 7f608375e8..e0fc1312bd 100644
--- a/thirdparty/freetype/include/freetype/ftmoderr.h
+++ b/thirdparty/freetype/include/freetype/ftmoderr.h
@@ -4,7 +4,7 @@
/* */
/* FreeType module error offsets (specification). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftotval.h b/thirdparty/freetype/include/freetype/ftotval.h
index b5d27cfe74..26731c2b9f 100644
--- a/thirdparty/freetype/include/freetype/ftotval.h
+++ b/thirdparty/freetype/include/freetype/ftotval.h
@@ -4,7 +4,7 @@
/* */
/* FreeType API for validating OpenType tables (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftoutln.h b/thirdparty/freetype/include/freetype/ftoutln.h
index 56f56a9603..89389a49b7 100644
--- a/thirdparty/freetype/include/freetype/ftoutln.h
+++ b/thirdparty/freetype/include/freetype/ftoutln.h
@@ -5,7 +5,7 @@
/* Support for the FT_Outline type used to store glyph shapes of */
/* most scalable font formats (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -89,7 +89,7 @@ FT_BEGIN_HEADER
/* */
/* <Description> */
/* Walk over an outline's structure to decompose it into individual */
- /* segments and Bézier arcs. This function also emits `move to' */
+ /* segments and Bezier arcs. This function also emits `move to' */
/* operations to indicate the start of new contours in the outline. */
/* */
/* <Input> */
@@ -190,9 +190,6 @@ FT_BEGIN_HEADER
/* If the outline's `owner' field is not set, only the outline */
/* descriptor will be released. */
/* */
- /* The reason why this function takes an `library' parameter is */
- /* simply to use ft_mem_free(). */
- /* */
FT_EXPORT( FT_Error )
FT_Outline_Done( FT_Library library,
FT_Outline* outline );
@@ -232,10 +229,10 @@ FT_BEGIN_HEADER
/* */
/* <Description> */
/* Return an outline's `control box'. The control box encloses all */
- /* the outline's points, including Bézier control points. Though it */
+ /* the outline's points, including Bezier control points. Though it */
/* coincides with the exact bounding box for most glyphs, it can be */
/* slightly larger in some situations (like when rotating an outline */
- /* that contains Bézier outside arcs). */
+ /* that contains Bezier outside arcs). */
/* */
/* Computing the control box is very fast, while getting the bounding */
/* box can take much more time as it needs to walk over all segments */
diff --git a/thirdparty/freetype/include/freetype/ftparams.h b/thirdparty/freetype/include/freetype/ftparams.h
new file mode 100644
index 0000000000..5a9006c505
--- /dev/null
+++ b/thirdparty/freetype/include/freetype/ftparams.h
@@ -0,0 +1,205 @@
+/***************************************************************************/
+/* */
+/* ftparams.h */
+/* */
+/* FreeType API for possible FT_Parameter tags (specification only). */
+/* */
+/* Copyright 2017-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef FTPARAMS_H_
+#define FTPARAMS_H_
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#ifdef FREETYPE_H
+#error "freetype.h of FreeType 1 has been loaded!"
+#error "Please fix the directory search order for header files"
+#error "so that freetype.h of FreeType 2 is found first."
+#endif
+
+
+FT_BEGIN_HEADER
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * parameter_tags
+ *
+ * @title:
+ * Parameter Tags
+ *
+ * @abstract:
+ * Macros for driver property and font loading parameter tags.
+ *
+ * @description:
+ * This section contains macros for the @FT_Parameter structure that are
+ * used with various functions to activate some special functionality or
+ * different behaviour of various components of FreeType.
+ *
+ */
+
+
+ /***************************************************************************
+ *
+ * @constant:
+ * FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_FAMILY
+ *
+ * @description:
+ * A tag for @FT_Parameter to make @FT_Open_Face ignore typographic
+ * family names in the `name' table (introduced in OpenType version
+ * 1.4). Use this for backward compatibility with legacy systems that
+ * have a four-faces-per-family restriction.
+ *
+ * @since:
+ * 2.8
+ *
+ */
+#define FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_FAMILY \
+ FT_MAKE_TAG( 'i', 'g', 'p', 'f' )
+
+
+ /* this constant is deprecated */
+#define FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY \
+ FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_FAMILY
+
+
+ /***************************************************************************
+ *
+ * @constant:
+ * FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_SUBFAMILY
+ *
+ * @description:
+ * A tag for @FT_Parameter to make @FT_Open_Face ignore typographic
+ * subfamily names in the `name' table (introduced in OpenType version
+ * 1.4). Use this for backward compatibility with legacy systems that
+ * have a four-faces-per-family restriction.
+ *
+ * @since:
+ * 2.8
+ *
+ */
+#define FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_SUBFAMILY \
+ FT_MAKE_TAG( 'i', 'g', 'p', 's' )
+
+
+ /* this constant is deprecated */
+#define FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY \
+ FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_SUBFAMILY
+
+
+ /***************************************************************************
+ *
+ * @constant:
+ * FT_PARAM_TAG_INCREMENTAL
+ *
+ * @description:
+ * An @FT_Parameter tag to be used with @FT_Open_Face to indicate
+ * incremental glyph loading.
+ *
+ */
+#define FT_PARAM_TAG_INCREMENTAL \
+ FT_MAKE_TAG( 'i', 'n', 'c', 'r' )
+
+
+ /**************************************************************************
+ *
+ * @constant:
+ * FT_PARAM_TAG_LCD_FILTER_WEIGHTS
+ *
+ * @description:
+ * An @FT_Parameter tag to be used with @FT_Face_Properties. The
+ * corresponding argument specifies the five LCD filter weights for a
+ * given face (if using @FT_LOAD_TARGET_LCD, for example), overriding
+ * the global default values or the values set up with
+ * @FT_Library_SetLcdFilterWeights.
+ *
+ * @since:
+ * 2.8
+ *
+ */
+#define FT_PARAM_TAG_LCD_FILTER_WEIGHTS \
+ FT_MAKE_TAG( 'l', 'c', 'd', 'f' )
+
+
+ /**************************************************************************
+ *
+ * @constant:
+ * FT_PARAM_TAG_RANDOM_SEED
+ *
+ * @description:
+ * An @FT_Parameter tag to be used with @FT_Face_Properties. The
+ * corresponding 32bit signed integer argument overrides the font
+ * driver's random seed value with a face-specific one; see
+ * @random-seed.
+ *
+ * @since:
+ * 2.8
+ *
+ */
+#define FT_PARAM_TAG_RANDOM_SEED \
+ FT_MAKE_TAG( 's', 'e', 'e', 'd' )
+
+
+ /**************************************************************************
+ *
+ * @constant:
+ * FT_PARAM_TAG_STEM_DARKENING
+ *
+ * @description:
+ * An @FT_Parameter tag to be used with @FT_Face_Properties. The
+ * corresponding Boolean argument specifies whether to apply stem
+ * darkening, overriding the global default values or the values set up
+ * with @FT_Property_Set (see @no-stem-darkening).
+ *
+ * This is a passive setting that only takes effect if the font driver
+ * or autohinter honors it, which the CFF, Type~1, and CID drivers
+ * always do, but the autohinter only in `light' hinting mode (as of
+ * version 2.9).
+ *
+ * @since:
+ * 2.8
+ *
+ */
+#define FT_PARAM_TAG_STEM_DARKENING \
+ FT_MAKE_TAG( 'd', 'a', 'r', 'k' )
+
+
+ /***************************************************************************
+ *
+ * @constant:
+ * FT_PARAM_TAG_UNPATENTED_HINTING
+ *
+ * @description:
+ * Deprecated, no effect.
+ *
+ * Previously: A constant used as the tag of an @FT_Parameter structure to
+ * indicate that unpatented methods only should be used by the TrueType
+ * bytecode interpreter for a typeface opened by @FT_Open_Face.
+ *
+ */
+#define FT_PARAM_TAG_UNPATENTED_HINTING \
+ FT_MAKE_TAG( 'u', 'n', 'p', 'a' )
+
+
+ /* */
+
+
+FT_END_HEADER
+
+
+#endif /* FTPARAMS_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/include/freetype/ftpfr.h b/thirdparty/freetype/include/freetype/ftpfr.h
index f2a6ae9349..a69cc482dc 100644
--- a/thirdparty/freetype/include/freetype/ftpfr.h
+++ b/thirdparty/freetype/include/freetype/ftpfr.h
@@ -4,7 +4,7 @@
/* */
/* FreeType API for accessing PFR-specific data (specification only). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -71,7 +71,7 @@ FT_BEGIN_HEADER
*
* ametrics_x_scale ::
* A 16.16 fixed-point number used to scale distance expressed
- * in metrics units to device sub-pixels. This is equivalent to
+ * in metrics units to device subpixels. This is equivalent to
* `face->size->x_scale', but for metrics only. Optional (parameter
* can be NULL).
*
@@ -123,7 +123,7 @@ FT_BEGIN_HEADER
* mode, which always returns distances converted to outline units.
*
* You can use the value of the `x_scale' and `y_scale' parameters
- * returned by @FT_Get_PFR_Metrics to scale these to device sub-pixels.
+ * returned by @FT_Get_PFR_Metrics to scale these to device subpixels.
*/
FT_EXPORT( FT_Error )
FT_Get_PFR_Kerning( FT_Face face,
@@ -154,7 +154,7 @@ FT_BEGIN_HEADER
*
* @note:
* You can use the `x_scale' or `y_scale' results of @FT_Get_PFR_Metrics
- * to convert the advance to device sub-pixels (i.e., 1/64th of pixels).
+ * to convert the advance to device subpixels (i.e., 1/64th of pixels).
*/
FT_EXPORT( FT_Error )
FT_Get_PFR_Advance( FT_Face face,
diff --git a/thirdparty/freetype/include/freetype/ftrender.h b/thirdparty/freetype/include/freetype/ftrender.h
index 960837580a..fa8ad22b98 100644
--- a/thirdparty/freetype/include/freetype/ftrender.h
+++ b/thirdparty/freetype/include/freetype/ftrender.h
@@ -4,7 +4,7 @@
/* */
/* FreeType renderer modules public interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -88,7 +88,7 @@ FT_BEGIN_HEADER
typedef FT_Error
(*FT_Renderer_RenderFunc)( FT_Renderer renderer,
FT_GlyphSlot slot,
- FT_UInt mode,
+ FT_Render_Mode mode,
const FT_Vector* origin );
typedef FT_Error
diff --git a/thirdparty/freetype/include/freetype/ftsizes.h b/thirdparty/freetype/include/freetype/ftsizes.h
index 2f3958a857..72cb08bf2a 100644
--- a/thirdparty/freetype/include/freetype/ftsizes.h
+++ b/thirdparty/freetype/include/freetype/ftsizes.h
@@ -4,7 +4,7 @@
/* */
/* FreeType size objects management (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftsnames.h b/thirdparty/freetype/include/freetype/ftsnames.h
index a316540576..8eb8d70ff7 100644
--- a/thirdparty/freetype/include/freetype/ftsnames.h
+++ b/thirdparty/freetype/include/freetype/ftsnames.h
@@ -7,7 +7,7 @@
/* */
/* This is _not_ used to retrieve glyph names! */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -25,6 +25,7 @@
#include <ft2build.h>
#include FT_FREETYPE_H
+#include FT_PARAMETER_TAGS_H
#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
@@ -189,6 +190,9 @@ FT_BEGIN_HEADER
/* Please refer to the TrueType or OpenType specification for more */
/* details. */
/* */
+ /* <Since> */
+ /* 2.8 */
+ /* */
typedef struct FT_SfntLangTag_
{
FT_Byte* string; /* this string is *not* null-terminated! */
@@ -229,53 +233,15 @@ FT_BEGIN_HEADER
/* invalid format~1 language ID values, FT_Err_Invalid_Argument is */
/* returned. */
/* */
+ /* <Since> */
+ /* 2.8 */
+ /* */
FT_EXPORT( FT_Error )
FT_Get_Sfnt_LangTag( FT_Face face,
FT_UInt langID,
FT_SfntLangTag *alangTag );
- /***************************************************************************
- *
- * @constant:
- * FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_FAMILY
- *
- * @description:
- * A tag for @FT_Parameter to make @FT_Open_Face ignore typographic
- * family names in the `name' table (introduced in OpenType version
- * 1.4). Use this for backward compatibility with legacy systems that
- * have a four-faces-per-family restriction.
- *
- */
-#define FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_FAMILY \
- FT_MAKE_TAG( 'i', 'g', 'p', 'f' )
-
-
- /* this constant is deprecated */
-#define FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY \
- FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_FAMILY
-
-
- /***************************************************************************
- *
- * @constant:
- * FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_SUBFAMILY
- *
- * @description:
- * A tag for @FT_Parameter to make @FT_Open_Face ignore typographic
- * subfamily names in the `name' table (introduced in OpenType version
- * 1.4). Use this for backward compatibility with legacy systems that
- * have a four-faces-per-family restriction.
- *
- */
-#define FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_SUBFAMILY \
- FT_MAKE_TAG( 'i', 'g', 'p', 's' )
-
-
- /* this constant is deprecated */
-#define FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY \
- FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_SUBFAMILY
-
/* */
diff --git a/thirdparty/freetype/include/freetype/ftstroke.h b/thirdparty/freetype/include/freetype/ftstroke.h
index 4a20667c5e..44b6fbe19f 100644
--- a/thirdparty/freetype/include/freetype/ftstroke.h
+++ b/thirdparty/freetype/include/freetype/ftstroke.h
@@ -4,7 +4,7 @@
/* */
/* FreeType path stroker (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -466,7 +466,7 @@ FT_BEGIN_HEADER
* FT_Stroker_ConicTo
*
* @description:
- * `Draw' a single quadratic Bézier in the stroker's current sub-path,
+ * `Draw' a single quadratic Bezier in the stroker's current sub-path,
* from the last position.
*
* @input:
@@ -474,7 +474,7 @@ FT_BEGIN_HEADER
* The target stroker handle.
*
* control ::
- * A pointer to a Bézier control point.
+ * A pointer to a Bezier control point.
*
* to ::
* A pointer to the destination point.
@@ -498,7 +498,7 @@ FT_BEGIN_HEADER
* FT_Stroker_CubicTo
*
* @description:
- * `Draw' a single cubic Bézier in the stroker's current sub-path,
+ * `Draw' a single cubic Bezier in the stroker's current sub-path,
* from the last position.
*
* @input:
@@ -506,10 +506,10 @@ FT_BEGIN_HEADER
* The target stroker handle.
*
* control1 ::
- * A pointer to the first Bézier control point.
+ * A pointer to the first Bezier control point.
*
* control2 ::
- * A pointer to second Bézier control point.
+ * A pointer to second Bezier control point.
*
* to ::
* A pointer to the destination point.
diff --git a/thirdparty/freetype/include/freetype/ftsynth.h b/thirdparty/freetype/include/freetype/ftsynth.h
index 1863fa2383..ff9fb43d96 100644
--- a/thirdparty/freetype/include/freetype/ftsynth.h
+++ b/thirdparty/freetype/include/freetype/ftsynth.h
@@ -5,7 +5,7 @@
/* FreeType synthesizing code for emboldening and slanting */
/* (specification). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/ftsystem.h b/thirdparty/freetype/include/freetype/ftsystem.h
index 1aa4762ad6..f6b1629ef2 100644
--- a/thirdparty/freetype/include/freetype/ftsystem.h
+++ b/thirdparty/freetype/include/freetype/ftsystem.h
@@ -4,7 +4,7 @@
/* */
/* FreeType low-level system interface definition (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/fttrigon.h b/thirdparty/freetype/include/freetype/fttrigon.h
index 89f0350675..2e3f3f1f73 100644
--- a/thirdparty/freetype/include/freetype/fttrigon.h
+++ b/thirdparty/freetype/include/freetype/fttrigon.h
@@ -4,7 +4,7 @@
/* */
/* FreeType trigonometric functions (specification). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/fttypes.h b/thirdparty/freetype/include/freetype/fttypes.h
index eab8adaad4..f638c2e54f 100644
--- a/thirdparty/freetype/include/freetype/fttypes.h
+++ b/thirdparty/freetype/include/freetype/fttypes.h
@@ -4,7 +4,7 @@
/* */
/* FreeType simple types definitions (specification only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -425,7 +425,7 @@ FT_BEGIN_HEADER
/* The address of the FreeType object that is under finalization. */
/* Its client data is accessed through its `generic' field. */
/* */
- typedef void (*FT_Generic_Finalizer)(void* object);
+ typedef void (*FT_Generic_Finalizer)( void* object );
/*************************************************************************/
diff --git a/thirdparty/freetype/include/freetype/ftwinfnt.h b/thirdparty/freetype/include/freetype/ftwinfnt.h
index 1eeef6c8ba..461c65b779 100644
--- a/thirdparty/freetype/include/freetype/ftwinfnt.h
+++ b/thirdparty/freetype/include/freetype/ftwinfnt.h
@@ -4,7 +4,7 @@
/* */
/* FreeType API for accessing Windows fnt-specific data. */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -78,7 +78,7 @@ FT_BEGIN_HEADER
* Mac Roman encoding.
*
* FT_WinFNT_ID_OEM ::
- * From Michael Pöttgen <michael@poettgen.de>:
+ * From Michael Poettgen <michael@poettgen.de>:
*
* The `Windows Font Mapping' article says that FT_WinFNT_ID_OEM
* is used for the charset of vector fonts, like `modern.fon',
diff --git a/thirdparty/freetype/include/freetype/internal/autohint.h b/thirdparty/freetype/include/freetype/internal/autohint.h
index bae83e7384..f4d308f68c 100644
--- a/thirdparty/freetype/include/freetype/internal/autohint.h
+++ b/thirdparty/freetype/include/freetype/internal/autohint.h
@@ -4,7 +4,7 @@
/* */
/* High-level `autohint' module-specific interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/cffotypes.h b/thirdparty/freetype/include/freetype/internal/cffotypes.h
new file mode 100644
index 0000000000..57e7591d41
--- /dev/null
+++ b/thirdparty/freetype/include/freetype/internal/cffotypes.h
@@ -0,0 +1,108 @@
+/***************************************************************************/
+/* */
+/* cffotypes.h */
+/* */
+/* Basic OpenType/CFF object type definitions (specification). */
+/* */
+/* Copyright 2017-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef CFFOTYPES_H_
+#define CFFOTYPES_H_
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_CFF_TYPES_H
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+#include FT_INTERNAL_POSTSCRIPT_HINTS_H
+
+
+FT_BEGIN_HEADER
+
+
+ typedef TT_Face CFF_Face;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* CFF_Size */
+ /* */
+ /* <Description> */
+ /* A handle to an OpenType size object. */
+ /* */
+ typedef struct CFF_SizeRec_
+ {
+ FT_SizeRec root;
+ FT_ULong strike_index; /* 0xFFFFFFFF to indicate invalid */
+
+ } CFF_SizeRec, *CFF_Size;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* CFF_GlyphSlot */
+ /* */
+ /* <Description> */
+ /* A handle to an OpenType glyph slot object. */
+ /* */
+ typedef struct CFF_GlyphSlotRec_
+ {
+ FT_GlyphSlotRec root;
+
+ FT_Bool hint;
+ FT_Bool scaled;
+
+ FT_Fixed x_scale;
+ FT_Fixed y_scale;
+
+ } CFF_GlyphSlotRec, *CFF_GlyphSlot;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* CFF_Internal */
+ /* */
+ /* <Description> */
+ /* The interface to the `internal' field of `FT_Size'. */
+ /* */
+ typedef struct CFF_InternalRec_
+ {
+ PSH_Globals topfont;
+ PSH_Globals subfonts[CFF_MAX_CID_FONTS];
+
+ } CFF_InternalRec, *CFF_Internal;
+
+
+ /*************************************************************************/
+ /* */
+ /* Subglyph transformation record. */
+ /* */
+ typedef struct CFF_Transform_
+ {
+ FT_Fixed xx, xy; /* transformation matrix coefficients */
+ FT_Fixed yx, yy;
+ FT_F26Dot6 ox, oy; /* offsets */
+
+ } CFF_Transform;
+
+
+FT_END_HEADER
+
+
+#endif /* CFFOTYPES_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/include/freetype/internal/cfftypes.h b/thirdparty/freetype/include/freetype/internal/cfftypes.h
new file mode 100644
index 0000000000..7c07e1a376
--- /dev/null
+++ b/thirdparty/freetype/include/freetype/internal/cfftypes.h
@@ -0,0 +1,412 @@
+/***************************************************************************/
+/* */
+/* cfftypes.h */
+/* */
+/* Basic OpenType/CFF type definitions and interface (specification */
+/* only). */
+/* */
+/* Copyright 1996-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef CFFTYPES_H_
+#define CFFTYPES_H_
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TYPE1_TABLES_H
+#include FT_INTERNAL_SERVICE_H
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+#include FT_INTERNAL_POSTSCRIPT_HINTS_H
+#include FT_INTERNAL_TYPE1_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* CFF_IndexRec */
+ /* */
+ /* <Description> */
+ /* A structure used to model a CFF Index table. */
+ /* */
+ /* <Fields> */
+ /* stream :: The source input stream. */
+ /* */
+ /* start :: The position of the first index byte in the */
+ /* input stream. */
+ /* */
+ /* count :: The number of elements in the index. */
+ /* */
+ /* off_size :: The size in bytes of object offsets in index. */
+ /* */
+ /* data_offset :: The position of first data byte in the index's */
+ /* bytes. */
+ /* */
+ /* data_size :: The size of the data table in this index. */
+ /* */
+ /* offsets :: A table of element offsets in the index. Must be */
+ /* loaded explicitly. */
+ /* */
+ /* bytes :: If the index is loaded in memory, its bytes. */
+ /* */
+ typedef struct CFF_IndexRec_
+ {
+ FT_Stream stream;
+ FT_ULong start;
+ FT_UInt hdr_size;
+ FT_UInt count;
+ FT_Byte off_size;
+ FT_ULong data_offset;
+ FT_ULong data_size;
+
+ FT_ULong* offsets;
+ FT_Byte* bytes;
+
+ } CFF_IndexRec, *CFF_Index;
+
+
+ typedef struct CFF_EncodingRec_
+ {
+ FT_UInt format;
+ FT_ULong offset;
+
+ FT_UInt count;
+ FT_UShort sids [256]; /* avoid dynamic allocations */
+ FT_UShort codes[256];
+
+ } CFF_EncodingRec, *CFF_Encoding;
+
+
+ typedef struct CFF_CharsetRec_
+ {
+
+ FT_UInt format;
+ FT_ULong offset;
+
+ FT_UShort* sids;
+ FT_UShort* cids; /* the inverse mapping of `sids'; only needed */
+ /* for CID-keyed fonts */
+ FT_UInt max_cid;
+ FT_UInt num_glyphs;
+
+ } CFF_CharsetRec, *CFF_Charset;
+
+
+ /* cf. similar fields in file `ttgxvar.h' from the `truetype' module */
+
+ typedef struct CFF_VarData_
+ {
+#if 0
+ FT_UInt itemCount; /* not used; always zero */
+ FT_UInt shortDeltaCount; /* not used; always zero */
+#endif
+
+ FT_UInt regionIdxCount; /* number of region indexes */
+ FT_UInt* regionIndices; /* array of `regionIdxCount' indices; */
+ /* these index `varRegionList' */
+ } CFF_VarData;
+
+
+ /* contribution of one axis to a region */
+ typedef struct CFF_AxisCoords_
+ {
+ FT_Fixed startCoord;
+ FT_Fixed peakCoord; /* zero peak means no effect (factor = 1) */
+ FT_Fixed endCoord;
+
+ } CFF_AxisCoords;
+
+
+ typedef struct CFF_VarRegion_
+ {
+ CFF_AxisCoords* axisList; /* array of axisCount records */
+
+ } CFF_VarRegion;
+
+
+ typedef struct CFF_VStoreRec_
+ {
+ FT_UInt dataCount;
+ CFF_VarData* varData; /* array of dataCount records */
+ /* vsindex indexes this array */
+ FT_UShort axisCount;
+ FT_UInt regionCount; /* total number of regions defined */
+ CFF_VarRegion* varRegionList;
+
+ } CFF_VStoreRec, *CFF_VStore;
+
+
+ /* forward reference */
+ typedef struct CFF_FontRec_* CFF_Font;
+
+
+ /* This object manages one cached blend vector. */
+ /* */
+ /* There is a BlendRec for Private DICT parsing in each subfont */
+ /* and a BlendRec for charstrings in CF2_Font instance data. */
+ /* A cached BV may be used across DICTs or Charstrings if inputs */
+ /* have not changed. */
+ /* */
+ /* `usedBV' is reset at the start of each parse or charstring. */
+ /* vsindex cannot be changed after a BV is used. */
+ /* */
+ /* Note: NDV is long (32/64 bit), while BV is 16.16 (FT_Int32). */
+ typedef struct CFF_BlendRec_
+ {
+ FT_Bool builtBV; /* blendV has been built */
+ FT_Bool usedBV; /* blendV has been used */
+ CFF_Font font; /* top level font struct */
+ FT_UInt lastVsindex; /* last vsindex used */
+ FT_UInt lenNDV; /* normDV length (aka numAxes) */
+ FT_Fixed* lastNDV; /* last NDV used */
+ FT_UInt lenBV; /* BlendV length (aka numMasters) */
+ FT_Int32* BV; /* current blendV (per DICT/glyph) */
+
+ } CFF_BlendRec, *CFF_Blend;
+
+
+ typedef struct CFF_FontRecDictRec_
+ {
+ FT_UInt version;
+ FT_UInt notice;
+ FT_UInt copyright;
+ FT_UInt full_name;
+ FT_UInt family_name;
+ FT_UInt weight;
+ FT_Bool is_fixed_pitch;
+ FT_Fixed italic_angle;
+ FT_Fixed underline_position;
+ FT_Fixed underline_thickness;
+ FT_Int paint_type;
+ FT_Int charstring_type;
+ FT_Matrix font_matrix;
+ FT_Bool has_font_matrix;
+ FT_ULong units_per_em; /* temporarily used as scaling value also */
+ FT_Vector font_offset;
+ FT_ULong unique_id;
+ FT_BBox font_bbox;
+ FT_Pos stroke_width;
+ FT_ULong charset_offset;
+ FT_ULong encoding_offset;
+ FT_ULong charstrings_offset;
+ FT_ULong private_offset;
+ FT_ULong private_size;
+ FT_Long synthetic_base;
+ FT_UInt embedded_postscript;
+
+ /* these should only be used for the top-level font dictionary */
+ FT_UInt cid_registry;
+ FT_UInt cid_ordering;
+ FT_Long cid_supplement;
+
+ FT_Long cid_font_version;
+ FT_Long cid_font_revision;
+ FT_Long cid_font_type;
+ FT_ULong cid_count;
+ FT_ULong cid_uid_base;
+ FT_ULong cid_fd_array_offset;
+ FT_ULong cid_fd_select_offset;
+ FT_UInt cid_font_name;
+
+ /* the next fields come from the data of the deprecated */
+ /* `MultipleMaster' operator; they are needed to parse the (also */
+ /* deprecated) `blend' operator in Type 2 charstrings */
+ FT_UShort num_designs;
+ FT_UShort num_axes;
+
+ /* fields for CFF2 */
+ FT_ULong vstore_offset;
+ FT_UInt maxstack;
+
+ } CFF_FontRecDictRec, *CFF_FontRecDict;
+
+
+ /* forward reference */
+ typedef struct CFF_SubFontRec_* CFF_SubFont;
+
+
+ typedef struct CFF_PrivateRec_
+ {
+ FT_Byte num_blue_values;
+ FT_Byte num_other_blues;
+ FT_Byte num_family_blues;
+ FT_Byte num_family_other_blues;
+
+ FT_Pos blue_values[14];
+ FT_Pos other_blues[10];
+ FT_Pos family_blues[14];
+ FT_Pos family_other_blues[10];
+
+ FT_Fixed blue_scale;
+ FT_Pos blue_shift;
+ FT_Pos blue_fuzz;
+ FT_Pos standard_width;
+ FT_Pos standard_height;
+
+ FT_Byte num_snap_widths;
+ FT_Byte num_snap_heights;
+ FT_Pos snap_widths[13];
+ FT_Pos snap_heights[13];
+ FT_Bool force_bold;
+ FT_Fixed force_bold_threshold;
+ FT_Int lenIV;
+ FT_Int language_group;
+ FT_Fixed expansion_factor;
+ FT_Long initial_random_seed;
+ FT_ULong local_subrs_offset;
+ FT_Pos default_width;
+ FT_Pos nominal_width;
+
+ /* fields for CFF2 */
+ FT_UInt vsindex;
+ CFF_SubFont subfont;
+
+ } CFF_PrivateRec, *CFF_Private;
+
+
+ typedef struct CFF_FDSelectRec_
+ {
+ FT_Byte format;
+ FT_UInt range_count;
+
+ /* that's the table, taken from the file `as is' */
+ FT_Byte* data;
+ FT_UInt data_size;
+
+ /* small cache for format 3 only */
+ FT_UInt cache_first;
+ FT_UInt cache_count;
+ FT_Byte cache_fd;
+
+ } CFF_FDSelectRec, *CFF_FDSelect;
+
+
+ /* A SubFont packs a font dict and a private dict together. They are */
+ /* needed to support CID-keyed CFF fonts. */
+ typedef struct CFF_SubFontRec_
+ {
+ CFF_FontRecDictRec font_dict;
+ CFF_PrivateRec private_dict;
+
+ /* fields for CFF2 */
+ CFF_BlendRec blend; /* current blend vector */
+ FT_UInt lenNDV; /* current length NDV or zero */
+ FT_Fixed* NDV; /* ptr to current NDV or NULL */
+
+ /* `blend_stack' is a writable buffer to hold blend results. */
+ /* This buffer is to the side of the normal cff parser stack; */
+ /* `cff_parse_blend' and `cff_blend_doBlend' push blend results here. */
+ /* The normal stack then points to these values instead of the DICT */
+ /* because all other operators in Private DICT clear the stack. */
+ /* `blend_stack' could be cleared at each operator other than blend. */
+ /* Blended values are stored as 5-byte fixed point values. */
+
+ FT_Byte* blend_stack; /* base of stack allocation */
+ FT_Byte* blend_top; /* first empty slot */
+ FT_UInt blend_used; /* number of bytes in use */
+ FT_UInt blend_alloc; /* number of bytes allocated */
+
+ CFF_IndexRec local_subrs_index;
+ FT_Byte** local_subrs; /* array of pointers */
+ /* into Local Subrs INDEX data */
+
+ FT_UInt32 random;
+
+ } CFF_SubFontRec;
+
+
+#define CFF_MAX_CID_FONTS 256
+
+
+ typedef struct CFF_FontRec_
+ {
+ FT_Library library;
+ FT_Stream stream;
+ FT_Memory memory; /* TODO: take this from stream->memory? */
+ FT_ULong base_offset; /* offset to start of CFF */
+ FT_UInt num_faces;
+ FT_UInt num_glyphs;
+
+ FT_Byte version_major;
+ FT_Byte version_minor;
+ FT_Byte header_size;
+
+ FT_UInt top_dict_length; /* cff2 only */
+
+ FT_Bool cff2;
+
+ CFF_IndexRec name_index;
+ CFF_IndexRec top_dict_index;
+ CFF_IndexRec global_subrs_index;
+
+ CFF_EncodingRec encoding;
+ CFF_CharsetRec charset;
+
+ CFF_IndexRec charstrings_index;
+ CFF_IndexRec font_dict_index;
+ CFF_IndexRec private_index;
+ CFF_IndexRec local_subrs_index;
+
+ FT_String* font_name;
+
+ /* array of pointers into Global Subrs INDEX data */
+ FT_Byte** global_subrs;
+
+ /* array of pointers into String INDEX data stored at string_pool */
+ FT_UInt num_strings;
+ FT_Byte** strings;
+ FT_Byte* string_pool;
+ FT_ULong string_pool_size;
+
+ CFF_SubFontRec top_font;
+ FT_UInt num_subfonts;
+ CFF_SubFont subfonts[CFF_MAX_CID_FONTS];
+
+ CFF_FDSelectRec fd_select;
+
+ /* interface to PostScript hinter */
+ PSHinter_Service pshinter;
+
+ /* interface to Postscript Names service */
+ FT_Service_PsCMaps psnames;
+
+ /* interface to CFFLoad service */
+ const void* cffload;
+
+ /* since version 2.3.0 */
+ PS_FontInfoRec* font_info; /* font info dictionary */
+
+ /* since version 2.3.6 */
+ FT_String* registry;
+ FT_String* ordering;
+
+ /* since version 2.4.12 */
+ FT_Generic cf2_instance;
+
+ /* since version 2.7.1 */
+ CFF_VStoreRec vstore; /* parsed vstore structure */
+
+ /* since version 2.9 */
+ PS_FontExtraRec* font_extra;
+
+ } CFF_FontRec;
+
+
+FT_END_HEADER
+
+#endif /* CFFTYPES_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/include/freetype/internal/ftcalc.h b/thirdparty/freetype/include/freetype/internal/ftcalc.h
index 8b35f03d88..818a812359 100644
--- a/thirdparty/freetype/include/freetype/internal/ftcalc.h
+++ b/thirdparty/freetype/include/freetype/internal/ftcalc.h
@@ -4,7 +4,7 @@
/* */
/* Arithmetic computations (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/ftdebug.h b/thirdparty/freetype/include/freetype/internal/ftdebug.h
index 5dcd2b1740..292a4eedb8 100644
--- a/thirdparty/freetype/include/freetype/internal/ftdebug.h
+++ b/thirdparty/freetype/include/freetype/internal/ftdebug.h
@@ -4,7 +4,7 @@
/* */
/* Debugging and logging component (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/ftdrv.h b/thirdparty/freetype/include/freetype/internal/ftdrv.h
new file mode 100644
index 0000000000..58dd35a933
--- /dev/null
+++ b/thirdparty/freetype/include/freetype/internal/ftdrv.h
@@ -0,0 +1,400 @@
+/***************************************************************************/
+/* */
+/* ftdrv.h */
+/* */
+/* FreeType internal font driver interface (specification). */
+/* */
+/* Copyright 1996-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef FTDRV_H_
+#define FTDRV_H_
+
+
+#include <ft2build.h>
+#include FT_MODULE_H
+
+
+FT_BEGIN_HEADER
+
+
+ typedef FT_Error
+ (*FT_Face_InitFunc)( FT_Stream stream,
+ FT_Face face,
+ FT_Int typeface_index,
+ FT_Int num_params,
+ FT_Parameter* parameters );
+
+ typedef void
+ (*FT_Face_DoneFunc)( FT_Face face );
+
+
+ typedef FT_Error
+ (*FT_Size_InitFunc)( FT_Size size );
+
+ typedef void
+ (*FT_Size_DoneFunc)( FT_Size size );
+
+
+ typedef FT_Error
+ (*FT_Slot_InitFunc)( FT_GlyphSlot slot );
+
+ typedef void
+ (*FT_Slot_DoneFunc)( FT_GlyphSlot slot );
+
+
+ typedef FT_Error
+ (*FT_Size_RequestFunc)( FT_Size size,
+ FT_Size_Request req );
+
+ typedef FT_Error
+ (*FT_Size_SelectFunc)( FT_Size size,
+ FT_ULong size_index );
+
+ typedef FT_Error
+ (*FT_Slot_LoadFunc)( FT_GlyphSlot slot,
+ FT_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags );
+
+
+ typedef FT_Error
+ (*FT_Face_GetKerningFunc)( FT_Face face,
+ FT_UInt left_glyph,
+ FT_UInt right_glyph,
+ FT_Vector* kerning );
+
+
+ typedef FT_Error
+ (*FT_Face_AttachFunc)( FT_Face face,
+ FT_Stream stream );
+
+
+ typedef FT_Error
+ (*FT_Face_GetAdvancesFunc)( FT_Face face,
+ FT_UInt first,
+ FT_UInt count,
+ FT_Int32 flags,
+ FT_Fixed* advances );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* FT_Driver_ClassRec */
+ /* */
+ /* <Description> */
+ /* The font driver class. This structure mostly contains pointers to */
+ /* driver methods. */
+ /* */
+ /* <Fields> */
+ /* root :: The parent module. */
+ /* */
+ /* face_object_size :: The size of a face object in bytes. */
+ /* */
+ /* size_object_size :: The size of a size object in bytes. */
+ /* */
+ /* slot_object_size :: The size of a glyph object in bytes. */
+ /* */
+ /* init_face :: The format-specific face constructor. */
+ /* */
+ /* done_face :: The format-specific face destructor. */
+ /* */
+ /* init_size :: The format-specific size constructor. */
+ /* */
+ /* done_size :: The format-specific size destructor. */
+ /* */
+ /* init_slot :: The format-specific slot constructor. */
+ /* */
+ /* done_slot :: The format-specific slot destructor. */
+ /* */
+ /* */
+ /* load_glyph :: A function handle to load a glyph to a slot. */
+ /* This field is mandatory! */
+ /* */
+ /* get_kerning :: A function handle to return the unscaled */
+ /* kerning for a given pair of glyphs. Can be */
+ /* set to 0 if the format doesn't support */
+ /* kerning. */
+ /* */
+ /* attach_file :: This function handle is used to read */
+ /* additional data for a face from another */
+ /* file/stream. For example, this can be used to */
+ /* add data from AFM or PFM files on a Type 1 */
+ /* face, or a CIDMap on a CID-keyed face. */
+ /* */
+ /* get_advances :: A function handle used to return advance */
+ /* widths of `count' glyphs (in font units), */
+ /* starting at `first'. The `vertical' flag must */
+ /* be set to get vertical advance heights. The */
+ /* `advances' buffer is caller-allocated. */
+ /* The idea of this function is to be able to */
+ /* perform device-independent text layout without */
+ /* loading a single glyph image. */
+ /* */
+ /* request_size :: A handle to a function used to request the new */
+ /* character size. Can be set to 0 if the */
+ /* scaling done in the base layer suffices. */
+ /* */
+ /* select_size :: A handle to a function used to select a new */
+ /* fixed size. It is used only if */
+ /* @FT_FACE_FLAG_FIXED_SIZES is set. Can be set */
+ /* to 0 if the scaling done in the base layer */
+ /* suffices. */
+ /* <Note> */
+ /* Most function pointers, with the exception of `load_glyph', can be */
+ /* set to 0 to indicate a default behaviour. */
+ /* */
+ typedef struct FT_Driver_ClassRec_
+ {
+ FT_Module_Class root;
+
+ FT_Long face_object_size;
+ FT_Long size_object_size;
+ FT_Long slot_object_size;
+
+ FT_Face_InitFunc init_face;
+ FT_Face_DoneFunc done_face;
+
+ FT_Size_InitFunc init_size;
+ FT_Size_DoneFunc done_size;
+
+ FT_Slot_InitFunc init_slot;
+ FT_Slot_DoneFunc done_slot;
+
+ FT_Slot_LoadFunc load_glyph;
+
+ FT_Face_GetKerningFunc get_kerning;
+ FT_Face_AttachFunc attach_file;
+ FT_Face_GetAdvancesFunc get_advances;
+
+ /* since version 2.2 */
+ FT_Size_RequestFunc request_size;
+ FT_Size_SelectFunc select_size;
+
+ } FT_Driver_ClassRec, *FT_Driver_Class;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Macro> */
+ /* FT_DECLARE_DRIVER */
+ /* */
+ /* <Description> */
+ /* Used to create a forward declaration of an FT_Driver_ClassRec */
+ /* struct instance. */
+ /* */
+ /* <Macro> */
+ /* FT_DEFINE_DRIVER */
+ /* */
+ /* <Description> */
+ /* Used to initialize an instance of FT_Driver_ClassRec struct. */
+ /* */
+ /* When FT_CONFIG_OPTION_PIC is defined a `create' function has to be */
+ /* called with a pointer where the allocated structure is returned. */
+ /* And when it is no longer needed a `destroy' function needs to be */
+ /* called to release that allocation. */
+ /* */
+ /* `ftinit.c' (ft_create_default_module_classes) already contains a */
+ /* mechanism to call these functions for the default modules */
+ /* described in `ftmodule.h'. */
+ /* */
+ /* Notice that the created `create' and `destroy' functions call */
+ /* `pic_init' and `pic_free' to allow you to manually allocate and */
+ /* initialize any additional global data, like a module specific */
+ /* interface, and put them in the global pic container defined in */
+ /* `ftpic.h'. If you don't need them just implement the functions as */
+ /* empty to resolve the link error. Also the `pic_init' and */
+ /* `pic_free' functions should be declared in `pic.h', to be referred */
+ /* by driver definition calling `FT_DEFINE_DRIVER' in following. */
+ /* */
+ /* When FT_CONFIG_OPTION_PIC is not defined the struct will be */
+ /* allocated in the global scope (or the scope where the macro is */
+ /* used). */
+ /* */
+#ifndef FT_CONFIG_OPTION_PIC
+
+#define FT_DECLARE_DRIVER( class_ ) \
+ FT_CALLBACK_TABLE \
+ const FT_Driver_ClassRec class_;
+
+#define FT_DEFINE_DRIVER( \
+ class_, \
+ flags_, \
+ size_, \
+ name_, \
+ version_, \
+ requires_, \
+ interface_, \
+ init_, \
+ done_, \
+ get_interface_, \
+ face_object_size_, \
+ size_object_size_, \
+ slot_object_size_, \
+ init_face_, \
+ done_face_, \
+ init_size_, \
+ done_size_, \
+ init_slot_, \
+ done_slot_, \
+ load_glyph_, \
+ get_kerning_, \
+ attach_file_, \
+ get_advances_, \
+ request_size_, \
+ select_size_ ) \
+ FT_CALLBACK_TABLE_DEF \
+ const FT_Driver_ClassRec class_ = \
+ { \
+ FT_DEFINE_ROOT_MODULE( flags_, \
+ size_, \
+ name_, \
+ version_, \
+ requires_, \
+ interface_, \
+ init_, \
+ done_, \
+ get_interface_ ) \
+ \
+ face_object_size_, \
+ size_object_size_, \
+ slot_object_size_, \
+ \
+ init_face_, \
+ done_face_, \
+ \
+ init_size_, \
+ done_size_, \
+ \
+ init_slot_, \
+ done_slot_, \
+ \
+ load_glyph_, \
+ \
+ get_kerning_, \
+ attach_file_, \
+ get_advances_, \
+ \
+ request_size_, \
+ select_size_ \
+ };
+
+#else /* FT_CONFIG_OPTION_PIC */
+
+#define FT_DECLARE_DRIVER( class_ ) FT_DECLARE_MODULE( class_ )
+
+#define FT_DEFINE_DRIVER( \
+ class_, \
+ flags_, \
+ size_, \
+ name_, \
+ version_, \
+ requires_, \
+ interface_, \
+ init_, \
+ done_, \
+ get_interface_, \
+ face_object_size_, \
+ size_object_size_, \
+ slot_object_size_, \
+ init_face_, \
+ done_face_, \
+ init_size_, \
+ done_size_, \
+ init_slot_, \
+ done_slot_, \
+ load_glyph_, \
+ get_kerning_, \
+ attach_file_, \
+ get_advances_, \
+ request_size_, \
+ select_size_ ) \
+ void \
+ FT_Destroy_Class_ ## class_( FT_Library library, \
+ FT_Module_Class* clazz ) \
+ { \
+ FT_Memory memory = library->memory; \
+ FT_Driver_Class dclazz = (FT_Driver_Class)clazz; \
+ \
+ \
+ class_ ## _pic_free( library ); \
+ if ( dclazz ) \
+ FT_FREE( dclazz ); \
+ } \
+ \
+ \
+ FT_Error \
+ FT_Create_Class_ ## class_( FT_Library library, \
+ FT_Module_Class** output_class ) \
+ { \
+ FT_Driver_Class clazz = NULL; \
+ FT_Error error; \
+ FT_Memory memory = library->memory; \
+ \
+ \
+ if ( FT_ALLOC( clazz, sizeof ( *clazz ) ) ) \
+ return error; \
+ \
+ error = class_ ## _pic_init( library ); \
+ if ( error ) \
+ { \
+ FT_FREE( clazz ); \
+ return error; \
+ } \
+ \
+ FT_DEFINE_ROOT_MODULE( flags_, \
+ size_, \
+ name_, \
+ version_, \
+ requires_, \
+ interface_, \
+ init_, \
+ done_, \
+ get_interface_ ) \
+ \
+ clazz->face_object_size = face_object_size_; \
+ clazz->size_object_size = size_object_size_; \
+ clazz->slot_object_size = slot_object_size_; \
+ \
+ clazz->init_face = init_face_; \
+ clazz->done_face = done_face_; \
+ \
+ clazz->init_size = init_size_; \
+ clazz->done_size = done_size_; \
+ \
+ clazz->init_slot = init_slot_; \
+ clazz->done_slot = done_slot_; \
+ \
+ clazz->load_glyph = load_glyph_; \
+ \
+ clazz->get_kerning = get_kerning_; \
+ clazz->attach_file = attach_file_; \
+ clazz->get_advances = get_advances_; \
+ \
+ clazz->request_size = request_size_; \
+ clazz->select_size = select_size_; \
+ \
+ *output_class = (FT_Module_Class*)clazz; \
+ \
+ return FT_Err_Ok; \
+ }
+
+
+#endif /* FT_CONFIG_OPTION_PIC */
+
+FT_END_HEADER
+
+#endif /* FTDRV_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/include/freetype/internal/ftgloadr.h b/thirdparty/freetype/include/freetype/internal/ftgloadr.h
index f41c3df554..a002fdbfca 100644
--- a/thirdparty/freetype/include/freetype/internal/ftgloadr.h
+++ b/thirdparty/freetype/include/freetype/internal/ftgloadr.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType glyph loader (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/ftmemory.h b/thirdparty/freetype/include/freetype/internal/ftmemory.h
index 59e5b58a57..054eaec31f 100644
--- a/thirdparty/freetype/include/freetype/internal/ftmemory.h
+++ b/thirdparty/freetype/include/freetype/internal/ftmemory.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType memory management macros (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -210,7 +210,7 @@ extern "C++"
NULL, \
&error ) )
-#define FT_MEM_QREALLOC_MULT( ptr, oldcnt, newcnt, itmsz) \
+#define FT_MEM_QREALLOC_MULT( ptr, oldcnt, newcnt, itmsz ) \
FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, \
(FT_Long)(itmsz), \
(FT_Long)(oldcnt), \
diff --git a/thirdparty/freetype/include/freetype/internal/ftobjs.h b/thirdparty/freetype/include/freetype/internal/ftobjs.h
index 4231be238a..1c3c6ad456 100644
--- a/thirdparty/freetype/include/freetype/internal/ftobjs.h
+++ b/thirdparty/freetype/include/freetype/internal/ftobjs.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType private base classes (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -85,9 +85,9 @@ FT_BEGIN_HEADER
: y + ( 3 * x >> 3 ) )
/* we use FT_TYPEOF to suppress signedness compilation warnings */
-#define FT_PAD_FLOOR( x, n ) ( (x) & ~FT_TYPEOF( x )( (n)-1 ) )
-#define FT_PAD_ROUND( x, n ) FT_PAD_FLOOR( (x) + (n)/2, n )
-#define FT_PAD_CEIL( x, n ) FT_PAD_FLOOR( (x) + (n)-1, n )
+#define FT_PAD_FLOOR( x, n ) ( (x) & ~FT_TYPEOF( x )( (n) - 1 ) )
+#define FT_PAD_ROUND( x, n ) FT_PAD_FLOOR( (x) + (n) / 2, n )
+#define FT_PAD_CEIL( x, n ) FT_PAD_FLOOR( (x) + (n) - 1, n )
#define FT_PIX_FLOOR( x ) ( (x) & ~FT_TYPEOF( x )63 )
#define FT_PIX_ROUND( x ) FT_PIX_FLOOR( (x) + 32 )
@@ -155,8 +155,8 @@ FT_BEGIN_HEADER
} FT_CMapRec;
- /* typecase any pointer to a charmap handle */
-#define FT_CMAP( x ) ((FT_CMap)( x ))
+ /* typecast any pointer to a charmap handle */
+#define FT_CMAP( x ) ( (FT_CMap)( x ) )
/* obvious macros */
#define FT_CMAP_PLATFORM_ID( x ) FT_CMAP( x )->charmap.platform_id
@@ -312,6 +312,27 @@ FT_BEGIN_HEADER
FT_CMap_Done( FT_CMap cmap );
+ /* adds LCD padding to Min and Max boundaries */
+ FT_BASE( void )
+ ft_lcd_padding( FT_Pos* Min,
+ FT_Pos* Max,
+ FT_GlyphSlot slot );
+
+#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+
+ typedef void (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap* bitmap,
+ FT_Render_Mode render_mode,
+ FT_Byte* weights );
+
+
+ /* This is the default LCD filter, an in-place, 5-tap FIR filter. */
+ FT_BASE( void )
+ ft_lcd_filter_fir( FT_Bitmap* bitmap,
+ FT_Render_Mode mode,
+ FT_LcdFiveTapFilter weights );
+
+#endif /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
+
/*************************************************************************/
/* */
/* <Struct> */
@@ -369,9 +390,10 @@ FT_BEGIN_HEADER
/* operator. Value~0 means to use the font's value. Value~-1 */
/* means to use the CFF driver's default. */
/* */
- /* lcd_weights :: */
- /* Overrides the library default with custom weights for the 5-tap */
- /* FIR filter. `{0, 0, 0, 0, 0}' means to use the library default. */
+ /* lcd_weights :: */
+ /* lcd_filter_func :: */
+ /* If subpixel rendering is activated, the LCD filtering weights */
+ /* and callback function. */
/* */
/* refcount :: */
/* A counter initialized to~1 at the time an @FT_Face structure is */
@@ -393,8 +415,10 @@ FT_BEGIN_HEADER
FT_Char no_stem_darkening;
FT_Int32 random_seed;
+
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
- FT_LcdFiveTapFilter lcd_weights; /* preset or custom filter weights */
+ FT_LcdFiveTapFilter lcd_weights; /* filter weights, if any */
+ FT_Bitmap_LcdFilterFunc lcd_filter_func; /* filtering callback */
#endif
FT_Int refcount;
@@ -516,7 +540,8 @@ FT_BEGIN_HEADER
/* typecast an object to an FT_Module */
-#define FT_MODULE( x ) ((FT_Module)( x ))
+#define FT_MODULE( x ) ( (FT_Module)(x) )
+
#define FT_MODULE_CLASS( x ) FT_MODULE( x )->clazz
#define FT_MODULE_LIBRARY( x ) FT_MODULE( x )->library
#define FT_MODULE_MEMORY( x ) FT_MODULE( x )->memory
@@ -602,9 +627,9 @@ FT_BEGIN_HEADER
/* a few macros used to perform easy typecasts with minimal brain damage */
-#define FT_FACE( x ) ((FT_Face)(x))
-#define FT_SIZE( x ) ((FT_Size)(x))
-#define FT_SLOT( x ) ((FT_GlyphSlot)(x))
+#define FT_FACE( x ) ( (FT_Face)(x) )
+#define FT_SIZE( x ) ( (FT_Size)(x) )
+#define FT_SLOT( x ) ( (FT_GlyphSlot)(x) )
#define FT_FACE_DRIVER( x ) FT_FACE( x )->driver
#define FT_FACE_LIBRARY( x ) FT_FACE_DRIVER( x )->root.library
@@ -705,6 +730,12 @@ FT_BEGIN_HEADER
ft_glyphslot_free_bitmap( FT_GlyphSlot slot );
+ /* Preset bitmap metrics of an outline glyphslot prior to rendering. */
+ FT_BASE( void )
+ ft_glyphslot_preset_bitmap( FT_GlyphSlot slot,
+ FT_Render_Mode mode,
+ const FT_Vector* origin );
+
/* Allocate a new bitmap buffer in a glyph slot. */
FT_BASE( FT_Error )
ft_glyphslot_alloc_bitmap( FT_GlyphSlot slot,
@@ -731,10 +762,10 @@ FT_BEGIN_HEADER
/*************************************************************************/
-#define FT_RENDERER( x ) ((FT_Renderer)( x ))
-#define FT_GLYPH( x ) ((FT_Glyph)( x ))
-#define FT_BITMAP_GLYPH( x ) ((FT_BitmapGlyph)( x ))
-#define FT_OUTLINE_GLYPH( x ) ((FT_OutlineGlyph)( x ))
+#define FT_RENDERER( x ) ( (FT_Renderer)(x) )
+#define FT_GLYPH( x ) ( (FT_Glyph)(x) )
+#define FT_BITMAP_GLYPH( x ) ( (FT_BitmapGlyph)(x) )
+#define FT_OUTLINE_GLYPH( x ) ( (FT_OutlineGlyph)(x) )
typedef struct FT_RendererRec_
@@ -765,7 +796,7 @@ FT_BEGIN_HEADER
/* typecast a module into a driver easily */
-#define FT_DRIVER( x ) ((FT_Driver)(x))
+#define FT_DRIVER( x ) ( (FT_Driver)(x) )
/* typecast a module as a driver, and get its driver class */
#define FT_DRIVER_CLASS( x ) FT_DRIVER( x )->clazz
@@ -821,18 +852,6 @@ FT_BEGIN_HEADER
#define FT_DEBUG_HOOK_TRUETYPE 0
- typedef void (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap* bitmap,
- FT_Render_Mode render_mode,
- FT_Byte* weights );
-
-
- /* This is the default LCD filter, an in-place, 5-tap FIR filter. */
- FT_BASE( void )
- ft_lcd_filter_fir( FT_Bitmap* bitmap,
- FT_Render_Mode mode,
- FT_LcdFiveTapFilter weights );
-
-
/*************************************************************************/
/* */
/* <Struct> */
@@ -878,9 +897,6 @@ FT_BEGIN_HEADER
/* interpreter. Currently, only the TrueType */
/* bytecode debugger uses this. */
/* */
- /* lcd_filter :: If subpixel rendering is activated, the */
- /* selected LCD filter mode. */
- /* */
/* lcd_weights :: If subpixel rendering is activated, the LCD */
/* filter weights, if any. */
/* */
@@ -915,7 +931,6 @@ FT_BEGIN_HEADER
FT_DebugHook_Func debug_hooks[4];
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
- FT_LcdFilter lcd_filter;
FT_LcdFiveTapFilter lcd_weights; /* filter weights, if any */
FT_Bitmap_LcdFilterFunc lcd_filter_func; /* filtering callback */
#endif
diff --git a/thirdparty/freetype/include/freetype/internal/ftpic.h b/thirdparty/freetype/include/freetype/internal/ftpic.h
index 0d43ed20f7..5214f05989 100644
--- a/thirdparty/freetype/include/freetype/internal/ftpic.h
+++ b/thirdparty/freetype/include/freetype/internal/ftpic.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services (declaration). */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/ftpsprop.h b/thirdparty/freetype/include/freetype/internal/ftpsprop.h
new file mode 100644
index 0000000000..abbb62862b
--- /dev/null
+++ b/thirdparty/freetype/include/freetype/internal/ftpsprop.h
@@ -0,0 +1,48 @@
+/***************************************************************************/
+/* */
+/* ftpsprop.h */
+/* */
+/* Get and set properties of PostScript drivers (specification). */
+/* */
+/* Copyright 2017-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef FTPSPROP_H_
+#define FTPSPROP_H_
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_BASE_CALLBACK( FT_Error )
+ ps_property_set( FT_Module module, /* PS_Driver */
+ const char* property_name,
+ const void* value,
+ FT_Bool value_is_string );
+
+ FT_BASE_CALLBACK( FT_Error )
+ ps_property_get( FT_Module module, /* PS_Driver */
+ const char* property_name,
+ void* value );
+
+
+FT_END_HEADER
+
+
+#endif /* FTPSPROP_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/include/freetype/internal/ftrfork.h b/thirdparty/freetype/include/freetype/internal/ftrfork.h
index 25a44a4487..1aca48a0e7 100644
--- a/thirdparty/freetype/include/freetype/internal/ftrfork.h
+++ b/thirdparty/freetype/include/freetype/internal/ftrfork.h
@@ -4,7 +4,7 @@
/* */
/* Embedded resource forks accessor (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* Masatake YAMATO and Redhat K.K. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/ftserv.h b/thirdparty/freetype/include/freetype/internal/ftserv.h
index 71ef9cac3a..e01c1679b5 100644
--- a/thirdparty/freetype/include/freetype/internal/ftserv.h
+++ b/thirdparty/freetype/include/freetype/internal/ftserv.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType services (specification only). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -330,6 +330,32 @@ FT_BEGIN_HEADER
{ NULL, NULL } \
};
+#define FT_DEFINE_SERVICEDESCREC10( class_, \
+ serv_id_1, serv_data_1, \
+ serv_id_2, serv_data_2, \
+ serv_id_3, serv_data_3, \
+ serv_id_4, serv_data_4, \
+ serv_id_5, serv_data_5, \
+ serv_id_6, serv_data_6, \
+ serv_id_7, serv_data_7, \
+ serv_id_8, serv_data_8, \
+ serv_id_9, serv_data_9, \
+ serv_id_10, serv_data_10 ) \
+ static const FT_ServiceDescRec class_[] = \
+ { \
+ { serv_id_1, serv_data_1 }, \
+ { serv_id_2, serv_data_2 }, \
+ { serv_id_3, serv_data_3 }, \
+ { serv_id_4, serv_data_4 }, \
+ { serv_id_5, serv_data_5 }, \
+ { serv_id_6, serv_data_6 }, \
+ { serv_id_7, serv_data_7 }, \
+ { serv_id_8, serv_data_8 }, \
+ { serv_id_9, serv_data_9 }, \
+ { serv_id_10, serv_data_10 }, \
+ { NULL, NULL } \
+ };
+
#else /* FT_CONFIG_OPTION_PIC */
#define FT_DEFINE_SERVICEDESCREC1( class_, \
@@ -557,7 +583,7 @@ FT_BEGIN_HEADER
\
FT_Error \
FT_Create_Class_ ## class_( FT_Library library, \
- FT_ServiceDescRec** output_class) \
+ FT_ServiceDescRec** output_class ) \
{ \
FT_ServiceDescRec* clazz = NULL; \
FT_Error error; \
@@ -608,7 +634,7 @@ FT_BEGIN_HEADER
\
FT_Error \
FT_Create_Class_ ## class_( FT_Library library, \
- FT_ServiceDescRec** output_class) \
+ FT_ServiceDescRec** output_class ) \
{ \
FT_ServiceDescRec* clazz = NULL; \
FT_Error error; \
@@ -662,7 +688,7 @@ FT_BEGIN_HEADER
\
FT_Error \
FT_Create_Class_ ## class_( FT_Library library, \
- FT_ServiceDescRec** output_class) \
+ FT_ServiceDescRec** output_class ) \
{ \
FT_ServiceDescRec* clazz = NULL; \
FT_Error error; \
@@ -719,7 +745,7 @@ FT_BEGIN_HEADER
\
FT_Error \
FT_Create_Class_ ## class_( FT_Library library, \
- FT_ServiceDescRec** output_class) \
+ FT_ServiceDescRec** output_class ) \
{ \
FT_ServiceDescRec* clazz = NULL; \
FT_Error error; \
@@ -755,6 +781,68 @@ FT_BEGIN_HEADER
return FT_Err_Ok; \
}
+#define FT_DEFINE_SERVICEDESCREC10( class_, \
+ serv_id_1, serv_data_1, \
+ serv_id_2, serv_data_2, \
+ serv_id_3, serv_data_3, \
+ serv_id_4, serv_data_4, \
+ serv_id_5, serv_data_5, \
+ serv_id_6, serv_data_6, \
+ serv_id_7, serv_data_7, \
+ serv_id_8, serv_data_8, \
+ serv_id_9, serv_data_9, \
+ serv_id_10, serv_data_10 ) \
+ void \
+ FT_Destroy_Class_ ## class_( FT_Library library, \
+ FT_ServiceDescRec* clazz ) \
+ { \
+ FT_Memory memory = library->memory; \
+ \
+ \
+ if ( clazz ) \
+ FT_FREE( clazz ); \
+ } \
+ \
+ FT_Error \
+ FT_Create_Class_ ## class_( FT_Library library, \
+ FT_ServiceDescRec** output_class ) \
+ { \
+ FT_ServiceDescRec* clazz = NULL; \
+ FT_Error error; \
+ FT_Memory memory = library->memory; \
+ \
+ \
+ if ( FT_ALLOC( clazz, sizeof ( *clazz ) * 11 ) ) \
+ return error; \
+ \
+ clazz[ 0].serv_id = serv_id_1; \
+ clazz[ 0].serv_data = serv_data_1; \
+ clazz[ 1].serv_id = serv_id_2; \
+ clazz[ 1].serv_data = serv_data_2; \
+ clazz[ 2].serv_id = serv_id_3; \
+ clazz[ 2].serv_data = serv_data_3; \
+ clazz[ 3].serv_id = serv_id_4; \
+ clazz[ 3].serv_data = serv_data_4; \
+ clazz[ 4].serv_id = serv_id_5; \
+ clazz[ 4].serv_data = serv_data_5; \
+ clazz[ 5].serv_id = serv_id_6; \
+ clazz[ 5].serv_data = serv_data_6; \
+ clazz[ 6].serv_id = serv_id_7; \
+ clazz[ 6].serv_data = serv_data_7; \
+ clazz[ 7].serv_id = serv_id_8; \
+ clazz[ 7].serv_data = serv_data_8; \
+ clazz[ 8].serv_id = serv_id_9; \
+ clazz[ 8].serv_data = serv_data_9; \
+ clazz[ 9].serv_id = serv_id_10; \
+ clazz[ 9].serv_data = serv_data_10; \
+ clazz[10].serv_id = NULL; \
+ clazz[10].serv_data = NULL; \
+ \
+ *output_class = clazz; \
+ \
+ return FT_Err_Ok; \
+ }
+
#endif /* FT_CONFIG_OPTION_PIC */
@@ -898,7 +986,9 @@ FT_BEGIN_HEADER
*/
#define FT_SERVICE_BDF_H <freetype/internal/services/svbdf.h>
+#define FT_SERVICE_CFF_TABLE_LOAD_H <freetype/internal/services/svcfftl.h>
#define FT_SERVICE_CID_H <freetype/internal/services/svcid.h>
+#define FT_SERVICE_FONT_FORMAT_H <freetype/internal/services/svfntfmt.h>
#define FT_SERVICE_GLYPH_DICT_H <freetype/internal/services/svgldict.h>
#define FT_SERVICE_GX_VALIDATE_H <freetype/internal/services/svgxval.h>
#define FT_SERVICE_KERNING_H <freetype/internal/services/svkern.h>
@@ -912,10 +1002,9 @@ FT_BEGIN_HEADER
#define FT_SERVICE_PROPERTIES_H <freetype/internal/services/svprop.h>
#define FT_SERVICE_SFNT_H <freetype/internal/services/svsfnt.h>
#define FT_SERVICE_TRUETYPE_ENGINE_H <freetype/internal/services/svtteng.h>
+#define FT_SERVICE_TRUETYPE_GLYF_H <freetype/internal/services/svttglyf.h>
#define FT_SERVICE_TT_CMAP_H <freetype/internal/services/svttcmap.h>
#define FT_SERVICE_WINFNT_H <freetype/internal/services/svwinfnt.h>
-#define FT_SERVICE_FONT_FORMAT_H <freetype/internal/services/svfntfmt.h>
-#define FT_SERVICE_TRUETYPE_GLYF_H <freetype/internal/services/svttglyf.h>
/* */
diff --git a/thirdparty/freetype/include/freetype/internal/ftstream.h b/thirdparty/freetype/include/freetype/internal/ftstream.h
index 3e2c07b269..f90002fe77 100644
--- a/thirdparty/freetype/include/freetype/internal/ftstream.h
+++ b/thirdparty/freetype/include/freetype/internal/ftstream.h
@@ -4,7 +4,7 @@
/* */
/* Stream handling (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -165,8 +165,8 @@ FT_BEGIN_HEADER
#define FT_BYTE_U32( p, i, s ) ( FT_UINT32( FT_BYTE_( p, i ) ) << (s) )
-#define FT_PEEK_SHORT( p ) FT_INT16( FT_BYTE_U16( p, 0, 8) | \
- FT_BYTE_U16( p, 1, 0) )
+#define FT_PEEK_SHORT( p ) FT_INT16( FT_BYTE_U16( p, 0, 8 ) | \
+ FT_BYTE_U16( p, 1, 0 ) )
#define FT_PEEK_USHORT( p ) FT_UINT16( FT_BYTE_U16( p, 0, 8 ) | \
FT_BYTE_U16( p, 1, 0 ) )
diff --git a/thirdparty/freetype/include/freetype/internal/fttrace.h b/thirdparty/freetype/include/freetype/internal/fttrace.h
index caf5fc9460..8092e41fd7 100644
--- a/thirdparty/freetype/include/freetype/internal/fttrace.h
+++ b/thirdparty/freetype/include/freetype/internal/fttrace.h
@@ -4,7 +4,7 @@
/* */
/* Tracing handling (specification only). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -39,6 +39,7 @@ FT_TRACE_DEF( mm ) /* MM interface (ftmm.c) */
FT_TRACE_DEF( raccess ) /* resource fork accessor (ftrfork.c) */
FT_TRACE_DEF( synth ) /* bold/slant synthesizer (ftsynth.c) */
FT_TRACE_DEF( bitmap ) /* bitmap checksum (ftobjs.c) */
+FT_TRACE_DEF( psprops ) /* PS driver properties (ftpsprop.c) */
/* Cache sub-system */
FT_TRACE_DEF( cache ) /* cache sub-system (ftcache.c, etc.) */
@@ -66,20 +67,19 @@ FT_TRACE_DEF( ttgxvar ) /* TrueType GX var handler (ttgxvar.c) */
FT_TRACE_DEF( t1afm )
FT_TRACE_DEF( t1driver )
FT_TRACE_DEF( t1gload )
-FT_TRACE_DEF( t1hint )
FT_TRACE_DEF( t1load )
FT_TRACE_DEF( t1objs )
FT_TRACE_DEF( t1parse )
/* PostScript helper module `psaux' */
FT_TRACE_DEF( t1decode )
+FT_TRACE_DEF( cffdecode )
FT_TRACE_DEF( psobjs )
FT_TRACE_DEF( psconv )
/* PostScript hinting module `pshinter' */
FT_TRACE_DEF( pshrec )
-FT_TRACE_DEF( pshalgo1 )
-FT_TRACE_DEF( pshalgo2 )
+FT_TRACE_DEF( pshalgo )
/* Type 2 driver components */
FT_TRACE_DEF( cffdriver )
@@ -96,7 +96,6 @@ FT_TRACE_DEF( cf2interp )
FT_TRACE_DEF( t42 )
/* CID driver components */
-FT_TRACE_DEF( cidafm )
FT_TRACE_DEF( ciddriver )
FT_TRACE_DEF( cidgload )
FT_TRACE_DEF( cidload )
diff --git a/thirdparty/freetype/include/freetype/internal/ftvalid.h b/thirdparty/freetype/include/freetype/internal/ftvalid.h
index df6f7c5778..cad47a556d 100644
--- a/thirdparty/freetype/include/freetype/internal/ftvalid.h
+++ b/thirdparty/freetype/include/freetype/internal/ftvalid.h
@@ -4,7 +4,7 @@
/* */
/* FreeType validation support (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/internal.h b/thirdparty/freetype/include/freetype/internal/internal.h
index 02046813a3..8f546e443b 100644
--- a/thirdparty/freetype/include/freetype/internal/internal.h
+++ b/thirdparty/freetype/include/freetype/internal/internal.h
@@ -4,7 +4,7 @@
/* */
/* Internal header files (specification only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -31,7 +31,7 @@
#define FT_INTERNAL_DEBUG_H <freetype/internal/ftdebug.h>
#define FT_INTERNAL_CALC_H <freetype/internal/ftcalc.h>
#define FT_INTERNAL_HASH_H <freetype/internal/fthash.h>
-#define FT_INTERNAL_DRIVER_H <freetype/internal/ftdriver.h>
+#define FT_INTERNAL_DRIVER_H <freetype/internal/ftdrv.h>
#define FT_INTERNAL_TRACE_H <freetype/internal/fttrace.h>
#define FT_INTERNAL_GLYPH_LOADER_H <freetype/internal/ftgloadr.h>
#define FT_INTERNAL_SFNT_H <freetype/internal/sfnt.h>
@@ -44,9 +44,13 @@
#define FT_INTERNAL_POSTSCRIPT_AUX_H <freetype/internal/psaux.h>
#define FT_INTERNAL_POSTSCRIPT_HINTS_H <freetype/internal/pshints.h>
+#define FT_INTERNAL_POSTSCRIPT_PROPS_H <freetype/internal/ftpsprop.h>
#define FT_INTERNAL_AUTOHINT_H <freetype/internal/autohint.h>
+#define FT_INTERNAL_CFF_TYPES_H <freetype/internal/cfftypes.h>
+#define FT_INTERNAL_CFF_OBJECTS_TYPES_H <freetype/internal/cffotypes.h>
+
#if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */
diff --git a/thirdparty/freetype/include/freetype/internal/psaux.h b/thirdparty/freetype/include/freetype/internal/psaux.h
index 935eb1a9c7..f77380d25f 100644
--- a/thirdparty/freetype/include/freetype/internal/psaux.h
+++ b/thirdparty/freetype/include/freetype/internal/psaux.h
@@ -5,7 +5,7 @@
/* Auxiliary functions and data structures related to PostScript fonts */
/* (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -25,12 +25,32 @@
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_TYPE1_TYPES_H
#include FT_INTERNAL_HASH_H
+#include FT_INTERNAL_TRUETYPE_TYPES_H
#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+#include FT_INTERNAL_CFF_TYPES_H
+#include FT_INTERNAL_CFF_OBJECTS_TYPES_H
+
FT_BEGIN_HEADER
+ /***********************************************************************/
+ /* */
+ /* PostScript modules driver class. */
+ /* */
+ typedef struct PS_DriverRec_
+ {
+ FT_DriverRec root;
+
+ FT_UInt hinting_engine;
+ FT_Bool no_stem_darkening;
+ FT_Int darken_params[8];
+ FT_Int32 random_seed;
+
+ } PS_DriverRec, *PS_Driver;
+
+
/*************************************************************************/
/*************************************************************************/
/***** *****/
@@ -442,6 +462,202 @@ FT_BEGIN_HEADER
/*************************************************************************/
/*************************************************************************/
/***** *****/
+ /***** PS BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ typedef struct PS_Builder_ PS_Builder;
+ typedef const struct PS_Builder_FuncsRec_* PS_Builder_Funcs;
+
+ typedef struct PS_Builder_FuncsRec_
+ {
+ void
+ (*init)( PS_Builder* ps_builder,
+ void* builder,
+ FT_Bool is_t1 );
+
+ void
+ (*done)( PS_Builder* builder );
+
+ } PS_Builder_FuncsRec;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Structure> */
+ /* PS_Builder */
+ /* */
+ /* <Description> */
+ /* A structure used during glyph loading to store its outline. */
+ /* */
+ /* <Fields> */
+ /* memory :: The current memory object. */
+ /* */
+ /* face :: The current face object. */
+ /* */
+ /* glyph :: The current glyph slot. */
+ /* */
+ /* loader :: XXX */
+ /* */
+ /* base :: The base glyph outline. */
+ /* */
+ /* current :: The current glyph outline. */
+ /* */
+ /* pos_x :: The horizontal translation (if composite glyph). */
+ /* */
+ /* pos_y :: The vertical translation (if composite glyph). */
+ /* */
+ /* left_bearing :: The left side bearing point. */
+ /* */
+ /* advance :: The horizontal advance vector. */
+ /* */
+ /* bbox :: Unused. */
+ /* */
+ /* path_begun :: A flag which indicates that a new path has begun. */
+ /* */
+ /* load_points :: If this flag is not set, no points are loaded. */
+ /* */
+ /* no_recurse :: Set but not used. */
+ /* */
+ /* metrics_only :: A boolean indicating that we only want to compute */
+ /* the metrics of a given glyph, not load all of its */
+ /* points. */
+ /* */
+ /* is_t1 :: Set if current font type is Type 1. */
+ /* */
+ /* funcs :: An array of function pointers for the builder. */
+ /* */
+ struct PS_Builder_
+ {
+ FT_Memory memory;
+ FT_Face face;
+ CFF_GlyphSlot glyph;
+ FT_GlyphLoader loader;
+ FT_Outline* base;
+ FT_Outline* current;
+
+ FT_Pos* pos_x;
+ FT_Pos* pos_y;
+
+ FT_Vector* left_bearing;
+ FT_Vector* advance;
+
+ FT_BBox* bbox; /* bounding box */
+ FT_Bool path_begun;
+ FT_Bool load_points;
+ FT_Bool no_recurse;
+
+ FT_Bool metrics_only;
+ FT_Bool is_t1;
+
+ PS_Builder_FuncsRec funcs;
+
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PS DECODER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define PS_MAX_OPERANDS 48
+#define PS_MAX_SUBRS_CALLS 16 /* maximum subroutine nesting; */
+ /* only 10 are allowed but there exist */
+ /* fonts like `HiraKakuProN-W3.ttf' */
+ /* (Hiragino Kaku Gothic ProN W3; */
+ /* 8.2d6e1; 2014-12-19) that exceed */
+ /* this limit */
+
+ /* execution context charstring zone */
+
+ typedef struct PS_Decoder_Zone_
+ {
+ FT_Byte* base;
+ FT_Byte* limit;
+ FT_Byte* cursor;
+
+ } PS_Decoder_Zone;
+
+
+ typedef FT_Error
+ (*CFF_Decoder_Get_Glyph_Callback)( TT_Face face,
+ FT_UInt glyph_index,
+ FT_Byte** pointer,
+ FT_ULong* length );
+
+ typedef void
+ (*CFF_Decoder_Free_Glyph_Callback)( TT_Face face,
+ FT_Byte** pointer,
+ FT_ULong length );
+
+
+ typedef struct PS_Decoder_
+ {
+ PS_Builder builder;
+
+ FT_Fixed stack[PS_MAX_OPERANDS + 1];
+ FT_Fixed* top;
+
+ PS_Decoder_Zone zones[PS_MAX_SUBRS_CALLS + 1];
+ PS_Decoder_Zone* zone;
+
+ FT_Int flex_state;
+ FT_Int num_flex_vectors;
+ FT_Vector flex_vectors[7];
+
+ CFF_Font cff;
+ CFF_SubFont current_subfont; /* for current glyph_index */
+ FT_Generic* cf2_instance;
+
+ FT_Pos* glyph_width;
+ FT_Bool width_only;
+ FT_Int num_hints;
+
+ FT_UInt num_locals;
+ FT_UInt num_globals;
+
+ FT_Int locals_bias;
+ FT_Int globals_bias;
+
+ FT_Byte** locals;
+ FT_Byte** globals;
+
+ FT_Byte** glyph_names; /* for pure CFF fonts only */
+ FT_UInt num_glyphs; /* number of glyphs in font */
+
+ FT_Render_Mode hint_mode;
+
+ FT_Bool seac;
+
+ CFF_Decoder_Get_Glyph_Callback get_glyph_callback;
+ CFF_Decoder_Free_Glyph_Callback free_glyph_callback;
+
+ /* Type 1 stuff */
+ FT_Service_PsCMaps psnames; /* for seac */
+
+ FT_Int lenIV; /* internal for sub routine calls */
+ FT_UInt* locals_len; /* array of subrs length (optional) */
+ FT_Hash locals_hash; /* used if `num_subrs' was massaged */
+
+ FT_Matrix font_matrix;
+ FT_Vector font_offset;
+
+ PS_Blend blend; /* for multiple master support */
+
+ FT_Long* buildchar;
+ FT_UInt len_buildchar;
+
+ } PS_Decoder;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
/***** T1 BUILDER *****/
/***** *****/
/*************************************************************************/
@@ -653,10 +869,23 @@ FT_BEGIN_HEADER
void
(*done)( T1_Decoder decoder );
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ FT_Error
+ (*parse_charstrings_old)( T1_Decoder decoder,
+ FT_Byte* base,
+ FT_UInt len );
+#else
+ FT_Error
+ (*parse_metrics)( T1_Decoder decoder,
+ FT_Byte* base,
+ FT_UInt len );
+#endif
+
FT_Error
- (*parse_charstrings)( T1_Decoder decoder,
- FT_Byte* base,
- FT_UInt len );
+ (*parse_charstrings)( PS_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len );
+
} T1_Decoder_FuncsRec;
@@ -700,12 +929,261 @@ FT_BEGIN_HEADER
FT_Bool seac;
+ FT_Generic cf2_instance;
+
} T1_DecoderRec;
/*************************************************************************/
/*************************************************************************/
/***** *****/
+ /***** CFF BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ typedef struct CFF_Builder_ CFF_Builder;
+
+
+ typedef FT_Error
+ (*CFF_Builder_Check_Points_Func)( CFF_Builder* builder,
+ FT_Int count );
+
+ typedef void
+ (*CFF_Builder_Add_Point_Func)( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag );
+ typedef FT_Error
+ (*CFF_Builder_Add_Point1_Func)( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y );
+ typedef FT_Error
+ (*CFF_Builder_Start_Point_Func)( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y );
+ typedef void
+ (*CFF_Builder_Close_Contour_Func)( CFF_Builder* builder );
+
+ typedef FT_Error
+ (*CFF_Builder_Add_Contour_Func)( CFF_Builder* builder );
+
+ typedef const struct CFF_Builder_FuncsRec_* CFF_Builder_Funcs;
+
+ typedef struct CFF_Builder_FuncsRec_
+ {
+ void
+ (*init)( CFF_Builder* builder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot glyph,
+ FT_Bool hinting );
+
+ void
+ (*done)( CFF_Builder* builder );
+
+ CFF_Builder_Check_Points_Func check_points;
+ CFF_Builder_Add_Point_Func add_point;
+ CFF_Builder_Add_Point1_Func add_point1;
+ CFF_Builder_Add_Contour_Func add_contour;
+ CFF_Builder_Start_Point_Func start_point;
+ CFF_Builder_Close_Contour_Func close_contour;
+
+ } CFF_Builder_FuncsRec;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Structure> */
+ /* CFF_Builder */
+ /* */
+ /* <Description> */
+ /* A structure used during glyph loading to store its outline. */
+ /* */
+ /* <Fields> */
+ /* memory :: The current memory object. */
+ /* */
+ /* face :: The current face object. */
+ /* */
+ /* glyph :: The current glyph slot. */
+ /* */
+ /* loader :: The current glyph loader. */
+ /* */
+ /* base :: The base glyph outline. */
+ /* */
+ /* current :: The current glyph outline. */
+ /* */
+ /* pos_x :: The horizontal translation (if composite glyph). */
+ /* */
+ /* pos_y :: The vertical translation (if composite glyph). */
+ /* */
+ /* left_bearing :: The left side bearing point. */
+ /* */
+ /* advance :: The horizontal advance vector. */
+ /* */
+ /* bbox :: Unused. */
+ /* */
+ /* path_begun :: A flag which indicates that a new path has begun. */
+ /* */
+ /* load_points :: If this flag is not set, no points are loaded. */
+ /* */
+ /* no_recurse :: Set but not used. */
+ /* */
+ /* metrics_only :: A boolean indicating that we only want to compute */
+ /* the metrics of a given glyph, not load all of its */
+ /* points. */
+ /* */
+ /* hints_funcs :: Auxiliary pointer for hinting. */
+ /* */
+ /* hints_globals :: Auxiliary pointer for hinting. */
+ /* */
+ /* funcs :: A table of method pointers for this object. */
+ /* */
+ struct CFF_Builder_
+ {
+ FT_Memory memory;
+ TT_Face face;
+ CFF_GlyphSlot glyph;
+ FT_GlyphLoader loader;
+ FT_Outline* base;
+ FT_Outline* current;
+
+ FT_Pos pos_x;
+ FT_Pos pos_y;
+
+ FT_Vector left_bearing;
+ FT_Vector advance;
+
+ FT_BBox bbox; /* bounding box */
+
+ FT_Bool path_begun;
+ FT_Bool load_points;
+ FT_Bool no_recurse;
+
+ FT_Bool metrics_only;
+
+ void* hints_funcs; /* hinter-specific */
+ void* hints_globals; /* hinter-specific */
+
+ CFF_Builder_FuncsRec funcs;
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CFF DECODER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+#define CFF_MAX_OPERANDS 48
+#define CFF_MAX_SUBRS_CALLS 16 /* maximum subroutine nesting; */
+ /* only 10 are allowed but there exist */
+ /* fonts like `HiraKakuProN-W3.ttf' */
+ /* (Hiragino Kaku Gothic ProN W3; */
+ /* 8.2d6e1; 2014-12-19) that exceed */
+ /* this limit */
+#define CFF_MAX_TRANS_ELEMENTS 32
+
+ /* execution context charstring zone */
+
+ typedef struct CFF_Decoder_Zone_
+ {
+ FT_Byte* base;
+ FT_Byte* limit;
+ FT_Byte* cursor;
+
+ } CFF_Decoder_Zone;
+
+
+ typedef struct CFF_Decoder_
+ {
+ CFF_Builder builder;
+ CFF_Font cff;
+
+ FT_Fixed stack[CFF_MAX_OPERANDS + 1];
+ FT_Fixed* top;
+
+ CFF_Decoder_Zone zones[CFF_MAX_SUBRS_CALLS + 1];
+ CFF_Decoder_Zone* zone;
+
+ FT_Int flex_state;
+ FT_Int num_flex_vectors;
+ FT_Vector flex_vectors[7];
+
+ FT_Pos glyph_width;
+ FT_Pos nominal_width;
+
+ FT_Bool read_width;
+ FT_Bool width_only;
+ FT_Int num_hints;
+ FT_Fixed buildchar[CFF_MAX_TRANS_ELEMENTS];
+
+ FT_UInt num_locals;
+ FT_UInt num_globals;
+
+ FT_Int locals_bias;
+ FT_Int globals_bias;
+
+ FT_Byte** locals;
+ FT_Byte** globals;
+
+ FT_Byte** glyph_names; /* for pure CFF fonts only */
+ FT_UInt num_glyphs; /* number of glyphs in font */
+
+ FT_Render_Mode hint_mode;
+
+ FT_Bool seac;
+
+ CFF_SubFont current_subfont; /* for current glyph_index */
+
+ CFF_Decoder_Get_Glyph_Callback get_glyph_callback;
+ CFF_Decoder_Free_Glyph_Callback free_glyph_callback;
+
+ } CFF_Decoder;
+
+
+ typedef const struct CFF_Decoder_FuncsRec_* CFF_Decoder_Funcs;
+
+ typedef struct CFF_Decoder_FuncsRec_
+ {
+ void
+ (*init)( CFF_Decoder* decoder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot slot,
+ FT_Bool hinting,
+ FT_Render_Mode hint_mode,
+ CFF_Decoder_Get_Glyph_Callback get_callback,
+ CFF_Decoder_Free_Glyph_Callback free_callback );
+
+ FT_Error
+ (*prepare)( CFF_Decoder* decoder,
+ CFF_Size size,
+ FT_UInt glyph_index );
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ FT_Error
+ (*parse_charstrings_old)( CFF_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len,
+ FT_Bool in_dict );
+#endif
+
+ FT_Error
+ (*parse_charstrings)( PS_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len );
+
+ } CFF_Decoder_FuncsRec;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
/***** AFM PARSER *****/
/***** *****/
/*************************************************************************/
@@ -810,11 +1288,26 @@ FT_BEGIN_HEADER
FT_Offset length,
FT_UShort seed );
+ FT_UInt32
+ (*cff_random)( FT_UInt32 r );
+
+ void
+ (*ps_decoder_init)( PS_Decoder* ps_decoder,
+ void* decoder,
+ FT_Bool is_t1 );
+
+ void
+ (*t1_make_subfont)( FT_Face face,
+ PS_Private priv,
+ CFF_SubFont subfont );
+
T1_CMap_Classes t1_cmap_classes;
/* fields after this comment line were added after version 2.1.10 */
const AFM_Parser_FuncsRec* afm_parser_funcs;
+ const CFF_Decoder_FuncsRec* cff_decoder_funcs;
+
} PSAux_ServiceRec, *PSAux_Service;
/* backward compatible type definition */
diff --git a/thirdparty/freetype/include/freetype/internal/pshints.h b/thirdparty/freetype/include/freetype/internal/pshints.h
index 49116eb443..d29314ec2e 100644
--- a/thirdparty/freetype/include/freetype/internal/pshints.h
+++ b/thirdparty/freetype/include/freetype/internal/pshints.h
@@ -6,7 +6,7 @@
/* recorders (specification only). These are used to support native */
/* T1/T2 hints in the `type1', `cid', and `cff' font drivers. */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svbdf.h b/thirdparty/freetype/include/freetype/internal/services/svbdf.h
index eeebf67da1..4a9ec20075 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svbdf.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svbdf.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType BDF services (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svcfftl.h b/thirdparty/freetype/include/freetype/internal/services/svcfftl.h
new file mode 100644
index 0000000000..db623e6840
--- /dev/null
+++ b/thirdparty/freetype/include/freetype/internal/services/svcfftl.h
@@ -0,0 +1,112 @@
+/***************************************************************************/
+/* */
+/* svcfftl.h */
+/* */
+/* The FreeType CFF tables loader service (specification). */
+/* */
+/* Copyright 2017-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef SVCFFTL_H_
+#define SVCFFTL_H_
+
+#include FT_INTERNAL_SERVICE_H
+#include FT_INTERNAL_CFF_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+#define FT_SERVICE_ID_CFF_LOAD "cff-load"
+
+
+ typedef FT_UShort
+ (*FT_Get_Standard_Encoding_Func)( FT_UInt charcode );
+
+ typedef FT_Error
+ (*FT_Load_Private_Dict_Func)( CFF_Font font,
+ CFF_SubFont subfont,
+ FT_UInt lenNDV,
+ FT_Fixed* NDV );
+
+ typedef FT_Byte
+ (*FT_FD_Select_Get_Func)( CFF_FDSelect fdselect,
+ FT_UInt glyph_index );
+
+ typedef FT_Bool
+ (*FT_Blend_Check_Vector_Func)( CFF_Blend blend,
+ FT_UInt vsindex,
+ FT_UInt lenNDV,
+ FT_Fixed* NDV );
+
+ typedef FT_Error
+ (*FT_Blend_Build_Vector_Func)( CFF_Blend blend,
+ FT_UInt vsindex,
+ FT_UInt lenNDV,
+ FT_Fixed* NDV );
+
+
+ FT_DEFINE_SERVICE( CFFLoad )
+ {
+ FT_Get_Standard_Encoding_Func get_standard_encoding;
+ FT_Load_Private_Dict_Func load_private_dict;
+ FT_FD_Select_Get_Func fd_select_get;
+ FT_Blend_Check_Vector_Func blend_check_vector;
+ FT_Blend_Build_Vector_Func blend_build_vector;
+ };
+
+
+#ifndef FT_CONFIG_OPTION_PIC
+
+#define FT_DEFINE_SERVICE_CFFLOADREC( class_, \
+ get_standard_encoding_, \
+ load_private_dict_, \
+ fd_select_get_, \
+ blend_check_vector_, \
+ blend_build_vector_ ) \
+ static const FT_Service_CFFLoadRec class_ = \
+ { \
+ get_standard_encoding_, \
+ load_private_dict_, \
+ fd_select_get_, \
+ blend_check_vector_, \
+ blend_build_vector_ \
+ };
+
+#else /* FT_CONFIG_OPTION_PIC */
+
+#define FT_DEFINE_SERVICE_CFFLOADREC( class_, \
+ get_standard_encoding_, \
+ load_private_dict_, \
+ fd_select_get_, \
+ blend_check_vector_, \
+ blend_build_vector_ ) \
+ void \
+ FT_Init_Class_ ## class_( FT_Service_CFFLoadRec* clazz ) \
+ { \
+ clazz->get_standard_encoding = get_standard_encoding_; \
+ clazz->load_private_dict = load_private_dict_; \
+ clazz->fd_select_get = fd_select_get_; \
+ clazz->blend_check_vector = blend_check_vector_; \
+ clazz->blend_build_vector = blend_build_vector_; \
+ }
+
+#endif /* FT_CONFIG_OPTION_PIC */
+
+
+FT_END_HEADER
+
+
+#endif
+
+
+/* END */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svcid.h b/thirdparty/freetype/include/freetype/internal/services/svcid.h
index cce94d8df6..cb59ac6a29 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svcid.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svcid.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType CID font services (specification). */
/* */
-/* Copyright 2007-2017 by */
+/* Copyright 2007-2018 by */
/* Derek Clegg and Michael Toftdal. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svfntfmt.h b/thirdparty/freetype/include/freetype/internal/services/svfntfmt.h
index 376d9255bb..3b732be1a1 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svfntfmt.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svfntfmt.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType font format service (specification only). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svgldict.h b/thirdparty/freetype/include/freetype/internal/services/svgldict.h
index 0cd13618d8..f1a68e3110 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svgldict.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svgldict.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType glyph dictionary services (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -56,7 +56,7 @@ FT_BEGIN_HEADER
#define FT_DEFINE_SERVICE_GLYPHDICTREC( class_, \
get_name_, \
- name_index_) \
+ name_index_ ) \
static const FT_Service_GlyphDictRec class_ = \
{ \
get_name_, name_index_ \
@@ -66,7 +66,7 @@ FT_BEGIN_HEADER
#define FT_DEFINE_SERVICE_GLYPHDICTREC( class_, \
get_name_, \
- name_index_) \
+ name_index_ ) \
void \
FT_Init_Class_ ## class_( FT_Library library, \
FT_Service_GlyphDictRec* clazz ) \
diff --git a/thirdparty/freetype/include/freetype/internal/services/svgxval.h b/thirdparty/freetype/include/freetype/internal/services/svgxval.h
index 71bfa97af8..ed79ebeaa8 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svgxval.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svgxval.h
@@ -4,7 +4,7 @@
/* */
/* FreeType API for validating TrueTypeGX/AAT tables (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svkern.h b/thirdparty/freetype/include/freetype/internal/services/svkern.h
index b8344e96e9..c7e8f6ef27 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svkern.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svkern.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType Kerning service (specification). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svmetric.h b/thirdparty/freetype/include/freetype/internal/services/svmetric.h
index 1f7d5ddd0c..abaacddbbe 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svmetric.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svmetric.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType services for metrics variations (specification). */
/* */
-/* Copyright 2016-2017 by */
+/* Copyright 2016-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svmm.h b/thirdparty/freetype/include/freetype/internal/services/svmm.h
index 1d51cd9090..bcbb38e2ce 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svmm.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svmm.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType Multiple Masters and GX var services (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -48,11 +48,15 @@ FT_BEGIN_HEADER
FT_UInt num_coords,
FT_Long* coords );
+ /* use return value -1 to indicate that the new coordinates */
+ /* are equal to the current ones; no changes are thus needed */
typedef FT_Error
(*FT_Set_Var_Design_Func)( FT_Face face,
FT_UInt num_coords,
FT_Fixed* coords );
+ /* use return value -1 to indicate that the new coordinates */
+ /* are equal to the current ones; no changes are thus needed */
typedef FT_Error
(*FT_Set_MM_Blend_Func)( FT_Face face,
FT_UInt num_coords,
@@ -64,6 +68,10 @@ FT_BEGIN_HEADER
FT_Fixed* coords );
typedef FT_Error
+ (*FT_Set_Instance_Func)( FT_Face face,
+ FT_UInt instance_index );
+
+ typedef FT_Error
(*FT_Get_MM_Blend_Func)( FT_Face face,
FT_UInt num_coords,
FT_Long* coords );
@@ -88,6 +96,7 @@ FT_BEGIN_HEADER
FT_Get_MM_Var_Func get_mm_var;
FT_Set_Var_Design_Func set_var_design;
FT_Get_Var_Design_Func get_var_design;
+ FT_Set_Instance_Func set_instance;
/* for internal use; only needed for code sharing between modules */
FT_Get_Var_Blend_Func get_var_blend;
@@ -97,27 +106,29 @@ FT_BEGIN_HEADER
#ifndef FT_CONFIG_OPTION_PIC
-#define FT_DEFINE_SERVICE_MULTIMASTERSREC( class_, \
- get_mm_, \
- set_mm_design_, \
- set_mm_blend_, \
- get_mm_blend_, \
- get_mm_var_, \
- set_var_design_, \
- get_var_design_, \
- get_var_blend_, \
- done_blend_ ) \
- static const FT_Service_MultiMastersRec class_ = \
- { \
- get_mm_, \
- set_mm_design_, \
- set_mm_blend_, \
- get_mm_blend_, \
- get_mm_var_, \
- set_var_design_, \
- get_var_design_, \
- get_var_blend_, \
- done_blend_ \
+#define FT_DEFINE_SERVICE_MULTIMASTERSREC( class_, \
+ get_mm_, \
+ set_mm_design_, \
+ set_mm_blend_, \
+ get_mm_blend_, \
+ get_mm_var_, \
+ set_var_design_, \
+ get_var_design_, \
+ set_instance_, \
+ get_var_blend_, \
+ done_blend_ ) \
+ static const FT_Service_MultiMastersRec class_ = \
+ { \
+ get_mm_, \
+ set_mm_design_, \
+ set_mm_blend_, \
+ get_mm_blend_, \
+ get_mm_var_, \
+ set_var_design_, \
+ get_var_design_, \
+ set_instance_, \
+ get_var_blend_, \
+ done_blend_ \
};
#else /* FT_CONFIG_OPTION_PIC */
@@ -130,6 +141,7 @@ FT_BEGIN_HEADER
get_mm_var_, \
set_var_design_, \
get_var_design_, \
+ set_instance_, \
get_var_blend_, \
done_blend_ ) \
void \
@@ -142,6 +154,7 @@ FT_BEGIN_HEADER
clazz->get_mm_var = get_mm_var_; \
clazz->set_var_design = set_var_design_; \
clazz->get_var_design = get_var_design_; \
+ clazz->set_instance = set_instance_; \
clazz->get_var_blend = get_var_blend_; \
clazz->done_blend = done_blend_; \
}
diff --git a/thirdparty/freetype/include/freetype/internal/services/svotval.h b/thirdparty/freetype/include/freetype/internal/services/svotval.h
index ac84abee46..31294296a6 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svotval.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svotval.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType OpenType validation service (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svpfr.h b/thirdparty/freetype/include/freetype/internal/services/svpfr.h
index c9a182fe39..e65d57e91b 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svpfr.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svpfr.h
@@ -4,7 +4,7 @@
/* */
/* Internal PFR service functions (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svpostnm.h b/thirdparty/freetype/include/freetype/internal/services/svpostnm.h
index 022cdec195..4a49d8b053 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svpostnm.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svpostnm.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType PostScript name services (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svprop.h b/thirdparty/freetype/include/freetype/internal/services/svprop.h
index eb2d4eed15..adc0bcf439 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svprop.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svprop.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType property service (specification). */
/* */
-/* Copyright 2012-2017 by */
+/* Copyright 2012-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svpscmap.h b/thirdparty/freetype/include/freetype/internal/services/svpscmap.h
index b32122e5d6..5589575b92 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svpscmap.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svpscmap.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType PostScript charmap service (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svpsinfo.h b/thirdparty/freetype/include/freetype/internal/services/svpsinfo.h
index 0220ce529c..408f406dfa 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svpsinfo.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svpsinfo.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType PostScript info service (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svsfnt.h b/thirdparty/freetype/include/freetype/internal/services/svsfnt.h
index 49d18e43e0..e8b37bc47f 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svsfnt.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svsfnt.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType SFNT table loading service (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svttcmap.h b/thirdparty/freetype/include/freetype/internal/services/svttcmap.h
index 30f7feec71..cd0e6fda6f 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svttcmap.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svttcmap.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType TrueType/sfnt cmap extra information service. */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* Masatake YAMATO, Redhat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svtteng.h b/thirdparty/freetype/include/freetype/internal/services/svtteng.h
index e4b368ad40..92e3c541f5 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svtteng.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svtteng.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType TrueType engine query service (specification). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svttglyf.h b/thirdparty/freetype/include/freetype/internal/services/svttglyf.h
index b7793059fd..16fac1ca18 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svttglyf.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svttglyf.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType TrueType glyph service. */
/* */
-/* Copyright 2007-2017 by */
+/* Copyright 2007-2018 by */
/* David Turner. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svwinfnt.h b/thirdparty/freetype/include/freetype/internal/services/svwinfnt.h
index c94b7e1073..80d481cbd1 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svwinfnt.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svwinfnt.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType Windows FNT/FONT service (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/sfnt.h b/thirdparty/freetype/include/freetype/internal/sfnt.h
index b8667a003a..fb1e327aeb 100644
--- a/thirdparty/freetype/include/freetype/internal/sfnt.h
+++ b/thirdparty/freetype/include/freetype/internal/sfnt.h
@@ -4,7 +4,7 @@
/* */
/* High-level `sfnt' driver interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/t1types.h b/thirdparty/freetype/include/freetype/internal/t1types.h
index b2e35d42d1..2118e33674 100644
--- a/thirdparty/freetype/include/freetype/internal/t1types.h
+++ b/thirdparty/freetype/include/freetype/internal/t1types.h
@@ -5,7 +5,7 @@
/* Basic Type1/Type2 type definitions and interface (specification */
/* only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/internal/tttypes.h b/thirdparty/freetype/include/freetype/internal/tttypes.h
index c0758e25fc..10dd336a89 100644
--- a/thirdparty/freetype/include/freetype/internal/tttypes.h
+++ b/thirdparty/freetype/include/freetype/internal/tttypes.h
@@ -5,7 +5,7 @@
/* Basic SFNT/TrueType type definitions and interface (specification */
/* only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -148,7 +148,7 @@ FT_BEGIN_HEADER
/* <Fields> */
/* See */
/* */
- /* http://www.w3.org/TR/WOFF/#WOFFHeader */
+ /* https://www.w3.org/TR/WOFF/#WOFFHeader */
/* */
typedef struct WOFF_HeaderRec_
{
@@ -1299,10 +1299,6 @@ FT_BEGIN_HEADER
/* variation tables (rather like Multiple */
/* Master data). */
/* */
- /* is_default_instance :: Set if the glyph outlines can be used */
- /* unmodified (i.e., without applying glyph */
- /* variation deltas). */
- /* */
/* variation_support :: Flags that indicate which OpenType */
/* functionality related to font variation */
/* support is present, valid, and usable. */
@@ -1445,6 +1441,9 @@ FT_BEGIN_HEADER
void* var;
#endif
+ /* a typeless pointer to the PostScript Aux service */
+ void* psaux;
+
/***********************************************************************/
/* */
@@ -1509,7 +1508,6 @@ FT_BEGIN_HEADER
FT_Bool doblend;
GX_Blend blend;
- FT_Bool is_default_instance; /* since 2.7.1 */
FT_UInt32 variation_support; /* since 2.7.1 */
const char* var_postscript_prefix; /* since 2.7.2 */
diff --git a/thirdparty/freetype/include/freetype/t1tables.h b/thirdparty/freetype/include/freetype/t1tables.h
index 3f6b36e108..3503c2616b 100644
--- a/thirdparty/freetype/include/freetype/t1tables.h
+++ b/thirdparty/freetype/include/freetype/t1tables.h
@@ -5,7 +5,7 @@
/* Basic Type 1/Type 2 tables definitions and interface (specification */
/* only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -554,6 +554,9 @@ FT_BEGIN_HEADER
/* T1_ENCODING_TYPE_ISOLATIN1 :: */
/* T1_ENCODING_TYPE_EXPERT :: */
/* */
+ /* <Since> */
+ /* 2.4.8 */
+ /* */
typedef enum T1_EncodingType_
{
T1_ENCODING_TYPE_NONE = 0,
@@ -622,6 +625,9 @@ FT_BEGIN_HEADER
/* PS_DICT_FS_TYPE :: */
/* PS_DICT_ITALIC_ANGLE :: */
/* */
+ /* <Since> */
+ /* 2.4.8 */
+ /* */
typedef enum PS_Dict_Keys_
{
/* conventionally in the font dictionary */
@@ -743,6 +749,9 @@ FT_BEGIN_HEADER
* If the font's format is not PostScript-based, this function returns
* the `FT_Err_Invalid_Argument' error code.
*
+ * @since:
+ * 2.4.8
+ *
*/
FT_EXPORT( FT_Long )
FT_Get_PS_Font_Value( FT_Face face,
diff --git a/thirdparty/freetype/include/freetype/ttnameid.h b/thirdparty/freetype/include/freetype/ttnameid.h
index 494d677186..8605183dc7 100644
--- a/thirdparty/freetype/include/freetype/ttnameid.h
+++ b/thirdparty/freetype/include/freetype/ttnameid.h
@@ -4,7 +4,7 @@
/* */
/* TrueType name ID definitions (specification only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -437,7 +437,7 @@ FT_BEGIN_HEADER
*
* The canonical source for Microsoft's IDs is
*
- * http://www.microsoft.com/globaldev/reference/lcid-all.mspx ,
+ * https://www.microsoft.com/globaldev/reference/lcid-all.mspx ,
*
* however, we only provide macros for language identifiers present in
* the OpenType specification: Microsoft has abandoned the concept of
diff --git a/thirdparty/freetype/include/freetype/tttables.h b/thirdparty/freetype/include/freetype/tttables.h
index 58312044ca..ce6a61779c 100644
--- a/thirdparty/freetype/include/freetype/tttables.h
+++ b/thirdparty/freetype/include/freetype/tttables.h
@@ -5,7 +5,7 @@
/* Basic SFNT/TrueType tables definitions and interface */
/* (specification only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/freetype/tttags.h b/thirdparty/freetype/include/freetype/tttags.h
index b7d3bac0f1..e5cee68a15 100644
--- a/thirdparty/freetype/include/freetype/tttags.h
+++ b/thirdparty/freetype/include/freetype/tttags.h
@@ -4,7 +4,7 @@
/* */
/* Tags for TrueType and OpenType tables (specification only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/include/ft2build.h b/thirdparty/freetype/include/ft2build.h
index e7d808f3f4..e7ce99bc94 100644
--- a/thirdparty/freetype/include/ft2build.h
+++ b/thirdparty/freetype/include/ft2build.h
@@ -4,7 +4,7 @@
/* */
/* FreeType 2 build and setup macros. */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afangles.c b/thirdparty/freetype/src/autofit/afangles.c
index ccdae84136..c65a3ae23e 100644
--- a/thirdparty/freetype/src/autofit/afangles.c
+++ b/thirdparty/freetype/src/autofit/afangles.c
@@ -5,7 +5,7 @@
/* Routines used to compute vector angles with limited accuracy */
/* and very high speed. It also contains sorting routines (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afblue.c b/thirdparty/freetype/src/autofit/afblue.c
index fedeacf797..e4078fd044 100644
--- a/thirdparty/freetype/src/autofit/afblue.c
+++ b/thirdparty/freetype/src/autofit/afblue.c
@@ -7,7 +7,7 @@
/* */
/* Auto-fitter data for blue strings (body). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -166,6 +166,10 @@
'\0',
'\xE2', '\xB4', '\x84', ' ', '\xE2', '\xB4', '\x85', ' ', '\xE2', '\xB4', '\x94', ' ', '\xE2', '\xB4', '\x95', ' ', '\xE2', '\xB4', '\x81', ' ', '\xE2', '\xB4', '\x82', ' ', '\xE2', '\xB4', '\x98', ' ', '\xE2', '\xB4', '\x9D', /* â´„ â´… â´” â´• â´ â´‚ â´˜ â´ */
'\0',
+ '\xE1', '\xB2', '\x9C', ' ', '\xE1', '\xB2', '\x9F', ' ', '\xE1', '\xB2', '\xB3', ' ', '\xE1', '\xB2', '\xB8', ' ', '\xE1', '\xB2', '\x92', ' ', '\xE1', '\xB2', '\x94', ' ', '\xE1', '\xB2', '\x9D', ' ', '\xE1', '\xB2', '\xB4', /* Ნ Ჟ Ჳ Ჸ Გ Ე ᲠᲴ */
+ '\0',
+ '\xE1', '\xB2', '\x98', ' ', '\xE1', '\xB2', '\xB2', ' ', '\xE1', '\xB2', '\x9D', ' ', '\xE1', '\xB2', '\xA9', ' ', '\xE1', '\xB2', '\x9B', ' ', '\xE1', '\xB2', '\xA8', ' ', '\xE1', '\xB2', '\xAF', ' ', '\xE1', '\xB2', '\xBD', /* Ი Ჲ ᲠᲩ Მ Შ Ჯ Ჽ */
+ '\0',
'\xE2', '\xB0', '\x85', ' ', '\xE2', '\xB0', '\x94', ' ', '\xE2', '\xB0', '\xAA', ' ', '\xE2', '\xB0', '\x84', ' ', '\xE2', '\xB0', '\x82', ' ', '\xE2', '\xB0', '\x8A', ' ', '\xE2', '\xB0', '\xAB', ' ', '\xE2', '\xB0', '\x8B', /* â°… â°” â°ª â°„ â°‚ â°Š â°« â°‹ */
'\0',
'\xE2', '\xB0', '\x85', ' ', '\xE2', '\xB0', '\x84', ' ', '\xE2', '\xB0', '\x82', ' ', '\xE2', '\xB0', '\xAA', ' ', '\xE2', '\xB0', '\x9E', ' ', '\xE2', '\xB0', '\xA1', ' ', '\xE2', '\xB0', '\x8A', ' ', '\xE2', '\xB0', '\x94', /* â°… â°„ â°‚ â°ª â°ž â°¡ â°Š â°” */
@@ -539,6 +543,8 @@
{ AF_BLUE_STRING_GEORGIAN_MKHEDRULI_BOTTOM, 0 },
{ AF_BLUE_STRING_GEORGIAN_MKHEDRULI_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP },
{ AF_BLUE_STRING_GEORGIAN_MKHEDRULI_DESCENDER, 0 },
+ { AF_BLUE_STRING_GEORGIAN_MTAVRULI_TOP, AF_BLUE_PROPERTY_LATIN_TOP },
+ { AF_BLUE_STRING_GEORGIAN_MTAVRULI_BOTTOM, 0 },
{ AF_BLUE_STRING_MAX, 0 },
{ AF_BLUE_STRING_GEORGIAN_ASOMTAVRULI_TOP, AF_BLUE_PROPERTY_LATIN_TOP },
{ AF_BLUE_STRING_GEORGIAN_ASOMTAVRULI_BOTTOM, 0 },
diff --git a/thirdparty/freetype/src/autofit/afblue.cin b/thirdparty/freetype/src/autofit/afblue.cin
index f9080c54d4..4913e2eb6f 100644
--- a/thirdparty/freetype/src/autofit/afblue.cin
+++ b/thirdparty/freetype/src/autofit/afblue.cin
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter data for blue strings (body). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afblue.dat b/thirdparty/freetype/src/autofit/afblue.dat
index f62eb82a1a..bc2f0d2754 100644
--- a/thirdparty/freetype/src/autofit/afblue.dat
+++ b/thirdparty/freetype/src/autofit/afblue.dat
@@ -2,7 +2,7 @@
//
// Auto-fitter data for blue strings.
//
-// Copyright 2013-2017 by
+// Copyright 2013-2018 by
// David Turner, Robert Wilhelm, and Werner Lemberg.
//
// This file is part of the FreeType project, and may only be used,
@@ -242,6 +242,11 @@ AF_BLUE_STRING_ENUM AF_BLUE_STRINGS_ARRAY AF_BLUE_STRING_MAX_LEN:
AF_BLUE_STRING_GEORGIAN_NUSKHURI_DESCENDER
"â´„ â´… â´” â´• â´ â´‚ â´˜ â´"
+ AF_BLUE_STRING_GEORGIAN_MTAVRULI_TOP
+ "Ნ Ჟ Ჳ Ჸ Გ Ე ᲠᲴ"
+ AF_BLUE_STRING_GEORGIAN_MTAVRULI_BOTTOM
+ "Ი Ჲ ᲠᲩ Მ Შ Ჯ Ჽ"
+
AF_BLUE_STRING_GLAGOLITIC_CAPITAL_TOP
"â°… â°” â°ª â°„ â°‚ â°Š â°« â°‹"
AF_BLUE_STRING_GLAGOLITIC_CAPITAL_BOTTOM
@@ -795,13 +800,14 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN:
{ AF_BLUE_STRING_ETHIOPIC_BOTTOM, 0 }
{ AF_BLUE_STRING_MAX, 0 }
- // blue zones for Mtavruli are missing (not yet defined in Unicode)
AF_BLUE_STRINGSET_GEOR
{ AF_BLUE_STRING_GEORGIAN_MKHEDRULI_TOP, AF_BLUE_PROPERTY_LATIN_TOP |
AF_BLUE_PROPERTY_LATIN_X_HEIGHT }
{ AF_BLUE_STRING_GEORGIAN_MKHEDRULI_BOTTOM, 0 }
{ AF_BLUE_STRING_GEORGIAN_MKHEDRULI_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }
{ AF_BLUE_STRING_GEORGIAN_MKHEDRULI_DESCENDER, 0 }
+ { AF_BLUE_STRING_GEORGIAN_MTAVRULI_TOP, AF_BLUE_PROPERTY_LATIN_TOP }
+ { AF_BLUE_STRING_GEORGIAN_MTAVRULI_BOTTOM, 0 }
{ AF_BLUE_STRING_MAX, 0 }
AF_BLUE_STRINGSET_GEOK
diff --git a/thirdparty/freetype/src/autofit/afblue.h b/thirdparty/freetype/src/autofit/afblue.h
index 99ef51cd4a..de31e259c3 100644
--- a/thirdparty/freetype/src/autofit/afblue.h
+++ b/thirdparty/freetype/src/autofit/afblue.h
@@ -7,7 +7,7 @@
/* */
/* Auto-fitter data for blue strings (specification). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -147,119 +147,121 @@ FT_BEGIN_HEADER
AF_BLUE_STRING_GEORGIAN_NUSKHURI_BOTTOM = 1813,
AF_BLUE_STRING_GEORGIAN_NUSKHURI_ASCENDER = 1845,
AF_BLUE_STRING_GEORGIAN_NUSKHURI_DESCENDER = 1877,
- AF_BLUE_STRING_GLAGOLITIC_CAPITAL_TOP = 1909,
- AF_BLUE_STRING_GLAGOLITIC_CAPITAL_BOTTOM = 1941,
- AF_BLUE_STRING_GLAGOLITIC_SMALL_TOP = 1973,
- AF_BLUE_STRING_GLAGOLITIC_SMALL_BOTTOM = 2005,
- AF_BLUE_STRING_GOTHIC_TOP = 2037,
- AF_BLUE_STRING_GOTHIC_BOTTOM = 2077,
- AF_BLUE_STRING_GREEK_CAPITAL_TOP = 2097,
- AF_BLUE_STRING_GREEK_CAPITAL_BOTTOM = 2118,
- AF_BLUE_STRING_GREEK_SMALL_BETA_TOP = 2136,
- AF_BLUE_STRING_GREEK_SMALL = 2154,
- AF_BLUE_STRING_GREEK_SMALL_DESCENDER = 2178,
- AF_BLUE_STRING_GUJARATI_TOP = 2202,
- AF_BLUE_STRING_GUJARATI_BOTTOM = 2234,
- AF_BLUE_STRING_GUJARATI_ASCENDER = 2266,
- AF_BLUE_STRING_GUJARATI_DESCENDER = 2316,
- AF_BLUE_STRING_GUJARATI_DIGIT_TOP = 2349,
- AF_BLUE_STRING_GURMUKHI_BASE = 2369,
- AF_BLUE_STRING_GURMUKHI_HEAD = 2401,
- AF_BLUE_STRING_GURMUKHI_TOP = 2433,
- AF_BLUE_STRING_GURMUKHI_BOTTOM = 2465,
- AF_BLUE_STRING_GURMUKHI_DIGIT_TOP = 2497,
- AF_BLUE_STRING_HEBREW_TOP = 2517,
- AF_BLUE_STRING_HEBREW_BOTTOM = 2541,
- AF_BLUE_STRING_HEBREW_DESCENDER = 2559,
- AF_BLUE_STRING_KANNADA_TOP = 2574,
- AF_BLUE_STRING_KANNADA_BOTTOM = 2618,
- AF_BLUE_STRING_KAYAH_LI_TOP = 2650,
- AF_BLUE_STRING_KAYAH_LI_BOTTOM = 2674,
- AF_BLUE_STRING_KAYAH_LI_ASCENDER = 2694,
- AF_BLUE_STRING_KAYAH_LI_DESCENDER = 2702,
- AF_BLUE_STRING_KAYAH_LI_LARGE_DESCENDER = 2714,
- AF_BLUE_STRING_KHMER_TOP = 2735,
- AF_BLUE_STRING_KHMER_SUBSCRIPT_TOP = 2759,
- AF_BLUE_STRING_KHMER_BOTTOM = 2799,
- AF_BLUE_STRING_KHMER_DESCENDER = 2831,
- AF_BLUE_STRING_KHMER_LARGE_DESCENDER = 2865,
- AF_BLUE_STRING_KHMER_SYMBOLS_WAXING_TOP = 2952,
- AF_BLUE_STRING_KHMER_SYMBOLS_WANING_BOTTOM = 2960,
- AF_BLUE_STRING_LAO_TOP = 2968,
- AF_BLUE_STRING_LAO_BOTTOM = 3000,
- AF_BLUE_STRING_LAO_ASCENDER = 3032,
- AF_BLUE_STRING_LAO_LARGE_ASCENDER = 3048,
- AF_BLUE_STRING_LAO_DESCENDER = 3060,
- AF_BLUE_STRING_LATIN_CAPITAL_TOP = 3084,
- AF_BLUE_STRING_LATIN_CAPITAL_BOTTOM = 3100,
- AF_BLUE_STRING_LATIN_SMALL_F_TOP = 3116,
- AF_BLUE_STRING_LATIN_SMALL_TOP = 3130,
- AF_BLUE_STRING_LATIN_SMALL_BOTTOM = 3146,
- AF_BLUE_STRING_LATIN_SMALL_DESCENDER = 3162,
- AF_BLUE_STRING_LATIN_SUBS_CAPITAL_TOP = 3172,
- AF_BLUE_STRING_LATIN_SUBS_CAPITAL_BOTTOM = 3192,
- AF_BLUE_STRING_LATIN_SUBS_SMALL_F_TOP = 3212,
- AF_BLUE_STRING_LATIN_SUBS_SMALL = 3232,
- AF_BLUE_STRING_LATIN_SUBS_SMALL_DESCENDER = 3268,
- AF_BLUE_STRING_LATIN_SUPS_CAPITAL_TOP = 3288,
- AF_BLUE_STRING_LATIN_SUPS_CAPITAL_BOTTOM = 3319,
- AF_BLUE_STRING_LATIN_SUPS_SMALL_F_TOP = 3348,
- AF_BLUE_STRING_LATIN_SUPS_SMALL = 3374,
- AF_BLUE_STRING_LATIN_SUPS_SMALL_DESCENDER = 3399,
- AF_BLUE_STRING_LISU_TOP = 3410,
- AF_BLUE_STRING_LISU_BOTTOM = 3442,
- AF_BLUE_STRING_MALAYALAM_TOP = 3474,
- AF_BLUE_STRING_MALAYALAM_BOTTOM = 3518,
- AF_BLUE_STRING_MYANMAR_TOP = 3550,
- AF_BLUE_STRING_MYANMAR_BOTTOM = 3582,
- AF_BLUE_STRING_MYANMAR_ASCENDER = 3614,
- AF_BLUE_STRING_MYANMAR_DESCENDER = 3642,
- AF_BLUE_STRING_NKO_TOP = 3674,
- AF_BLUE_STRING_NKO_BOTTOM = 3698,
- AF_BLUE_STRING_NKO_SMALL_TOP = 3713,
- AF_BLUE_STRING_NKO_SMALL_BOTTOM = 3722,
- AF_BLUE_STRING_OL_CHIKI = 3734,
- AF_BLUE_STRING_OLD_TURKIC_TOP = 3758,
- AF_BLUE_STRING_OLD_TURKIC_BOTTOM = 3773,
- AF_BLUE_STRING_OSAGE_CAPITAL_TOP = 3793,
- AF_BLUE_STRING_OSAGE_CAPITAL_BOTTOM = 3833,
- AF_BLUE_STRING_OSAGE_CAPITAL_DESCENDER = 3863,
- AF_BLUE_STRING_OSAGE_SMALL_TOP = 3878,
- AF_BLUE_STRING_OSAGE_SMALL_BOTTOM = 3918,
- AF_BLUE_STRING_OSAGE_SMALL_ASCENDER = 3958,
- AF_BLUE_STRING_OSAGE_SMALL_DESCENDER = 3983,
- AF_BLUE_STRING_OSMANYA_TOP = 3998,
- AF_BLUE_STRING_OSMANYA_BOTTOM = 4038,
- AF_BLUE_STRING_SAURASHTRA_TOP = 4078,
- AF_BLUE_STRING_SAURASHTRA_BOTTOM = 4110,
- AF_BLUE_STRING_SHAVIAN_TOP = 4130,
- AF_BLUE_STRING_SHAVIAN_BOTTOM = 4140,
- AF_BLUE_STRING_SHAVIAN_DESCENDER = 4165,
- AF_BLUE_STRING_SHAVIAN_SMALL_TOP = 4175,
- AF_BLUE_STRING_SHAVIAN_SMALL_BOTTOM = 4210,
- AF_BLUE_STRING_SINHALA_TOP = 4225,
- AF_BLUE_STRING_SINHALA_BOTTOM = 4257,
- AF_BLUE_STRING_SINHALA_DESCENDER = 4289,
- AF_BLUE_STRING_SUNDANESE_TOP = 4333,
- AF_BLUE_STRING_SUNDANESE_BOTTOM = 4357,
- AF_BLUE_STRING_SUNDANESE_DESCENDER = 4389,
- AF_BLUE_STRING_TAI_VIET_TOP = 4397,
- AF_BLUE_STRING_TAI_VIET_BOTTOM = 4417,
- AF_BLUE_STRING_TAMIL_TOP = 4429,
- AF_BLUE_STRING_TAMIL_BOTTOM = 4461,
- AF_BLUE_STRING_TELUGU_TOP = 4493,
- AF_BLUE_STRING_TELUGU_BOTTOM = 4521,
- AF_BLUE_STRING_THAI_TOP = 4549,
- AF_BLUE_STRING_THAI_BOTTOM = 4573,
- AF_BLUE_STRING_THAI_ASCENDER = 4601,
- AF_BLUE_STRING_THAI_LARGE_ASCENDER = 4613,
- AF_BLUE_STRING_THAI_DESCENDER = 4625,
- AF_BLUE_STRING_THAI_LARGE_DESCENDER = 4641,
- AF_BLUE_STRING_THAI_DIGIT_TOP = 4649,
- AF_BLUE_STRING_TIFINAGH = 4661,
- AF_BLUE_STRING_VAI_TOP = 4693,
- AF_BLUE_STRING_VAI_BOTTOM = 4725,
- af_blue_1_1 = 4756,
+ AF_BLUE_STRING_GEORGIAN_MTAVRULI_TOP = 1909,
+ AF_BLUE_STRING_GEORGIAN_MTAVRULI_BOTTOM = 1941,
+ AF_BLUE_STRING_GLAGOLITIC_CAPITAL_TOP = 1973,
+ AF_BLUE_STRING_GLAGOLITIC_CAPITAL_BOTTOM = 2005,
+ AF_BLUE_STRING_GLAGOLITIC_SMALL_TOP = 2037,
+ AF_BLUE_STRING_GLAGOLITIC_SMALL_BOTTOM = 2069,
+ AF_BLUE_STRING_GOTHIC_TOP = 2101,
+ AF_BLUE_STRING_GOTHIC_BOTTOM = 2141,
+ AF_BLUE_STRING_GREEK_CAPITAL_TOP = 2161,
+ AF_BLUE_STRING_GREEK_CAPITAL_BOTTOM = 2182,
+ AF_BLUE_STRING_GREEK_SMALL_BETA_TOP = 2200,
+ AF_BLUE_STRING_GREEK_SMALL = 2218,
+ AF_BLUE_STRING_GREEK_SMALL_DESCENDER = 2242,
+ AF_BLUE_STRING_GUJARATI_TOP = 2266,
+ AF_BLUE_STRING_GUJARATI_BOTTOM = 2298,
+ AF_BLUE_STRING_GUJARATI_ASCENDER = 2330,
+ AF_BLUE_STRING_GUJARATI_DESCENDER = 2380,
+ AF_BLUE_STRING_GUJARATI_DIGIT_TOP = 2413,
+ AF_BLUE_STRING_GURMUKHI_BASE = 2433,
+ AF_BLUE_STRING_GURMUKHI_HEAD = 2465,
+ AF_BLUE_STRING_GURMUKHI_TOP = 2497,
+ AF_BLUE_STRING_GURMUKHI_BOTTOM = 2529,
+ AF_BLUE_STRING_GURMUKHI_DIGIT_TOP = 2561,
+ AF_BLUE_STRING_HEBREW_TOP = 2581,
+ AF_BLUE_STRING_HEBREW_BOTTOM = 2605,
+ AF_BLUE_STRING_HEBREW_DESCENDER = 2623,
+ AF_BLUE_STRING_KANNADA_TOP = 2638,
+ AF_BLUE_STRING_KANNADA_BOTTOM = 2682,
+ AF_BLUE_STRING_KAYAH_LI_TOP = 2714,
+ AF_BLUE_STRING_KAYAH_LI_BOTTOM = 2738,
+ AF_BLUE_STRING_KAYAH_LI_ASCENDER = 2758,
+ AF_BLUE_STRING_KAYAH_LI_DESCENDER = 2766,
+ AF_BLUE_STRING_KAYAH_LI_LARGE_DESCENDER = 2778,
+ AF_BLUE_STRING_KHMER_TOP = 2799,
+ AF_BLUE_STRING_KHMER_SUBSCRIPT_TOP = 2823,
+ AF_BLUE_STRING_KHMER_BOTTOM = 2863,
+ AF_BLUE_STRING_KHMER_DESCENDER = 2895,
+ AF_BLUE_STRING_KHMER_LARGE_DESCENDER = 2929,
+ AF_BLUE_STRING_KHMER_SYMBOLS_WAXING_TOP = 3016,
+ AF_BLUE_STRING_KHMER_SYMBOLS_WANING_BOTTOM = 3024,
+ AF_BLUE_STRING_LAO_TOP = 3032,
+ AF_BLUE_STRING_LAO_BOTTOM = 3064,
+ AF_BLUE_STRING_LAO_ASCENDER = 3096,
+ AF_BLUE_STRING_LAO_LARGE_ASCENDER = 3112,
+ AF_BLUE_STRING_LAO_DESCENDER = 3124,
+ AF_BLUE_STRING_LATIN_CAPITAL_TOP = 3148,
+ AF_BLUE_STRING_LATIN_CAPITAL_BOTTOM = 3164,
+ AF_BLUE_STRING_LATIN_SMALL_F_TOP = 3180,
+ AF_BLUE_STRING_LATIN_SMALL_TOP = 3194,
+ AF_BLUE_STRING_LATIN_SMALL_BOTTOM = 3210,
+ AF_BLUE_STRING_LATIN_SMALL_DESCENDER = 3226,
+ AF_BLUE_STRING_LATIN_SUBS_CAPITAL_TOP = 3236,
+ AF_BLUE_STRING_LATIN_SUBS_CAPITAL_BOTTOM = 3256,
+ AF_BLUE_STRING_LATIN_SUBS_SMALL_F_TOP = 3276,
+ AF_BLUE_STRING_LATIN_SUBS_SMALL = 3296,
+ AF_BLUE_STRING_LATIN_SUBS_SMALL_DESCENDER = 3332,
+ AF_BLUE_STRING_LATIN_SUPS_CAPITAL_TOP = 3352,
+ AF_BLUE_STRING_LATIN_SUPS_CAPITAL_BOTTOM = 3383,
+ AF_BLUE_STRING_LATIN_SUPS_SMALL_F_TOP = 3412,
+ AF_BLUE_STRING_LATIN_SUPS_SMALL = 3438,
+ AF_BLUE_STRING_LATIN_SUPS_SMALL_DESCENDER = 3463,
+ AF_BLUE_STRING_LISU_TOP = 3474,
+ AF_BLUE_STRING_LISU_BOTTOM = 3506,
+ AF_BLUE_STRING_MALAYALAM_TOP = 3538,
+ AF_BLUE_STRING_MALAYALAM_BOTTOM = 3582,
+ AF_BLUE_STRING_MYANMAR_TOP = 3614,
+ AF_BLUE_STRING_MYANMAR_BOTTOM = 3646,
+ AF_BLUE_STRING_MYANMAR_ASCENDER = 3678,
+ AF_BLUE_STRING_MYANMAR_DESCENDER = 3706,
+ AF_BLUE_STRING_NKO_TOP = 3738,
+ AF_BLUE_STRING_NKO_BOTTOM = 3762,
+ AF_BLUE_STRING_NKO_SMALL_TOP = 3777,
+ AF_BLUE_STRING_NKO_SMALL_BOTTOM = 3786,
+ AF_BLUE_STRING_OL_CHIKI = 3798,
+ AF_BLUE_STRING_OLD_TURKIC_TOP = 3822,
+ AF_BLUE_STRING_OLD_TURKIC_BOTTOM = 3837,
+ AF_BLUE_STRING_OSAGE_CAPITAL_TOP = 3857,
+ AF_BLUE_STRING_OSAGE_CAPITAL_BOTTOM = 3897,
+ AF_BLUE_STRING_OSAGE_CAPITAL_DESCENDER = 3927,
+ AF_BLUE_STRING_OSAGE_SMALL_TOP = 3942,
+ AF_BLUE_STRING_OSAGE_SMALL_BOTTOM = 3982,
+ AF_BLUE_STRING_OSAGE_SMALL_ASCENDER = 4022,
+ AF_BLUE_STRING_OSAGE_SMALL_DESCENDER = 4047,
+ AF_BLUE_STRING_OSMANYA_TOP = 4062,
+ AF_BLUE_STRING_OSMANYA_BOTTOM = 4102,
+ AF_BLUE_STRING_SAURASHTRA_TOP = 4142,
+ AF_BLUE_STRING_SAURASHTRA_BOTTOM = 4174,
+ AF_BLUE_STRING_SHAVIAN_TOP = 4194,
+ AF_BLUE_STRING_SHAVIAN_BOTTOM = 4204,
+ AF_BLUE_STRING_SHAVIAN_DESCENDER = 4229,
+ AF_BLUE_STRING_SHAVIAN_SMALL_TOP = 4239,
+ AF_BLUE_STRING_SHAVIAN_SMALL_BOTTOM = 4274,
+ AF_BLUE_STRING_SINHALA_TOP = 4289,
+ AF_BLUE_STRING_SINHALA_BOTTOM = 4321,
+ AF_BLUE_STRING_SINHALA_DESCENDER = 4353,
+ AF_BLUE_STRING_SUNDANESE_TOP = 4397,
+ AF_BLUE_STRING_SUNDANESE_BOTTOM = 4421,
+ AF_BLUE_STRING_SUNDANESE_DESCENDER = 4453,
+ AF_BLUE_STRING_TAI_VIET_TOP = 4461,
+ AF_BLUE_STRING_TAI_VIET_BOTTOM = 4481,
+ AF_BLUE_STRING_TAMIL_TOP = 4493,
+ AF_BLUE_STRING_TAMIL_BOTTOM = 4525,
+ AF_BLUE_STRING_TELUGU_TOP = 4557,
+ AF_BLUE_STRING_TELUGU_BOTTOM = 4585,
+ AF_BLUE_STRING_THAI_TOP = 4613,
+ AF_BLUE_STRING_THAI_BOTTOM = 4637,
+ AF_BLUE_STRING_THAI_ASCENDER = 4665,
+ AF_BLUE_STRING_THAI_LARGE_ASCENDER = 4677,
+ AF_BLUE_STRING_THAI_DESCENDER = 4689,
+ AF_BLUE_STRING_THAI_LARGE_DESCENDER = 4705,
+ AF_BLUE_STRING_THAI_DIGIT_TOP = 4713,
+ AF_BLUE_STRING_TIFINAGH = 4725,
+ AF_BLUE_STRING_VAI_TOP = 4757,
+ AF_BLUE_STRING_VAI_BOTTOM = 4789,
+ af_blue_1_1 = 4820,
#ifdef AF_CONFIG_OPTION_CJK
AF_BLUE_STRING_CJK_TOP = af_blue_1_1 + 1,
AF_BLUE_STRING_CJK_BOTTOM = af_blue_1_1 + 203,
@@ -336,41 +338,41 @@ FT_BEGIN_HEADER
AF_BLUE_STRINGSET_DSRT = 75,
AF_BLUE_STRINGSET_ETHI = 80,
AF_BLUE_STRINGSET_GEOR = 83,
- AF_BLUE_STRINGSET_GEOK = 88,
- AF_BLUE_STRINGSET_GLAG = 95,
- AF_BLUE_STRINGSET_GOTH = 100,
- AF_BLUE_STRINGSET_GREK = 103,
- AF_BLUE_STRINGSET_GUJR = 110,
- AF_BLUE_STRINGSET_GURU = 116,
- AF_BLUE_STRINGSET_HEBR = 122,
- AF_BLUE_STRINGSET_KALI = 126,
- AF_BLUE_STRINGSET_KHMR = 132,
- AF_BLUE_STRINGSET_KHMS = 138,
- AF_BLUE_STRINGSET_KNDA = 141,
- AF_BLUE_STRINGSET_LAO = 144,
- AF_BLUE_STRINGSET_LATN = 150,
- AF_BLUE_STRINGSET_LATB = 157,
- AF_BLUE_STRINGSET_LATP = 164,
- AF_BLUE_STRINGSET_LISU = 171,
- AF_BLUE_STRINGSET_MLYM = 174,
- AF_BLUE_STRINGSET_MYMR = 177,
- AF_BLUE_STRINGSET_NKOO = 182,
- AF_BLUE_STRINGSET_NONE = 187,
- AF_BLUE_STRINGSET_OLCK = 188,
- AF_BLUE_STRINGSET_ORKH = 191,
- AF_BLUE_STRINGSET_OSGE = 194,
- AF_BLUE_STRINGSET_OSMA = 202,
- AF_BLUE_STRINGSET_SAUR = 205,
- AF_BLUE_STRINGSET_SHAW = 208,
- AF_BLUE_STRINGSET_SINH = 214,
- AF_BLUE_STRINGSET_SUND = 218,
- AF_BLUE_STRINGSET_TAML = 222,
- AF_BLUE_STRINGSET_TAVT = 225,
- AF_BLUE_STRINGSET_TELU = 228,
- AF_BLUE_STRINGSET_TFNG = 231,
- AF_BLUE_STRINGSET_THAI = 234,
- AF_BLUE_STRINGSET_VAII = 242,
- af_blue_2_1 = 245,
+ AF_BLUE_STRINGSET_GEOK = 90,
+ AF_BLUE_STRINGSET_GLAG = 97,
+ AF_BLUE_STRINGSET_GOTH = 102,
+ AF_BLUE_STRINGSET_GREK = 105,
+ AF_BLUE_STRINGSET_GUJR = 112,
+ AF_BLUE_STRINGSET_GURU = 118,
+ AF_BLUE_STRINGSET_HEBR = 124,
+ AF_BLUE_STRINGSET_KALI = 128,
+ AF_BLUE_STRINGSET_KHMR = 134,
+ AF_BLUE_STRINGSET_KHMS = 140,
+ AF_BLUE_STRINGSET_KNDA = 143,
+ AF_BLUE_STRINGSET_LAO = 146,
+ AF_BLUE_STRINGSET_LATN = 152,
+ AF_BLUE_STRINGSET_LATB = 159,
+ AF_BLUE_STRINGSET_LATP = 166,
+ AF_BLUE_STRINGSET_LISU = 173,
+ AF_BLUE_STRINGSET_MLYM = 176,
+ AF_BLUE_STRINGSET_MYMR = 179,
+ AF_BLUE_STRINGSET_NKOO = 184,
+ AF_BLUE_STRINGSET_NONE = 189,
+ AF_BLUE_STRINGSET_OLCK = 190,
+ AF_BLUE_STRINGSET_ORKH = 193,
+ AF_BLUE_STRINGSET_OSGE = 196,
+ AF_BLUE_STRINGSET_OSMA = 204,
+ AF_BLUE_STRINGSET_SAUR = 207,
+ AF_BLUE_STRINGSET_SHAW = 210,
+ AF_BLUE_STRINGSET_SINH = 216,
+ AF_BLUE_STRINGSET_SUND = 220,
+ AF_BLUE_STRINGSET_TAML = 224,
+ AF_BLUE_STRINGSET_TAVT = 227,
+ AF_BLUE_STRINGSET_TELU = 230,
+ AF_BLUE_STRINGSET_TFNG = 233,
+ AF_BLUE_STRINGSET_THAI = 236,
+ AF_BLUE_STRINGSET_VAII = 244,
+ af_blue_2_1 = 247,
#ifdef AF_CONFIG_OPTION_CJK
AF_BLUE_STRINGSET_HANI = af_blue_2_1 + 0,
af_blue_2_1_1 = af_blue_2_1 + 2,
diff --git a/thirdparty/freetype/src/autofit/afblue.hin b/thirdparty/freetype/src/autofit/afblue.hin
index 268bcbc531..682147cb30 100644
--- a/thirdparty/freetype/src/autofit/afblue.hin
+++ b/thirdparty/freetype/src/autofit/afblue.hin
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter data for blue strings (specification). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afcjk.c b/thirdparty/freetype/src/autofit/afcjk.c
index 897533d148..21b6bffa33 100644
--- a/thirdparty/freetype/src/autofit/afcjk.c
+++ b/thirdparty/freetype/src/autofit/afcjk.c
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter hinting routines for CJK writing system (body). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -16,9 +16,9 @@
/***************************************************************************/
/*
- * The algorithm is based on akito's autohint patch, available here:
+ * The algorithm is based on akito's autohint patch, archived at
*
- * http://www.kde.gr.jp/~akito/patch/freetype2/
+ * https://web.archive.org/web/20051219160454/http://www.kde.gr.jp:80/~akito/patch/freetype2/2.1.7/
*
*/
diff --git a/thirdparty/freetype/src/autofit/afcjk.h b/thirdparty/freetype/src/autofit/afcjk.h
index 84f892f909..d229c0c9cf 100644
--- a/thirdparty/freetype/src/autofit/afcjk.h
+++ b/thirdparty/freetype/src/autofit/afcjk.h
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter hinting routines for CJK writing system (specification). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afcover.h b/thirdparty/freetype/src/autofit/afcover.h
index 1b18c666ab..6eeb8fc9fb 100644
--- a/thirdparty/freetype/src/autofit/afcover.h
+++ b/thirdparty/freetype/src/autofit/afcover.h
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter coverages (specification only). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afdummy.c b/thirdparty/freetype/src/autofit/afdummy.c
index 61c32db5bf..f30c517cbb 100644
--- a/thirdparty/freetype/src/autofit/afdummy.c
+++ b/thirdparty/freetype/src/autofit/afdummy.c
@@ -5,7 +5,7 @@
/* Auto-fitter dummy routines to be used if no hinting should be */
/* performed (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afdummy.h b/thirdparty/freetype/src/autofit/afdummy.h
index ebaa7d76ea..b382acd92a 100644
--- a/thirdparty/freetype/src/autofit/afdummy.h
+++ b/thirdparty/freetype/src/autofit/afdummy.h
@@ -5,7 +5,7 @@
/* Auto-fitter dummy routines to be used if no hinting should be */
/* performed (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/aferrors.h b/thirdparty/freetype/src/autofit/aferrors.h
index dde182f3c8..e5de54360f 100644
--- a/thirdparty/freetype/src/autofit/aferrors.h
+++ b/thirdparty/freetype/src/autofit/aferrors.h
@@ -4,7 +4,7 @@
/* */
/* Autofitter error codes (specification only). */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afglobal.c b/thirdparty/freetype/src/autofit/afglobal.c
index 85bef001f7..3d09c53e8a 100644
--- a/thirdparty/freetype/src/autofit/afglobal.c
+++ b/thirdparty/freetype/src/autofit/afglobal.c
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter routines to compute global hinting values (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afglobal.h b/thirdparty/freetype/src/autofit/afglobal.h
index de6142ea26..489ed46d9e 100644
--- a/thirdparty/freetype/src/autofit/afglobal.h
+++ b/thirdparty/freetype/src/autofit/afglobal.h
@@ -5,7 +5,7 @@
/* Auto-fitter routines to compute global hinting values */
/* (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afhints.c b/thirdparty/freetype/src/autofit/afhints.c
index 1b21c06c2c..0666dbc8e2 100644
--- a/thirdparty/freetype/src/autofit/afhints.c
+++ b/thirdparty/freetype/src/autofit/afhints.c
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter hinting routines (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -314,8 +314,12 @@
AF_DUMP(( "Table of points:\n" ));
if ( hints->num_points )
+ {
AF_DUMP(( " index hedge hseg vedge vseg flags "
+ /* " XXXXX XXXXX XXXXX XXXXX XXXXX XXXXXX" */
" xorg yorg xscale yscale xfit yfit" ));
+ /* " XXXXX XXXXX XXXX.XX XXXX.XX XXXX.XX XXXX.XX" */
+ }
else
AF_DUMP(( " (none)\n" ));
@@ -420,9 +424,14 @@
dimension == AF_DIMENSION_HORZ ? "vertical"
: "horizontal" ));
if ( axis->num_segments )
+ {
AF_DUMP(( " index pos delta dir from to "
+ /* " XXXXX XXXXX XXXXX XXXXX XXXX XXXX" */
" link serif edge"
+ /* " XXXX XXXXX XXXX" */
" height extra flags\n" ));
+ /* " XXXXXX XXXXX XXXXXXXXXXX" */
+ }
else
AF_DUMP(( " (none)\n" ));
@@ -564,8 +573,12 @@
10.0 * hints->y_scale / 65536.0 / 64.0 ));
if ( axis->num_edges )
+ {
AF_DUMP(( " index pos dir link serif"
+ /* " XXXXX XXXX.XX XXXXX XXXX XXXXX" */
" blue opos pos flags\n" ));
+ /* " X XXXX.XX XXXX.XX XXXXXXXXXXX" */
+ }
else
AF_DUMP(( " (none)\n" ));
diff --git a/thirdparty/freetype/src/autofit/afhints.h b/thirdparty/freetype/src/autofit/afhints.h
index 16638b1030..3326ebc44e 100644
--- a/thirdparty/freetype/src/autofit/afhints.h
+++ b/thirdparty/freetype/src/autofit/afhints.h
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter hinting routines (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -62,7 +62,7 @@ FT_BEGIN_HEADER
*
* by David Turner and Werner Lemberg
*
- * http://www.tug.org/TUGboat/Articles/tb24-3/lemberg.pdf
+ * https://www.tug.org/TUGboat/Articles/tb24-3/lemberg.pdf
*
* with appropriate updates.
*
diff --git a/thirdparty/freetype/src/autofit/afindic.c b/thirdparty/freetype/src/autofit/afindic.c
index 23be46ed51..dfbea5f34c 100644
--- a/thirdparty/freetype/src/autofit/afindic.c
+++ b/thirdparty/freetype/src/autofit/afindic.c
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter hinting routines for Indic writing system (body). */
/* */
-/* Copyright 2007-2017 by */
+/* Copyright 2007-2018 by */
/* Rahul Bhalerao <rahul.bhalerao@redhat.com>, <b.rahul.pm@gmail.com>. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afindic.h b/thirdparty/freetype/src/autofit/afindic.h
index ec9e263161..5688738e6e 100644
--- a/thirdparty/freetype/src/autofit/afindic.h
+++ b/thirdparty/freetype/src/autofit/afindic.h
@@ -5,7 +5,7 @@
/* Auto-fitter hinting routines for Indic writing system */
/* (specification). */
/* */
-/* Copyright 2007-2017 by */
+/* Copyright 2007-2018 by */
/* Rahul Bhalerao <rahul.bhalerao@redhat.com>, <b.rahul.pm@gmail.com>. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/aflatin.c b/thirdparty/freetype/src/autofit/aflatin.c
index 02b3b8bbd3..9f1b54056f 100644
--- a/thirdparty/freetype/src/autofit/aflatin.c
+++ b/thirdparty/freetype/src/autofit/aflatin.c
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter hinting routines for latin writing system (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/aflatin.h b/thirdparty/freetype/src/autofit/aflatin.h
index d80e125ddc..432cccce4e 100644
--- a/thirdparty/freetype/src/autofit/aflatin.h
+++ b/thirdparty/freetype/src/autofit/aflatin.h
@@ -5,7 +5,7 @@
/* Auto-fitter hinting routines for latin writing system */
/* (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/aflatin2.c b/thirdparty/freetype/src/autofit/aflatin2.c
index fb42445116..5c71378118 100644
--- a/thirdparty/freetype/src/autofit/aflatin2.c
+++ b/thirdparty/freetype/src/autofit/aflatin2.c
@@ -9,7 +9,7 @@
/* */
/* Auto-fitter hinting routines for latin writing system (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/aflatin2.h b/thirdparty/freetype/src/autofit/aflatin2.h
index 2d0b154f58..0129dc707e 100644
--- a/thirdparty/freetype/src/autofit/aflatin2.h
+++ b/thirdparty/freetype/src/autofit/aflatin2.h
@@ -10,7 +10,7 @@
/* Auto-fitter hinting routines for latin writing system */
/* (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afloader.c b/thirdparty/freetype/src/autofit/afloader.c
index 067ebd17f6..a55550b338 100644
--- a/thirdparty/freetype/src/autofit/afloader.c
+++ b/thirdparty/freetype/src/autofit/afloader.c
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter glyph loading routines (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afloader.h b/thirdparty/freetype/src/autofit/afloader.h
index 2578abed16..d4d72d1583 100644
--- a/thirdparty/freetype/src/autofit/afloader.h
+++ b/thirdparty/freetype/src/autofit/afloader.h
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter glyph loading routines (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afmodule.c b/thirdparty/freetype/src/autofit/afmodule.c
index 9d7ba224da..dcaa17a27e 100644
--- a/thirdparty/freetype/src/autofit/afmodule.c
+++ b/thirdparty/freetype/src/autofit/afmodule.c
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter module implementation (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -56,7 +56,7 @@
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H
-#include FT_AUTOHINTER_H
+#include FT_DRIVER_H
#include FT_SERVICE_PROPERTIES_H
diff --git a/thirdparty/freetype/src/autofit/afmodule.h b/thirdparty/freetype/src/autofit/afmodule.h
index 0571d14d59..56f64eaf23 100644
--- a/thirdparty/freetype/src/autofit/afmodule.h
+++ b/thirdparty/freetype/src/autofit/afmodule.h
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter module implementation (specification). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afpic.c b/thirdparty/freetype/src/autofit/afpic.c
index 3125e03e27..d48d016a0e 100644
--- a/thirdparty/freetype/src/autofit/afpic.c
+++ b/thirdparty/freetype/src/autofit/afpic.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for autofit module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afpic.h b/thirdparty/freetype/src/autofit/afpic.h
index 8cd3392123..0c73456785 100644
--- a/thirdparty/freetype/src/autofit/afpic.h
+++ b/thirdparty/freetype/src/autofit/afpic.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for autofit module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afranges.c b/thirdparty/freetype/src/autofit/afranges.c
index 7f37eea1e0..cf67fafb11 100644
--- a/thirdparty/freetype/src/autofit/afranges.c
+++ b/thirdparty/freetype/src/autofit/afranges.c
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter Unicode script ranges (body). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -91,7 +91,7 @@
AF_UNIRANGE_REC( 0x06E7, 0x06E8 ),
AF_UNIRANGE_REC( 0x06EA, 0x06ED ),
AF_UNIRANGE_REC( 0x08D4, 0x08E1 ),
- AF_UNIRANGE_REC( 0x08E3, 0x08FF ),
+ AF_UNIRANGE_REC( 0x08D3, 0x08FF ),
AF_UNIRANGE_REC( 0xFBB2, 0xFBC1 ),
AF_UNIRANGE_REC( 0xFE70, 0xFE70 ),
AF_UNIRANGE_REC( 0xFE72, 0xFE72 ),
@@ -163,6 +163,7 @@
AF_UNIRANGE_REC( 0x09C1, 0x09C4 ),
AF_UNIRANGE_REC( 0x09CD, 0x09CD ),
AF_UNIRANGE_REC( 0x09E2, 0x09E3 ),
+ AF_UNIRANGE_REC( 0x09FE, 0x09FE ),
AF_UNIRANGE_REC( 0, 0 )
};
@@ -190,6 +191,7 @@
{
AF_UNIRANGE_REC( 0x11100, 0x11102 ),
AF_UNIRANGE_REC( 0x11127, 0x11134 ),
+ AF_UNIRANGE_REC( 0x11146, 0x11146 ),
AF_UNIRANGE_REC( 0, 0 )
};
@@ -304,6 +306,7 @@
AF_UNIRANGE_REC( 0x0953, 0x0957 ),
AF_UNIRANGE_REC( 0x0962, 0x0963 ),
AF_UNIRANGE_REC( 0xA8E0, 0xA8F1 ),
+ AF_UNIRANGE_REC( 0xA8FF, 0xA8FF ),
AF_UNIRANGE_REC( 0, 0 )
};
@@ -338,11 +341,8 @@
const AF_Script_UniRangeRec af_geor_uniranges[] =
{
- AF_UNIRANGE_REC( 0x10D0, 0x10FF ), /* Georgian (Mkhedruli) */
-#if 0
- /* the following range is proposed for inclusion in Unicode */
- AF_UNIRANGE_REC( 0x1C90, 0x1CBF ), /* Georgian (Mtavruli) */
-#endif
+ AF_UNIRANGE_REC( 0x10D0, 0x10FF ), /* Georgian (Mkhedruli) */
+ AF_UNIRANGE_REC( 0x1C90, 0x1CBF ), /* Georgian Extended (Mtavruli) */
AF_UNIRANGE_REC( 0, 0 )
};
@@ -355,8 +355,8 @@
const AF_Script_UniRangeRec af_geok_uniranges[] =
{
/* Khutsuri */
- AF_UNIRANGE_REC( 0x10A0, 0x10CD ), /* Georgian (Asomtavruli) */
- AF_UNIRANGE_REC( 0x2D00, 0x2D2D ), /* Georgian (Nuskhuri) */
+ AF_UNIRANGE_REC( 0x10A0, 0x10CD ), /* Georgian (Asomtavruli) */
+ AF_UNIRANGE_REC( 0x2D00, 0x2D2D ), /* Georgian Supplement (Nuskhuri) */
AF_UNIRANGE_REC( 0, 0 )
};
@@ -425,6 +425,7 @@
AF_UNIRANGE_REC( 0x0AC1, 0x0AC8 ),
AF_UNIRANGE_REC( 0x0ACD, 0x0ACD ),
AF_UNIRANGE_REC( 0x0AE2, 0x0AE3 ),
+ AF_UNIRANGE_REC( 0x0AFA, 0x0AFF ),
AF_UNIRANGE_REC( 0, 0 )
};
@@ -655,7 +656,8 @@
const AF_Script_UniRangeRec af_mlym_nonbase_uniranges[] =
{
- AF_UNIRANGE_REC( 0x0D01, 0x0D01 ),
+ AF_UNIRANGE_REC( 0x0D00, 0x0D01 ),
+ AF_UNIRANGE_REC( 0x0D3B, 0x0D3C ),
AF_UNIRANGE_REC( 0x0D4D, 0x0D4E ),
AF_UNIRANGE_REC( 0x0D62, 0x0D63 ),
AF_UNIRANGE_REC( 0, 0 )
@@ -697,6 +699,7 @@
const AF_Script_UniRangeRec af_nkoo_nonbase_uniranges[] =
{
AF_UNIRANGE_REC( 0x07EB, 0x07F5 ),
+ AF_UNIRANGE_REC( 0x07FD, 0x07FD ),
AF_UNIRANGE_REC( 0, 0 )
};
@@ -856,6 +859,7 @@
const AF_Script_UniRangeRec af_telu_nonbase_uniranges[] =
{
AF_UNIRANGE_REC( 0x0C00, 0x0C00 ),
+ AF_UNIRANGE_REC( 0x0C04, 0x0C04 ),
AF_UNIRANGE_REC( 0x0C3E, 0x0C40 ),
AF_UNIRANGE_REC( 0x0C46, 0x0C56 ),
AF_UNIRANGE_REC( 0x0C62, 0x0C63 ),
@@ -1006,10 +1010,13 @@
AF_UNIRANGE_REC( 0xFE30, 0xFE4F ), /* CJK Compatibility Forms */
AF_UNIRANGE_REC( 0xFF00, 0xFFEF ), /* Halfwidth and Fullwidth Forms */
AF_UNIRANGE_REC( 0x1B000, 0x1B0FF ), /* Kana Supplement */
+ AF_UNIRANGE_REC( 0x1B100, 0x1B12F ), /* Kana Extended-A */
AF_UNIRANGE_REC( 0x1D300, 0x1D35F ), /* Tai Xuan Hing Symbols */
AF_UNIRANGE_REC( 0x20000, 0x2A6DF ), /* CJK Unified Ideographs Extension B */
AF_UNIRANGE_REC( 0x2A700, 0x2B73F ), /* CJK Unified Ideographs Extension C */
AF_UNIRANGE_REC( 0x2B740, 0x2B81F ), /* CJK Unified Ideographs Extension D */
+ AF_UNIRANGE_REC( 0x2B820, 0x2CEAF ), /* CJK Unified Ideographs Extension E */
+ AF_UNIRANGE_REC( 0x2CEB0, 0x2EBEF ), /* CJK Unified Ideographs Extension F */
AF_UNIRANGE_REC( 0x2F800, 0x2FA1F ), /* CJK Compatibility Ideographs Supplement */
AF_UNIRANGE_REC( 0, 0 )
};
diff --git a/thirdparty/freetype/src/autofit/afranges.h b/thirdparty/freetype/src/autofit/afranges.h
index 72d9eaad2c..ba3b5e7ccb 100644
--- a/thirdparty/freetype/src/autofit/afranges.h
+++ b/thirdparty/freetype/src/autofit/afranges.h
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter Unicode script ranges (specification). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afscript.h b/thirdparty/freetype/src/autofit/afscript.h
index cb815dbb40..623a1734a6 100644
--- a/thirdparty/freetype/src/autofit/afscript.h
+++ b/thirdparty/freetype/src/autofit/afscript.h
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter scripts (specification only). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -137,7 +137,7 @@
"Georgian (Mkhedruli)",
HB_SCRIPT_GEORGIAN,
HINTING_BOTTOM_TO_TOP,
- "\xE1\x83\x98 \xE1\x83\x94 \xE1\x83\x90" ) /* ი ე რ*/
+ "\xE1\x83\x98 \xE1\x83\x94 \xE1\x83\x90 \xE1\xB2\xBF" ) /* ი ე რᲘ */
SCRIPT( geok, GEOK,
"Georgian (Khutsuri)",
diff --git a/thirdparty/freetype/src/autofit/afshaper.c b/thirdparty/freetype/src/autofit/afshaper.c
index d259964217..f30828173c 100644
--- a/thirdparty/freetype/src/autofit/afshaper.c
+++ b/thirdparty/freetype/src/autofit/afshaper.c
@@ -4,7 +4,7 @@
/* */
/* HarfBuzz interface for accessing OpenType features (body). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -104,10 +104,10 @@
{
hb_face_t* face;
- hb_set_t* gsub_lookups; /* GSUB lookups for a given script */
- hb_set_t* gsub_glyphs; /* glyphs covered by GSUB lookups */
- hb_set_t* gpos_lookups; /* GPOS lookups for a given script */
- hb_set_t* gpos_glyphs; /* glyphs covered by GPOS lookups */
+ hb_set_t* gsub_lookups = NULL; /* GSUB lookups for a given script */
+ hb_set_t* gsub_glyphs = NULL; /* glyphs covered by GSUB lookups */
+ hb_set_t* gpos_lookups = NULL; /* GPOS lookups for a given script */
+ hb_set_t* gpos_glyphs = NULL; /* glyphs covered by GPOS lookups */
hb_script_t script;
const hb_tag_t* coverage_tags;
@@ -127,11 +127,6 @@
face = hb_font_get_face( globals->hb_font );
- gsub_lookups = hb_set_create();
- gsub_glyphs = hb_set_create();
- gpos_lookups = hb_set_create();
- gpos_glyphs = hb_set_create();
-
coverage_tags = coverages[style_class->coverage];
script = scripts[style_class->script];
@@ -168,6 +163,7 @@
script_tags[1] = HB_TAG_NONE;
}
+ gsub_lookups = hb_set_create();
hb_ot_layout_collect_lookups( face,
HB_OT_TAG_GSUB,
script_tags,
@@ -178,13 +174,6 @@
if ( hb_set_is_empty( gsub_lookups ) )
goto Exit; /* nothing to do */
- hb_ot_layout_collect_lookups( face,
- HB_OT_TAG_GPOS,
- script_tags,
- NULL,
- coverage_tags,
- gpos_lookups );
-
FT_TRACE4(( "GSUB lookups (style `%s'):\n"
" ",
af_style_names[style_class->style] ));
@@ -193,6 +182,7 @@
count = 0;
#endif
+ gsub_glyphs = hb_set_create();
for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups, &idx ); )
{
#ifdef FT_DEBUG_LEVEL_TRACE
@@ -220,10 +210,19 @@
" ",
af_style_names[style_class->style] ));
+ gpos_lookups = hb_set_create();
+ hb_ot_layout_collect_lookups( face,
+ HB_OT_TAG_GPOS,
+ script_tags,
+ NULL,
+ coverage_tags,
+ gpos_lookups );
+
#ifdef FT_DEBUG_LEVEL_TRACE
count = 0;
#endif
+ gpos_glyphs = hb_set_create();
for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gpos_lookups, &idx ); )
{
#ifdef FT_DEBUG_LEVEL_TRACE
diff --git a/thirdparty/freetype/src/autofit/afshaper.h b/thirdparty/freetype/src/autofit/afshaper.h
index 9185d19003..7efd9f6a4e 100644
--- a/thirdparty/freetype/src/autofit/afshaper.h
+++ b/thirdparty/freetype/src/autofit/afshaper.h
@@ -4,7 +4,7 @@
/* */
/* HarfBuzz interface for accessing OpenType features (specification). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afstyles.h b/thirdparty/freetype/src/autofit/afstyles.h
index 281559eea2..e2688b3fc2 100644
--- a/thirdparty/freetype/src/autofit/afstyles.h
+++ b/thirdparty/freetype/src/autofit/afstyles.h
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter styles (specification only). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/aftypes.h b/thirdparty/freetype/src/autofit/aftypes.h
index 718dab70b6..6bd8c895b2 100644
--- a/thirdparty/freetype/src/autofit/aftypes.h
+++ b/thirdparty/freetype/src/autofit/aftypes.h
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter types (specification only). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -76,9 +76,9 @@ extern void* _af_debug_hints;
typedef struct AF_WidthRec_
{
- FT_Pos org; /* original position/width in font units */
- FT_Pos cur; /* current/scaled position/width in device sub-pixels */
- FT_Pos fit; /* current/fitted position/width in device sub-pixels */
+ FT_Pos org; /* original position/width in font units */
+ FT_Pos cur; /* current/scaled position/width in device subpixels */
+ FT_Pos fit; /* current/fitted position/width in device subpixels */
} AF_WidthRec, *AF_Width;
diff --git a/thirdparty/freetype/src/autofit/afwarp.c b/thirdparty/freetype/src/autofit/afwarp.c
index f99aa6d987..2a75ea7b35 100644
--- a/thirdparty/freetype/src/autofit/afwarp.c
+++ b/thirdparty/freetype/src/autofit/afwarp.c
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter warping algorithm (body). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afwarp.h b/thirdparty/freetype/src/autofit/afwarp.h
index 2e85cbd851..520b1be907 100644
--- a/thirdparty/freetype/src/autofit/afwarp.h
+++ b/thirdparty/freetype/src/autofit/afwarp.h
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter warping algorithm (specification). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/afwrtsys.h b/thirdparty/freetype/src/autofit/afwrtsys.h
index 86749a2a83..4675f3242d 100644
--- a/thirdparty/freetype/src/autofit/afwrtsys.h
+++ b/thirdparty/freetype/src/autofit/afwrtsys.h
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter writing systems (specification only). */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/autofit.c b/thirdparty/freetype/src/autofit/autofit.c
index bbedad7b5f..c1605160a1 100644
--- a/thirdparty/freetype/src/autofit/autofit.c
+++ b/thirdparty/freetype/src/autofit/autofit.c
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter module (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/autofit/module.mk b/thirdparty/freetype/src/autofit/module.mk
index c4e249b6f1..ff05f83e7e 100644
--- a/thirdparty/freetype/src/autofit/module.mk
+++ b/thirdparty/freetype/src/autofit/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2003-2017 by
+# Copyright 2003-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/autofit/rules.mk b/thirdparty/freetype/src/autofit/rules.mk
index ec4e1302d1..75171b412c 100644
--- a/thirdparty/freetype/src/autofit/rules.mk
+++ b/thirdparty/freetype/src/autofit/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2003-2017 by
+# Copyright 2003-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/base/basepic.c b/thirdparty/freetype/src/base/basepic.c
index 57fb8169ad..bc80406441 100644
--- a/thirdparty/freetype/src/base/basepic.c
+++ b/thirdparty/freetype/src/base/basepic.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for base. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/basepic.h b/thirdparty/freetype/src/base/basepic.h
index 258d4ce2ba..492d1ede56 100644
--- a/thirdparty/freetype/src/base/basepic.h
+++ b/thirdparty/freetype/src/base/basepic.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for base. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftadvanc.c b/thirdparty/freetype/src/base/ftadvanc.c
index 1557607fc5..230c84d6ad 100644
--- a/thirdparty/freetype/src/base/ftadvanc.c
+++ b/thirdparty/freetype/src/base/ftadvanc.c
@@ -4,7 +4,7 @@
/* */
/* Quick computation of advance widths (body). */
/* */
-/* Copyright 2008-2017 by */
+/* Copyright 2008-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -116,9 +116,12 @@
FT_Int32 flags,
FT_Fixed *padvances )
{
+ FT_Error error = FT_Err_Ok;
+
FT_Face_GetAdvancesFunc func;
- FT_UInt num, end, nn;
- FT_Error error = FT_Err_Ok;
+
+ FT_UInt num, end, nn;
+ FT_Int factor;
if ( !face )
@@ -152,16 +155,17 @@
return FT_THROW( Unimplemented_Feature );
flags |= (FT_UInt32)FT_LOAD_ADVANCE_ONLY;
+ factor = ( flags & FT_LOAD_NO_SCALE ) ? 1 : 1024;
for ( nn = 0; nn < count; nn++ )
{
error = FT_Load_Glyph( face, start + nn, flags );
if ( error )
break;
- /* scale from 26.6 to 16.16 */
+ /* scale from 26.6 to 16.16, unless NO_SCALE was requested */
padvances[nn] = ( flags & FT_LOAD_VERTICAL_LAYOUT )
- ? face->glyph->advance.y * 1024
- : face->glyph->advance.x * 1024;
+ ? face->glyph->advance.y * factor
+ : face->glyph->advance.x * factor;
}
return error;
diff --git a/thirdparty/freetype/src/base/ftapi.c b/thirdparty/freetype/src/base/ftapi.c
index 4262d37e39..32d6e95d19 100644
--- a/thirdparty/freetype/src/base/ftapi.c
+++ b/thirdparty/freetype/src/base/ftapi.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType compatibility functions (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftbase.c b/thirdparty/freetype/src/base/ftbase.c
index 55f7359942..f914b9b247 100644
--- a/thirdparty/freetype/src/base/ftbase.c
+++ b/thirdparty/freetype/src/base/ftbase.c
@@ -4,7 +4,7 @@
/* */
/* Single object library component (body only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -23,12 +23,15 @@
#include "ftadvanc.c"
#include "ftcalc.c"
#include "ftdbgmem.c"
+#include "ftfntfmt.c"
#include "ftgloadr.c"
#include "fthash.c"
+#include "ftlcdfil.c"
#include "ftmac.c"
#include "ftobjs.c"
#include "ftoutln.c"
#include "ftpic.c"
+#include "ftpsprop.c"
#include "ftrfork.c"
#include "ftsnames.c"
#include "ftstream.c"
diff --git a/thirdparty/freetype/src/base/ftbase.h b/thirdparty/freetype/src/base/ftbase.h
index 2072284f06..7e8cfad959 100644
--- a/thirdparty/freetype/src/base/ftbase.h
+++ b/thirdparty/freetype/src/base/ftbase.h
@@ -2,9 +2,9 @@
/* */
/* ftbase.h */
/* */
-/* The FreeType private functions used in base module (specification). */
+/* Private functions used in the `base' module (specification). */
/* */
-/* Copyright 2008-2017 by */
+/* Copyright 2008-2018 by */
/* David Turner, Robert Wilhelm, Werner Lemberg, and suzuki toshiya. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -27,6 +27,8 @@
FT_BEGIN_HEADER
+#ifdef FT_CONFIG_OPTION_MAC_FONTS
+
/* MacOS resource fork cannot exceed 16MB at least for Carbon code; */
/* see https://support.microsoft.com/en-us/kb/130437 */
#define FT_MAC_RFORK_MAX_LEN 0x00FFFFFFUL
@@ -65,6 +67,8 @@ FT_BEGIN_HEADER
ft_raccess_rule_by_darwin_vfs( FT_Library library, FT_UInt rule_index );
#endif
+#endif /* FT_CONFIG_OPTION_MAC_FONTS */
+
FT_END_HEADER
diff --git a/thirdparty/freetype/src/base/ftbbox.c b/thirdparty/freetype/src/base/ftbbox.c
index 6e19da63cb..151e85c97a 100644
--- a/thirdparty/freetype/src/base/ftbbox.c
+++ b/thirdparty/freetype/src/base/ftbbox.c
@@ -4,7 +4,7 @@
/* */
/* FreeType bbox computation (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used */
diff --git a/thirdparty/freetype/src/base/ftbdf.c b/thirdparty/freetype/src/base/ftbdf.c
index 40f0ca2bb8..c4ea502fbc 100644
--- a/thirdparty/freetype/src/base/ftbdf.c
+++ b/thirdparty/freetype/src/base/ftbdf.c
@@ -4,7 +4,7 @@
/* */
/* FreeType API for accessing BDF-specific strings (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftbitmap.c b/thirdparty/freetype/src/base/ftbitmap.c
index e567a0453e..a9746663fa 100644
--- a/thirdparty/freetype/src/base/ftbitmap.c
+++ b/thirdparty/freetype/src/base/ftbitmap.c
@@ -4,7 +4,7 @@
/* */
/* FreeType utility functions for bitmaps (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -153,38 +153,36 @@
FT_UInt ypixels )
{
FT_Error error;
- int pitch;
- int new_pitch;
+ unsigned int pitch;
+ unsigned int new_pitch;
FT_UInt bpp;
- FT_UInt i, width, height;
+ FT_UInt width, height;
unsigned char* buffer = NULL;
width = bitmap->width;
height = bitmap->rows;
- pitch = bitmap->pitch;
- if ( pitch < 0 )
- pitch = -pitch;
+ pitch = (unsigned int)FT_ABS( bitmap->pitch );
switch ( bitmap->pixel_mode )
{
case FT_PIXEL_MODE_MONO:
bpp = 1;
- new_pitch = (int)( ( width + xpixels + 7 ) >> 3 );
+ new_pitch = ( width + xpixels + 7 ) >> 3;
break;
case FT_PIXEL_MODE_GRAY2:
bpp = 2;
- new_pitch = (int)( ( width + xpixels + 3 ) >> 2 );
+ new_pitch = ( width + xpixels + 3 ) >> 2;
break;
case FT_PIXEL_MODE_GRAY4:
bpp = 4;
- new_pitch = (int)( ( width + xpixels + 1 ) >> 1 );
+ new_pitch = ( width + xpixels + 1 ) >> 1;
break;
case FT_PIXEL_MODE_GRAY:
case FT_PIXEL_MODE_LCD:
case FT_PIXEL_MODE_LCD_V:
bpp = 8;
- new_pitch = (int)( width + xpixels );
+ new_pitch = width + xpixels;
break;
default:
return FT_THROW( Invalid_Glyph_Format );
@@ -194,7 +192,7 @@
if ( ypixels == 0 && new_pitch <= pitch )
{
/* zero the padding */
- FT_UInt bit_width = (FT_UInt)pitch * 8;
+ FT_UInt bit_width = pitch * 8;
FT_UInt bit_last = ( width + xpixels ) * bpp;
@@ -235,31 +233,60 @@
{
FT_UInt len = ( width * bpp + 7 ) >> 3;
+ unsigned char* in = bitmap->buffer;
+ unsigned char* out = buffer;
+
+ unsigned char* limit = bitmap->buffer + pitch * bitmap->rows;
+ unsigned int delta = new_pitch - len;
+
+
+ FT_MEM_ZERO( out, new_pitch * ypixels );
+ out += new_pitch * ypixels;
- for ( i = 0; i < bitmap->rows; i++ )
- FT_MEM_COPY( buffer + (FT_UInt)new_pitch * ( ypixels + i ),
- bitmap->buffer + (FT_UInt)pitch * i,
- len );
+ while ( in < limit )
+ {
+ FT_MEM_COPY( out, in, len );
+ in += pitch;
+ out += len;
+
+ /* we use FT_QALLOC_MULT, which doesn't zero out the buffer; */
+ /* consequently, we have to manually zero out the remaining bytes */
+ FT_MEM_ZERO( out, delta );
+ out += delta;
+ }
}
else
{
FT_UInt len = ( width * bpp + 7 ) >> 3;
+ unsigned char* in = bitmap->buffer;
+ unsigned char* out = buffer;
+
+ unsigned char* limit = bitmap->buffer + pitch * bitmap->rows;
+ unsigned int delta = new_pitch - len;
+
- for ( i = 0; i < bitmap->rows; i++ )
- FT_MEM_COPY( buffer + (FT_UInt)new_pitch * i,
- bitmap->buffer + (FT_UInt)pitch * i,
- len );
+ while ( in < limit )
+ {
+ FT_MEM_COPY( out, in, len );
+ in += pitch;
+ out += len;
+
+ FT_MEM_ZERO( out, delta );
+ out += delta;
+ }
+
+ FT_MEM_ZERO( out, new_pitch * ypixels );
}
FT_FREE( bitmap->buffer );
bitmap->buffer = buffer;
- if ( bitmap->pitch < 0 )
- new_pitch = -new_pitch;
-
/* set pitch only, width and height are left untouched */
- bitmap->pitch = new_pitch;
+ if ( bitmap->pitch < 0 )
+ bitmap->pitch = -(int)new_pitch;
+ else
+ bitmap->pitch = (int)new_pitch;
return FT_Err_Ok;
}
@@ -444,7 +471,7 @@
* A gamma of 2.2 is fair to assume. And then, we need to
* undo the premultiplication too.
*
- * http://accessibility.kde.org/hsl-adjusted.php
+ * https://accessibility.kde.org/hsl-adjusted.php
*
* We do the computation with integers only, applying a gamma of 2.0.
* We guarantee 32-bit arithmetic to avoid overflow but the resulting
diff --git a/thirdparty/freetype/src/base/ftcalc.c b/thirdparty/freetype/src/base/ftcalc.c
index 00d63c6e6b..f4ff45f8ef 100644
--- a/thirdparty/freetype/src/base/ftcalc.c
+++ b/thirdparty/freetype/src/base/ftcalc.c
@@ -4,7 +4,7 @@
/* */
/* Arithmetic computations (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftcid.c b/thirdparty/freetype/src/base/ftcid.c
index 398396b845..f5184649bf 100644
--- a/thirdparty/freetype/src/base/ftcid.c
+++ b/thirdparty/freetype/src/base/ftcid.c
@@ -4,7 +4,7 @@
/* */
/* FreeType API for accessing CID font information. */
/* */
-/* Copyright 2007-2017 by */
+/* Copyright 2007-2018 by */
/* Derek Clegg and Michael Toftdal. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftdbgmem.c b/thirdparty/freetype/src/base/ftdbgmem.c
index 242246bfd1..c33d8acb4e 100644
--- a/thirdparty/freetype/src/base/ftdbgmem.c
+++ b/thirdparty/freetype/src/base/ftdbgmem.c
@@ -4,7 +4,7 @@
/* */
/* Memory debugger (body). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -826,7 +826,7 @@
FT_Int result = 0;
- if ( getenv( "FT2_DEBUG_MEMORY" ) )
+ if ( ft_getenv( "FT2_DEBUG_MEMORY" ) )
{
table = ft_mem_table_new( memory );
if ( table )
@@ -839,7 +839,7 @@
memory->realloc = ft_mem_debug_realloc;
memory->free = ft_mem_debug_free;
- p = getenv( "FT2_ALLOC_TOTAL_MAX" );
+ p = ft_getenv( "FT2_ALLOC_TOTAL_MAX" );
if ( p )
{
FT_Long total_max = ft_strtol( p, NULL, 10 );
@@ -852,7 +852,7 @@
}
}
- p = getenv( "FT2_ALLOC_COUNT_MAX" );
+ p = ft_getenv( "FT2_ALLOC_COUNT_MAX" );
if ( p )
{
FT_Long total_count = ft_strtol( p, NULL, 10 );
@@ -865,7 +865,7 @@
}
}
- p = getenv( "FT2_KEEP_ALIVE" );
+ p = ft_getenv( "FT2_KEEP_ALIVE" );
if ( p )
{
FT_Long keep_alive = ft_strtol( p, NULL, 10 );
diff --git a/thirdparty/freetype/src/base/ftdebug.c b/thirdparty/freetype/src/base/ftdebug.c
index 20c617089f..fe26309101 100644
--- a/thirdparty/freetype/src/base/ftdebug.c
+++ b/thirdparty/freetype/src/base/ftdebug.c
@@ -4,7 +4,7 @@
/* */
/* Debugging and logging component (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -161,7 +161,7 @@
FT_BASE_DEF( void )
ft_debug_init( void )
{
- const char* ft2_debug = getenv( "FT2_DEBUG" );
+ const char* ft2_debug = ft_getenv( "FT2_DEBUG" );
if ( ft2_debug )
diff --git a/thirdparty/freetype/src/base/ftfntfmt.c b/thirdparty/freetype/src/base/ftfntfmt.c
index dcbeba0053..a2900ceb09 100644
--- a/thirdparty/freetype/src/base/ftfntfmt.c
+++ b/thirdparty/freetype/src/base/ftfntfmt.c
@@ -4,7 +4,7 @@
/* */
/* FreeType utility file for font formats (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftfstype.c b/thirdparty/freetype/src/base/ftfstype.c
index cec4fb3025..e6cdf6e2ec 100644
--- a/thirdparty/freetype/src/base/ftfstype.c
+++ b/thirdparty/freetype/src/base/ftfstype.c
@@ -4,7 +4,7 @@
/* */
/* FreeType utility file to access FSType data (body). */
/* */
-/* Copyright 2008-2017 by */
+/* Copyright 2008-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftgasp.c b/thirdparty/freetype/src/base/ftgasp.c
index 477b72558c..4f80bba630 100644
--- a/thirdparty/freetype/src/base/ftgasp.c
+++ b/thirdparty/freetype/src/base/ftgasp.c
@@ -4,7 +4,7 @@
/* */
/* Access of TrueType's `gasp' table (body). */
/* */
-/* Copyright 2007-2017 by */
+/* Copyright 2007-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftgloadr.c b/thirdparty/freetype/src/base/ftgloadr.c
index 8134003b4b..47202496b9 100644
--- a/thirdparty/freetype/src/base/ftgloadr.c
+++ b/thirdparty/freetype/src/base/ftgloadr.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType glyph loader (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftglyph.c b/thirdparty/freetype/src/base/ftglyph.c
index 3f78a8c36b..6759aa25d0 100644
--- a/thirdparty/freetype/src/base/ftglyph.c
+++ b/thirdparty/freetype/src/base/ftglyph.c
@@ -4,7 +4,7 @@
/* */
/* FreeType convenience functions to handle glyphs (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftgxval.c b/thirdparty/freetype/src/base/ftgxval.c
index ff24d336df..19e2d6acb5 100644
--- a/thirdparty/freetype/src/base/ftgxval.c
+++ b/thirdparty/freetype/src/base/ftgxval.c
@@ -4,7 +4,7 @@
/* */
/* FreeType API for validating TrueTypeGX/AAT tables (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* Masatake YAMATO, Redhat K.K, */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/base/ftinit.c b/thirdparty/freetype/src/base/ftinit.c
index b3b08fa541..1fa4721094 100644
--- a/thirdparty/freetype/src/base/ftinit.c
+++ b/thirdparty/freetype/src/base/ftinit.c
@@ -4,7 +4,7 @@
/* */
/* FreeType initialization layer (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftlcdfil.c b/thirdparty/freetype/src/base/ftlcdfil.c
index 60c813fd9e..8d314df080 100644
--- a/thirdparty/freetype/src/base/ftlcdfil.c
+++ b/thirdparty/freetype/src/base/ftlcdfil.c
@@ -4,7 +4,7 @@
/* */
/* FreeType API for color filtering of subpixel bitmap glyphs (body). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -31,8 +31,41 @@
#define FT_SHIFTCLAMP( x ) ( x >>= 8, (FT_Byte)( x > 255 ? 255 : x ) )
+
+ /* add padding according to filter weights */
+ FT_BASE_DEF (void)
+ ft_lcd_padding( FT_Pos* Min,
+ FT_Pos* Max,
+ FT_GlyphSlot slot )
+ {
+ FT_Byte* lcd_weights;
+ FT_Bitmap_LcdFilterFunc lcd_filter_func;
+
+
+ /* Per-face LCD filtering takes priority if set up. */
+ if ( slot->face && slot->face->internal->lcd_filter_func )
+ {
+ lcd_weights = slot->face->internal->lcd_weights;
+ lcd_filter_func = slot->face->internal->lcd_filter_func;
+ }
+ else
+ {
+ lcd_weights = slot->library->lcd_weights;
+ lcd_filter_func = slot->library->lcd_filter_func;
+ }
+
+ if ( lcd_filter_func == ft_lcd_filter_fir )
+ {
+ *Min -= lcd_weights[0] ? 43 :
+ lcd_weights[1] ? 22 : 0;
+ *Max += lcd_weights[4] ? 43 :
+ lcd_weights[3] ? 22 : 0;
+ }
+ }
+
+
/* FIR filter used by the default and light filters */
- FT_BASE( void )
+ FT_BASE_DEF( void )
ft_lcd_filter_fir( FT_Bitmap* bitmap,
FT_Render_Mode mode,
FT_LcdFiveTapFilter weights )
@@ -44,7 +77,7 @@
/* take care of bitmap flow */
- if ( pitch > 0 )
+ if ( pitch > 0 && height > 0 )
origin += pitch * (FT_Int)( height - 1 );
/* horizontal in-place FIR filter */
@@ -159,7 +192,7 @@
/* take care of bitmap flow */
- if ( pitch > 0 )
+ if ( pitch > 0 && height > 0 )
origin += pitch * (FT_Int)( height - 1 );
/* horizontal in-place intra-pixel filter */
@@ -305,21 +338,21 @@
return FT_THROW( Invalid_Argument );
}
- library->lcd_filter = filter;
-
return FT_Err_Ok;
}
#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
- FT_BASE( void )
- ft_lcd_filter_fir( FT_Bitmap* bitmap,
- FT_Render_Mode mode,
- FT_LcdFiveTapFilter weights )
+ /* add padding according to accommodate outline shifts */
+ FT_BASE_DEF (void)
+ ft_lcd_padding( FT_Pos* Min,
+ FT_Pos* Max,
+ FT_GlyphSlot slot )
{
- FT_UNUSED( bitmap );
- FT_UNUSED( mode );
- FT_UNUSED( weights );
+ FT_UNUSED( slot );
+
+ *Min -= 21;
+ *Max += 21;
}
diff --git a/thirdparty/freetype/src/base/ftmac.c b/thirdparty/freetype/src/base/ftmac.c
index 4e76585e5f..fd4c0cc274 100644
--- a/thirdparty/freetype/src/base/ftmac.c
+++ b/thirdparty/freetype/src/base/ftmac.c
@@ -8,7 +8,7 @@
/* This file is for Mac OS X only; see builds/mac/ftoldmac.c for */
/* classic platforms built by MPW. */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftmm.c b/thirdparty/freetype/src/base/ftmm.c
index 43877ece45..800441bcac 100644
--- a/thirdparty/freetype/src/base/ftmm.c
+++ b/thirdparty/freetype/src/base/ftmm.c
@@ -4,7 +4,7 @@
/* */
/* Multiple Master font support (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -148,6 +148,25 @@
/* documentation is in ftmm.h */
FT_EXPORT_DEF( FT_Error )
+ FT_Done_MM_Var( FT_Library library,
+ FT_MM_Var* amaster )
+ {
+ FT_Memory memory;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ memory = library->memory;
+ FT_FREE( amaster );
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
FT_Set_MM_Design_Coordinates( FT_Face face,
FT_UInt num_coords,
FT_Long* coords )
@@ -203,6 +222,10 @@
error = FT_ERR( Invalid_Argument );
if ( service_mm->set_var_design )
error = service_mm->set_var_design( face, num_coords, coords );
+
+ /* internal error code -1 means `no change'; we can exit immediately */
+ if ( error == -1 )
+ return FT_Err_Ok;
}
if ( !error )
@@ -275,6 +298,10 @@
error = FT_ERR( Invalid_Argument );
if ( service_mm->set_mm_blend )
error = service_mm->set_mm_blend( face, num_coords, coords );
+
+ /* internal error code -1 means `no change'; we can exit immediately */
+ if ( error == -1 )
+ return FT_Err_Ok;
}
if ( !error )
@@ -322,6 +349,10 @@
error = FT_ERR( Invalid_Argument );
if ( service_mm->set_mm_blend )
error = service_mm->set_mm_blend( face, num_coords, coords );
+
+ /* internal error code -1 means `no change'; we can exit immediately */
+ if ( error == -1 )
+ return FT_Err_Ok;
}
if ( !error )
@@ -426,4 +457,52 @@
}
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Named_Instance( FT_Face face,
+ FT_UInt instance_index )
+ {
+ FT_Error error;
+
+ FT_Service_MultiMasters service_mm = NULL;
+ FT_Service_MetricsVariations service_mvar = NULL;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ error = ft_face_get_mm_service( face, &service_mm );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service_mm->set_instance )
+ error = service_mm->set_instance( face, instance_index );
+ }
+
+ if ( !error )
+ {
+ (void)ft_face_get_mvar_service( face, &service_mvar );
+
+ if ( service_mvar && service_mvar->metrics_adjust )
+ service_mvar->metrics_adjust( face );
+ }
+
+ /* enforce recomputation of auto-hinting data */
+ if ( !error && face->autohint.finalizer )
+ {
+ face->autohint.finalizer( face->autohint.data );
+ face->autohint.data = NULL;
+ }
+
+ if ( !error )
+ {
+ face->face_index = ( instance_index << 16 ) |
+ ( face->face_index & 0xFFFFL );
+ face->face_flags &= ~FT_FACE_FLAG_VARIATION;
+ }
+
+ return error;
+ }
+
+
/* END */
diff --git a/thirdparty/freetype/src/base/ftobjs.c b/thirdparty/freetype/src/base/ftobjs.c
index 6db8136cfc..8d07e35ae3 100644
--- a/thirdparty/freetype/src/base/ftobjs.c
+++ b/thirdparty/freetype/src/base/ftobjs.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType private base classes (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -19,12 +19,16 @@
#include <ft2build.h>
#include FT_LIST_H
#include FT_OUTLINE_H
+#include FT_FONT_FORMATS_H
+
#include FT_INTERNAL_VALIDATE_H
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_RFORK_H
#include FT_INTERNAL_STREAM_H
-#include FT_INTERNAL_SFNT_H /* for SFNT_Load_Table_Func */
+#include FT_INTERNAL_SFNT_H /* for SFNT_Load_Table_Func */
+#include FT_INTERNAL_POSTSCRIPT_AUX_H /* for PS_Driver */
+
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_TAGS_H
#include FT_TRUETYPE_IDS_H
@@ -37,8 +41,7 @@
#include FT_SERVICE_KERNING_H
#include FT_SERVICE_TRUETYPE_ENGINE_H
-#include FT_AUTOHINTER_H
-#include FT_CFF_DRIVER_H
+#include FT_DRIVER_H
#ifdef FT_CONFIG_OPTION_MAC_FONTS
#include "ftbase.h"
@@ -328,6 +331,138 @@
FT_BASE_DEF( void )
+ ft_glyphslot_preset_bitmap( FT_GlyphSlot slot,
+ FT_Render_Mode mode,
+ const FT_Vector* origin )
+ {
+ FT_Outline* outline = &slot->outline;
+ FT_Bitmap* bitmap = &slot->bitmap;
+
+ FT_Pixel_Mode pixel_mode;
+
+ FT_BBox cbox;
+ FT_Pos x_shift = 0;
+ FT_Pos y_shift = 0;
+ FT_Pos x_left, y_top;
+ FT_Pos width, height, pitch;
+
+
+ if ( slot->internal && ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) )
+ return;
+
+ if ( origin )
+ {
+ x_shift = origin->x;
+ y_shift = origin->y;
+ }
+
+ /* compute the control box, and grid-fit it, */
+ /* taking into account the origin shift */
+ FT_Outline_Get_CBox( outline, &cbox );
+
+ cbox.xMin += x_shift;
+ cbox.yMin += y_shift;
+ cbox.xMax += x_shift;
+ cbox.yMax += y_shift;
+
+ switch ( mode )
+ {
+ case FT_RENDER_MODE_MONO:
+ pixel_mode = FT_PIXEL_MODE_MONO;
+#if 1
+ /* undocumented but confirmed: bbox values get rounded */
+ /* unless the rounded box can collapse for a narrow glyph */
+ if ( cbox.xMax - cbox.xMin < 64 )
+ {
+ cbox.xMin = FT_PIX_FLOOR( cbox.xMin );
+ cbox.xMax = FT_PIX_CEIL_LONG( cbox.xMax );
+ }
+ else
+ {
+ cbox.xMin = FT_PIX_ROUND_LONG( cbox.xMin );
+ cbox.xMax = FT_PIX_ROUND_LONG( cbox.xMax );
+ }
+
+ if ( cbox.yMax - cbox.yMin < 64 )
+ {
+ cbox.yMin = FT_PIX_FLOOR( cbox.yMin );
+ cbox.yMax = FT_PIX_CEIL_LONG( cbox.yMax );
+ }
+ else
+ {
+ cbox.yMin = FT_PIX_ROUND_LONG( cbox.yMin );
+ cbox.yMax = FT_PIX_ROUND_LONG( cbox.yMax );
+ }
+#else
+ cbox.xMin = FT_PIX_FLOOR( cbox.xMin );
+ cbox.yMin = FT_PIX_FLOOR( cbox.yMin );
+ cbox.xMax = FT_PIX_CEIL_LONG( cbox.xMax );
+ cbox.yMax = FT_PIX_CEIL_LONG( cbox.yMax );
+#endif
+ break;
+
+ case FT_RENDER_MODE_LCD:
+ pixel_mode = FT_PIXEL_MODE_LCD;
+ ft_lcd_padding( &cbox.xMin, &cbox.xMax, slot );
+ goto Round;
+
+ case FT_RENDER_MODE_LCD_V:
+ pixel_mode = FT_PIXEL_MODE_LCD_V;
+ ft_lcd_padding( &cbox.yMin, &cbox.yMax, slot );
+ goto Round;
+
+ case FT_RENDER_MODE_NORMAL:
+ case FT_RENDER_MODE_LIGHT:
+ default:
+ pixel_mode = FT_PIXEL_MODE_GRAY;
+ Round:
+ cbox.xMin = FT_PIX_FLOOR( cbox.xMin );
+ cbox.yMin = FT_PIX_FLOOR( cbox.yMin );
+ cbox.xMax = FT_PIX_CEIL_LONG( cbox.xMax );
+ cbox.yMax = FT_PIX_CEIL_LONG( cbox.yMax );
+ }
+
+ x_shift = SUB_LONG( x_shift, cbox.xMin );
+ y_shift = SUB_LONG( y_shift, cbox.yMin );
+
+ x_left = cbox.xMin >> 6;
+ y_top = cbox.yMax >> 6;
+
+ width = ( (FT_ULong)cbox.xMax - (FT_ULong)cbox.xMin ) >> 6;
+ height = ( (FT_ULong)cbox.yMax - (FT_ULong)cbox.yMin ) >> 6;
+
+ switch ( pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO:
+ pitch = ( ( width + 15 ) >> 4 ) << 1;
+ break;
+
+ case FT_PIXEL_MODE_LCD:
+ width *= 3;
+ pitch = FT_PAD_CEIL( width, 4 );
+ break;
+
+ case FT_PIXEL_MODE_LCD_V:
+ height *= 3;
+ /* fall through */
+
+ case FT_PIXEL_MODE_GRAY:
+ default:
+ pitch = width;
+ }
+
+ slot->bitmap_left = (FT_Int)x_left;
+ slot->bitmap_top = (FT_Int)y_top;
+
+ bitmap->pixel_mode = (unsigned char)pixel_mode;
+ bitmap->num_grays = 256;
+ bitmap->width = (unsigned int)width;
+ bitmap->rows = (unsigned int)height;
+ bitmap->pitch = pitch;
+ }
+
+
+ FT_BASE_DEF( void )
ft_glyphslot_set_bitmap( FT_GlyphSlot slot,
FT_Byte* buffer )
{
@@ -669,9 +804,13 @@
* Determine whether we need to auto-hint or not.
* The general rules are:
*
- * - Do only auto-hinting if we have a hinter module, a scalable font
- * format dealing with outlines, and no transforms except simple
- * slants and/or rotations by integer multiples of 90 degrees.
+ * - Do only auto-hinting if we have
+ *
+ * - a hinter module,
+ * - a scalable font format dealing with outlines,
+ * - not a tricky font, and
+ * - no transforms except simple slants and/or rotations by
+ * integer multiples of 90 degrees.
*
* - Then, auto-hint if FT_LOAD_FORCE_AUTOHINT is set or if we don't
* have a native font hinter.
@@ -701,8 +840,15 @@
else
{
FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags );
+ FT_Bool is_light_type1;
+ /* only the new Adobe engine (for both CFF and Type 1) is `light'; */
+ /* we use `strstr' to catch both `Type 1' and `CID Type 1' */
+ is_light_type1 =
+ ft_strstr( FT_Get_Font_Format( face ), "Type 1" ) != NULL &&
+ ((PS_Driver)driver)->hinting_engine == FT_HINTING_ADOBE;
+
/* the check for `num_locations' assures that we actually */
/* test for instructions in a TTF and not in a CFF-based OTF */
/* */
@@ -710,8 +856,9 @@
/* check the size of the `fpgm' and `prep' tables, too -- */
/* the assumption is that there don't exist real TTFs where */
/* both `fpgm' and `prep' tables are missing */
- if ( ( mode == FT_RENDER_MODE_LIGHT &&
- !FT_DRIVER_HINTS_LIGHTLY( driver ) ) ||
+ if ( ( mode == FT_RENDER_MODE_LIGHT &&
+ ( !FT_DRIVER_HINTS_LIGHTLY( driver ) &&
+ !is_light_type1 ) ) ||
( FT_IS_SFNT( face ) &&
ttface->num_locations &&
ttface->max_profile.maxSizeOfInstructions == 0 &&
@@ -731,8 +878,8 @@
/* XXX: This is really a temporary hack that should disappear */
/* promptly with FreeType 2.1! */
/* */
- if ( FT_HAS_FIXED_SIZES( face ) &&
- ( load_flags & FT_LOAD_NO_BITMAP ) == 0 )
+ if ( FT_HAS_FIXED_SIZES( face ) &&
+ ( load_flags & FT_LOAD_NO_BITMAP ) == 0 )
{
error = driver->clazz->load_glyph( slot, face->size,
glyph_index,
@@ -800,7 +947,7 @@
/* compute the linear advance in 16.16 pixels */
if ( ( load_flags & FT_LOAD_LINEAR_DESIGN ) == 0 &&
- ( FT_IS_SCALABLE( face ) ) )
+ FT_IS_SCALABLE( face ) )
{
FT_Size_Metrics* metrics = &face->size->metrics;
@@ -848,28 +995,37 @@
}
}
- FT_TRACE5(( " x advance: %d\n" , slot->advance.x ));
- FT_TRACE5(( " y advance: %d\n" , slot->advance.y ));
-
- FT_TRACE5(( " linear x advance: %d\n" , slot->linearHoriAdvance ));
- FT_TRACE5(( " linear y advance: %d\n" , slot->linearVertAdvance ));
-
- /* do we need to render the image now? */
+ /* do we need to render the image or preset the bitmap now? */
if ( !error &&
+ ( load_flags & FT_LOAD_NO_SCALE ) == 0 &&
slot->format != FT_GLYPH_FORMAT_BITMAP &&
- slot->format != FT_GLYPH_FORMAT_COMPOSITE &&
- load_flags & FT_LOAD_RENDER )
+ slot->format != FT_GLYPH_FORMAT_COMPOSITE )
{
FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags );
- if ( mode == FT_RENDER_MODE_NORMAL &&
- (load_flags & FT_LOAD_MONOCHROME ) )
+ if ( mode == FT_RENDER_MODE_NORMAL &&
+ load_flags & FT_LOAD_MONOCHROME )
mode = FT_RENDER_MODE_MONO;
- error = FT_Render_Glyph( slot, mode );
+ if ( load_flags & FT_LOAD_RENDER )
+ error = FT_Render_Glyph( slot, mode );
+ else
+ ft_glyphslot_preset_bitmap( slot, mode, NULL );
}
+ FT_TRACE5(( "FT_Load_Glyph: index %d, flags %x\n",
+ glyph_index, load_flags ));
+ FT_TRACE5(( " x advance: %f\n", slot->advance.x / 64.0 ));
+ FT_TRACE5(( " y advance: %f\n", slot->advance.y / 64.0 ));
+ FT_TRACE5(( " linear x advance: %f\n",
+ slot->linearHoriAdvance / 65536.0 ));
+ FT_TRACE5(( " linear y advance: %f\n",
+ slot->linearVertAdvance / 65536.0 ));
+ FT_TRACE5(( " bitmap %dx%d, mode %d\n",
+ slot->bitmap.width, slot->bitmap.rows,
+ slot->bitmap.pixel_mode ));
+
Exit:
return error;
}
@@ -2441,7 +2597,8 @@
internal->no_stem_darkening = -1;
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
- ft_memset( internal->lcd_weights, 0, FT_LCD_FILTER_FIVE_TAPS );
+ /* Per-face filtering can only be set up by FT_Face_Properties */
+ internal->lcd_filter_func = NULL;
#endif
}
@@ -2864,18 +3021,6 @@
metrics->height = bsize->height << 6;
metrics->max_advance = bsize->x_ppem;
}
-
- FT_TRACE5(( "FT_Select_Metrics:\n" ));
- FT_TRACE5(( " x scale: %d (%f)\n",
- metrics->x_scale, metrics->x_scale / 65536.0 ));
- FT_TRACE5(( " y scale: %d (%f)\n",
- metrics->y_scale, metrics->y_scale / 65536.0 ));
- FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 ));
- FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 ));
- FT_TRACE5(( " height: %f\n", metrics->height / 64.0 ));
- FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 ));
- FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
- FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
}
@@ -2984,18 +3129,6 @@
metrics->x_scale = 1L << 16;
metrics->y_scale = 1L << 16;
}
-
- FT_TRACE5(( "FT_Request_Metrics:\n" ));
- FT_TRACE5(( " x scale: %d (%f)\n",
- metrics->x_scale, metrics->x_scale / 65536.0 ));
- FT_TRACE5(( " y scale: %d (%f)\n",
- metrics->y_scale, metrics->y_scale / 65536.0 ));
- FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 ));
- FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 ));
- FT_TRACE5(( " height: %f\n", metrics->height / 64.0 ));
- FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 ));
- FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
- FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
}
@@ -3005,6 +3138,7 @@
FT_Select_Size( FT_Face face,
FT_Int strike_index )
{
+ FT_Error error = FT_Err_Ok;
FT_Driver_Class clazz;
@@ -3018,36 +3152,37 @@
if ( clazz->select_size )
{
- FT_Error error;
+ error = clazz->select_size( face->size, (FT_ULong)strike_index );
+ FT_TRACE5(( "FT_Select_Size (%s driver):\n",
+ face->driver->root.clazz->module_name ));
+ }
+ else
+ {
+ FT_Select_Metrics( face, (FT_ULong)strike_index );
- error = clazz->select_size( face->size, (FT_ULong)strike_index );
+ FT_TRACE5(( "FT_Select_Size:\n" ));
+ }
#ifdef FT_DEBUG_LEVEL_TRACE
- {
- FT_Size_Metrics* metrics = &face->size->metrics;
-
-
- FT_TRACE5(( "FT_Select_Size (font driver's `select_size'):\n" ));
- FT_TRACE5(( " x scale: %d (%f)\n",
- metrics->x_scale, metrics->x_scale / 65536.0 ));
- FT_TRACE5(( " y scale: %d (%f)\n",
- metrics->y_scale, metrics->y_scale / 65536.0 ));
- FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 ));
- FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 ));
- FT_TRACE5(( " height: %f\n", metrics->height / 64.0 ));
- FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 ));
- FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
- FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
- }
-#endif
+ {
+ FT_Size_Metrics* metrics = &face->size->metrics;
- return error;
- }
- FT_Select_Metrics( face, (FT_ULong)strike_index );
+ FT_TRACE5(( " x scale: %d (%f)\n",
+ metrics->x_scale, metrics->x_scale / 65536.0 ));
+ FT_TRACE5(( " y scale: %d (%f)\n",
+ metrics->y_scale, metrics->y_scale / 65536.0 ));
+ FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 ));
+ FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 ));
+ FT_TRACE5(( " height: %f\n", metrics->height / 64.0 ));
+ FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 ));
+ FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
+ FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
+ }
+#endif
- return FT_Err_Ok;
+ return error;
}
@@ -3057,6 +3192,7 @@
FT_Request_Size( FT_Face face,
FT_Size_Request req )
{
+ FT_Error error = FT_Err_Ok;
FT_Driver_Class clazz;
FT_ULong strike_index;
@@ -3076,55 +3212,52 @@
if ( clazz->request_size )
{
- FT_Error error;
-
-
error = clazz->request_size( face->size, req );
-#ifdef FT_DEBUG_LEVEL_TRACE
- {
- FT_Size_Metrics* metrics = &face->size->metrics;
-
-
- FT_TRACE5(( "FT_Request_Size (font driver's `request_size'):\n" ));
- FT_TRACE5(( " x scale: %d (%f)\n",
- metrics->x_scale, metrics->x_scale / 65536.0 ));
- FT_TRACE5(( " y scale: %d (%f)\n",
- metrics->y_scale, metrics->y_scale / 65536.0 ));
- FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 ));
- FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 ));
- FT_TRACE5(( " height: %f\n", metrics->height / 64.0 ));
- FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 ));
- FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
- FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
- }
-#endif
-
- return error;
+ FT_TRACE5(( "FT_Request_Size (%s driver):\n",
+ face->driver->root.clazz->module_name ));
}
-
- /*
- * The reason that a driver doesn't have `request_size' defined is
- * either that the scaling here suffices or that the supported formats
- * are bitmap-only and size matching is not implemented.
- *
- * In the latter case, a simple size matching is done.
- */
- if ( !FT_IS_SCALABLE( face ) && FT_HAS_FIXED_SIZES( face ) )
+ else if ( !FT_IS_SCALABLE( face ) && FT_HAS_FIXED_SIZES( face ) )
{
- FT_Error error;
-
-
+ /*
+ * The reason that a driver doesn't have `request_size' defined is
+ * either that the scaling here suffices or that the supported formats
+ * are bitmap-only and size matching is not implemented.
+ *
+ * In the latter case, a simple size matching is done.
+ */
error = FT_Match_Size( face, req, 0, &strike_index );
if ( error )
return error;
return FT_Select_Size( face, (FT_Int)strike_index );
}
+ else
+ {
+ FT_Request_Metrics( face, req );
- FT_Request_Metrics( face, req );
+ FT_TRACE5(( "FT_Request_Size:\n" ));
+ }
- return FT_Err_Ok;
+#ifdef FT_DEBUG_LEVEL_TRACE
+ {
+ FT_Size_Metrics* metrics = &face->size->metrics;
+
+
+ FT_TRACE5(( " x scale: %d (%f)\n",
+ metrics->x_scale, metrics->x_scale / 65536.0 ));
+ FT_TRACE5(( " y scale: %d (%f)\n",
+ metrics->y_scale, metrics->y_scale / 65536.0 ));
+ FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 ));
+ FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 ));
+ FT_TRACE5(( " height: %f\n", metrics->height / 64.0 ));
+ FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 ));
+ FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
+ FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
+ }
+#endif
+
+ return error;
}
@@ -3653,16 +3786,11 @@
{
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
if ( properties->data )
+ {
ft_memcpy( face->internal->lcd_weights,
properties->data,
FT_LCD_FILTER_FIVE_TAPS );
- else
- {
- /* Value NULL indicates `no custom weights, use library */
- /* defaults', signaled by filling the weight field with zeros. */
- ft_memset( face->internal->lcd_weights,
- 0,
- FT_LCD_FILTER_FIVE_TAPS );
+ face->internal->lcd_filter_func = ft_lcd_filter_fir;
}
#else
error = FT_THROW( Unimplemented_Feature );
@@ -4414,43 +4542,97 @@
*/
/* we use FT_TRACE3 in this block */
- if ( ft_trace_levels[trace_bitmap] >= 3 )
+ if ( !error &&
+ ft_trace_levels[trace_bitmap] >= 3 &&
+ slot->bitmap.buffer )
{
+ FT_Bitmap bitmap;
+ FT_Error err;
+
+
+ FT_Bitmap_Init( &bitmap );
+
/* we convert to a single bitmap format for computing the checksum */
- if ( !error && slot->bitmap.buffer )
+ /* this also converts the bitmap flow to `down' (i.e., pitch > 0) */
+ err = FT_Bitmap_Convert( library, &slot->bitmap, &bitmap, 1 );
+ if ( !err )
{
- FT_Bitmap bitmap;
- FT_Error err;
+ MD5_CTX ctx;
+ unsigned char md5[16];
+ unsigned long coverage = 0;
+ int i, j;
+ int rows = (int)bitmap.rows;
+ int pitch = bitmap.pitch;
+
+
+ FT_TRACE3(( "FT_Render_Glyph: bitmap %dx%d, mode %d\n",
+ rows, pitch, slot->bitmap.pixel_mode ));
+
+ for ( i = 0; i < rows; i++ )
+ for ( j = 0; j < pitch; j++ )
+ coverage += bitmap.buffer[i * pitch + j];
+ FT_TRACE3(( " Total coverage: %lu\n", coverage ));
- FT_Bitmap_Init( &bitmap );
+ MD5_Init( &ctx );
+ if ( bitmap.buffer )
+ MD5_Update( &ctx, bitmap.buffer,
+ (unsigned long)rows * (unsigned long)pitch );
+ MD5_Final( md5, &ctx );
- /* this also converts the bitmap flow to `down' (i.e., pitch > 0) */
- err = FT_Bitmap_Convert( library, &slot->bitmap, &bitmap, 1 );
- if ( !err )
+ FT_TRACE3(( " MD5 checksum: " ));
+ for ( i = 0; i < 16; i++ )
+ FT_TRACE3(( "%02X", md5[i] ));
+ FT_TRACE3(( "\n" ));
+ }
+
+ FT_Bitmap_Done( library, &bitmap );
+ }
+
+ /*
+ * Dump bitmap in Netpbm format (PBM or PGM).
+ */
+
+ /* we use FT_TRACE7 in this block */
+ if ( !error &&
+ ft_trace_levels[trace_bitmap] >= 7 &&
+ slot->bitmap.rows < 128U &&
+ slot->bitmap.width < 128U &&
+ slot->bitmap.buffer )
+ {
+ int rows = (int)slot->bitmap.rows;
+ int width = (int)slot->bitmap.width;
+ int pitch = slot->bitmap.pitch;
+ int i, j, m;
+ unsigned char* topleft = slot->bitmap.buffer;
+
+ if ( pitch < 0 )
+ topleft -= pitch * ( rows - 1 );
+
+ FT_TRACE7(( "Netpbm image: start\n" ));
+ switch ( slot->bitmap.pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO:
+ FT_TRACE7(( "P1 %d %d\n", width, rows ));
+ for ( i = 0; i < rows; i++ )
{
- MD5_CTX ctx;
- unsigned char md5[16];
- int i;
- unsigned int rows = bitmap.rows;
- unsigned int pitch = (unsigned int)bitmap.pitch;
-
-
- MD5_Init( &ctx );
- if ( bitmap.buffer )
- MD5_Update( &ctx, bitmap.buffer, rows * pitch );
- MD5_Final( md5, &ctx );
-
- FT_TRACE3(( "MD5 checksum for %dx%d bitmap:\n"
- " ",
- rows, pitch ));
- for ( i = 0; i < 16; i++ )
- FT_TRACE3(( "%02X", md5[i] ));
- FT_TRACE3(( "\n" ));
+ for ( j = 0; j < width; )
+ for ( m = 128; m > 0 && j < width; m >>= 1, j++ )
+ FT_TRACE7(( " %d", ( topleft[i * pitch + j / 8] & m ) != 0 ));
+ FT_TRACE7(( "\n" ));
}
+ break;
- FT_Bitmap_Done( library, &bitmap );
+ default:
+ FT_TRACE7(( "P2 %d %d 255\n", width, rows ));
+ for ( i = 0; i < rows; i++ )
+ {
+ for ( j = 0; j < width; j += 1 )
+ FT_TRACE7(( " %3u", topleft[i * pitch + j] ));
+ FT_TRACE7(( "\n" ));
+ }
}
+ FT_TRACE7(( "Netpbm image: end\n" ));
}
#undef FT_COMPONENT
@@ -4995,9 +5177,9 @@
#ifdef FT_CONFIG_OPTION_PIC
Fail:
ft_pic_container_destroy( library );
-#endif
FT_FREE( library );
return error;
+#endif
}
diff --git a/thirdparty/freetype/src/base/ftotval.c b/thirdparty/freetype/src/base/ftotval.c
index 5fa098691e..a2944a7950 100644
--- a/thirdparty/freetype/src/base/ftotval.c
+++ b/thirdparty/freetype/src/base/ftotval.c
@@ -4,7 +4,7 @@
/* */
/* FreeType API for validating OpenType tables (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftoutln.c b/thirdparty/freetype/src/base/ftoutln.c
index 9ceb9cf1ba..cb91321deb 100644
--- a/thirdparty/freetype/src/base/ftoutln.c
+++ b/thirdparty/freetype/src/base/ftoutln.c
@@ -4,7 +4,7 @@
/* */
/* FreeType outline management (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -286,12 +286,13 @@
FT_TRACE5(( "FT_Outline_Decompose: Done\n", n ));
return FT_Err_Ok;
+ Invalid_Outline:
+ error = FT_THROW( Invalid_Outline );
+ /* fall through */
+
Exit:
FT_TRACE5(( "FT_Outline_Decompose: Error 0x%x\n", error ));
return error;
-
- Invalid_Outline:
- return FT_THROW( Invalid_Outline );
}
@@ -540,8 +541,8 @@
for ( n = 0; n < outline->n_points; n++ )
{
- vec->x += xOffset;
- vec->y += yOffset;
+ vec->x = ADD_LONG( vec->x, xOffset );
+ vec->y = ADD_LONG( vec->y, yOffset );
vec++;
}
}
diff --git a/thirdparty/freetype/src/base/ftpatent.c b/thirdparty/freetype/src/base/ftpatent.c
index 9900f99bfc..e23ee2e3f4 100644
--- a/thirdparty/freetype/src/base/ftpatent.c
+++ b/thirdparty/freetype/src/base/ftpatent.c
@@ -5,7 +5,7 @@
/* FreeType API for checking patented TrueType bytecode instructions */
/* (body). Obsolete, retained for backward compatibility. */
/* */
-/* Copyright 2007-2017 by */
+/* Copyright 2007-2018 by */
/* David Turner. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftpfr.c b/thirdparty/freetype/src/base/ftpfr.c
index 5cc0b70726..bfe13520eb 100644
--- a/thirdparty/freetype/src/base/ftpfr.c
+++ b/thirdparty/freetype/src/base/ftpfr.c
@@ -4,7 +4,7 @@
/* */
/* FreeType API for accessing PFR-specific data (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftpic.c b/thirdparty/freetype/src/base/ftpic.c
index 0f84fddc98..1492e1809a 100644
--- a/thirdparty/freetype/src/base/ftpic.c
+++ b/thirdparty/freetype/src/base/ftpic.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services (body). */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftpsprop.c b/thirdparty/freetype/src/base/ftpsprop.c
new file mode 100644
index 0000000000..459b5e6054
--- /dev/null
+++ b/thirdparty/freetype/src/base/ftpsprop.c
@@ -0,0 +1,285 @@
+/***************************************************************************/
+/* */
+/* ftpsprop.c */
+/* */
+/* Get and set properties of PostScript drivers (body). */
+/* See `ftdriver.h' for available properties. */
+/* */
+/* Copyright 2017-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_DRIVER_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_POSTSCRIPT_PROPS_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_psprops
+
+
+ FT_BASE_CALLBACK_DEF( FT_Error )
+ ps_property_set( FT_Module module, /* PS_Driver */
+ const char* property_name,
+ const void* value,
+ FT_Bool value_is_string )
+ {
+ FT_Error error = FT_Err_Ok;
+ PS_Driver driver = (PS_Driver)module;
+
+#ifndef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+ FT_UNUSED( value_is_string );
+#endif
+
+
+ if ( !ft_strcmp( property_name, "darkening-parameters" ) )
+ {
+ FT_Int* darken_params;
+ FT_Int x1, y1, x2, y2, x3, y3, x4, y4;
+
+#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+ FT_Int dp[8];
+
+
+ if ( value_is_string )
+ {
+ const char* s = (const char*)value;
+ char* ep;
+ int i;
+
+
+ /* eight comma-separated numbers */
+ for ( i = 0; i < 7; i++ )
+ {
+ dp[i] = (FT_Int)ft_strtol( s, &ep, 10 );
+ if ( *ep != ',' || s == ep )
+ return FT_THROW( Invalid_Argument );
+
+ s = ep + 1;
+ }
+
+ dp[7] = (FT_Int)ft_strtol( s, &ep, 10 );
+ if ( !( *ep == '\0' || *ep == ' ' ) || s == ep )
+ return FT_THROW( Invalid_Argument );
+
+ darken_params = dp;
+ }
+ else
+#endif
+ darken_params = (FT_Int*)value;
+
+ x1 = darken_params[0];
+ y1 = darken_params[1];
+ x2 = darken_params[2];
+ y2 = darken_params[3];
+ x3 = darken_params[4];
+ y3 = darken_params[5];
+ x4 = darken_params[6];
+ y4 = darken_params[7];
+
+ if ( x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 ||
+ y1 < 0 || y2 < 0 || y3 < 0 || y4 < 0 ||
+ x1 > x2 || x2 > x3 || x3 > x4 ||
+ y1 > 500 || y2 > 500 || y3 > 500 || y4 > 500 )
+ return FT_THROW( Invalid_Argument );
+
+ driver->darken_params[0] = x1;
+ driver->darken_params[1] = y1;
+ driver->darken_params[2] = x2;
+ driver->darken_params[3] = y2;
+ driver->darken_params[4] = x3;
+ driver->darken_params[5] = y3;
+ driver->darken_params[6] = x4;
+ driver->darken_params[7] = y4;
+
+ return error;
+ }
+
+ else if ( !ft_strcmp( property_name, "hinting-engine" ) )
+ {
+#if defined( CFF_CONFIG_OPTION_OLD_ENGINE ) || \
+ defined( T1_CONFIG_OPTION_OLD_ENGINE )
+ const char* module_name = module->clazz->module_name;
+#endif
+
+
+#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+ if ( value_is_string )
+ {
+ const char* s = (const char*)value;
+
+
+ if ( !ft_strcmp( s, "adobe" ) )
+ driver->hinting_engine = FT_HINTING_ADOBE;
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ else if ( !ft_strcmp( module_name, "cff" ) &&
+ !ft_strcmp( s, "freetype" ) )
+ driver->hinting_engine = FT_HINTING_FREETYPE;
+#endif
+
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ else if ( ( !ft_strcmp( module_name, "type1" ) ||
+ !ft_strcmp( module_name, "t1cid" ) ) &&
+ !ft_strcmp( s, "freetype" ) )
+ driver->hinting_engine = FT_HINTING_FREETYPE;
+#endif
+
+ else
+ return FT_THROW( Invalid_Argument );
+ }
+ else
+#endif /* FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES */
+ {
+ FT_UInt* hinting_engine = (FT_UInt*)value;
+
+
+ if ( *hinting_engine == FT_HINTING_ADOBE
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ || ( *hinting_engine == FT_HINTING_FREETYPE &&
+ !ft_strcmp( module_name, "cff" ) )
+#endif
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ || ( *hinting_engine == FT_HINTING_FREETYPE &&
+ ( !ft_strcmp( module_name, "type1" ) ||
+ !ft_strcmp( module_name, "t1cid" ) ) )
+#endif
+ )
+ driver->hinting_engine = *hinting_engine;
+ else
+ error = FT_ERR( Unimplemented_Feature );
+
+ return error;
+ }
+ }
+
+ else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
+ {
+#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+ if ( value_is_string )
+ {
+ const char* s = (const char*)value;
+ long nsd = ft_strtol( s, NULL, 10 );
+
+
+ if ( !nsd )
+ driver->no_stem_darkening = FALSE;
+ else
+ driver->no_stem_darkening = TRUE;
+ }
+ else
+#endif
+ {
+ FT_Bool* no_stem_darkening = (FT_Bool*)value;
+
+
+ driver->no_stem_darkening = *no_stem_darkening;
+ }
+
+ return error;
+ }
+
+ else if ( !ft_strcmp( property_name, "random-seed" ) )
+ {
+ FT_Int32 random_seed;
+
+
+#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+ if ( value_is_string )
+ {
+ const char* s = (const char*)value;
+
+
+ random_seed = (FT_Int32)ft_strtol( s, NULL, 10 );
+ }
+ else
+#endif
+ random_seed = *(FT_Int32*)value;
+
+ if ( random_seed < 0 )
+ random_seed = 0;
+
+ driver->random_seed = random_seed;
+
+ return error;
+ }
+
+ FT_TRACE0(( "ps_property_set: missing property `%s'\n",
+ property_name ));
+ return FT_THROW( Missing_Property );
+ }
+
+
+ FT_BASE_CALLBACK_DEF( FT_Error )
+ ps_property_get( FT_Module module, /* PS_Driver */
+ const char* property_name,
+ void* value )
+ {
+ FT_Error error = FT_Err_Ok;
+ PS_Driver driver = (PS_Driver)module;
+
+
+ if ( !ft_strcmp( property_name, "darkening-parameters" ) )
+ {
+ FT_Int* darken_params = driver->darken_params;
+ FT_Int* val = (FT_Int*)value;
+
+
+ val[0] = darken_params[0];
+ val[1] = darken_params[1];
+ val[2] = darken_params[2];
+ val[3] = darken_params[3];
+ val[4] = darken_params[4];
+ val[5] = darken_params[5];
+ val[6] = darken_params[6];
+ val[7] = darken_params[7];
+
+ return error;
+ }
+
+ else if ( !ft_strcmp( property_name, "hinting-engine" ) )
+ {
+ FT_UInt hinting_engine = driver->hinting_engine;
+ FT_UInt* val = (FT_UInt*)value;
+
+
+ *val = hinting_engine;
+
+ return error;
+ }
+
+ else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
+ {
+ FT_Bool no_stem_darkening = driver->no_stem_darkening;
+ FT_Bool* val = (FT_Bool*)value;
+
+
+ *val = no_stem_darkening;
+
+ return error;
+ }
+
+ FT_TRACE0(( "ps_property_get: missing property `%s'\n",
+ property_name ));
+ return FT_THROW( Missing_Property );
+ }
+
+
+/* END */
diff --git a/thirdparty/freetype/src/base/ftrfork.c b/thirdparty/freetype/src/base/ftrfork.c
index f5ad2874d8..c3a2b9151a 100644
--- a/thirdparty/freetype/src/base/ftrfork.c
+++ b/thirdparty/freetype/src/base/ftrfork.c
@@ -4,7 +4,7 @@
/* */
/* Embedded resource forks accessor (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* Masatake YAMATO and Redhat K.K. */
/* */
/* FT_Raccess_Get_HeaderInfo() and raccess_guess_darwin_hfsplus() are */
@@ -478,7 +478,7 @@
}
-#ifndef FT_MACINTOSH
+#if defined( FT_CONFIG_OPTION_MAC_FONTS ) && !defined( FT_MACINTOSH )
static FT_RFork_Rule
raccess_get_rule_type_from_rule_index( FT_Library library,
FT_UInt rule_index )
diff --git a/thirdparty/freetype/src/base/ftsnames.c b/thirdparty/freetype/src/base/ftsnames.c
index 3609450088..90ea1e2be7 100644
--- a/thirdparty/freetype/src/base/ftsnames.c
+++ b/thirdparty/freetype/src/base/ftsnames.c
@@ -7,7 +7,7 @@
/* */
/* This is _not_ used to retrieve glyph names! */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftstream.c b/thirdparty/freetype/src/base/ftstream.c
index a3f8c8b3c9..18df7dcfef 100644
--- a/thirdparty/freetype/src/base/ftstream.c
+++ b/thirdparty/freetype/src/base/ftstream.c
@@ -4,7 +4,7 @@
/* */
/* I/O stream support (body). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftstroke.c b/thirdparty/freetype/src/base/ftstroke.c
index d32de0d62b..6ae1819067 100644
--- a/thirdparty/freetype/src/base/ftstroke.c
+++ b/thirdparty/freetype/src/base/ftstroke.c
@@ -4,7 +4,7 @@
/* */
/* FreeType path stroker (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftsynth.c b/thirdparty/freetype/src/base/ftsynth.c
index 5cf386f48d..c28346707b 100644
--- a/thirdparty/freetype/src/base/ftsynth.c
+++ b/thirdparty/freetype/src/base/ftsynth.c
@@ -4,7 +4,7 @@
/* */
/* FreeType synthesizing code for emboldening and slanting (body). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftsystem.c b/thirdparty/freetype/src/base/ftsystem.c
index 324f949a49..6adebdb938 100644
--- a/thirdparty/freetype/src/base/ftsystem.c
+++ b/thirdparty/freetype/src/base/ftsystem.c
@@ -4,7 +4,7 @@
/* */
/* ANSI-specific FreeType low-level system interface (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/fttrigon.c b/thirdparty/freetype/src/base/fttrigon.c
index 7a4d17c829..d6dd098c42 100644
--- a/thirdparty/freetype/src/base/fttrigon.c
+++ b/thirdparty/freetype/src/base/fttrigon.c
@@ -4,7 +4,7 @@
/* */
/* FreeType trigonometric functions (body). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/fttype1.c b/thirdparty/freetype/src/base/fttype1.c
index 4d16a6371a..aa8f8ccbbb 100644
--- a/thirdparty/freetype/src/base/fttype1.c
+++ b/thirdparty/freetype/src/base/fttype1.c
@@ -4,7 +4,7 @@
/* */
/* FreeType utility file for PS names support (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftutil.c b/thirdparty/freetype/src/base/ftutil.c
index 7bd5bee87c..4de5f2c145 100644
--- a/thirdparty/freetype/src/base/ftutil.c
+++ b/thirdparty/freetype/src/base/ftutil.c
@@ -4,7 +4,7 @@
/* */
/* FreeType utility file for memory and list management (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/ftver.rc b/thirdparty/freetype/src/base/ftver.rc
new file mode 100644
index 0000000000..a2903d5883
--- /dev/null
+++ b/thirdparty/freetype/src/base/ftver.rc
@@ -0,0 +1,61 @@
+/***************************************************************************/
+/* */
+/* ftver.rc */
+/* */
+/* FreeType VERSIONINFO resource for Windows DLLs. */
+/* */
+/* Copyright 2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include<windows.h>
+
+#define FT_VERSION 2,9,1,0
+#define FT_VERSION_STR "2.9.1"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION FT_VERSION
+PRODUCTVERSION FT_VERSION
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+FILEFLAGS VS_FF_DEBUG
+#endif
+#ifdef _DLL
+FILETYPE VFT_DLL
+#define FT_FILENAME "freetype.dll"
+#else
+FILETYPE VFT_STATIC_LIB
+#define FT_FILENAME "freetype.lib"
+#endif
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4"
+ BEGIN
+ VALUE "CompanyName", "The FreeType Project"
+ VALUE "FileDescription", "Font Rendering Library"
+ VALUE "FileVersion", FT_VERSION_STR
+ VALUE "ProductName", "FreeType"
+ VALUE "ProductVersion", FT_VERSION_STR
+ VALUE "LegalCopyright", "\251 2018 The FreeType Project www.freetype.org. All rights reserved."
+ VALUE "InternalName", "freetype"
+ VALUE "OriginalFilename", FT_FILENAME
+ END
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ /* The following line should only be modified for localized versions. */
+ /* It consists of any number of WORD,WORD pairs, with each pair */
+ /* describing a "language,codepage" combination supported by the file. */
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/thirdparty/freetype/src/base/ftwinfnt.c b/thirdparty/freetype/src/base/ftwinfnt.c
index 05baa02da6..11bd28afb7 100644
--- a/thirdparty/freetype/src/base/ftwinfnt.c
+++ b/thirdparty/freetype/src/base/ftwinfnt.c
@@ -4,7 +4,7 @@
/* */
/* FreeType API for accessing Windows FNT specific info (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/base/rules.mk b/thirdparty/freetype/src/base/rules.mk
index 2a1e93cb00..e9805bd068 100644
--- a/thirdparty/freetype/src/base/rules.mk
+++ b/thirdparty/freetype/src/base/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
@@ -40,11 +40,14 @@ BASE_SRC := $(BASE_DIR)/basepic.c \
$(BASE_DIR)/ftadvanc.c \
$(BASE_DIR)/ftcalc.c \
$(BASE_DIR)/ftdbgmem.c \
+ $(BASE_DIR)/ftfntfmt.c \
$(BASE_DIR)/ftgloadr.c \
$(BASE_DIR)/fthash.c \
+ $(BASE_DIR)/ftlcdfil.c \
$(BASE_DIR)/ftobjs.c \
$(BASE_DIR)/ftoutln.c \
$(BASE_DIR)/ftpic.c \
+ $(BASE_DIR)/ftpsprop.c \
$(BASE_DIR)/ftrfork.c \
$(BASE_DIR)/ftsnames.c \
$(BASE_DIR)/ftstream.c \
diff --git a/thirdparty/freetype/src/bdf/README b/thirdparty/freetype/src/bdf/README
index b761aba2b2..996ac2d2aa 100644
--- a/thirdparty/freetype/src/bdf/README
+++ b/thirdparty/freetype/src/bdf/README
@@ -13,7 +13,7 @@ This code implements a BDF driver for the FreeType library, following the
Adobe Specification V 2.2. The specification of the BDF font format is
available from Adobe's web site:
- http://partners.adobe.com/public/developer/en/font/5005.BDF_Spec.pdf
+ https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5005.BDF_Spec.pdf
Many good bitmap fonts in bdf format come with XFree86 (www.XFree86.org).
They do not define vertical metrics, because the X Consortium BDF
diff --git a/thirdparty/freetype/src/bdf/bdfdrivr.c b/thirdparty/freetype/src/bdf/bdfdrivr.c
index fb77810007..ca937f89ce 100644
--- a/thirdparty/freetype/src/bdf/bdfdrivr.c
+++ b/thirdparty/freetype/src/bdf/bdfdrivr.c
@@ -813,7 +813,7 @@ THE SOFTWARE.
bitmap->rows = glyph.bbx.height;
bitmap->width = glyph.bbx.width;
- if ( glyph.bpr > INT_MAX )
+ if ( glyph.bpr > FT_INT_MAX )
FT_TRACE1(( "BDF_Glyph_Load: too large pitch %d is truncated\n",
glyph.bpr ));
bitmap->pitch = (int)glyph.bpr; /* same as FT_Bitmap.pitch */
diff --git a/thirdparty/freetype/src/bdf/bdflib.c b/thirdparty/freetype/src/bdf/bdflib.c
index bf10887fd4..2f5c99d544 100644
--- a/thirdparty/freetype/src/bdf/bdflib.c
+++ b/thirdparty/freetype/src/bdf/bdflib.c
@@ -705,11 +705,11 @@
for ( v = 0; sbitset( ddigits, *s ); s++ )
{
- if ( v < ( ULONG_MAX - 9 ) / 10 )
+ if ( v < ( FT_ULONG_MAX - 9 ) / 10 )
v = v * 10 + a2i[(int)*s];
else
{
- v = ULONG_MAX;
+ v = FT_ULONG_MAX;
break;
}
}
@@ -738,11 +738,11 @@
for ( v = 0; sbitset( ddigits, *s ); s++ )
{
- if ( v < ( LONG_MAX - 9 ) / 10 )
+ if ( v < ( FT_LONG_MAX - 9 ) / 10 )
v = v * 10 + a2i[(int)*s];
else
{
- v = LONG_MAX;
+ v = FT_LONG_MAX;
break;
}
}
@@ -763,11 +763,11 @@
for ( v = 0; sbitset( ddigits, *s ); s++ )
{
- if ( v < ( USHRT_MAX - 9 ) / 10 )
+ if ( v < ( FT_USHORT_MAX - 9 ) / 10 )
v = (unsigned short)( v * 10 + a2i[(int)*s] );
else
{
- v = USHRT_MAX;
+ v = FT_USHORT_MAX;
break;
}
}
diff --git a/thirdparty/freetype/src/bzip2/ftbzip2.c b/thirdparty/freetype/src/bzip2/ftbzip2.c
index 7fc71e7079..16019485a9 100644
--- a/thirdparty/freetype/src/bzip2/ftbzip2.c
+++ b/thirdparty/freetype/src/bzip2/ftbzip2.c
@@ -8,7 +8,7 @@
/* parse compressed PCF fonts, as found with many X11 server */
/* distributions. */
/* */
-/* Copyright 2010-2017 by */
+/* Copyright 2010-2018 by */
/* Joel Klinghed. */
/* */
/* based on `src/gzip/ftgzip.c' */
diff --git a/thirdparty/freetype/src/bzip2/rules.mk b/thirdparty/freetype/src/bzip2/rules.mk
index f63ddc4d34..95954d7520 100644
--- a/thirdparty/freetype/src/bzip2/rules.mk
+++ b/thirdparty/freetype/src/bzip2/rules.mk
@@ -2,7 +2,7 @@
# FreeType 2 BZIP2 support configuration rules
#
-# Copyright 2010-2017 by
+# Copyright 2010-2018 by
# Joel Klinghed.
#
# based on `src/lzw/rules.mk'
diff --git a/thirdparty/freetype/src/cache/ftcache.c b/thirdparty/freetype/src/cache/ftcache.c
index 8226188314..1b425af911 100644
--- a/thirdparty/freetype/src/cache/ftcache.c
+++ b/thirdparty/freetype/src/cache/ftcache.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType Caching sub-system (body only). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcbasic.c b/thirdparty/freetype/src/cache/ftcbasic.c
index e804776ab4..994aa12286 100644
--- a/thirdparty/freetype/src/cache/ftcbasic.c
+++ b/thirdparty/freetype/src/cache/ftcbasic.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType basic cache interface (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftccache.c b/thirdparty/freetype/src/cache/ftccache.c
index 37dc3abb1d..12ec585a25 100644
--- a/thirdparty/freetype/src/cache/ftccache.c
+++ b/thirdparty/freetype/src/cache/ftccache.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType internal cache interface (body). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftccache.h b/thirdparty/freetype/src/cache/ftccache.h
index d3c11ce082..859c547e46 100644
--- a/thirdparty/freetype/src/cache/ftccache.h
+++ b/thirdparty/freetype/src/cache/ftccache.h
@@ -4,7 +4,7 @@
/* */
/* FreeType internal cache interface (specification). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftccback.h b/thirdparty/freetype/src/cache/ftccback.h
index 2681e8c022..e51d8d6e55 100644
--- a/thirdparty/freetype/src/cache/ftccback.h
+++ b/thirdparty/freetype/src/cache/ftccback.h
@@ -4,7 +4,7 @@
/* */
/* Callback functions of the caching sub-system (specification only). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftccmap.c b/thirdparty/freetype/src/cache/ftccmap.c
index 2fa84979c8..d20b0f48fe 100644
--- a/thirdparty/freetype/src/cache/ftccmap.c
+++ b/thirdparty/freetype/src/cache/ftccmap.c
@@ -4,7 +4,7 @@
/* */
/* FreeType CharMap cache (body) */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcerror.h b/thirdparty/freetype/src/cache/ftcerror.h
index 84fe52f546..a26cd5935b 100644
--- a/thirdparty/freetype/src/cache/ftcerror.h
+++ b/thirdparty/freetype/src/cache/ftcerror.h
@@ -4,7 +4,7 @@
/* */
/* Caching sub-system error codes (specification only). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcglyph.c b/thirdparty/freetype/src/cache/ftcglyph.c
index d2468f2f43..782cc0ed09 100644
--- a/thirdparty/freetype/src/cache/ftcglyph.c
+++ b/thirdparty/freetype/src/cache/ftcglyph.c
@@ -4,7 +4,7 @@
/* */
/* FreeType Glyph Image (FT_Glyph) cache (body). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcglyph.h b/thirdparty/freetype/src/cache/ftcglyph.h
index cab58ed311..23c24d223f 100644
--- a/thirdparty/freetype/src/cache/ftcglyph.h
+++ b/thirdparty/freetype/src/cache/ftcglyph.h
@@ -4,7 +4,7 @@
/* */
/* FreeType abstract glyph cache (specification). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcimage.c b/thirdparty/freetype/src/cache/ftcimage.c
index 359f818cce..77a100153e 100644
--- a/thirdparty/freetype/src/cache/ftcimage.c
+++ b/thirdparty/freetype/src/cache/ftcimage.c
@@ -4,7 +4,7 @@
/* */
/* FreeType Image cache (body). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcimage.h b/thirdparty/freetype/src/cache/ftcimage.h
index 14049af9d2..24a221053b 100644
--- a/thirdparty/freetype/src/cache/ftcimage.h
+++ b/thirdparty/freetype/src/cache/ftcimage.h
@@ -4,7 +4,7 @@
/* */
/* FreeType Generic Image cache (specification) */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcmanag.c b/thirdparty/freetype/src/cache/ftcmanag.c
index edec2b6b5b..2bcd9df502 100644
--- a/thirdparty/freetype/src/cache/ftcmanag.c
+++ b/thirdparty/freetype/src/cache/ftcmanag.c
@@ -4,7 +4,7 @@
/* */
/* FreeType Cache Manager (body). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcmanag.h b/thirdparty/freetype/src/cache/ftcmanag.h
index 556842e131..b4b4755356 100644
--- a/thirdparty/freetype/src/cache/ftcmanag.h
+++ b/thirdparty/freetype/src/cache/ftcmanag.h
@@ -4,7 +4,7 @@
/* */
/* FreeType Cache Manager (specification). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcmru.c b/thirdparty/freetype/src/cache/ftcmru.c
index e293269f2f..1087be4d89 100644
--- a/thirdparty/freetype/src/cache/ftcmru.c
+++ b/thirdparty/freetype/src/cache/ftcmru.c
@@ -4,7 +4,7 @@
/* */
/* FreeType MRU support (body). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcmru.h b/thirdparty/freetype/src/cache/ftcmru.h
index c4c330d558..82396b917d 100644
--- a/thirdparty/freetype/src/cache/ftcmru.h
+++ b/thirdparty/freetype/src/cache/ftcmru.h
@@ -4,7 +4,7 @@
/* */
/* Simple MRU list-cache (specification). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcsbits.c b/thirdparty/freetype/src/cache/ftcsbits.c
index 2f9336decd..018f1ecdb7 100644
--- a/thirdparty/freetype/src/cache/ftcsbits.c
+++ b/thirdparty/freetype/src/cache/ftcsbits.c
@@ -4,7 +4,7 @@
/* */
/* FreeType sbits manager (body). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/ftcsbits.h b/thirdparty/freetype/src/cache/ftcsbits.h
index 1e15ce9764..206a1bb3fc 100644
--- a/thirdparty/freetype/src/cache/ftcsbits.h
+++ b/thirdparty/freetype/src/cache/ftcsbits.h
@@ -4,7 +4,7 @@
/* */
/* A small-bitmap cache (specification). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cache/rules.mk b/thirdparty/freetype/src/cache/rules.mk
index 6204689505..558935976d 100644
--- a/thirdparty/freetype/src/cache/rules.mk
+++ b/thirdparty/freetype/src/cache/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2000-2017 by
+# Copyright 2000-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/cff/cff.c b/thirdparty/freetype/src/cff/cff.c
index 397f6dfafe..1a755d5dad 100644
--- a/thirdparty/freetype/src/cff/cff.c
+++ b/thirdparty/freetype/src/cff/cff.c
@@ -4,7 +4,7 @@
/* */
/* FreeType OpenType driver component (body only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -27,15 +27,4 @@
#include "cffload.c"
#include "cffobjs.c"
-#include "cf2arrst.c"
-#include "cf2blues.c"
-#include "cf2error.c"
-#include "cf2font.c"
-#include "cf2ft.c"
-#include "cf2hints.c"
-#include "cf2intrp.c"
-#include "cf2read.c"
-#include "cf2stack.c"
-
-
/* END */
diff --git a/thirdparty/freetype/src/cff/cffcmap.c b/thirdparty/freetype/src/cff/cffcmap.c
index 4adce7a54d..e45ae1127b 100644
--- a/thirdparty/freetype/src/cff/cffcmap.c
+++ b/thirdparty/freetype/src/cff/cffcmap.c
@@ -4,7 +4,7 @@
/* */
/* CFF character mapping table (cmap) support (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cff/cffcmap.h b/thirdparty/freetype/src/cff/cffcmap.h
index 7792e04248..856a43dd1b 100644
--- a/thirdparty/freetype/src/cff/cffcmap.h
+++ b/thirdparty/freetype/src/cff/cffcmap.h
@@ -4,7 +4,7 @@
/* */
/* CFF character mapping table (cmap) support (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -19,7 +19,7 @@
#ifndef CFFCMAP_H_
#define CFFCMAP_H_
-#include "cffobjs.h"
+#include FT_INTERNAL_CFF_OBJECTS_TYPES_H
FT_BEGIN_HEADER
diff --git a/thirdparty/freetype/src/cff/cffdrivr.c b/thirdparty/freetype/src/cff/cffdrivr.c
index 38bfc2ca3d..df896848da 100644
--- a/thirdparty/freetype/src/cff/cffdrivr.c
+++ b/thirdparty/freetype/src/cff/cffdrivr.c
@@ -4,7 +4,7 @@
/* */
/* OpenType font driver implementation (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -21,16 +21,20 @@
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_SFNT_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_POSTSCRIPT_PROPS_H
#include FT_SERVICE_CID_H
#include FT_SERVICE_POSTSCRIPT_INFO_H
#include FT_SERVICE_POSTSCRIPT_NAME_H
#include FT_SERVICE_TT_CMAP_H
+#include FT_SERVICE_CFF_TABLE_LOAD_H
#include "cffdrivr.h"
#include "cffgload.h"
#include "cffload.h"
#include "cffcmap.h"
#include "cffparse.h"
+#include "cffobjs.h"
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
#include FT_SERVICE_MULTIPLE_MASTERS_H
@@ -43,7 +47,7 @@
#include FT_SERVICE_FONT_FORMAT_H
#include FT_SERVICE_GLYPH_DICT_H
#include FT_SERVICE_PROPERTIES_H
-#include FT_CFF_DRIVER_H
+#include FT_DRIVER_H
/*************************************************************************/
@@ -214,8 +218,8 @@
{
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
/* no fast retrieval for blended MM fonts without VVAR table */
- if ( !ttface->is_default_instance &&
- !( ttface->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
+ if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) &&
+ !( ttface->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
return FT_THROW( Unimplemented_Feature );
#endif
@@ -237,8 +241,10 @@
&dummy,
&ah );
- FT_TRACE5(( " idx %d: advance height %d font units\n",
- start + nn, ah ));
+ FT_TRACE5(( " idx %d: advance height %d font unit%s\n",
+ start + nn,
+ ah,
+ ah == 1 ? "" : "s" ));
advances[nn] = ah;
}
}
@@ -246,8 +252,8 @@
{
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
/* no fast retrieval for blended MM fonts without HVAR table */
- if ( !ttface->is_default_instance &&
- !( ttface->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
+ if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) &&
+ !( ttface->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
return FT_THROW( Unimplemented_Feature );
#endif
@@ -266,8 +272,10 @@
&dummy,
&aw );
- FT_TRACE5(( " idx %d: advance width %d font units\n",
- start + nn, aw ));
+ FT_TRACE5(( " idx %d: advance width %d font unit%s\n",
+ start + nn,
+ aw,
+ aw == 1 ? "" : "s" ));
advances[nn] = aw;
}
}
@@ -493,11 +501,89 @@
}
+ static FT_Error
+ cff_ps_get_font_extra( CFF_Face face,
+ PS_FontExtraRec* afont_extra )
+ {
+ CFF_Font cff = (CFF_Font)face->extra.data;
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( cff && cff->font_extra == NULL )
+ {
+ CFF_FontRecDict dict = &cff->top_font.font_dict;
+ PS_FontExtraRec* font_extra = NULL;
+ FT_Memory memory = face->root.memory;
+ FT_String* embedded_postscript;
+
+
+ if ( FT_ALLOC( font_extra, sizeof ( *font_extra ) ) )
+ goto Fail;
+
+ font_extra->fs_type = 0U;
+
+ embedded_postscript = cff_index_get_sid_string(
+ cff,
+ dict->embedded_postscript );
+ if ( embedded_postscript )
+ {
+ FT_String* start_fstype;
+ FT_String* start_def;
+
+
+ /* Identify the XYZ integer in `/FSType XYZ def' substring. */
+ if ( ( start_fstype = ft_strstr( embedded_postscript,
+ "/FSType" ) ) != NULL &&
+ ( start_def = ft_strstr( start_fstype +
+ sizeof ( "/FSType" ) - 1,
+ "def" ) ) != NULL )
+ {
+ FT_String* s;
+
+
+ for ( s = start_fstype + sizeof ( "/FSType" ) - 1;
+ s != start_def;
+ s++ )
+ {
+ if ( *s >= '0' && *s <= '9' )
+ {
+ if ( font_extra->fs_type >= ( FT_USHORT_MAX - 9 ) / 10 )
+ {
+ /* Overflow - ignore the FSType value. */
+ font_extra->fs_type = 0U;
+ break;
+ }
+
+ font_extra->fs_type *= 10;
+ font_extra->fs_type += (FT_UShort)( *s - '0' );
+ }
+ else if ( *s != ' ' && *s != '\n' && *s != '\r' )
+ {
+ /* Non-whitespace character between `/FSType' and next `def' */
+ /* - ignore the FSType value. */
+ font_extra->fs_type = 0U;
+ break;
+ }
+ }
+ }
+ }
+
+ cff->font_extra = font_extra;
+ }
+
+ if ( cff )
+ *afont_extra = *cff->font_extra;
+
+ Fail:
+ return error;
+ }
+
+
FT_DEFINE_SERVICE_PSINFOREC(
cff_service_ps_info,
(PS_GetFontInfoFunc) cff_ps_get_font_info, /* ps_get_font_info */
- (PS_GetFontExtraFunc) NULL, /* ps_get_font_extra */
+ (PS_GetFontExtraFunc) cff_ps_get_font_extra, /* ps_get_font_extra */
(PS_HasGlyphNamesFunc) cff_ps_has_glyph_names, /* ps_has_glyph_names */
/* unsupported with CFF fonts */
(PS_GetFontPrivateFunc)NULL, /* ps_get_font_private */
@@ -735,231 +821,12 @@
* PROPERTY SERVICE
*
*/
- static FT_Error
- cff_property_set( FT_Module module, /* CFF_Driver */
- const char* property_name,
- const void* value,
- FT_Bool value_is_string )
- {
- FT_Error error = FT_Err_Ok;
- CFF_Driver driver = (CFF_Driver)module;
-
-#ifndef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
- FT_UNUSED( value_is_string );
-#endif
-
-
- if ( !ft_strcmp( property_name, "darkening-parameters" ) )
- {
- FT_Int* darken_params;
- FT_Int x1, y1, x2, y2, x3, y3, x4, y4;
-
-#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
- FT_Int dp[8];
-
-
- if ( value_is_string )
- {
- const char* s = (const char*)value;
- char* ep;
- int i;
-
-
- /* eight comma-separated numbers */
- for ( i = 0; i < 7; i++ )
- {
- dp[i] = (FT_Int)ft_strtol( s, &ep, 10 );
- if ( *ep != ',' || s == ep )
- return FT_THROW( Invalid_Argument );
-
- s = ep + 1;
- }
-
- dp[7] = (FT_Int)ft_strtol( s, &ep, 10 );
- if ( !( *ep == '\0' || *ep == ' ' ) || s == ep )
- return FT_THROW( Invalid_Argument );
-
- darken_params = dp;
- }
- else
-#endif
- darken_params = (FT_Int*)value;
-
- x1 = darken_params[0];
- y1 = darken_params[1];
- x2 = darken_params[2];
- y2 = darken_params[3];
- x3 = darken_params[4];
- y3 = darken_params[5];
- x4 = darken_params[6];
- y4 = darken_params[7];
-
- if ( x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 ||
- y1 < 0 || y2 < 0 || y3 < 0 || y4 < 0 ||
- x1 > x2 || x2 > x3 || x3 > x4 ||
- y1 > 500 || y2 > 500 || y3 > 500 || y4 > 500 )
- return FT_THROW( Invalid_Argument );
-
- driver->darken_params[0] = x1;
- driver->darken_params[1] = y1;
- driver->darken_params[2] = x2;
- driver->darken_params[3] = y2;
- driver->darken_params[4] = x3;
- driver->darken_params[5] = y3;
- driver->darken_params[6] = x4;
- driver->darken_params[7] = y4;
-
- return error;
- }
- else if ( !ft_strcmp( property_name, "hinting-engine" ) )
- {
-#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
- if ( value_is_string )
- {
- const char* s = (const char*)value;
-
-
- if ( !ft_strcmp( s, "adobe" ) )
- driver->hinting_engine = FT_CFF_HINTING_ADOBE;
-#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
- else if ( !ft_strcmp( s, "freetype" ) )
- driver->hinting_engine = FT_CFF_HINTING_FREETYPE;
-#endif
- else
- return FT_THROW( Invalid_Argument );
- }
- else
-#endif
- {
- FT_UInt* hinting_engine = (FT_UInt*)value;
-
-
- if ( *hinting_engine == FT_CFF_HINTING_ADOBE
-#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
- || *hinting_engine == FT_CFF_HINTING_FREETYPE
-#endif
- )
- driver->hinting_engine = *hinting_engine;
- else
- error = FT_ERR( Unimplemented_Feature );
-
- return error;
- }
- }
- else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
- {
-#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
- if ( value_is_string )
- {
- const char* s = (const char*)value;
- long nsd = ft_strtol( s, NULL, 10 );
-
-
- if ( !nsd )
- driver->no_stem_darkening = FALSE;
- else
- driver->no_stem_darkening = TRUE;
- }
- else
-#endif
- {
- FT_Bool* no_stem_darkening = (FT_Bool*)value;
-
-
- driver->no_stem_darkening = *no_stem_darkening;
- }
-
- return error;
- }
- else if ( !ft_strcmp( property_name, "random-seed" ) )
- {
- FT_Int32 random_seed;
-
-
-#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
- if ( value_is_string )
- {
- const char* s = (const char*)value;
-
-
- random_seed = (FT_Int32)ft_strtol( s, NULL, 10 );
- }
- else
-#endif
- random_seed = *(FT_Int32*)value;
-
- if ( random_seed < 0 )
- random_seed = 0;
-
- driver->random_seed = random_seed;
-
- return error;
- }
-
- FT_TRACE0(( "cff_property_set: missing property `%s'\n",
- property_name ));
- return FT_THROW( Missing_Property );
- }
-
-
- static FT_Error
- cff_property_get( FT_Module module, /* CFF_Driver */
- const char* property_name,
- const void* value )
- {
- FT_Error error = FT_Err_Ok;
- CFF_Driver driver = (CFF_Driver)module;
-
-
- if ( !ft_strcmp( property_name, "darkening-parameters" ) )
- {
- FT_Int* darken_params = driver->darken_params;
- FT_Int* val = (FT_Int*)value;
-
-
- val[0] = darken_params[0];
- val[1] = darken_params[1];
- val[2] = darken_params[2];
- val[3] = darken_params[3];
- val[4] = darken_params[4];
- val[5] = darken_params[5];
- val[6] = darken_params[6];
- val[7] = darken_params[7];
-
- return error;
- }
- else if ( !ft_strcmp( property_name, "hinting-engine" ) )
- {
- FT_UInt hinting_engine = driver->hinting_engine;
- FT_UInt* val = (FT_UInt*)value;
-
-
- *val = hinting_engine;
-
- return error;
- }
- else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
- {
- FT_Bool no_stem_darkening = driver->no_stem_darkening;
- FT_Bool* val = (FT_Bool*)value;
-
-
- *val = no_stem_darkening;
-
- return error;
- }
-
- FT_TRACE0(( "cff_property_get: missing property `%s'\n",
- property_name ));
- return FT_THROW( Missing_Property );
- }
-
FT_DEFINE_SERVICE_PROPERTIESREC(
cff_service_properties,
- (FT_Properties_SetFunc)cff_property_set, /* set_property */
- (FT_Properties_GetFunc)cff_property_get ) /* get_property */
+ (FT_Properties_SetFunc)ps_property_set, /* set_property */
+ (FT_Properties_GetFunc)ps_property_get ) /* get_property */
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
@@ -1028,6 +895,17 @@
}
+ static FT_Error
+ cff_set_instance( CFF_Face face,
+ FT_UInt instance_index )
+ {
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+
+
+ return mm->set_instance( FT_FACE( face ), instance_index );
+ }
+
+
FT_DEFINE_SERVICE_MULTIMASTERSREC(
cff_service_multi_masters,
@@ -1038,6 +916,7 @@
(FT_Get_MM_Var_Func) cff_get_mm_var, /* get_mm_var */
(FT_Set_Var_Design_Func)cff_set_var_design, /* set_var_design */
(FT_Get_Var_Design_Func)cff_get_var_design, /* get_var_design */
+ (FT_Set_Instance_Func) cff_set_instance, /* set_instance */
(FT_Get_Var_Blend_Func) cff_get_var_blend, /* get_var_blend */
(FT_Done_Blend_Func) cff_done_blend /* done_blend */
@@ -1088,6 +967,22 @@
#endif
+ /*
+ * CFFLOAD SERVICE
+ *
+ */
+
+ FT_DEFINE_SERVICE_CFFLOADREC(
+ cff_service_cff_load,
+
+ (FT_Get_Standard_Encoding_Func)cff_get_standard_encoding,
+ (FT_Load_Private_Dict_Func) cff_load_private_dict,
+ (FT_FD_Select_Get_Func) cff_fd_select_get,
+ (FT_Blend_Check_Vector_Func) cff_blend_check_vector,
+ (FT_Blend_Build_Vector_Func) cff_blend_build_vector
+ )
+
+
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
@@ -1102,7 +997,7 @@
#if !defined FT_CONFIG_OPTION_NO_GLYPH_NAMES && \
defined TT_CONFIG_OPTION_GX_VAR_SUPPORT
- FT_DEFINE_SERVICEDESCREC9(
+ FT_DEFINE_SERVICEDESCREC10(
cff_services,
FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_CFF,
@@ -1113,10 +1008,11 @@
FT_SERVICE_ID_GLYPH_DICT, &CFF_SERVICE_GLYPH_DICT_GET,
FT_SERVICE_ID_TT_CMAP, &CFF_SERVICE_GET_CMAP_INFO_GET,
FT_SERVICE_ID_CID, &CFF_SERVICE_CID_INFO_GET,
- FT_SERVICE_ID_PROPERTIES, &CFF_SERVICE_PROPERTIES_GET
+ FT_SERVICE_ID_PROPERTIES, &CFF_SERVICE_PROPERTIES_GET,
+ FT_SERVICE_ID_CFF_LOAD, &CFF_SERVICE_CFF_LOAD_GET
)
#elif !defined FT_CONFIG_OPTION_NO_GLYPH_NAMES
- FT_DEFINE_SERVICEDESCREC7(
+ FT_DEFINE_SERVICEDESCREC8(
cff_services,
FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_CFF,
@@ -1125,10 +1021,11 @@
FT_SERVICE_ID_GLYPH_DICT, &CFF_SERVICE_GLYPH_DICT_GET,
FT_SERVICE_ID_TT_CMAP, &CFF_SERVICE_GET_CMAP_INFO_GET,
FT_SERVICE_ID_CID, &CFF_SERVICE_CID_INFO_GET,
- FT_SERVICE_ID_PROPERTIES, &CFF_SERVICE_PROPERTIES_GET
+ FT_SERVICE_ID_PROPERTIES, &CFF_SERVICE_PROPERTIES_GET,
+ FT_SERVICE_ID_CFF_LOAD, &CFF_SERVICE_CFF_LOAD_GET
)
#elif defined TT_CONFIG_OPTION_GX_VAR_SUPPORT
- FT_DEFINE_SERVICEDESCREC8(
+ FT_DEFINE_SERVICEDESCREC9(
cff_services,
FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_CFF,
@@ -1138,10 +1035,11 @@
FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &CFF_SERVICE_PS_NAME_GET,
FT_SERVICE_ID_TT_CMAP, &CFF_SERVICE_GET_CMAP_INFO_GET,
FT_SERVICE_ID_CID, &CFF_SERVICE_CID_INFO_GET,
- FT_SERVICE_ID_PROPERTIES, &CFF_SERVICE_PROPERTIES_GET
+ FT_SERVICE_ID_PROPERTIES, &CFF_SERVICE_PROPERTIES_GET,
+ FT_SERVICE_ID_CFF_LOAD, &CFF_SERVICE_CFF_LOAD_GET
)
#else
- FT_DEFINE_SERVICEDESCREC6(
+ FT_DEFINE_SERVICEDESCREC7(
cff_services,
FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_CFF,
@@ -1149,7 +1047,8 @@
FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &CFF_SERVICE_PS_NAME_GET,
FT_SERVICE_ID_TT_CMAP, &CFF_SERVICE_GET_CMAP_INFO_GET,
FT_SERVICE_ID_CID, &CFF_SERVICE_CID_INFO_GET,
- FT_SERVICE_ID_PROPERTIES, &CFF_SERVICE_PROPERTIES_GET
+ FT_SERVICE_ID_PROPERTIES, &CFF_SERVICE_PROPERTIES_GET,
+ FT_SERVICE_ID_CFF_LOAD, &CFF_SERVICE_CFF_LOAD_GET
)
#endif
@@ -1208,7 +1107,7 @@
FT_MODULE_DRIVER_HAS_HINTER |
FT_MODULE_DRIVER_HINTS_LIGHTLY,
- sizeof ( CFF_DriverRec ),
+ sizeof ( PS_DriverRec ),
"cff",
0x10000L,
0x20000L,
diff --git a/thirdparty/freetype/src/cff/cffdrivr.h b/thirdparty/freetype/src/cff/cffdrivr.h
index 05381e66db..ad7c3ad70a 100644
--- a/thirdparty/freetype/src/cff/cffdrivr.h
+++ b/thirdparty/freetype/src/cff/cffdrivr.h
@@ -4,7 +4,7 @@
/* */
/* High-level OpenType driver interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cff/cfferrs.h b/thirdparty/freetype/src/cff/cfferrs.h
index 40808c1051..b2e1bfaf9d 100644
--- a/thirdparty/freetype/src/cff/cfferrs.h
+++ b/thirdparty/freetype/src/cff/cfferrs.h
@@ -4,7 +4,7 @@
/* */
/* CFF error codes (specification only). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cff/cffgload.c b/thirdparty/freetype/src/cff/cffgload.c
index 20f3a2c28e..c58471ce86 100644
--- a/thirdparty/freetype/src/cff/cffgload.c
+++ b/thirdparty/freetype/src/cff/cffgload.c
@@ -4,7 +4,7 @@
/* */
/* OpenType Glyph Loader (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -21,13 +21,12 @@
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_SFNT_H
#include FT_INTERNAL_CALC_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
#include FT_OUTLINE_H
-#include FT_CFF_DRIVER_H
+#include FT_DRIVER_H
-#include "cffobjs.h"
#include "cffload.h"
#include "cffgload.h"
-#include "cf2ft.h" /* for cf2_decoder_parse_charstrings */
#include "cfferrs.h"
@@ -42,623 +41,6 @@
#define FT_COMPONENT trace_cffgload
-#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
-
- typedef enum CFF_Operator_
- {
- cff_op_unknown = 0,
-
- cff_op_rmoveto,
- cff_op_hmoveto,
- cff_op_vmoveto,
-
- cff_op_rlineto,
- cff_op_hlineto,
- cff_op_vlineto,
-
- cff_op_rrcurveto,
- cff_op_hhcurveto,
- cff_op_hvcurveto,
- cff_op_rcurveline,
- cff_op_rlinecurve,
- cff_op_vhcurveto,
- cff_op_vvcurveto,
-
- cff_op_flex,
- cff_op_hflex,
- cff_op_hflex1,
- cff_op_flex1,
-
- cff_op_endchar,
-
- cff_op_hstem,
- cff_op_vstem,
- cff_op_hstemhm,
- cff_op_vstemhm,
-
- cff_op_hintmask,
- cff_op_cntrmask,
- cff_op_dotsection, /* deprecated, acts as no-op */
-
- cff_op_abs,
- cff_op_add,
- cff_op_sub,
- cff_op_div,
- cff_op_neg,
- cff_op_random,
- cff_op_mul,
- cff_op_sqrt,
-
- cff_op_blend,
-
- cff_op_drop,
- cff_op_exch,
- cff_op_index,
- cff_op_roll,
- cff_op_dup,
-
- cff_op_put,
- cff_op_get,
- cff_op_store,
- cff_op_load,
-
- cff_op_and,
- cff_op_or,
- cff_op_not,
- cff_op_eq,
- cff_op_ifelse,
-
- cff_op_callsubr,
- cff_op_callgsubr,
- cff_op_return,
-
- /* Type 1 opcodes: invalid but seen in real life */
- cff_op_hsbw,
- cff_op_closepath,
- cff_op_callothersubr,
- cff_op_pop,
- cff_op_seac,
- cff_op_sbw,
- cff_op_setcurrentpoint,
-
- /* do not remove */
- cff_op_max
-
- } CFF_Operator;
-
-
-#define CFF_COUNT_CHECK_WIDTH 0x80
-#define CFF_COUNT_EXACT 0x40
-#define CFF_COUNT_CLEAR_STACK 0x20
-
- /* count values which have the `CFF_COUNT_CHECK_WIDTH' flag set are */
- /* used for checking the width and requested numbers of arguments */
- /* only; they are set to zero afterwards */
-
- /* the other two flags are informative only and unused currently */
-
- static const FT_Byte cff_argument_counts[] =
- {
- 0, /* unknown */
-
- 2 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT, /* rmoveto */
- 1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT,
- 1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT,
-
- 0 | CFF_COUNT_CLEAR_STACK, /* rlineto */
- 0 | CFF_COUNT_CLEAR_STACK,
- 0 | CFF_COUNT_CLEAR_STACK,
-
- 0 | CFF_COUNT_CLEAR_STACK, /* rrcurveto */
- 0 | CFF_COUNT_CLEAR_STACK,
- 0 | CFF_COUNT_CLEAR_STACK,
- 0 | CFF_COUNT_CLEAR_STACK,
- 0 | CFF_COUNT_CLEAR_STACK,
- 0 | CFF_COUNT_CLEAR_STACK,
- 0 | CFF_COUNT_CLEAR_STACK,
-
- 13, /* flex */
- 7,
- 9,
- 11,
-
- 0 | CFF_COUNT_CHECK_WIDTH, /* endchar */
-
- 2 | CFF_COUNT_CHECK_WIDTH, /* hstem */
- 2 | CFF_COUNT_CHECK_WIDTH,
- 2 | CFF_COUNT_CHECK_WIDTH,
- 2 | CFF_COUNT_CHECK_WIDTH,
-
- 0 | CFF_COUNT_CHECK_WIDTH, /* hintmask */
- 0 | CFF_COUNT_CHECK_WIDTH, /* cntrmask */
- 0, /* dotsection */
-
- 1, /* abs */
- 2,
- 2,
- 2,
- 1,
- 0,
- 2,
- 1,
-
- 1, /* blend */
-
- 1, /* drop */
- 2,
- 1,
- 2,
- 1,
-
- 2, /* put */
- 1,
- 4,
- 3,
-
- 2, /* and */
- 2,
- 1,
- 2,
- 4,
-
- 1, /* callsubr */
- 1,
- 0,
-
- 2, /* hsbw */
- 0,
- 0,
- 0,
- 5, /* seac */
- 4, /* sbw */
- 2 /* setcurrentpoint */
- };
-
-#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */
-
-
- /*************************************************************************/
- /*************************************************************************/
- /*************************************************************************/
- /********** *********/
- /********** *********/
- /********** GENERIC CHARSTRING PARSING *********/
- /********** *********/
- /********** *********/
- /*************************************************************************/
- /*************************************************************************/
- /*************************************************************************/
-
-
- /*************************************************************************/
- /* */
- /* <Function> */
- /* cff_builder_init */
- /* */
- /* <Description> */
- /* Initializes a given glyph builder. */
- /* */
- /* <InOut> */
- /* builder :: A pointer to the glyph builder to initialize. */
- /* */
- /* <Input> */
- /* face :: The current face object. */
- /* */
- /* size :: The current size object. */
- /* */
- /* glyph :: The current glyph object. */
- /* */
- /* hinting :: Whether hinting is active. */
- /* */
- static void
- cff_builder_init( CFF_Builder* builder,
- TT_Face face,
- CFF_Size size,
- CFF_GlyphSlot glyph,
- FT_Bool hinting )
- {
- builder->path_begun = 0;
- builder->load_points = 1;
-
- builder->face = face;
- builder->glyph = glyph;
- builder->memory = face->root.memory;
-
- if ( glyph )
- {
- FT_GlyphLoader loader = glyph->root.internal->loader;
-
-
- builder->loader = loader;
- builder->base = &loader->base.outline;
- builder->current = &loader->current.outline;
- FT_GlyphLoader_Rewind( loader );
-
- builder->hints_globals = NULL;
- builder->hints_funcs = NULL;
-
- if ( hinting && size )
- {
- FT_Size ftsize = FT_SIZE( size );
- CFF_Internal internal = (CFF_Internal)ftsize->internal->module_data;
-
-
- if ( internal )
- {
- builder->hints_globals = (void *)internal->topfont;
- builder->hints_funcs = glyph->root.internal->glyph_hints;
- }
- }
- }
-
- builder->pos_x = 0;
- builder->pos_y = 0;
-
- builder->left_bearing.x = 0;
- builder->left_bearing.y = 0;
- builder->advance.x = 0;
- builder->advance.y = 0;
- }
-
-
- /*************************************************************************/
- /* */
- /* <Function> */
- /* cff_builder_done */
- /* */
- /* <Description> */
- /* Finalizes a given glyph builder. Its contents can still be used */
- /* after the call, but the function saves important information */
- /* within the corresponding glyph slot. */
- /* */
- /* <Input> */
- /* builder :: A pointer to the glyph builder to finalize. */
- /* */
- static void
- cff_builder_done( CFF_Builder* builder )
- {
- CFF_GlyphSlot glyph = builder->glyph;
-
-
- if ( glyph )
- glyph->root.outline = *builder->base;
- }
-
-
- /*************************************************************************/
- /* */
- /* <Function> */
- /* cff_compute_bias */
- /* */
- /* <Description> */
- /* Computes the bias value in dependence of the number of glyph */
- /* subroutines. */
- /* */
- /* <Input> */
- /* in_charstring_type :: The `CharstringType' value of the top DICT */
- /* dictionary. */
- /* */
- /* num_subrs :: The number of glyph subroutines. */
- /* */
- /* <Return> */
- /* The bias value. */
- static FT_Int
- cff_compute_bias( FT_Int in_charstring_type,
- FT_UInt num_subrs )
- {
- FT_Int result;
-
-
- if ( in_charstring_type == 1 )
- result = 0;
- else if ( num_subrs < 1240 )
- result = 107;
- else if ( num_subrs < 33900U )
- result = 1131;
- else
- result = 32768U;
-
- return result;
- }
-
-
- /*************************************************************************/
- /* */
- /* <Function> */
- /* cff_decoder_init */
- /* */
- /* <Description> */
- /* Initializes a given glyph decoder. */
- /* */
- /* <InOut> */
- /* decoder :: A pointer to the glyph builder to initialize. */
- /* */
- /* <Input> */
- /* face :: The current face object. */
- /* */
- /* size :: The current size object. */
- /* */
- /* slot :: The current glyph object. */
- /* */
- /* hinting :: Whether hinting is active. */
- /* */
- /* hint_mode :: The hinting mode. */
- /* */
- FT_LOCAL_DEF( void )
- cff_decoder_init( CFF_Decoder* decoder,
- TT_Face face,
- CFF_Size size,
- CFF_GlyphSlot slot,
- FT_Bool hinting,
- FT_Render_Mode hint_mode )
- {
- CFF_Font cff = (CFF_Font)face->extra.data;
-
-
- /* clear everything */
- FT_ZERO( decoder );
-
- /* initialize builder */
- cff_builder_init( &decoder->builder, face, size, slot, hinting );
-
- /* initialize Type2 decoder */
- decoder->cff = cff;
- decoder->num_globals = cff->global_subrs_index.count;
- decoder->globals = cff->global_subrs;
- decoder->globals_bias = cff_compute_bias(
- cff->top_font.font_dict.charstring_type,
- decoder->num_globals );
-
- decoder->hint_mode = hint_mode;
- }
-
-
- /* this function is used to select the subfont */
- /* and the locals subrs array */
- FT_LOCAL_DEF( FT_Error )
- cff_decoder_prepare( CFF_Decoder* decoder,
- CFF_Size size,
- FT_UInt glyph_index )
- {
- CFF_Builder *builder = &decoder->builder;
- CFF_Font cff = (CFF_Font)builder->face->extra.data;
- CFF_SubFont sub = &cff->top_font;
- FT_Error error = FT_Err_Ok;
-
-
- /* manage CID fonts */
- if ( cff->num_subfonts )
- {
- FT_Byte fd_index = cff_fd_select_get( &cff->fd_select, glyph_index );
-
-
- if ( fd_index >= cff->num_subfonts )
- {
- FT_TRACE4(( "cff_decoder_prepare: invalid CID subfont index\n" ));
- error = FT_THROW( Invalid_File_Format );
- goto Exit;
- }
-
- FT_TRACE3(( " in subfont %d:\n", fd_index ));
-
- sub = cff->subfonts[fd_index];
-
- if ( builder->hints_funcs && size )
- {
- FT_Size ftsize = FT_SIZE( size );
- CFF_Internal internal = (CFF_Internal)ftsize->internal->module_data;
-
-
- /* for CFFs without subfonts, this value has already been set */
- builder->hints_globals = (void *)internal->subfonts[fd_index];
- }
- }
-
- decoder->num_locals = sub->local_subrs_index.count;
- decoder->locals = sub->local_subrs;
- decoder->locals_bias = cff_compute_bias(
- decoder->cff->top_font.font_dict.charstring_type,
- decoder->num_locals );
-
- decoder->glyph_width = sub->private_dict.default_width;
- decoder->nominal_width = sub->private_dict.nominal_width;
-
- decoder->current_subfont = sub;
-
- Exit:
- return error;
- }
-
-
- /* check that there is enough space for `count' more points */
- FT_LOCAL_DEF( FT_Error )
- cff_check_points( CFF_Builder* builder,
- FT_Int count )
- {
- return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
- }
-
-
- /* add a new point, do not check space */
- FT_LOCAL_DEF( void )
- cff_builder_add_point( CFF_Builder* builder,
- FT_Pos x,
- FT_Pos y,
- FT_Byte flag )
- {
- FT_Outline* outline = builder->current;
-
-
- if ( builder->load_points )
- {
- FT_Vector* point = outline->points + outline->n_points;
- FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points;
-
-#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
- CFF_Driver driver = (CFF_Driver)FT_FACE_DRIVER( builder->face );
-
-
- if ( driver->hinting_engine == FT_CFF_HINTING_FREETYPE )
- {
- point->x = x >> 16;
- point->y = y >> 16;
- }
- else
-#endif
- {
- /* cf2_decoder_parse_charstrings uses 16.16 coordinates */
- point->x = x >> 10;
- point->y = y >> 10;
- }
- *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
- }
-
- outline->n_points++;
- }
-
-
- /* check space for a new on-curve point, then add it */
- FT_LOCAL_DEF( FT_Error )
- cff_builder_add_point1( CFF_Builder* builder,
- FT_Pos x,
- FT_Pos y )
- {
- FT_Error error;
-
-
- error = cff_check_points( builder, 1 );
- if ( !error )
- cff_builder_add_point( builder, x, y, 1 );
-
- return error;
- }
-
-
- /* check space for a new contour, then add it */
- static FT_Error
- cff_builder_add_contour( CFF_Builder* builder )
- {
- FT_Outline* outline = builder->current;
- FT_Error error;
-
-
- if ( !builder->load_points )
- {
- outline->n_contours++;
- return FT_Err_Ok;
- }
-
- error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
- if ( !error )
- {
- if ( outline->n_contours > 0 )
- outline->contours[outline->n_contours - 1] =
- (short)( outline->n_points - 1 );
-
- outline->n_contours++;
- }
-
- return error;
- }
-
-
- /* if a path was begun, add its first on-curve point */
- FT_LOCAL_DEF( FT_Error )
- cff_builder_start_point( CFF_Builder* builder,
- FT_Pos x,
- FT_Pos y )
- {
- FT_Error error = FT_Err_Ok;
-
-
- /* test whether we are building a new contour */
- if ( !builder->path_begun )
- {
- builder->path_begun = 1;
- error = cff_builder_add_contour( builder );
- if ( !error )
- error = cff_builder_add_point1( builder, x, y );
- }
-
- return error;
- }
-
-
- /* close the current contour */
- FT_LOCAL_DEF( void )
- cff_builder_close_contour( CFF_Builder* builder )
- {
- FT_Outline* outline = builder->current;
- FT_Int first;
-
-
- if ( !outline )
- return;
-
- first = outline->n_contours <= 1
- ? 0 : outline->contours[outline->n_contours - 2] + 1;
-
- /* We must not include the last point in the path if it */
- /* is located on the first point. */
- if ( outline->n_points > 1 )
- {
- FT_Vector* p1 = outline->points + first;
- FT_Vector* p2 = outline->points + outline->n_points - 1;
- FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1;
-
-
- /* `delete' last point only if it coincides with the first */
- /* point and if it is not a control point (which can happen). */
- if ( p1->x == p2->x && p1->y == p2->y )
- if ( *control == FT_CURVE_TAG_ON )
- outline->n_points--;
- }
-
- if ( outline->n_contours > 0 )
- {
- /* Don't add contours only consisting of one point, i.e., */
- /* check whether begin point and last point are the same. */
- if ( first == outline->n_points - 1 )
- {
- outline->n_contours--;
- outline->n_points--;
- }
- else
- outline->contours[outline->n_contours - 1] =
- (short)( outline->n_points - 1 );
- }
- }
-
-
- FT_LOCAL_DEF( FT_Int )
- cff_lookup_glyph_by_stdcharcode( CFF_Font cff,
- FT_Int charcode )
- {
- FT_UInt n;
- FT_UShort glyph_sid;
-
-
- /* CID-keyed fonts don't have glyph names */
- if ( !cff->charset.sids )
- return -1;
-
- /* check range of standard char code */
- if ( charcode < 0 || charcode > 255 )
- return -1;
-
- /* Get code to SID mapping from `cff_standard_encoding'. */
- glyph_sid = cff_get_standard_encoding( (FT_UInt)charcode );
-
- for ( n = 0; n < cff->num_glyphs; n++ )
- {
- if ( cff->charset.sids[n] == glyph_sid )
- return (FT_Int)n;
- }
-
- return -1;
- }
-
-
FT_LOCAL_DEF( FT_Error )
cff_get_glyph_data( TT_Face face,
FT_UInt glyph_index,
@@ -730,1950 +112,6 @@
}
-#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
-
- static FT_Error
- cff_operator_seac( CFF_Decoder* decoder,
- FT_Pos asb,
- FT_Pos adx,
- FT_Pos ady,
- FT_Int bchar,
- FT_Int achar )
- {
- FT_Error error;
- CFF_Builder* builder = &decoder->builder;
- FT_Int bchar_index, achar_index;
- TT_Face face = decoder->builder.face;
- FT_Vector left_bearing, advance;
- FT_Byte* charstring;
- FT_ULong charstring_len;
- FT_Pos glyph_width;
-
-
- if ( decoder->seac )
- {
- FT_ERROR(( "cff_operator_seac: invalid nested seac\n" ));
- return FT_THROW( Syntax_Error );
- }
-
- adx += decoder->builder.left_bearing.x;
- ady += decoder->builder.left_bearing.y;
-
-#ifdef FT_CONFIG_OPTION_INCREMENTAL
- /* Incremental fonts don't necessarily have valid charsets. */
- /* They use the character code, not the glyph index, in this case. */
- if ( face->root.internal->incremental_interface )
- {
- bchar_index = bchar;
- achar_index = achar;
- }
- else
-#endif /* FT_CONFIG_OPTION_INCREMENTAL */
- {
- CFF_Font cff = (CFF_Font)(face->extra.data);
-
-
- bchar_index = cff_lookup_glyph_by_stdcharcode( cff, bchar );
- achar_index = cff_lookup_glyph_by_stdcharcode( cff, achar );
- }
-
- if ( bchar_index < 0 || achar_index < 0 )
- {
- FT_ERROR(( "cff_operator_seac:"
- " invalid seac character code arguments\n" ));
- return FT_THROW( Syntax_Error );
- }
-
- /* If we are trying to load a composite glyph, do not load the */
- /* accent character and return the array of subglyphs. */
- if ( builder->no_recurse )
- {
- FT_GlyphSlot glyph = (FT_GlyphSlot)builder->glyph;
- FT_GlyphLoader loader = glyph->internal->loader;
- FT_SubGlyph subg;
-
-
- /* reallocate subglyph array if necessary */
- error = FT_GlyphLoader_CheckSubGlyphs( loader, 2 );
- if ( error )
- goto Exit;
-
- subg = loader->current.subglyphs;
-
- /* subglyph 0 = base character */
- subg->index = bchar_index;
- subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES |
- FT_SUBGLYPH_FLAG_USE_MY_METRICS;
- subg->arg1 = 0;
- subg->arg2 = 0;
- subg++;
-
- /* subglyph 1 = accent character */
- subg->index = achar_index;
- subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES;
- subg->arg1 = (FT_Int)( adx >> 16 );
- subg->arg2 = (FT_Int)( ady >> 16 );
-
- /* set up remaining glyph fields */
- glyph->num_subglyphs = 2;
- glyph->subglyphs = loader->base.subglyphs;
- glyph->format = FT_GLYPH_FORMAT_COMPOSITE;
-
- loader->current.num_subglyphs = 2;
- }
-
- FT_GlyphLoader_Prepare( builder->loader );
-
- /* First load `bchar' in builder */
- error = cff_get_glyph_data( face, (FT_UInt)bchar_index,
- &charstring, &charstring_len );
- if ( !error )
- {
- /* the seac operator must not be nested */
- decoder->seac = TRUE;
- error = cff_decoder_parse_charstrings( decoder, charstring,
- charstring_len, 0 );
- decoder->seac = FALSE;
-
- cff_free_glyph_data( face, &charstring, charstring_len );
-
- if ( error )
- goto Exit;
- }
-
- /* Save the left bearing, advance and glyph width of the base */
- /* character as they will be erased by the next load. */
-
- left_bearing = builder->left_bearing;
- advance = builder->advance;
- glyph_width = decoder->glyph_width;
-
- builder->left_bearing.x = 0;
- builder->left_bearing.y = 0;
-
- builder->pos_x = adx - asb;
- builder->pos_y = ady;
-
- /* Now load `achar' on top of the base outline. */
- error = cff_get_glyph_data( face, (FT_UInt)achar_index,
- &charstring, &charstring_len );
- if ( !error )
- {
- /* the seac operator must not be nested */
- decoder->seac = TRUE;
- error = cff_decoder_parse_charstrings( decoder, charstring,
- charstring_len, 0 );
- decoder->seac = FALSE;
-
- cff_free_glyph_data( face, &charstring, charstring_len );
-
- if ( error )
- goto Exit;
- }
-
- /* Restore the left side bearing, advance and glyph width */
- /* of the base character. */
- builder->left_bearing = left_bearing;
- builder->advance = advance;
- decoder->glyph_width = glyph_width;
-
- builder->pos_x = 0;
- builder->pos_y = 0;
-
- Exit:
- return error;
- }
-
-
- /*************************************************************************/
- /* */
- /* <Function> */
- /* cff_decoder_parse_charstrings */
- /* */
- /* <Description> */
- /* Parses a given Type 2 charstrings program. */
- /* */
- /* <InOut> */
- /* decoder :: The current Type 1 decoder. */
- /* */
- /* <Input> */
- /* charstring_base :: The base of the charstring stream. */
- /* */
- /* charstring_len :: The length in bytes of the charstring stream. */
- /* */
- /* in_dict :: Set to 1 if function is called from top or */
- /* private DICT (needed for Multiple Master CFFs). */
- /* */
- /* <Return> */
- /* FreeType error code. 0 means success. */
- /* */
- FT_LOCAL_DEF( FT_Error )
- cff_decoder_parse_charstrings( CFF_Decoder* decoder,
- FT_Byte* charstring_base,
- FT_ULong charstring_len,
- FT_Bool in_dict )
- {
- FT_Error error;
- CFF_Decoder_Zone* zone;
- FT_Byte* ip;
- FT_Byte* limit;
- CFF_Builder* builder = &decoder->builder;
- FT_Pos x, y;
- FT_Fixed* stack;
- FT_Int charstring_type =
- decoder->cff->top_font.font_dict.charstring_type;
- FT_UShort num_designs =
- decoder->cff->top_font.font_dict.num_designs;
- FT_UShort num_axes =
- decoder->cff->top_font.font_dict.num_axes;
-
- T2_Hints_Funcs hinter;
-
-
- /* set default width */
- decoder->num_hints = 0;
- decoder->read_width = 1;
-
- /* initialize the decoder */
- decoder->top = decoder->stack;
- decoder->zone = decoder->zones;
- zone = decoder->zones;
- stack = decoder->top;
-
- hinter = (T2_Hints_Funcs)builder->hints_funcs;
-
- builder->path_begun = 0;
-
- zone->base = charstring_base;
- limit = zone->limit = charstring_base + charstring_len;
- ip = zone->cursor = zone->base;
-
- error = FT_Err_Ok;
-
- x = builder->pos_x;
- y = builder->pos_y;
-
- /* begin hints recording session, if any */
- if ( hinter )
- hinter->open( hinter->hints );
-
- /* now execute loop */
- while ( ip < limit )
- {
- CFF_Operator op;
- FT_Byte v;
-
-
- /********************************************************************/
- /* */
- /* Decode operator or operand */
- /* */
- v = *ip++;
- if ( v >= 32 || v == 28 )
- {
- FT_Int shift = 16;
- FT_Int32 val;
-
-
- /* this is an operand, push it on the stack */
-
- /* if we use shifts, all computations are done with unsigned */
- /* values; the conversion to a signed value is the last step */
- if ( v == 28 )
- {
- if ( ip + 1 >= limit )
- goto Syntax_Error;
- val = (FT_Short)( ( (FT_UShort)ip[0] << 8 ) | ip[1] );
- ip += 2;
- }
- else if ( v < 247 )
- val = (FT_Int32)v - 139;
- else if ( v < 251 )
- {
- if ( ip >= limit )
- goto Syntax_Error;
- val = ( (FT_Int32)v - 247 ) * 256 + *ip++ + 108;
- }
- else if ( v < 255 )
- {
- if ( ip >= limit )
- goto Syntax_Error;
- val = -( (FT_Int32)v - 251 ) * 256 - *ip++ - 108;
- }
- else
- {
- if ( ip + 3 >= limit )
- goto Syntax_Error;
- val = (FT_Int32)( ( (FT_UInt32)ip[0] << 24 ) |
- ( (FT_UInt32)ip[1] << 16 ) |
- ( (FT_UInt32)ip[2] << 8 ) |
- (FT_UInt32)ip[3] );
- ip += 4;
- if ( charstring_type == 2 )
- shift = 0;
- }
- if ( decoder->top - stack >= CFF_MAX_OPERANDS )
- goto Stack_Overflow;
-
- val = (FT_Int32)( (FT_UInt32)val << shift );
- *decoder->top++ = val;
-
-#ifdef FT_DEBUG_LEVEL_TRACE
- if ( !( val & 0xFFFFL ) )
- FT_TRACE4(( " %hd", (FT_Short)( (FT_UInt32)val >> 16 ) ));
- else
- FT_TRACE4(( " %.5f", val / 65536.0 ));
-#endif
-
- }
- else
- {
- /* The specification says that normally arguments are to be taken */
- /* from the bottom of the stack. However, this seems not to be */
- /* correct, at least for Acroread 7.0.8 on GNU/Linux: It pops the */
- /* arguments similar to a PS interpreter. */
-
- FT_Fixed* args = decoder->top;
- FT_Int num_args = (FT_Int)( args - decoder->stack );
- FT_Int req_args;
-
-
- /* find operator */
- op = cff_op_unknown;
-
- switch ( v )
- {
- case 1:
- op = cff_op_hstem;
- break;
- case 3:
- op = cff_op_vstem;
- break;
- case 4:
- op = cff_op_vmoveto;
- break;
- case 5:
- op = cff_op_rlineto;
- break;
- case 6:
- op = cff_op_hlineto;
- break;
- case 7:
- op = cff_op_vlineto;
- break;
- case 8:
- op = cff_op_rrcurveto;
- break;
- case 9:
- op = cff_op_closepath;
- break;
- case 10:
- op = cff_op_callsubr;
- break;
- case 11:
- op = cff_op_return;
- break;
- case 12:
- {
- if ( ip >= limit )
- goto Syntax_Error;
- v = *ip++;
-
- switch ( v )
- {
- case 0:
- op = cff_op_dotsection;
- break;
- case 1: /* this is actually the Type1 vstem3 operator */
- op = cff_op_vstem;
- break;
- case 2: /* this is actually the Type1 hstem3 operator */
- op = cff_op_hstem;
- break;
- case 3:
- op = cff_op_and;
- break;
- case 4:
- op = cff_op_or;
- break;
- case 5:
- op = cff_op_not;
- break;
- case 6:
- op = cff_op_seac;
- break;
- case 7:
- op = cff_op_sbw;
- break;
- case 8:
- op = cff_op_store;
- break;
- case 9:
- op = cff_op_abs;
- break;
- case 10:
- op = cff_op_add;
- break;
- case 11:
- op = cff_op_sub;
- break;
- case 12:
- op = cff_op_div;
- break;
- case 13:
- op = cff_op_load;
- break;
- case 14:
- op = cff_op_neg;
- break;
- case 15:
- op = cff_op_eq;
- break;
- case 16:
- op = cff_op_callothersubr;
- break;
- case 17:
- op = cff_op_pop;
- break;
- case 18:
- op = cff_op_drop;
- break;
- case 20:
- op = cff_op_put;
- break;
- case 21:
- op = cff_op_get;
- break;
- case 22:
- op = cff_op_ifelse;
- break;
- case 23:
- op = cff_op_random;
- break;
- case 24:
- op = cff_op_mul;
- break;
- case 26:
- op = cff_op_sqrt;
- break;
- case 27:
- op = cff_op_dup;
- break;
- case 28:
- op = cff_op_exch;
- break;
- case 29:
- op = cff_op_index;
- break;
- case 30:
- op = cff_op_roll;
- break;
- case 33:
- op = cff_op_setcurrentpoint;
- break;
- case 34:
- op = cff_op_hflex;
- break;
- case 35:
- op = cff_op_flex;
- break;
- case 36:
- op = cff_op_hflex1;
- break;
- case 37:
- op = cff_op_flex1;
- break;
- default:
- FT_TRACE4(( " unknown op (12, %d)\n", v ));
- break;
- }
- }
- break;
- case 13:
- op = cff_op_hsbw;
- break;
- case 14:
- op = cff_op_endchar;
- break;
- case 16:
- op = cff_op_blend;
- break;
- case 18:
- op = cff_op_hstemhm;
- break;
- case 19:
- op = cff_op_hintmask;
- break;
- case 20:
- op = cff_op_cntrmask;
- break;
- case 21:
- op = cff_op_rmoveto;
- break;
- case 22:
- op = cff_op_hmoveto;
- break;
- case 23:
- op = cff_op_vstemhm;
- break;
- case 24:
- op = cff_op_rcurveline;
- break;
- case 25:
- op = cff_op_rlinecurve;
- break;
- case 26:
- op = cff_op_vvcurveto;
- break;
- case 27:
- op = cff_op_hhcurveto;
- break;
- case 29:
- op = cff_op_callgsubr;
- break;
- case 30:
- op = cff_op_vhcurveto;
- break;
- case 31:
- op = cff_op_hvcurveto;
- break;
- default:
- FT_TRACE4(( " unknown op (%d)\n", v ));
- break;
- }
-
- if ( op == cff_op_unknown )
- continue;
-
- /* in Multiple Master CFFs, T2 charstrings can appear in */
- /* dictionaries, but some operators are prohibited */
- if ( in_dict )
- {
- switch ( op )
- {
- case cff_op_hstem:
- case cff_op_vstem:
- case cff_op_vmoveto:
- case cff_op_rlineto:
- case cff_op_hlineto:
- case cff_op_vlineto:
- case cff_op_rrcurveto:
- case cff_op_hstemhm:
- case cff_op_hintmask:
- case cff_op_cntrmask:
- case cff_op_rmoveto:
- case cff_op_hmoveto:
- case cff_op_vstemhm:
- case cff_op_rcurveline:
- case cff_op_rlinecurve:
- case cff_op_vvcurveto:
- case cff_op_hhcurveto:
- case cff_op_vhcurveto:
- case cff_op_hvcurveto:
- case cff_op_hflex:
- case cff_op_flex:
- case cff_op_hflex1:
- case cff_op_flex1:
- case cff_op_callsubr:
- case cff_op_callgsubr:
- goto MM_Error;
-
- default:
- break;
- }
- }
-
- /* check arguments */
- req_args = cff_argument_counts[op];
- if ( req_args & CFF_COUNT_CHECK_WIDTH )
- {
- if ( num_args > 0 && decoder->read_width )
- {
- /* If `nominal_width' is non-zero, the number is really a */
- /* difference against `nominal_width'. Else, the number here */
- /* is truly a width, not a difference against `nominal_width'. */
- /* If the font does not set `nominal_width', then */
- /* `nominal_width' defaults to zero, and so we can set */
- /* `glyph_width' to `nominal_width' plus number on the stack */
- /* -- for either case. */
-
- FT_Int set_width_ok;
-
-
- switch ( op )
- {
- case cff_op_hmoveto:
- case cff_op_vmoveto:
- set_width_ok = num_args & 2;
- break;
-
- case cff_op_hstem:
- case cff_op_vstem:
- case cff_op_hstemhm:
- case cff_op_vstemhm:
- case cff_op_rmoveto:
- case cff_op_hintmask:
- case cff_op_cntrmask:
- set_width_ok = num_args & 1;
- break;
-
- case cff_op_endchar:
- /* If there is a width specified for endchar, we either have */
- /* 1 argument or 5 arguments. We like to argue. */
- set_width_ok = in_dict
- ? 0
- : ( ( num_args == 5 ) || ( num_args == 1 ) );
- break;
-
- default:
- set_width_ok = 0;
- break;
- }
-
- if ( set_width_ok )
- {
- decoder->glyph_width = decoder->nominal_width +
- ( stack[0] >> 16 );
-
- if ( decoder->width_only )
- {
- /* we only want the advance width; stop here */
- break;
- }
-
- /* Consumed an argument. */
- num_args--;
- }
- }
-
- decoder->read_width = 0;
- req_args = 0;
- }
-
- req_args &= 0x000F;
- if ( num_args < req_args )
- goto Stack_Underflow;
- args -= req_args;
- num_args -= req_args;
-
- /* At this point, `args' points to the first argument of the */
- /* operand in case `req_args' isn't zero. Otherwise, we have */
- /* to adjust `args' manually. */
-
- /* Note that we only pop arguments from the stack which we */
- /* really need and can digest so that we can continue in case */
- /* of superfluous stack elements. */
-
- switch ( op )
- {
- case cff_op_hstem:
- case cff_op_vstem:
- case cff_op_hstemhm:
- case cff_op_vstemhm:
- /* the number of arguments is always even here */
- FT_TRACE4((
- op == cff_op_hstem ? " hstem\n" :
- ( op == cff_op_vstem ? " vstem\n" :
- ( op == cff_op_hstemhm ? " hstemhm\n" : " vstemhm\n" ) ) ));
-
- if ( hinter )
- hinter->stems( hinter->hints,
- ( op == cff_op_hstem || op == cff_op_hstemhm ),
- num_args / 2,
- args - ( num_args & ~1 ) );
-
- decoder->num_hints += num_args / 2;
- args = stack;
- break;
-
- case cff_op_hintmask:
- case cff_op_cntrmask:
- FT_TRACE4(( op == cff_op_hintmask ? " hintmask" : " cntrmask" ));
-
- /* implement vstem when needed -- */
- /* the specification doesn't say it, but this also works */
- /* with the 'cntrmask' operator */
- /* */
- if ( num_args > 0 )
- {
- if ( hinter )
- hinter->stems( hinter->hints,
- 0,
- num_args / 2,
- args - ( num_args & ~1 ) );
-
- decoder->num_hints += num_args / 2;
- }
-
- /* In a valid charstring there must be at least one byte */
- /* after `hintmask' or `cntrmask' (e.g., for a `return' */
- /* instruction). Additionally, there must be space for */
- /* `num_hints' bits. */
-
- if ( ( ip + ( ( decoder->num_hints + 7 ) >> 3 ) ) >= limit )
- goto Syntax_Error;
-
- if ( hinter )
- {
- if ( op == cff_op_hintmask )
- hinter->hintmask( hinter->hints,
- (FT_UInt)builder->current->n_points,
- (FT_UInt)decoder->num_hints,
- ip );
- else
- hinter->counter( hinter->hints,
- (FT_UInt)decoder->num_hints,
- ip );
- }
-
-#ifdef FT_DEBUG_LEVEL_TRACE
- {
- FT_UInt maskbyte;
-
-
- FT_TRACE4(( " (maskbytes:" ));
-
- for ( maskbyte = 0;
- maskbyte < (FT_UInt)( ( decoder->num_hints + 7 ) >> 3 );
- maskbyte++, ip++ )
- FT_TRACE4(( " 0x%02X", *ip ));
-
- FT_TRACE4(( ")\n" ));
- }
-#else
- ip += ( decoder->num_hints + 7 ) >> 3;
-#endif
- args = stack;
- break;
-
- case cff_op_rmoveto:
- FT_TRACE4(( " rmoveto\n" ));
-
- cff_builder_close_contour( builder );
- builder->path_begun = 0;
- x = ADD_LONG( x, args[-2] );
- y = ADD_LONG( y, args[-1] );
- args = stack;
- break;
-
- case cff_op_vmoveto:
- FT_TRACE4(( " vmoveto\n" ));
-
- cff_builder_close_contour( builder );
- builder->path_begun = 0;
- y = ADD_LONG( y, args[-1] );
- args = stack;
- break;
-
- case cff_op_hmoveto:
- FT_TRACE4(( " hmoveto\n" ));
-
- cff_builder_close_contour( builder );
- builder->path_begun = 0;
- x = ADD_LONG( x, args[-1] );
- args = stack;
- break;
-
- case cff_op_rlineto:
- FT_TRACE4(( " rlineto\n" ));
-
- if ( cff_builder_start_point( builder, x, y ) ||
- cff_check_points( builder, num_args / 2 ) )
- goto Fail;
-
- if ( num_args < 2 )
- goto Stack_Underflow;
-
- args -= num_args & ~1;
- while ( args < decoder->top )
- {
- x = ADD_LONG( x, args[0] );
- y = ADD_LONG( y, args[1] );
- cff_builder_add_point( builder, x, y, 1 );
- args += 2;
- }
- args = stack;
- break;
-
- case cff_op_hlineto:
- case cff_op_vlineto:
- {
- FT_Int phase = ( op == cff_op_hlineto );
-
-
- FT_TRACE4(( op == cff_op_hlineto ? " hlineto\n"
- : " vlineto\n" ));
-
- if ( num_args < 0 )
- goto Stack_Underflow;
-
- /* there exist subsetted fonts (found in PDFs) */
- /* which call `hlineto' without arguments */
- if ( num_args == 0 )
- break;
-
- if ( cff_builder_start_point( builder, x, y ) ||
- cff_check_points( builder, num_args ) )
- goto Fail;
-
- args = stack;
- while ( args < decoder->top )
- {
- if ( phase )
- x = ADD_LONG( x, args[0] );
- else
- y = ADD_LONG( y, args[0] );
-
- if ( cff_builder_add_point1( builder, x, y ) )
- goto Fail;
-
- args++;
- phase ^= 1;
- }
- args = stack;
- }
- break;
-
- case cff_op_rrcurveto:
- {
- FT_Int nargs;
-
-
- FT_TRACE4(( " rrcurveto\n" ));
-
- if ( num_args < 6 )
- goto Stack_Underflow;
-
- nargs = num_args - num_args % 6;
-
- if ( cff_builder_start_point( builder, x, y ) ||
- cff_check_points( builder, nargs / 2 ) )
- goto Fail;
-
- args -= nargs;
- while ( args < decoder->top )
- {
- x = ADD_LONG( x, args[0] );
- y = ADD_LONG( y, args[1] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[2] );
- y = ADD_LONG( y, args[3] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[4] );
- y = ADD_LONG( y, args[5] );
- cff_builder_add_point( builder, x, y, 1 );
-
- args += 6;
- }
- args = stack;
- }
- break;
-
- case cff_op_vvcurveto:
- {
- FT_Int nargs;
-
-
- FT_TRACE4(( " vvcurveto\n" ));
-
- if ( num_args < 4 )
- goto Stack_Underflow;
-
- /* if num_args isn't of the form 4n or 4n+1, */
- /* we enforce it by clearing the second bit */
-
- nargs = num_args & ~2;
-
- if ( cff_builder_start_point( builder, x, y ) )
- goto Fail;
-
- args -= nargs;
-
- if ( nargs & 1 )
- {
- x = ADD_LONG( x, args[0] );
- args++;
- nargs--;
- }
-
- if ( cff_check_points( builder, 3 * ( nargs / 4 ) ) )
- goto Fail;
-
- while ( args < decoder->top )
- {
- y = ADD_LONG( y, args[0] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[1] );
- y = ADD_LONG( y, args[2] );
- cff_builder_add_point( builder, x, y, 0 );
-
- y = ADD_LONG( y, args[3] );
- cff_builder_add_point( builder, x, y, 1 );
-
- args += 4;
- }
- args = stack;
- }
- break;
-
- case cff_op_hhcurveto:
- {
- FT_Int nargs;
-
-
- FT_TRACE4(( " hhcurveto\n" ));
-
- if ( num_args < 4 )
- goto Stack_Underflow;
-
- /* if num_args isn't of the form 4n or 4n+1, */
- /* we enforce it by clearing the second bit */
-
- nargs = num_args & ~2;
-
- if ( cff_builder_start_point( builder, x, y ) )
- goto Fail;
-
- args -= nargs;
- if ( nargs & 1 )
- {
- y = ADD_LONG( y, args[0] );
- args++;
- nargs--;
- }
-
- if ( cff_check_points( builder, 3 * ( nargs / 4 ) ) )
- goto Fail;
-
- while ( args < decoder->top )
- {
- x = ADD_LONG( x, args[0] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[1] );
- y = ADD_LONG( y, args[2] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[3] );
- cff_builder_add_point( builder, x, y, 1 );
-
- args += 4;
- }
- args = stack;
- }
- break;
-
- case cff_op_vhcurveto:
- case cff_op_hvcurveto:
- {
- FT_Int phase;
- FT_Int nargs;
-
-
- FT_TRACE4(( op == cff_op_vhcurveto ? " vhcurveto\n"
- : " hvcurveto\n" ));
-
- if ( cff_builder_start_point( builder, x, y ) )
- goto Fail;
-
- if ( num_args < 4 )
- goto Stack_Underflow;
-
- /* if num_args isn't of the form 8n, 8n+1, 8n+4, or 8n+5, */
- /* we enforce it by clearing the second bit */
-
- nargs = num_args & ~2;
-
- args -= nargs;
- if ( cff_check_points( builder, ( nargs / 4 ) * 3 ) )
- goto Stack_Underflow;
-
- phase = ( op == cff_op_hvcurveto );
-
- while ( nargs >= 4 )
- {
- nargs -= 4;
- if ( phase )
- {
- x = ADD_LONG( x, args[0] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[1] );
- y = ADD_LONG( y, args[2] );
- cff_builder_add_point( builder, x, y, 0 );
-
- y = ADD_LONG( y, args[3] );
- if ( nargs == 1 )
- x = ADD_LONG( x, args[4] );
- cff_builder_add_point( builder, x, y, 1 );
- }
- else
- {
- y = ADD_LONG( y, args[0] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[1] );
- y = ADD_LONG( y, args[2] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[3] );
- if ( nargs == 1 )
- y = ADD_LONG( y, args[4] );
- cff_builder_add_point( builder, x, y, 1 );
- }
- args += 4;
- phase ^= 1;
- }
- args = stack;
- }
- break;
-
- case cff_op_rlinecurve:
- {
- FT_Int num_lines;
- FT_Int nargs;
-
-
- FT_TRACE4(( " rlinecurve\n" ));
-
- if ( num_args < 8 )
- goto Stack_Underflow;
-
- nargs = num_args & ~1;
- num_lines = ( nargs - 6 ) / 2;
-
- if ( cff_builder_start_point( builder, x, y ) ||
- cff_check_points( builder, num_lines + 3 ) )
- goto Fail;
-
- args -= nargs;
-
- /* first, add the line segments */
- while ( num_lines > 0 )
- {
- x = ADD_LONG( x, args[0] );
- y = ADD_LONG( y, args[1] );
- cff_builder_add_point( builder, x, y, 1 );
-
- args += 2;
- num_lines--;
- }
-
- /* then the curve */
- x = ADD_LONG( x, args[0] );
- y = ADD_LONG( y, args[1] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[2] );
- y = ADD_LONG( y, args[3] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[4] );
- y = ADD_LONG( y, args[5] );
- cff_builder_add_point( builder, x, y, 1 );
-
- args = stack;
- }
- break;
-
- case cff_op_rcurveline:
- {
- FT_Int num_curves;
- FT_Int nargs;
-
-
- FT_TRACE4(( " rcurveline\n" ));
-
- if ( num_args < 8 )
- goto Stack_Underflow;
-
- nargs = num_args - 2;
- nargs = nargs - nargs % 6 + 2;
- num_curves = ( nargs - 2 ) / 6;
-
- if ( cff_builder_start_point( builder, x, y ) ||
- cff_check_points( builder, num_curves * 3 + 2 ) )
- goto Fail;
-
- args -= nargs;
-
- /* first, add the curves */
- while ( num_curves > 0 )
- {
- x = ADD_LONG( x, args[0] );
- y = ADD_LONG( y, args[1] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[2] );
- y = ADD_LONG( y, args[3] );
- cff_builder_add_point( builder, x, y, 0 );
-
- x = ADD_LONG( x, args[4] );
- y = ADD_LONG( y, args[5] );
- cff_builder_add_point( builder, x, y, 1 );
-
- args += 6;
- num_curves--;
- }
-
- /* then the final line */
- x = ADD_LONG( x, args[0] );
- y = ADD_LONG( y, args[1] );
- cff_builder_add_point( builder, x, y, 1 );
-
- args = stack;
- }
- break;
-
- case cff_op_hflex1:
- {
- FT_Pos start_y;
-
-
- FT_TRACE4(( " hflex1\n" ));
-
- /* adding five more points: 4 control points, 1 on-curve point */
- /* -- make sure we have enough space for the start point if it */
- /* needs to be added */
- if ( cff_builder_start_point( builder, x, y ) ||
- cff_check_points( builder, 6 ) )
- goto Fail;
-
- /* record the starting point's y position for later use */
- start_y = y;
-
- /* first control point */
- x = ADD_LONG( x, args[0] );
- y = ADD_LONG( y, args[1] );
- cff_builder_add_point( builder, x, y, 0 );
-
- /* second control point */
- x = ADD_LONG( x, args[2] );
- y = ADD_LONG( y, args[3] );
- cff_builder_add_point( builder, x, y, 0 );
-
- /* join point; on curve, with y-value the same as the last */
- /* control point's y-value */
- x = ADD_LONG( x, args[4] );
- cff_builder_add_point( builder, x, y, 1 );
-
- /* third control point, with y-value the same as the join */
- /* point's y-value */
- x = ADD_LONG( x, args[5] );
- cff_builder_add_point( builder, x, y, 0 );
-
- /* fourth control point */
- x = ADD_LONG( x, args[6] );
- y = ADD_LONG( y, args[7] );
- cff_builder_add_point( builder, x, y, 0 );
-
- /* ending point, with y-value the same as the start */
- x = ADD_LONG( x, args[8] );
- y = start_y;
- cff_builder_add_point( builder, x, y, 1 );
-
- args = stack;
- break;
- }
-
- case cff_op_hflex:
- {
- FT_Pos start_y;
-
-
- FT_TRACE4(( " hflex\n" ));
-
- /* adding six more points; 4 control points, 2 on-curve points */
- if ( cff_builder_start_point( builder, x, y ) ||
- cff_check_points( builder, 6 ) )
- goto Fail;
-
- /* record the starting point's y-position for later use */
- start_y = y;
-
- /* first control point */
- x = ADD_LONG( x, args[0] );
- cff_builder_add_point( builder, x, y, 0 );
-
- /* second control point */
- x = ADD_LONG( x, args[1] );
- y = ADD_LONG( y, args[2] );
- cff_builder_add_point( builder, x, y, 0 );
-
- /* join point; on curve, with y-value the same as the last */
- /* control point's y-value */
- x = ADD_LONG( x, args[3] );
- cff_builder_add_point( builder, x, y, 1 );
-
- /* third control point, with y-value the same as the join */
- /* point's y-value */
- x = ADD_LONG( x, args[4] );
- cff_builder_add_point( builder, x, y, 0 );
-
- /* fourth control point */
- x = ADD_LONG( x, args[5] );
- y = start_y;
- cff_builder_add_point( builder, x, y, 0 );
-
- /* ending point, with y-value the same as the start point's */
- /* y-value -- we don't add this point, though */
- x = ADD_LONG( x, args[6] );
- cff_builder_add_point( builder, x, y, 1 );
-
- args = stack;
- break;
- }
-
- case cff_op_flex1:
- {
- FT_Pos start_x, start_y; /* record start x, y values for */
- /* alter use */
- FT_Fixed dx = 0, dy = 0; /* used in horizontal/vertical */
- /* algorithm below */
- FT_Int horizontal, count;
- FT_Fixed* temp;
-
-
- FT_TRACE4(( " flex1\n" ));
-
- /* adding six more points; 4 control points, 2 on-curve points */
- if ( cff_builder_start_point( builder, x, y ) ||
- cff_check_points( builder, 6 ) )
- goto Fail;
-
- /* record the starting point's x, y position for later use */
- start_x = x;
- start_y = y;
-
- /* XXX: figure out whether this is supposed to be a horizontal */
- /* or vertical flex; the Type 2 specification is vague... */
-
- temp = args;
-
- /* grab up to the last argument */
- for ( count = 5; count > 0; count-- )
- {
- dx = ADD_LONG( dx, temp[0] );
- dy = ADD_LONG( dy, temp[1] );
- temp += 2;
- }
-
- if ( dx < 0 )
- dx = -dx;
- if ( dy < 0 )
- dy = -dy;
-
- /* strange test, but here it is... */
- horizontal = ( dx > dy );
-
- for ( count = 5; count > 0; count-- )
- {
- x = ADD_LONG( x, args[0] );
- y = ADD_LONG( y, args[1] );
- cff_builder_add_point( builder, x, y,
- (FT_Bool)( count == 3 ) );
- args += 2;
- }
-
- /* is last operand an x- or y-delta? */
- if ( horizontal )
- {
- x = ADD_LONG( x, args[0] );
- y = start_y;
- }
- else
- {
- x = start_x;
- y = ADD_LONG( y, args[0] );
- }
-
- cff_builder_add_point( builder, x, y, 1 );
-
- args = stack;
- break;
- }
-
- case cff_op_flex:
- {
- FT_UInt count;
-
-
- FT_TRACE4(( " flex\n" ));
-
- if ( cff_builder_start_point( builder, x, y ) ||
- cff_check_points( builder, 6 ) )
- goto Fail;
-
- for ( count = 6; count > 0; count-- )
- {
- x = ADD_LONG( x, args[0] );
- y = ADD_LONG( y, args[1] );
- cff_builder_add_point( builder, x, y,
- (FT_Bool)( count == 4 || count == 1 ) );
- args += 2;
- }
-
- args = stack;
- }
- break;
-
- case cff_op_seac:
- FT_TRACE4(( " seac\n" ));
-
- error = cff_operator_seac( decoder,
- args[0], args[1], args[2],
- (FT_Int)( args[3] >> 16 ),
- (FT_Int)( args[4] >> 16 ) );
-
- /* add current outline to the glyph slot */
- FT_GlyphLoader_Add( builder->loader );
-
- /* return now! */
- FT_TRACE4(( "\n" ));
- return error;
-
- case cff_op_endchar:
- /* in dictionaries, `endchar' simply indicates end of data */
- if ( in_dict )
- return error;
-
- FT_TRACE4(( " endchar\n" ));
-
- /* We are going to emulate the seac operator. */
- if ( num_args >= 4 )
- {
- /* Save glyph width so that the subglyphs don't overwrite it. */
- FT_Pos glyph_width = decoder->glyph_width;
-
-
- error = cff_operator_seac( decoder,
- 0L, args[-4], args[-3],
- (FT_Int)( args[-2] >> 16 ),
- (FT_Int)( args[-1] >> 16 ) );
-
- decoder->glyph_width = glyph_width;
- }
- else
- {
- cff_builder_close_contour( builder );
-
- /* close hints recording session */
- if ( hinter )
- {
- if ( hinter->close( hinter->hints,
- (FT_UInt)builder->current->n_points ) )
- goto Syntax_Error;
-
- /* apply hints to the loaded glyph outline now */
- error = hinter->apply( hinter->hints,
- builder->current,
- (PSH_Globals)builder->hints_globals,
- decoder->hint_mode );
- if ( error )
- goto Fail;
- }
-
- /* add current outline to the glyph slot */
- FT_GlyphLoader_Add( builder->loader );
- }
-
- /* return now! */
- FT_TRACE4(( "\n" ));
- return error;
-
- case cff_op_abs:
- FT_TRACE4(( " abs\n" ));
-
- if ( args[0] < 0 )
- {
- if ( args[0] == FT_LONG_MIN )
- args[0] = FT_LONG_MAX;
- else
- args[0] = -args[0];
- }
- args++;
- break;
-
- case cff_op_add:
- FT_TRACE4(( " add\n" ));
-
- args[0] = ADD_LONG( args[0], args[1] );
- args++;
- break;
-
- case cff_op_sub:
- FT_TRACE4(( " sub\n" ));
-
- args[0] = SUB_LONG( args[0], args[1] );
- args++;
- break;
-
- case cff_op_div:
- FT_TRACE4(( " div\n" ));
-
- args[0] = FT_DivFix( args[0], args[1] );
- args++;
- break;
-
- case cff_op_neg:
- FT_TRACE4(( " neg\n" ));
-
- if ( args[0] == FT_LONG_MIN )
- args[0] = FT_LONG_MAX;
- args[0] = -args[0];
- args++;
- break;
-
- case cff_op_random:
- FT_TRACE4(( " random\n" ));
-
- /* only use the lower 16 bits of `random' */
- /* to generate a number in the range (0;1] */
- args[0] = (FT_Fixed)
- ( ( decoder->current_subfont->random & 0xFFFF ) + 1 );
- args++;
-
- decoder->current_subfont->random =
- cff_random( decoder->current_subfont->random );
- break;
-
- case cff_op_mul:
- FT_TRACE4(( " mul\n" ));
-
- args[0] = FT_MulFix( args[0], args[1] );
- args++;
- break;
-
- case cff_op_sqrt:
- FT_TRACE4(( " sqrt\n" ));
-
- if ( args[0] > 0 )
- {
- FT_Fixed root = args[0];
- FT_Fixed new_root;
-
-
- for (;;)
- {
- new_root = ( root + FT_DivFix( args[0], root ) + 1 ) >> 1;
- if ( new_root == root )
- break;
- root = new_root;
- }
- args[0] = new_root;
- }
- else
- args[0] = 0;
- args++;
- break;
-
- case cff_op_drop:
- /* nothing */
- FT_TRACE4(( " drop\n" ));
-
- break;
-
- case cff_op_exch:
- {
- FT_Fixed tmp;
-
-
- FT_TRACE4(( " exch\n" ));
-
- tmp = args[0];
- args[0] = args[1];
- args[1] = tmp;
- args += 2;
- }
- break;
-
- case cff_op_index:
- {
- FT_Int idx = (FT_Int)( args[0] >> 16 );
-
-
- FT_TRACE4(( " index\n" ));
-
- if ( idx < 0 )
- idx = 0;
- else if ( idx > num_args - 2 )
- idx = num_args - 2;
- args[0] = args[-( idx + 1 )];
- args++;
- }
- break;
-
- case cff_op_roll:
- {
- FT_Int count = (FT_Int)( args[0] >> 16 );
- FT_Int idx = (FT_Int)( args[1] >> 16 );
-
-
- FT_TRACE4(( " roll\n" ));
-
- if ( count <= 0 )
- count = 1;
-
- args -= count;
- if ( args < stack )
- goto Stack_Underflow;
-
- if ( idx >= 0 )
- {
- while ( idx > 0 )
- {
- FT_Fixed tmp = args[count - 1];
- FT_Int i;
-
-
- for ( i = count - 2; i >= 0; i-- )
- args[i + 1] = args[i];
- args[0] = tmp;
- idx--;
- }
- }
- else
- {
- while ( idx < 0 )
- {
- FT_Fixed tmp = args[0];
- FT_Int i;
-
-
- for ( i = 0; i < count - 1; i++ )
- args[i] = args[i + 1];
- args[count - 1] = tmp;
- idx++;
- }
- }
- args += count;
- }
- break;
-
- case cff_op_dup:
- FT_TRACE4(( " dup\n" ));
-
- args[1] = args[0];
- args += 2;
- break;
-
- case cff_op_put:
- {
- FT_Fixed val = args[0];
- FT_Int idx = (FT_Int)( args[1] >> 16 );
-
-
- FT_TRACE4(( " put\n" ));
-
- /* the Type2 specification before version 16-March-2000 */
- /* didn't give a hard-coded size limit of the temporary */
- /* storage array; instead, an argument of the */
- /* `MultipleMaster' operator set the size */
- if ( idx >= 0 && idx < CFF_MAX_TRANS_ELEMENTS )
- decoder->buildchar[idx] = val;
- }
- break;
-
- case cff_op_get:
- {
- FT_Int idx = (FT_Int)( args[0] >> 16 );
- FT_Fixed val = 0;
-
-
- FT_TRACE4(( " get\n" ));
-
- if ( idx >= 0 && idx < CFF_MAX_TRANS_ELEMENTS )
- val = decoder->buildchar[idx];
-
- args[0] = val;
- args++;
- }
- break;
-
- case cff_op_store:
- /* this operator was removed from the Type2 specification */
- /* in version 16-March-2000 */
-
- /* since we currently don't handle interpolation of multiple */
- /* master fonts, this is a no-op */
- FT_TRACE4(( " store\n"));
- break;
-
- case cff_op_load:
- /* this operator was removed from the Type2 specification */
- /* in version 16-March-2000 */
- {
- FT_Int reg_idx = (FT_Int)args[0];
- FT_Int idx = (FT_Int)args[1];
- FT_Int count = (FT_Int)args[2];
-
-
- FT_TRACE4(( " load\n" ));
-
- /* since we currently don't handle interpolation of multiple */
- /* master fonts, we store a vector [1 0 0 ...] in the */
- /* temporary storage array regardless of the Registry index */
- if ( reg_idx >= 0 && reg_idx <= 2 &&
- idx >= 0 && idx < CFF_MAX_TRANS_ELEMENTS &&
- count >= 0 && count <= num_axes )
- {
- FT_Int end, i;
-
-
- end = FT_MIN( idx + count, CFF_MAX_TRANS_ELEMENTS );
-
- if ( idx < end )
- decoder->buildchar[idx] = 1 << 16;
-
- for ( i = idx + 1; i < end; i++ )
- decoder->buildchar[i] = 0;
- }
- }
- break;
-
- case cff_op_blend:
- /* this operator was removed from the Type2 specification */
- /* in version 16-March-2000 */
- {
- FT_Int num_results = (FT_Int)( args[0] >> 16 );
-
-
- FT_TRACE4(( " blend\n" ));
-
- if ( num_results < 0 )
- goto Syntax_Error;
-
- if ( num_results * (FT_Int)num_designs > num_args )
- goto Stack_Underflow;
-
- /* since we currently don't handle interpolation of multiple */
- /* master fonts, return the `num_results' values of the */
- /* first master */
- args -= num_results * ( num_designs - 1 );
- num_args -= num_results * ( num_designs - 1 );
- }
- break;
-
- case cff_op_dotsection:
- /* this operator is deprecated and ignored by the parser */
- FT_TRACE4(( " dotsection\n" ));
- break;
-
- case cff_op_closepath:
- /* this is an invalid Type 2 operator; however, there */
- /* exist fonts which are incorrectly converted from probably */
- /* Type 1 to CFF, and some parsers seem to accept it */
-
- FT_TRACE4(( " closepath (invalid op)\n" ));
-
- args = stack;
- break;
-
- case cff_op_hsbw:
- /* this is an invalid Type 2 operator; however, there */
- /* exist fonts which are incorrectly converted from probably */
- /* Type 1 to CFF, and some parsers seem to accept it */
-
- FT_TRACE4(( " hsbw (invalid op)\n" ));
-
- decoder->glyph_width =
- ADD_LONG( decoder->nominal_width, ( args[1] >> 16 ) );
-
- decoder->builder.left_bearing.x = args[0];
- decoder->builder.left_bearing.y = 0;
-
- x = ADD_LONG( decoder->builder.pos_x, args[0] );
- y = decoder->builder.pos_y;
- args = stack;
- break;
-
- case cff_op_sbw:
- /* this is an invalid Type 2 operator; however, there */
- /* exist fonts which are incorrectly converted from probably */
- /* Type 1 to CFF, and some parsers seem to accept it */
-
- FT_TRACE4(( " sbw (invalid op)\n" ));
-
- decoder->glyph_width =
- ADD_LONG( decoder->nominal_width, ( args[2] >> 16 ) );
-
- decoder->builder.left_bearing.x = args[0];
- decoder->builder.left_bearing.y = args[1];
-
- x = ADD_LONG( decoder->builder.pos_x, args[0] );
- y = ADD_LONG( decoder->builder.pos_y, args[1] );
- args = stack;
- break;
-
- case cff_op_setcurrentpoint:
- /* this is an invalid Type 2 operator; however, there */
- /* exist fonts which are incorrectly converted from probably */
- /* Type 1 to CFF, and some parsers seem to accept it */
-
- FT_TRACE4(( " setcurrentpoint (invalid op)\n" ));
-
- x = ADD_LONG( decoder->builder.pos_x, args[0] );
- y = ADD_LONG( decoder->builder.pos_y, args[1] );
- args = stack;
- break;
-
- case cff_op_callothersubr:
- /* this is an invalid Type 2 operator; however, there */
- /* exist fonts which are incorrectly converted from probably */
- /* Type 1 to CFF, and some parsers seem to accept it */
-
- FT_TRACE4(( " callothersubr (invalid op)\n" ));
-
- /* subsequent `pop' operands should add the arguments, */
- /* this is the implementation described for `unknown' other */
- /* subroutines in the Type1 spec. */
- /* */
- /* XXX Fix return arguments (see discussion below). */
- args -= 2 + ( args[-2] >> 16 );
- if ( args < stack )
- goto Stack_Underflow;
- break;
-
- case cff_op_pop:
- /* this is an invalid Type 2 operator; however, there */
- /* exist fonts which are incorrectly converted from probably */
- /* Type 1 to CFF, and some parsers seem to accept it */
-
- FT_TRACE4(( " pop (invalid op)\n" ));
-
- /* XXX Increasing `args' is wrong: After a certain number of */
- /* `pop's we get a stack overflow. Reason for doing it is */
- /* code like this (actually found in a CFF font): */
- /* */
- /* 17 1 3 callothersubr */
- /* pop */
- /* callsubr */
- /* */
- /* Since we handle `callothersubr' as a no-op, and */
- /* `callsubr' needs at least one argument, `pop' can't be a */
- /* no-op too as it basically should be. */
- /* */
- /* The right solution would be to provide real support for */
- /* `callothersubr' as done in `t1decode.c', however, given */
- /* the fact that CFF fonts with `pop' are invalid, it is */
- /* questionable whether it is worth the time. */
- args++;
- break;
-
- case cff_op_and:
- {
- FT_Fixed cond = ( args[0] && args[1] );
-
-
- FT_TRACE4(( " and\n" ));
-
- args[0] = cond ? 0x10000L : 0;
- args++;
- }
- break;
-
- case cff_op_or:
- {
- FT_Fixed cond = ( args[0] || args[1] );
-
-
- FT_TRACE4(( " or\n" ));
-
- args[0] = cond ? 0x10000L : 0;
- args++;
- }
- break;
-
- case cff_op_not:
- {
- FT_Fixed cond = !args[0];
-
-
- FT_TRACE4(( " not\n" ));
-
- args[0] = cond ? 0x10000L : 0;
- args++;
- }
- break;
-
- case cff_op_eq:
- {
- FT_Fixed cond = ( args[0] == args[1] );
-
-
- FT_TRACE4(( " eq\n" ));
-
- args[0] = cond ? 0x10000L : 0;
- args++;
- }
- break;
-
- case cff_op_ifelse:
- {
- FT_Fixed cond = ( args[2] <= args[3] );
-
-
- FT_TRACE4(( " ifelse\n" ));
-
- if ( !cond )
- args[0] = args[1];
- args++;
- }
- break;
-
- case cff_op_callsubr:
- {
- FT_UInt idx = (FT_UInt)( ( args[0] >> 16 ) +
- decoder->locals_bias );
-
-
- FT_TRACE4(( " callsubr (idx %d, entering level %d)\n",
- idx,
- zone - decoder->zones + 1 ));
-
- if ( idx >= decoder->num_locals )
- {
- FT_ERROR(( "cff_decoder_parse_charstrings:"
- " invalid local subr index\n" ));
- goto Syntax_Error;
- }
-
- if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS )
- {
- FT_ERROR(( "cff_decoder_parse_charstrings:"
- " too many nested subrs\n" ));
- goto Syntax_Error;
- }
-
- zone->cursor = ip; /* save current instruction pointer */
-
- zone++;
- zone->base = decoder->locals[idx];
- zone->limit = decoder->locals[idx + 1];
- zone->cursor = zone->base;
-
- if ( !zone->base || zone->limit == zone->base )
- {
- FT_ERROR(( "cff_decoder_parse_charstrings:"
- " invoking empty subrs\n" ));
- goto Syntax_Error;
- }
-
- decoder->zone = zone;
- ip = zone->base;
- limit = zone->limit;
- }
- break;
-
- case cff_op_callgsubr:
- {
- FT_UInt idx = (FT_UInt)( ( args[0] >> 16 ) +
- decoder->globals_bias );
-
-
- FT_TRACE4(( " callgsubr (idx %d, entering level %d)\n",
- idx,
- zone - decoder->zones + 1 ));
-
- if ( idx >= decoder->num_globals )
- {
- FT_ERROR(( "cff_decoder_parse_charstrings:"
- " invalid global subr index\n" ));
- goto Syntax_Error;
- }
-
- if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS )
- {
- FT_ERROR(( "cff_decoder_parse_charstrings:"
- " too many nested subrs\n" ));
- goto Syntax_Error;
- }
-
- zone->cursor = ip; /* save current instruction pointer */
-
- zone++;
- zone->base = decoder->globals[idx];
- zone->limit = decoder->globals[idx + 1];
- zone->cursor = zone->base;
-
- if ( !zone->base || zone->limit == zone->base )
- {
- FT_ERROR(( "cff_decoder_parse_charstrings:"
- " invoking empty subrs\n" ));
- goto Syntax_Error;
- }
-
- decoder->zone = zone;
- ip = zone->base;
- limit = zone->limit;
- }
- break;
-
- case cff_op_return:
- FT_TRACE4(( " return (leaving level %d)\n",
- decoder->zone - decoder->zones ));
-
- if ( decoder->zone <= decoder->zones )
- {
- FT_ERROR(( "cff_decoder_parse_charstrings:"
- " unexpected return\n" ));
- goto Syntax_Error;
- }
-
- decoder->zone--;
- zone = decoder->zone;
- ip = zone->cursor;
- limit = zone->limit;
- break;
-
- default:
- FT_ERROR(( "Unimplemented opcode: %d", ip[-1] ));
-
- if ( ip[-1] == 12 )
- FT_ERROR(( " %d", ip[0] ));
- FT_ERROR(( "\n" ));
-
- return FT_THROW( Unimplemented_Feature );
- }
-
- decoder->top = args;
-
- if ( decoder->top - stack >= CFF_MAX_OPERANDS )
- goto Stack_Overflow;
-
- } /* general operator processing */
-
- } /* while ip < limit */
-
- FT_TRACE4(( "..end..\n\n" ));
-
- Fail:
- return error;
-
- MM_Error:
- FT_TRACE4(( "cff_decoder_parse_charstrings:"
- " invalid opcode found in top DICT charstring\n"));
- return FT_THROW( Invalid_File_Format );
-
- Syntax_Error:
- FT_TRACE4(( "cff_decoder_parse_charstrings: syntax error\n" ));
- return FT_THROW( Invalid_File_Format );
-
- Stack_Underflow:
- FT_TRACE4(( "cff_decoder_parse_charstrings: stack underflow\n" ));
- return FT_THROW( Too_Few_Arguments );
-
- Stack_Overflow:
- FT_TRACE4(( "cff_decoder_parse_charstrings: stack overflow\n" ));
- return FT_THROW( Stack_Overflow );
- }
-
-#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */
-
-
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
@@ -2704,11 +142,14 @@
FT_Int glyph_index;
CFF_Font cff = (CFF_Font)face->other;
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+ const CFF_Decoder_Funcs decoder_funcs = psaux->cff_decoder_funcs;
+
*max_advance = 0;
/* Initialize load decoder */
- cff_decoder_init( &decoder, face, 0, 0, 0, 0 );
+ decoder_funcs->init( &decoder, face, 0, 0, 0, 0, 0, 0 );
decoder.builder.metrics_only = 1;
decoder.builder.load_points = 0;
@@ -2727,12 +168,12 @@
&charstring, &charstring_len );
if ( !error )
{
- error = cff_decoder_prepare( &decoder, size, glyph_index );
+ error = decoder_funcs->prepare( &decoder, size, glyph_index );
if ( !error )
- error = cff_decoder_parse_charstrings( &decoder,
- charstring,
- charstring_len,
- 0 );
+ error = decoder_funcs->parse_charstrings_old( &decoder,
+ charstring,
+ charstring_len,
+ 0 );
cff_free_glyph_data( face, &charstring, &charstring_len );
}
@@ -2758,10 +199,14 @@
{
FT_Error error;
CFF_Decoder decoder;
+ PS_Decoder psdecoder;
TT_Face face = (TT_Face)glyph->root.face;
FT_Bool hinting, scaled, force_scaling;
CFF_Font cff = (CFF_Font)face->extra.data;
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+ const CFF_Decoder_Funcs decoder_funcs = psaux->cff_decoder_funcs;
+
FT_Matrix font_matrix;
FT_Vector font_offset;
@@ -2951,7 +396,7 @@
{
#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
- CFF_Driver driver = (CFF_Driver)FT_FACE_DRIVER( face );
+ PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( face );
#endif
@@ -2959,8 +404,10 @@
FT_ULong charstring_len;
- cff_decoder_init( &decoder, face, size, glyph, hinting,
- FT_LOAD_TARGET_MODE( load_flags ) );
+ decoder_funcs->init( &decoder, face, size, glyph, hinting,
+ FT_LOAD_TARGET_MODE( load_flags ),
+ cff_get_glyph_data,
+ cff_free_glyph_data );
/* this is for pure CFFs */
if ( load_flags & FT_LOAD_ADVANCE_ONLY )
@@ -2975,23 +422,25 @@
if ( error )
goto Glyph_Build_Finished;
- error = cff_decoder_prepare( &decoder, size, glyph_index );
+ error = decoder_funcs->prepare( &decoder, size, glyph_index );
if ( error )
goto Glyph_Build_Finished;
#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
/* choose which CFF renderer to use */
- if ( driver->hinting_engine == FT_CFF_HINTING_FREETYPE )
- error = cff_decoder_parse_charstrings( &decoder,
- charstring,
- charstring_len,
- 0 );
+ if ( driver->hinting_engine == FT_HINTING_FREETYPE )
+ error = decoder_funcs->parse_charstrings_old( &decoder,
+ charstring,
+ charstring_len,
+ 0 );
else
#endif
{
- error = cf2_decoder_parse_charstrings( &decoder,
- charstring,
- charstring_len );
+ psaux->ps_decoder_init( &psdecoder, &decoder, FALSE );
+
+ error = decoder_funcs->parse_charstrings( &psdecoder,
+ charstring,
+ charstring_len );
/* Adobe's engine uses 16.16 numbers everywhere; */
/* as a consequence, glyphs larger than 2000ppem get rejected */
@@ -3004,9 +453,9 @@
force_scaling = TRUE;
glyph->hint = hinting;
- error = cf2_decoder_parse_charstrings( &decoder,
- charstring,
- charstring_len );
+ error = decoder_funcs->parse_charstrings( &psdecoder,
+ charstring,
+ charstring_len );
}
}
@@ -3044,7 +493,7 @@
Glyph_Build_Finished:
/* save new glyph tables, if no error */
if ( !error )
- cff_builder_done( &decoder.builder );
+ decoder.builder.funcs.done( &decoder.builder );
/* XXX: anything to do for broken glyph entry? */
}
diff --git a/thirdparty/freetype/src/cff/cffgload.h b/thirdparty/freetype/src/cff/cffgload.h
index 0fa93b4398..803f3974fc 100644
--- a/thirdparty/freetype/src/cff/cffgload.h
+++ b/thirdparty/freetype/src/cff/cffgload.h
@@ -4,7 +4,7 @@
/* */
/* OpenType Glyph Loader (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -22,119 +22,11 @@
#include <ft2build.h>
#include FT_FREETYPE_H
-#include "cffobjs.h"
+#include FT_INTERNAL_CFF_OBJECTS_TYPES_H
FT_BEGIN_HEADER
-
-#define CFF_MAX_OPERANDS 48
-#define CFF_MAX_SUBRS_CALLS 16 /* maximum subroutine nesting; */
- /* only 10 are allowed but there exist */
- /* fonts like `HiraKakuProN-W3.ttf' */
- /* (Hiragino Kaku Gothic ProN W3; */
- /* 8.2d6e1; 2014-12-19) that exceed */
- /* this limit */
-#define CFF_MAX_TRANS_ELEMENTS 32
-
-
- /*************************************************************************/
- /* */
- /* <Structure> */
- /* CFF_Builder */
- /* */
- /* <Description> */
- /* A structure used during glyph loading to store its outline. */
- /* */
- /* <Fields> */
- /* memory :: The current memory object. */
- /* */
- /* face :: The current face object. */
- /* */
- /* glyph :: The current glyph slot. */
- /* */
- /* loader :: The current glyph loader. */
- /* */
- /* base :: The base glyph outline. */
- /* */
- /* current :: The current glyph outline. */
- /* */
- /* pos_x :: The horizontal translation (if composite glyph). */
- /* */
- /* pos_y :: The vertical translation (if composite glyph). */
- /* */
- /* left_bearing :: The left side bearing point. */
- /* */
- /* advance :: The horizontal advance vector. */
- /* */
- /* bbox :: Unused. */
- /* */
- /* path_begun :: A flag which indicates that a new path has begun. */
- /* */
- /* load_points :: If this flag is not set, no points are loaded. */
- /* */
- /* no_recurse :: Set but not used. */
- /* */
- /* metrics_only :: A boolean indicating that we only want to compute */
- /* the metrics of a given glyph, not load all of its */
- /* points. */
- /* */
- /* hints_funcs :: Auxiliary pointer for hinting. */
- /* */
- /* hints_globals :: Auxiliary pointer for hinting. */
- /* */
- typedef struct CFF_Builder_
- {
- FT_Memory memory;
- TT_Face face;
- CFF_GlyphSlot glyph;
- FT_GlyphLoader loader;
- FT_Outline* base;
- FT_Outline* current;
-
- FT_Pos pos_x;
- FT_Pos pos_y;
-
- FT_Vector left_bearing;
- FT_Vector advance;
-
- FT_BBox bbox; /* bounding box */
- FT_Bool path_begun;
- FT_Bool load_points;
- FT_Bool no_recurse;
-
- FT_Bool metrics_only;
-
- void* hints_funcs; /* hinter-specific */
- void* hints_globals; /* hinter-specific */
-
- } CFF_Builder;
-
-
- FT_LOCAL( FT_Error )
- cff_check_points( CFF_Builder* builder,
- FT_Int count );
-
- FT_LOCAL( void )
- cff_builder_add_point( CFF_Builder* builder,
- FT_Pos x,
- FT_Pos y,
- FT_Byte flag );
- FT_LOCAL( FT_Error )
- cff_builder_add_point1( CFF_Builder* builder,
- FT_Pos x,
- FT_Pos y );
- FT_LOCAL( FT_Error )
- cff_builder_start_point( CFF_Builder* builder,
- FT_Pos x,
- FT_Pos y );
- FT_LOCAL( void )
- cff_builder_close_contour( CFF_Builder* builder );
-
-
- FT_LOCAL( FT_Int )
- cff_lookup_glyph_by_stdcharcode( CFF_Font cff,
- FT_Int charcode );
FT_LOCAL( FT_Error )
cff_get_glyph_data( TT_Face face,
FT_UInt glyph_index,
@@ -146,74 +38,6 @@ FT_BEGIN_HEADER
FT_ULong length );
- /* execution context charstring zone */
-
- typedef struct CFF_Decoder_Zone_
- {
- FT_Byte* base;
- FT_Byte* limit;
- FT_Byte* cursor;
-
- } CFF_Decoder_Zone;
-
-
- typedef struct CFF_Decoder_
- {
- CFF_Builder builder;
- CFF_Font cff;
-
- FT_Fixed stack[CFF_MAX_OPERANDS + 1];
- FT_Fixed* top;
-
- CFF_Decoder_Zone zones[CFF_MAX_SUBRS_CALLS + 1];
- CFF_Decoder_Zone* zone;
-
- FT_Int flex_state;
- FT_Int num_flex_vectors;
- FT_Vector flex_vectors[7];
-
- FT_Pos glyph_width;
- FT_Pos nominal_width;
-
- FT_Bool read_width;
- FT_Bool width_only;
- FT_Int num_hints;
- FT_Fixed buildchar[CFF_MAX_TRANS_ELEMENTS];
-
- FT_UInt num_locals;
- FT_UInt num_globals;
-
- FT_Int locals_bias;
- FT_Int globals_bias;
-
- FT_Byte** locals;
- FT_Byte** globals;
-
- FT_Byte** glyph_names; /* for pure CFF fonts only */
- FT_UInt num_glyphs; /* number of glyphs in font */
-
- FT_Render_Mode hint_mode;
-
- FT_Bool seac;
-
- CFF_SubFont current_subfont; /* for current glyph_index */
-
- } CFF_Decoder;
-
-
- FT_LOCAL( void )
- cff_decoder_init( CFF_Decoder* decoder,
- TT_Face face,
- CFF_Size size,
- CFF_GlyphSlot slot,
- FT_Bool hinting,
- FT_Render_Mode hint_mode );
-
- FT_LOCAL( FT_Error )
- cff_decoder_prepare( CFF_Decoder* decoder,
- CFF_Size size,
- FT_UInt glyph_index );
-
#if 0 /* unused until we support pure CFF fonts */
/* Compute the maximum advance width of a font through quick parsing */
@@ -223,13 +47,6 @@ FT_BEGIN_HEADER
#endif /* 0 */
-#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
- FT_LOCAL( FT_Error )
- cff_decoder_parse_charstrings( CFF_Decoder* decoder,
- FT_Byte* charstring_base,
- FT_ULong charstring_len,
- FT_Bool in_dict );
-#endif
FT_LOCAL( FT_Error )
cff_slot_load( CFF_GlyphSlot glyph,
diff --git a/thirdparty/freetype/src/cff/cffload.c b/thirdparty/freetype/src/cff/cffload.c
index 12420384af..1c6fe51566 100644
--- a/thirdparty/freetype/src/cff/cffload.c
+++ b/thirdparty/freetype/src/cff/cffload.c
@@ -4,7 +4,7 @@
/* */
/* OpenType and CFF data/program tables loader (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -22,6 +22,7 @@
#include FT_INTERNAL_STREAM_H
#include FT_TRUETYPE_TAGS_H
#include FT_TYPE1_TABLES_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
#include FT_MULTIPLE_MASTERS_H
@@ -1297,7 +1298,9 @@
if ( numOperands > count )
{
- FT_TRACE4(( " cff_blend_doBlend: Stack underflow %d args\n", count ));
+ FT_TRACE4(( " cff_blend_doBlend: Stack underflow %d argument%s\n",
+ count,
+ count == 1 ? "" : "s" ));
error = FT_THROW( Stack_Underflow );
goto Exit;
@@ -1345,19 +1348,14 @@
for ( i = 0; i < numBlends; i++ )
{
const FT_Int32* weight = &blend->BV[1];
- FT_Int32 sum;
+ FT_UInt32 sum;
/* convert inputs to 16.16 fixed point */
- sum = cff_parse_num( parser, &parser->stack[i + base] ) * 65536;
+ sum = cff_parse_num( parser, &parser->stack[i + base] ) * 0x10000;
for ( j = 1; j < blend->lenBV; j++ )
- sum = ADD_INT32(
- sum,
- FT_MulFix(
- *weight++,
- cff_parse_num( parser,
- &parser->stack[delta++] ) * 65536 ) );
+ sum += cff_parse_num( parser, &parser->stack[delta++] ) * *weight++;
/* point parser stack to new value on blend_stack */
parser->stack[i + base] = subFont->blend_top;
@@ -1367,10 +1365,10 @@
/* opcode in both CFF and CFF2 DICTs. See `cff_parse_num' for */
/* decode of this, which rounds to an integer. */
*subFont->blend_top++ = 255;
- *subFont->blend_top++ = ( (FT_UInt32)sum & 0xFF000000U ) >> 24;
- *subFont->blend_top++ = ( (FT_UInt32)sum & 0x00FF0000U ) >> 16;
- *subFont->blend_top++ = ( (FT_UInt32)sum & 0x0000FF00U ) >> 8;
- *subFont->blend_top++ = (FT_UInt32)sum & 0x000000FFU;
+ *subFont->blend_top++ = (FT_Byte)( sum >> 24 );
+ *subFont->blend_top++ = (FT_Byte)( sum >> 16 );
+ *subFont->blend_top++ = (FT_Byte)( sum >> 8 );
+ *subFont->blend_top++ = (FT_Byte)sum;
}
/* leave only numBlends results on parser stack */
@@ -1558,13 +1556,13 @@
FT_UInt lenNDV,
FT_Fixed* NDV )
{
- if ( !blend->builtBV ||
- blend->lastVsindex != vsindex ||
- blend->lenNDV != lenNDV ||
- ( lenNDV &&
- memcmp( NDV,
- blend->lastNDV,
- lenNDV * sizeof ( *NDV ) ) != 0 ) )
+ if ( !blend->builtBV ||
+ blend->lastVsindex != vsindex ||
+ blend->lenNDV != lenNDV ||
+ ( lenNDV &&
+ ft_memcmp( NDV,
+ blend->lastNDV,
+ lenNDV * sizeof ( *NDV ) ) != 0 ) )
{
/* need to build blend vector */
return TRUE;
@@ -1600,7 +1598,8 @@
FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
- mm->done_blend( FT_FACE( face ) );
+ if (mm)
+ mm->done_blend( FT_FACE( face ) );
}
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
@@ -1934,6 +1933,24 @@
else if ( priv->initial_random_seed == 0 )
priv->initial_random_seed = 987654321;
+ /* some sanitizing to avoid overflows later on; */
+ /* the upper limits are ad-hoc values */
+ if ( priv->blue_shift > 1000 || priv->blue_shift < 0 )
+ {
+ FT_TRACE2(( "cff_load_private_dict:"
+ " setting unlikely BlueShift value %d to default (7)\n",
+ priv->blue_shift ));
+ priv->blue_shift = 7;
+ }
+
+ if ( priv->blue_fuzz > 1000 || priv->blue_fuzz < 0 )
+ {
+ FT_TRACE2(( "cff_load_private_dict:"
+ " setting unlikely BlueFuzz value %d to default (1)\n",
+ priv->blue_fuzz ));
+ priv->blue_fuzz = 1;
+ }
+
Exit:
/* clean up */
cff_blend_clear( subfont ); /* clear blend stack */
@@ -1945,18 +1962,6 @@
}
- FT_LOCAL_DEF( FT_UInt32 )
- cff_random( FT_UInt32 r )
- {
- /* a 32bit version of the `xorshift' algorithm */
- r ^= r << 13;
- r ^= r >> 17;
- r ^= r << 5;
-
- return r;
- }
-
-
/* There are 3 ways to call this function, distinguished by code. */
/* */
/* . CFF_CODE_TOPDICT for either a CFF Top DICT or a CFF Font DICT */
@@ -1980,6 +1985,8 @@
CFF_FontRecDict top = &subfont->font_dict;
CFF_Private priv = &subfont->private_dict;
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
FT_Bool cff2 = FT_BOOL( code == CFF2_CODE_TOPDICT ||
code == CFF2_CODE_FONTDICT );
FT_UInt stackSize = cff2 ? CFF2_DEFAULT_STACK
@@ -2085,7 +2092,7 @@
*/
if ( face->root.internal->random_seed == -1 )
{
- CFF_Driver driver = (CFF_Driver)FT_FACE_DRIVER( face );
+ PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( face );
subfont->random = (FT_UInt32)driver->random_seed;
@@ -2094,7 +2101,7 @@
do
{
driver->random_seed =
- (FT_Int32)cff_random( (FT_UInt32)driver->random_seed );
+ (FT_Int32)psaux->cff_random( (FT_UInt32)driver->random_seed );
} while ( driver->random_seed < 0 );
}
@@ -2107,7 +2114,8 @@
do
{
face->root.internal->random_seed =
- (FT_Int32)cff_random( (FT_UInt32)face->root.internal->random_seed );
+ (FT_Int32)psaux->cff_random(
+ (FT_UInt32)face->root.internal->random_seed );
} while ( face->root.internal->random_seed < 0 );
}
@@ -2548,6 +2556,8 @@
font->cf2_instance.finalizer( font->cf2_instance.data );
FT_FREE( font->cf2_instance.data );
}
+
+ FT_FREE( font->font_extra );
}
diff --git a/thirdparty/freetype/src/cff/cffload.h b/thirdparty/freetype/src/cff/cffload.h
index c745e8127b..14d14e2112 100644
--- a/thirdparty/freetype/src/cff/cffload.h
+++ b/thirdparty/freetype/src/cff/cffload.h
@@ -4,7 +4,7 @@
/* */
/* OpenType & CFF data/program tables loader (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -21,9 +21,9 @@
#include <ft2build.h>
-#include "cfftypes.h"
+#include FT_INTERNAL_CFF_TYPES_H
#include "cffparse.h"
-#include "cffobjs.h" /* for CFF_Face */
+#include FT_INTERNAL_CFF_OBJECTS_TYPES_H /* for CFF_Face */
FT_BEGIN_HEADER
@@ -61,9 +61,6 @@ FT_BEGIN_HEADER
FT_UInt cid );
- FT_LOCAL( FT_UInt32 )
- cff_random( FT_UInt32 r );
-
FT_LOCAL( FT_Error )
cff_font_load( FT_Library library,
FT_Stream stream,
diff --git a/thirdparty/freetype/src/cff/cffobjs.c b/thirdparty/freetype/src/cff/cffobjs.c
index 61613933ff..a2d7aec65e 100644
--- a/thirdparty/freetype/src/cff/cffobjs.c
+++ b/thirdparty/freetype/src/cff/cffobjs.c
@@ -4,7 +4,7 @@
/* */
/* OpenType objects manager (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -25,13 +25,15 @@
#include FT_TRUETYPE_IDS_H
#include FT_TRUETYPE_TAGS_H
#include FT_INTERNAL_SFNT_H
-#include FT_CFF_DRIVER_H
+#include FT_DRIVER_H
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
#include FT_MULTIPLE_MASTERS_H
#include FT_SERVICE_MULTIPLE_MASTERS_H
+#include FT_SERVICE_METRICS_VARIATIONS_H
#endif
+#include FT_INTERNAL_CFF_OBJECTS_TYPES_H
#include "cffobjs.h"
#include "cffload.h"
#include "cffcmap.h"
@@ -39,6 +41,9 @@
#include "cfferrs.h"
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_SERVICE_CFF_TABLE_LOAD_H
+
/*************************************************************************/
/* */
@@ -408,7 +413,7 @@
remove_subset_prefix( FT_String* name )
{
FT_Int32 idx = 0;
- FT_Int32 length = (FT_Int32)strlen( name ) + 1;
+ FT_Int32 length = (FT_Int32)ft_strlen( name ) + 1;
FT_Bool continue_search = 1;
@@ -445,8 +450,8 @@
FT_Int32 family_name_length, style_name_length;
- family_name_length = (FT_Int32)strlen( family_name );
- style_name_length = (FT_Int32)strlen( style_name );
+ family_name_length = (FT_Int32)ft_strlen( family_name );
+ style_name_length = (FT_Int32)ft_strlen( style_name );
if ( family_name_length > style_name_length )
{
@@ -493,14 +498,16 @@
SFNT_Service sfnt;
FT_Service_PsCMaps psnames;
PSHinter_Service pshinter;
+ PSAux_Service psaux;
+ FT_Service_CFFLoad cffload;
FT_Bool pure_cff = 1;
FT_Bool cff2 = 0;
FT_Bool sfnt_format = 0;
FT_Library library = cffface->driver->root.library;
- sfnt = (SFNT_Service)FT_Get_Module_Interface(
- library, "sfnt" );
+ sfnt = (SFNT_Service)FT_Get_Module_Interface( library,
+ "sfnt" );
if ( !sfnt )
{
FT_ERROR(( "cff_face_init: cannot access `sfnt' module\n" ));
@@ -510,8 +517,20 @@
FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
- pshinter = (PSHinter_Service)FT_Get_Module_Interface(
- library, "pshinter" );
+ pshinter = (PSHinter_Service)FT_Get_Module_Interface( library,
+ "pshinter" );
+
+ psaux = (PSAux_Service)FT_Get_Module_Interface( library,
+ "psaux" );
+ if ( !psaux )
+ {
+ FT_ERROR(( "cff_face_init: cannot access `psaux' module\n" ));
+ error = FT_THROW( Missing_Module );
+ goto Exit;
+ }
+ face->psaux = psaux;
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, cffload, CFF_LOAD );
FT_TRACE2(( "CFF driver\n" ));
@@ -614,6 +633,7 @@
cff->pshinter = pshinter;
cff->psnames = psnames;
+ cff->cffload = cffload;
cffface->face_index = face_index & 0xFFFF;
@@ -690,50 +710,22 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ FT_Service_MetricsVariations var = (FT_Service_MetricsVariations)face->var;
- FT_Int instance_index = face_index >> 16;
+ FT_UInt instance_index = (FT_UInt)face_index >> 16;
if ( FT_HAS_MULTIPLE_MASTERS( cffface ) &&
mm &&
instance_index > 0 )
{
- FT_MM_Var* mm_var;
-
-
- error = mm->get_mm_var( cffface, NULL );
+ error = mm->set_instance( cffface, instance_index );
if ( error )
goto Exit;
- mm->get_var_blend( cffface, NULL, NULL, NULL, &mm_var );
-
- if ( mm_var->namedstyle )
- {
- FT_Var_Named_Style* named_style;
- FT_String* style_name;
-
-
- /* in `face_index', the instance index starts with value 1 */
- named_style = mm_var->namedstyle + instance_index - 1;
- error = sfnt->get_name( face,
- (FT_UShort)named_style->strid,
- &style_name );
- if ( error )
- goto Exit;
-
- /* set style name; if already set, replace it */
- if ( face->root.style_name )
- FT_FREE( face->root.style_name );
- face->root.style_name = style_name;
-
- /* finally, select the named instance */
- error = mm->set_var_design( cffface,
- mm_var->num_axis,
- named_style->coords );
- if ( error )
- goto Exit;
- }
+ if ( var )
+ var->metrics_adjust( cffface );
}
}
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
@@ -876,7 +868,8 @@
cffface->height = (FT_Short)( ( cffface->units_per_EM * 12 ) / 10 );
if ( cffface->height < cffface->ascender - cffface->descender )
- cffface->height = (FT_Short)( cffface->ascender - cffface->descender );
+ cffface->height = (FT_Short)( cffface->ascender -
+ cffface->descender );
cffface->underline_position =
(FT_Short)( dict->underline_position >> 16 );
@@ -884,27 +877,32 @@
(FT_Short)( dict->underline_thickness >> 16 );
/* retrieve font family & style name */
- cffface->family_name = cff_index_get_name(
- cff,
- (FT_UInt)( face_index & 0xFFFF ) );
+ if ( dict->family_name )
+ {
+ char* family_name;
+
+
+ family_name = cff_index_get_sid_string( cff, dict->family_name );
+ if ( family_name )
+ cffface->family_name = cff_strcpy( memory, family_name );
+ }
+
+ if ( !cffface->family_name )
+ {
+ cffface->family_name = cff_index_get_name(
+ cff,
+ (FT_UInt)( face_index & 0xFFFF ) );
+ if ( cffface->family_name )
+ remove_subset_prefix( cffface->family_name );
+ }
+
if ( cffface->family_name )
{
char* full = cff_index_get_sid_string( cff,
dict->full_name );
char* fullp = full;
char* family = cffface->family_name;
- char* family_name = NULL;
-
- remove_subset_prefix( cffface->family_name );
-
- if ( dict->family_name )
- {
- family_name = cff_index_get_sid_string( cff,
- dict->family_name );
- if ( family_name )
- family = family_name;
- }
/* We try to extract the style name from the full name. */
/* We need to ignore spaces and dashes during the search. */
@@ -1159,16 +1157,16 @@
FT_LOCAL_DEF( FT_Error )
cff_driver_init( FT_Module module ) /* CFF_Driver */
{
- CFF_Driver driver = (CFF_Driver)module;
+ PS_Driver driver = (PS_Driver)module;
FT_UInt32 seed;
/* set default property values, cf. `ftcffdrv.h' */
#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
- driver->hinting_engine = FT_CFF_HINTING_FREETYPE;
+ driver->hinting_engine = FT_HINTING_FREETYPE;
#else
- driver->hinting_engine = FT_CFF_HINTING_ADOBE;
+ driver->hinting_engine = FT_HINTING_ADOBE;
#endif
driver->no_stem_darkening = TRUE;
diff --git a/thirdparty/freetype/src/cff/cffobjs.h b/thirdparty/freetype/src/cff/cffobjs.h
index 1dba694c53..616a25b3b5 100644
--- a/thirdparty/freetype/src/cff/cffobjs.h
+++ b/thirdparty/freetype/src/cff/cffobjs.h
@@ -4,7 +4,7 @@
/* */
/* OpenType objects manager (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -21,111 +21,11 @@
#include <ft2build.h>
-#include FT_INTERNAL_OBJECTS_H
-#include "cfftypes.h"
-#include FT_INTERNAL_TRUETYPE_TYPES_H
-#include FT_SERVICE_POSTSCRIPT_CMAPS_H
-#include FT_INTERNAL_POSTSCRIPT_HINTS_H
FT_BEGIN_HEADER
- /*************************************************************************/
- /* */
- /* <Type> */
- /* CFF_Driver */
- /* */
- /* <Description> */
- /* A handle to an OpenType driver object. */
- /* */
- typedef struct CFF_DriverRec_* CFF_Driver;
-
- typedef TT_Face CFF_Face;
-
-
- /*************************************************************************/
- /* */
- /* <Type> */
- /* CFF_Size */
- /* */
- /* <Description> */
- /* A handle to an OpenType size object. */
- /* */
- typedef struct CFF_SizeRec_
- {
- FT_SizeRec root;
- FT_ULong strike_index; /* 0xFFFFFFFF to indicate invalid */
-
- } CFF_SizeRec, *CFF_Size;
-
-
- /*************************************************************************/
- /* */
- /* <Type> */
- /* CFF_GlyphSlot */
- /* */
- /* <Description> */
- /* A handle to an OpenType glyph slot object. */
- /* */
- typedef struct CFF_GlyphSlotRec_
- {
- FT_GlyphSlotRec root;
-
- FT_Bool hint;
- FT_Bool scaled;
-
- FT_Fixed x_scale;
- FT_Fixed y_scale;
-
- } CFF_GlyphSlotRec, *CFF_GlyphSlot;
-
-
- /*************************************************************************/
- /* */
- /* <Type> */
- /* CFF_Internal */
- /* */
- /* <Description> */
- /* The interface to the `internal' field of `FT_Size'. */
- /* */
- typedef struct CFF_InternalRec_
- {
- PSH_Globals topfont;
- PSH_Globals subfonts[CFF_MAX_CID_FONTS];
-
- } CFF_InternalRec, *CFF_Internal;
-
-
- /*************************************************************************/
- /* */
- /* Subglyph transformation record. */
- /* */
- typedef struct CFF_Transform_
- {
- FT_Fixed xx, xy; /* transformation matrix coefficients */
- FT_Fixed yx, yy;
- FT_F26Dot6 ox, oy; /* offsets */
-
- } CFF_Transform;
-
-
- /***********************************************************************/
- /* */
- /* CFF driver class. */
- /* */
- typedef struct CFF_DriverRec_
- {
- FT_DriverRec root;
-
- FT_UInt hinting_engine;
- FT_Bool no_stem_darkening;
- FT_Int darken_params[8];
- FT_Int32 random_seed;
-
- } CFF_DriverRec;
-
-
FT_LOCAL( FT_Error )
cff_size_init( FT_Size size ); /* CFF_Size */
@@ -171,10 +71,10 @@ FT_BEGIN_HEADER
/* Driver functions */
/* */
FT_LOCAL( FT_Error )
- cff_driver_init( FT_Module module ); /* CFF_Driver */
+ cff_driver_init( FT_Module module ); /* PS_Driver */
FT_LOCAL( void )
- cff_driver_done( FT_Module module ); /* CFF_Driver */
+ cff_driver_done( FT_Module module ); /* PS_Driver */
FT_END_HEADER
diff --git a/thirdparty/freetype/src/cff/cffparse.c b/thirdparty/freetype/src/cff/cffparse.c
index 9d7bf6d22c..b9611cf548 100644
--- a/thirdparty/freetype/src/cff/cffparse.c
+++ b/thirdparty/freetype/src/cff/cffparse.c
@@ -4,7 +4,7 @@
/* */
/* CFF token stream parser (body) */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -21,10 +21,10 @@
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_CALC_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
#include "cfferrs.h"
#include "cffpic.h"
-#include "cffgload.h"
#include "cffload.h"
@@ -949,7 +949,9 @@
goto Exit;
}
- FT_TRACE4(( " %d values blended\n", numBlends ));
+ FT_TRACE4(( " %d value%s blended\n",
+ numBlends,
+ numBlends == 1 ? "" : "s" ));
error = cff_blend_doBlend( subFont, parser, numBlends );
@@ -1299,9 +1301,14 @@
FT_Byte* start,
FT_Byte* limit )
{
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ PSAux_Service psaux;
+#endif
+
FT_Byte* p = start;
FT_Error error = FT_Err_Ok;
FT_Library library = parser->library;
+
FT_UNUSED( library );
@@ -1388,10 +1395,16 @@
cff_rec.top_font.font_dict.num_axes = parser->num_axes;
decoder.cff = &cff_rec;
- error = cff_decoder_parse_charstrings( &decoder,
- charstring_base,
- charstring_len,
- 1 );
+ psaux = (PSAux_Service)FT_Get_Module_Interface( library, "psaux" );
+ if ( !psaux )
+ {
+ FT_ERROR(( "cff_parser_run: cannot access `psaux' module\n" ));
+ error = FT_THROW( Missing_Module );
+ goto Exit;
+ }
+
+ error = psaux->cff_decoder_funcs->parse_charstrings_old(
+ &decoder, charstring_base, charstring_len, 1 );
/* Now copy the stack data in the temporary decoder object, */
/* converting it back to charstring number representations */
diff --git a/thirdparty/freetype/src/cff/cffparse.h b/thirdparty/freetype/src/cff/cffparse.h
index 83d1bba45f..8a8caeca44 100644
--- a/thirdparty/freetype/src/cff/cffparse.h
+++ b/thirdparty/freetype/src/cff/cffparse.h
@@ -4,7 +4,7 @@
/* */
/* CFF token stream parser (specification) */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -21,7 +21,7 @@
#include <ft2build.h>
-#include "cfftypes.h"
+#include FT_INTERNAL_CFF_TYPES_H
#include FT_INTERNAL_OBJECTS_H
diff --git a/thirdparty/freetype/src/cff/cffpic.c b/thirdparty/freetype/src/cff/cffpic.c
index 4e9ba12b3f..08b74c7cf2 100644
--- a/thirdparty/freetype/src/cff/cffpic.c
+++ b/thirdparty/freetype/src/cff/cffpic.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for cff module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cff/cffpic.h b/thirdparty/freetype/src/cff/cffpic.h
index 5db39cd627..8ba4203a8d 100644
--- a/thirdparty/freetype/src/cff/cffpic.h
+++ b/thirdparty/freetype/src/cff/cffpic.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for cff module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -22,7 +22,6 @@
#include FT_INTERNAL_PIC_H
-
#ifndef FT_CONFIG_OPTION_PIC
#define CFF_SERVICE_PS_INFO_GET cff_service_ps_info
@@ -34,6 +33,7 @@
#define CFF_SERVICES_GET cff_services
#define CFF_SERVICE_MULTI_MASTERS_GET cff_service_multi_masters
#define CFF_SERVICE_METRICS_VAR_GET cff_service_metrics_variations
+#define CFF_SERVICE_CFF_LOAD_GET cff_service_cff_load
#define CFF_CMAP_ENCODING_CLASS_REC_GET cff_cmap_encoding_class_rec
#define CFF_CMAP_UNICODE_CLASS_REC_GET cff_cmap_unicode_class_rec
#define CFF_FIELD_HANDLERS_GET cff_field_handlers
@@ -65,6 +65,7 @@ FT_BEGIN_HEADER
FT_Service_PropertiesRec cff_service_properties;
FT_Service_MultiMastersRec cff_service_multi_masters;
FT_Service_MetricsVariationsRec cff_service_metrics_variations;
+ FT_Service_CFFLoadRec cff_service_cff_load;
FT_CMap_ClassRec cff_cmap_encoding_class_rec;
FT_CMap_ClassRec cff_cmap_unicode_class_rec;
@@ -92,6 +93,8 @@ FT_BEGIN_HEADER
( GET_PIC( library )->cff_service_multi_masters )
#define CFF_SERVICE_METRICS_VAR_GET \
( GET_PIC( library )->cff_service_metrics_variations )
+#define CFF_SERVICE_CFF_LOAD_GET \
+ ( GET_PIC( library )->cff_service_cff_load )
#define CFF_CMAP_ENCODING_CLASS_REC_GET \
( GET_PIC( library )->cff_cmap_encoding_class_rec )
#define CFF_CMAP_UNICODE_CLASS_REC_GET \
diff --git a/thirdparty/freetype/src/cff/cfftoken.h b/thirdparty/freetype/src/cff/cfftoken.h
index 3222e933f1..fec1ca20bd 100644
--- a/thirdparty/freetype/src/cff/cfftoken.h
+++ b/thirdparty/freetype/src/cff/cfftoken.h
@@ -4,7 +4,7 @@
/* */
/* CFF token definitions (specification only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cff/module.mk b/thirdparty/freetype/src/cff/module.mk
index 2975aeed8c..8013d5dcab 100644
--- a/thirdparty/freetype/src/cff/module.mk
+++ b/thirdparty/freetype/src/cff/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/cff/rules.mk b/thirdparty/freetype/src/cff/rules.mk
index 86840bfe3c..bce672927e 100644
--- a/thirdparty/freetype/src/cff/rules.mk
+++ b/thirdparty/freetype/src/cff/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
@@ -32,27 +32,14 @@ CFF_DRV_SRC := $(CFF_DIR)/cffcmap.c \
$(CFF_DIR)/cffload.c \
$(CFF_DIR)/cffobjs.c \
$(CFF_DIR)/cffparse.c \
- $(CFF_DIR)/cffpic.c \
- $(CFF_DIR)/cf2arrst.c \
- $(CFF_DIR)/cf2blues.c \
- $(CFF_DIR)/cf2error.c \
- $(CFF_DIR)/cf2font.c \
- $(CFF_DIR)/cf2ft.c \
- $(CFF_DIR)/cf2hints.c \
- $(CFF_DIR)/cf2intrp.c \
- $(CFF_DIR)/cf2read.c \
- $(CFF_DIR)/cf2stack.c
+ $(CFF_DIR)/cffpic.c
# CFF driver headers
#
CFF_DRV_H := $(CFF_DRV_SRC:%.c=%.h) \
$(CFF_DIR)/cfferrs.h \
- $(CFF_DIR)/cfftoken.h \
- $(CFF_DIR)/cfftypes.h \
- $(CFF_DIR)/cf2fixed.h \
- $(CFF_DIR)/cf2glue.h \
- $(CFF_DIR)/cf2types.h
+ $(CFF_DIR)/cfftoken.h
# CFF driver object(s)
diff --git a/thirdparty/freetype/src/cid/ciderrs.h b/thirdparty/freetype/src/cid/ciderrs.h
index 709dc8cd1e..a5a86e3fc6 100644
--- a/thirdparty/freetype/src/cid/ciderrs.h
+++ b/thirdparty/freetype/src/cid/ciderrs.h
@@ -4,7 +4,7 @@
/* */
/* CID error codes (specification only). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cid/cidgload.c b/thirdparty/freetype/src/cid/cidgload.c
index b96c33334d..d14f9a2cc9 100644
--- a/thirdparty/freetype/src/cid/cidgload.c
+++ b/thirdparty/freetype/src/cid/cidgload.c
@@ -4,7 +4,7 @@
/* */
/* CID-keyed Type1 Glyph Loader (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -24,6 +24,10 @@
#include FT_OUTLINE_H
#include FT_INTERNAL_CALC_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_CFF_TYPES_H
+#include FT_DRIVER_H
+
#include "ciderrs.h"
@@ -52,9 +56,11 @@
FT_ULong glyph_length = 0;
PSAux_Service psaux = (PSAux_Service)face->psaux;
+ FT_Bool force_scaling = FALSE;
+
#ifdef FT_CONFIG_OPTION_INCREMENTAL
- FT_Incremental_InterfaceRec *inc =
- face->root.internal->incremental_interface;
+ FT_Incremental_InterfaceRec *inc =
+ face->root.internal->incremental_interface;
#endif
@@ -169,9 +175,57 @@
if ( decoder->lenIV >= 0 )
psaux->t1_decrypt( charstring, glyph_length, 4330 );
- error = decoder->funcs.parse_charstrings(
- decoder, charstring + cs_offset,
- glyph_length - cs_offset );
+ /* choose which renderer to use */
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ if ( ( (PS_Driver)FT_FACE_DRIVER( face ) )->hinting_engine ==
+ FT_HINTING_FREETYPE ||
+ decoder->builder.metrics_only )
+ error = psaux->t1_decoder_funcs->parse_charstrings_old(
+ decoder,
+ charstring + cs_offset,
+ glyph_length - cs_offset );
+#else
+ if ( decoder->builder.metrics_only )
+ error = psaux->t1_decoder_funcs->parse_metrics(
+ decoder,
+ charstring + cs_offset,
+ glyph_length - cs_offset );
+#endif
+ else
+ {
+ PS_Decoder psdecoder;
+ CFF_SubFontRec subfont;
+
+
+ psaux->ps_decoder_init( &psdecoder, decoder, TRUE );
+
+ psaux->t1_make_subfont( FT_FACE( face ),
+ &dict->private_dict,
+ &subfont );
+ psdecoder.current_subfont = &subfont;
+
+ error = psaux->t1_decoder_funcs->parse_charstrings(
+ &psdecoder,
+ charstring + cs_offset,
+ glyph_length - cs_offset );
+
+ /* Adobe's engine uses 16.16 numbers everywhere; */
+ /* as a consequence, glyphs larger than 2000ppem get rejected */
+ if ( FT_ERR_EQ( error, Glyph_Too_Big ) )
+ {
+ /* this time, we retry unhinted and scale up the glyph later on */
+ /* (the engine uses and sets the hardcoded value 0x10000 / 64 = */
+ /* 0x400 for both `x_scale' and `y_scale' in this case) */
+ ((CID_GlyphSlot)decoder->builder.glyph)->hint = FALSE;
+
+ force_scaling = TRUE;
+
+ error = psaux->t1_decoder_funcs->parse_charstrings(
+ &psdecoder,
+ charstring + cs_offset,
+ glyph_length - cs_offset );
+ }
+ }
}
#ifdef FT_CONFIG_OPTION_INCREMENTAL
@@ -200,6 +254,8 @@
Exit:
FT_FREE( charstring );
+ ((CID_GlyphSlot)decoder->builder.glyph)->scaled = force_scaling;
+
return error;
}
@@ -288,10 +344,12 @@
T1_DecoderRec decoder;
CID_Face face = (CID_Face)cidglyph->face;
FT_Bool hinting;
+ FT_Bool scaled;
PSAux_Service psaux = (PSAux_Service)face->psaux;
FT_Matrix font_matrix;
FT_Vector font_offset;
+ FT_Bool must_finish_decoder = FALSE;
if ( glyph_index >= (FT_UInt)face->root.num_glyphs )
@@ -311,7 +369,10 @@
hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 &&
( load_flags & FT_LOAD_NO_HINTING ) == 0 );
+ scaled = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 );
+ glyph->hint = hinting;
+ glyph->scaled = scaled;
cidglyph->format = FT_GLYPH_FORMAT_OUTLINE;
error = psaux->t1_decoder_funcs->init( &decoder,
@@ -329,6 +390,8 @@
/* TODO: initialize decoder.len_buildchar and decoder.buildchar */
/* if we ever support CID-keyed multiple master fonts */
+ must_finish_decoder = TRUE;
+
/* set up the decoder */
decoder.builder.no_recurse = FT_BOOL(
( ( load_flags & FT_LOAD_NO_RECURSE ) != 0 ) );
@@ -337,12 +400,18 @@
if ( error )
goto Exit;
+ /* copy flags back for forced scaling */
+ hinting = glyph->hint;
+ scaled = glyph->scaled;
+
font_matrix = decoder.font_matrix;
font_offset = decoder.font_offset;
/* save new glyph tables */
psaux->t1_decoder_funcs->done( &decoder );
+ must_finish_decoder = FALSE;
+
/* now set the metrics -- this is rather simple, as */
/* the left side bearing is the xMin, and the top side */
/* bearing the yMax */
@@ -410,7 +479,7 @@
metrics->vertAdvance += font_offset.y;
}
- if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 )
+ if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 || scaled )
{
/* scale the outline and the metrics */
FT_Int n;
@@ -451,6 +520,10 @@
}
Exit:
+
+ if ( must_finish_decoder )
+ psaux->t1_decoder_funcs->done( &decoder );
+
return error;
}
diff --git a/thirdparty/freetype/src/cid/cidgload.h b/thirdparty/freetype/src/cid/cidgload.h
index 7f816b5bc7..4811852ae4 100644
--- a/thirdparty/freetype/src/cid/cidgload.h
+++ b/thirdparty/freetype/src/cid/cidgload.h
@@ -4,7 +4,7 @@
/* */
/* OpenType Glyph Loader (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cid/cidload.c b/thirdparty/freetype/src/cid/cidload.c
index ff0722110d..27cd09b3c3 100644
--- a/thirdparty/freetype/src/cid/cidload.c
+++ b/thirdparty/freetype/src/cid/cidload.c
@@ -4,7 +4,7 @@
/* */
/* CID-keyed Type1 font loader (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cid/cidload.h b/thirdparty/freetype/src/cid/cidload.h
index 45a0e6df25..3f8bd08620 100644
--- a/thirdparty/freetype/src/cid/cidload.h
+++ b/thirdparty/freetype/src/cid/cidload.h
@@ -4,7 +4,7 @@
/* */
/* CID-keyed Type1 font loader (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cid/cidobjs.c b/thirdparty/freetype/src/cid/cidobjs.c
index ceda8ff97f..77afe1c875 100644
--- a/thirdparty/freetype/src/cid/cidobjs.c
+++ b/thirdparty/freetype/src/cid/cidobjs.c
@@ -4,7 +4,7 @@
/* */
/* CID objects manager (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -26,6 +26,7 @@
#include FT_SERVICE_POSTSCRIPT_CMAPS_H
#include FT_INTERNAL_POSTSCRIPT_AUX_H
#include FT_INTERNAL_POSTSCRIPT_HINTS_H
+#include FT_DRIVER_H
#include "ciderrs.h"
@@ -463,9 +464,42 @@
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
- cid_driver_init( FT_Module driver )
+ cid_driver_init( FT_Module module )
{
- FT_UNUSED( driver );
+ PS_Driver driver = (PS_Driver)module;
+
+ FT_UInt32 seed;
+
+
+ /* set default property values, cf. `ftt1drv.h' */
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ driver->hinting_engine = FT_HINTING_FREETYPE;
+#else
+ driver->hinting_engine = FT_HINTING_ADOBE;
+#endif
+
+ driver->no_stem_darkening = TRUE;
+
+ driver->darken_params[0] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1;
+ driver->darken_params[1] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1;
+ driver->darken_params[2] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2;
+ driver->darken_params[3] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2;
+ driver->darken_params[4] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3;
+ driver->darken_params[5] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3;
+ driver->darken_params[6] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4;
+ driver->darken_params[7] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4;
+
+ /* compute random seed from some memory addresses */
+ seed = (FT_UInt32)( (FT_Offset)(char*)&seed ^
+ (FT_Offset)(char*)&module ^
+ (FT_Offset)(char*)module->memory );
+ seed = seed ^ ( seed >> 10 ) ^ ( seed >> 20 );
+
+ driver->random_seed = (FT_Int32)seed;
+ if ( driver->random_seed < 0 )
+ driver->random_seed = -driver->random_seed;
+ else if ( driver->random_seed == 0 )
+ driver->random_seed = 123456789;
return FT_Err_Ok;
}
diff --git a/thirdparty/freetype/src/cid/cidobjs.h b/thirdparty/freetype/src/cid/cidobjs.h
index 8bcf886e10..0221f017dd 100644
--- a/thirdparty/freetype/src/cid/cidobjs.h
+++ b/thirdparty/freetype/src/cid/cidobjs.h
@@ -4,7 +4,7 @@
/* */
/* CID objects manager (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cid/cidparse.c b/thirdparty/freetype/src/cid/cidparse.c
index 007609bbdf..b1c7f3cb2c 100644
--- a/thirdparty/freetype/src/cid/cidparse.c
+++ b/thirdparty/freetype/src/cid/cidparse.c
@@ -4,7 +4,7 @@
/* */
/* CID-keyed Type1 parser (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cid/cidparse.h b/thirdparty/freetype/src/cid/cidparse.h
index 20bebb942b..61602f7674 100644
--- a/thirdparty/freetype/src/cid/cidparse.h
+++ b/thirdparty/freetype/src/cid/cidparse.h
@@ -4,7 +4,7 @@
/* */
/* CID-keyed Type1 parser (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cid/cidriver.c b/thirdparty/freetype/src/cid/cidriver.c
index bb611a961e..d9faf353ea 100644
--- a/thirdparty/freetype/src/cid/cidriver.c
+++ b/thirdparty/freetype/src/cid/cidriver.c
@@ -4,7 +4,7 @@
/* */
/* CID driver interface (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -20,6 +20,7 @@
#include "cidriver.h"
#include "cidgload.h"
#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_POSTSCRIPT_PROPS_H
#include "ciderrs.h"
@@ -27,6 +28,10 @@
#include FT_SERVICE_FONT_FORMAT_H
#include FT_SERVICE_POSTSCRIPT_INFO_H
#include FT_SERVICE_CID_H
+#include FT_SERVICE_PROPERTIES_H
+#include FT_DRIVER_H
+
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
/*************************************************************************/
@@ -168,6 +173,18 @@
/*
+ * PROPERTY SERVICE
+ *
+ */
+
+ FT_DEFINE_SERVICE_PROPERTIESREC(
+ cid_service_properties,
+
+ (FT_Properties_SetFunc)ps_property_set, /* set_property */
+ (FT_Properties_GetFunc)ps_property_get ) /* get_property */
+
+
+ /*
* SERVICE LIST
*
*/
@@ -178,6 +195,7 @@
{ FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &cid_service_ps_name },
{ FT_SERVICE_ID_POSTSCRIPT_INFO, &cid_service_ps_info },
{ FT_SERVICE_ID_CID, &cid_service_cid_info },
+ { FT_SERVICE_ID_PROPERTIES, &cid_service_properties },
{ NULL, NULL }
};
@@ -200,7 +218,7 @@
FT_MODULE_FONT_DRIVER |
FT_MODULE_DRIVER_SCALABLE |
FT_MODULE_DRIVER_HAS_HINTER,
- sizeof ( FT_DriverRec ),
+ sizeof ( PS_DriverRec ),
"t1cid", /* module name */
0x10000L, /* version 1.0 of driver */
diff --git a/thirdparty/freetype/src/cid/cidriver.h b/thirdparty/freetype/src/cid/cidriver.h
index 76640c57ba..59d9ded901 100644
--- a/thirdparty/freetype/src/cid/cidriver.h
+++ b/thirdparty/freetype/src/cid/cidriver.h
@@ -4,7 +4,7 @@
/* */
/* High-level CID driver interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cid/cidtoken.h b/thirdparty/freetype/src/cid/cidtoken.h
index 653cc5586e..b0e2dac6aa 100644
--- a/thirdparty/freetype/src/cid/cidtoken.h
+++ b/thirdparty/freetype/src/cid/cidtoken.h
@@ -4,7 +4,7 @@
/* */
/* CID token definitions (specification only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/cid/module.mk b/thirdparty/freetype/src/cid/module.mk
index b30b8679b9..9010e339a4 100644
--- a/thirdparty/freetype/src/cid/module.mk
+++ b/thirdparty/freetype/src/cid/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/cid/rules.mk b/thirdparty/freetype/src/cid/rules.mk
index fcddd92eca..94333bda06 100644
--- a/thirdparty/freetype/src/cid/rules.mk
+++ b/thirdparty/freetype/src/cid/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/cid/type1cid.c b/thirdparty/freetype/src/cid/type1cid.c
index 93e6f810db..61770e3f1e 100644
--- a/thirdparty/freetype/src/cid/type1cid.c
+++ b/thirdparty/freetype/src/cid/type1cid.c
@@ -4,7 +4,7 @@
/* */
/* FreeType OpenType driver component (body only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/gxvalid/README b/thirdparty/freetype/src/gxvalid/README
index 200f66cb12..af8128e0e7 100644
--- a/thirdparty/freetype/src/gxvalid/README
+++ b/thirdparty/freetype/src/gxvalid/README
@@ -287,11 +287,11 @@ gxvalid: TrueType GX validator
4-5. invalid feature number (117/183)
-------------------------------------
- The GX/AAT extension can include 255 different layout features, but
- popular layout features are predefined (see
- http://developer.apple.com/fonts/Registry/index.html). Some fonts
- include feature numbers which are incompatible with the predefined
- feature registry.
+ The GX/AAT extension can include 255 different layout features,
+ but popular layout features are predefined (see
+ https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html).
+ Some fonts include feature numbers which are incompatible with the
+ predefined feature registry.
In our survey, there are 140 fonts including `feat' table.
@@ -518,7 +518,7 @@ gxvalid: TrueType GX validator
------------------------------------------------------------------------
-Copyright 2004-2017 by
+Copyright 2004-2018 by
suzuki toshiya, Masatake YAMATO, Red hat K.K.,
David Turner, Robert Wilhelm, and Werner Lemberg.
diff --git a/thirdparty/freetype/src/gxvalid/gxvalid.c b/thirdparty/freetype/src/gxvalid/gxvalid.c
index da485141d3..d0577a247e 100644
--- a/thirdparty/freetype/src/gxvalid/gxvalid.c
+++ b/thirdparty/freetype/src/gxvalid/gxvalid.c
@@ -4,7 +4,7 @@
/* */
/* FreeType validator for TrueTypeGX/AAT tables (body only). */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvalid.h b/thirdparty/freetype/src/gxvalid/gxvalid.h
index 78116ef85a..19f0379982 100644
--- a/thirdparty/freetype/src/gxvalid/gxvalid.h
+++ b/thirdparty/freetype/src/gxvalid/gxvalid.h
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT table validation (specification only). */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvbsln.c b/thirdparty/freetype/src/gxvalid/gxvbsln.c
index 81dff7304d..c367d38483 100644
--- a/thirdparty/freetype/src/gxvalid/gxvbsln.c
+++ b/thirdparty/freetype/src/gxvalid/gxvbsln.c
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT bsln table validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvcommn.c b/thirdparty/freetype/src/gxvalid/gxvcommn.c
index db0a91ea74..b96601108b 100644
--- a/thirdparty/freetype/src/gxvalid/gxvcommn.c
+++ b/thirdparty/freetype/src/gxvalid/gxvcommn.c
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT common tables validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvcommn.h b/thirdparty/freetype/src/gxvalid/gxvcommn.h
index 10b1c800f0..8e4ff9cafc 100644
--- a/thirdparty/freetype/src/gxvalid/gxvcommn.h
+++ b/thirdparty/freetype/src/gxvalid/gxvcommn.h
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT common tables validation (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxverror.h b/thirdparty/freetype/src/gxvalid/gxverror.h
index 80a2b8a262..d1151258a9 100644
--- a/thirdparty/freetype/src/gxvalid/gxverror.h
+++ b/thirdparty/freetype/src/gxvalid/gxverror.h
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT validation module error codes (specification only). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvfeat.c b/thirdparty/freetype/src/gxvalid/gxvfeat.c
index 2e3ec6f9be..2c805d1d11 100644
--- a/thirdparty/freetype/src/gxvalid/gxvfeat.c
+++ b/thirdparty/freetype/src/gxvalid/gxvfeat.c
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT feat table validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvfeat.h b/thirdparty/freetype/src/gxvalid/gxvfeat.h
index 8c0e847eb7..2d943806c1 100644
--- a/thirdparty/freetype/src/gxvalid/gxvfeat.h
+++ b/thirdparty/freetype/src/gxvalid/gxvfeat.h
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT feat table validation (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvfgen.c b/thirdparty/freetype/src/gxvalid/gxvfgen.c
index 8cc08cd9c3..840c0f3524 100644
--- a/thirdparty/freetype/src/gxvalid/gxvfgen.c
+++ b/thirdparty/freetype/src/gxvalid/gxvfgen.c
@@ -5,7 +5,7 @@
/* Generate feature registry data for gxv `feat' validator. */
/* This program is derived from gxfeatreg.c in gxlayout. */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* Masatake YAMATO and Redhat K.K. */
/* */
/* This file may only be used, */
@@ -21,7 +21,7 @@
/* gxfeatreg.c */
/* */
/* Database of font features pre-defined by Apple Computer, Inc. */
-/* http://developer.apple.com/fonts/Registry/ */
+/* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html */
/* (body). */
/* */
/* Copyright 2003 by */
diff --git a/thirdparty/freetype/src/gxvalid/gxvjust.c b/thirdparty/freetype/src/gxvalid/gxvjust.c
index c8cfd83eac..00c4293195 100644
--- a/thirdparty/freetype/src/gxvalid/gxvjust.c
+++ b/thirdparty/freetype/src/gxvalid/gxvjust.c
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT just table validation (body). */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvkern.c b/thirdparty/freetype/src/gxvalid/gxvkern.c
index 9f9037387d..9c0efd7a4f 100644
--- a/thirdparty/freetype/src/gxvalid/gxvkern.c
+++ b/thirdparty/freetype/src/gxvalid/gxvkern.c
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT kern table validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvlcar.c b/thirdparty/freetype/src/gxvalid/gxvlcar.c
index 775d5229f2..0f261a9ace 100644
--- a/thirdparty/freetype/src/gxvalid/gxvlcar.c
+++ b/thirdparty/freetype/src/gxvalid/gxvlcar.c
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT lcar table validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmod.c b/thirdparty/freetype/src/gxvalid/gxvmod.c
index 84e9275baf..1a3c862927 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmod.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmod.c
@@ -4,7 +4,7 @@
/* */
/* FreeType's TrueTypeGX/AAT validation module implementation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmod.h b/thirdparty/freetype/src/gxvalid/gxvmod.h
index df25e60cb2..745c62e105 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmod.h
+++ b/thirdparty/freetype/src/gxvalid/gxvmod.h
@@ -5,7 +5,7 @@
/* FreeType's TrueTypeGX/AAT validation module implementation */
/* (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmort.c b/thirdparty/freetype/src/gxvalid/gxvmort.c
index 184a6317c0..b361cb2b9d 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmort.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmort.c
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT mort table validation (body). */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmort.h b/thirdparty/freetype/src/gxvalid/gxvmort.h
index d0543adcc5..d8030645e9 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmort.h
+++ b/thirdparty/freetype/src/gxvalid/gxvmort.h
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT common definition for mort table (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmort0.c b/thirdparty/freetype/src/gxvalid/gxvmort0.c
index a75fad7768..95cf53d5eb 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmort0.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmort0.c
@@ -5,7 +5,7 @@
/* TrueTypeGX/AAT mort table validation */
/* body for type0 (Indic Script Rearrangement) subtable. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmort1.c b/thirdparty/freetype/src/gxvalid/gxvmort1.c
index 361ef2262d..a7683a17b0 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmort1.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmort1.c
@@ -5,7 +5,7 @@
/* TrueTypeGX/AAT mort table validation */
/* body for type1 (Contextual Substitution) subtable. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmort2.c b/thirdparty/freetype/src/gxvalid/gxvmort2.c
index c17e51e323..c23c2775a1 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmort2.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmort2.c
@@ -5,7 +5,7 @@
/* TrueTypeGX/AAT mort table validation */
/* body for type2 (Ligature Substitution) subtable. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmort4.c b/thirdparty/freetype/src/gxvalid/gxvmort4.c
index 041bab369f..9d21a5fc29 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmort4.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmort4.c
@@ -5,7 +5,7 @@
/* TrueTypeGX/AAT mort table validation */
/* body for type4 (Non-Contextual Glyph Substitution) subtable. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmort5.c b/thirdparty/freetype/src/gxvalid/gxvmort5.c
index 4751eceaa0..42cb428aa8 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmort5.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmort5.c
@@ -5,7 +5,7 @@
/* TrueTypeGX/AAT mort table validation */
/* body for type5 (Contextual Glyph Insertion) subtable. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmorx.c b/thirdparty/freetype/src/gxvalid/gxvmorx.c
index 2bb4f3b8b1..9fd6e6b971 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmorx.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmorx.c
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT morx table validation (body). */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmorx.h b/thirdparty/freetype/src/gxvalid/gxvmorx.h
index 20cec58672..6d9925e92b 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmorx.h
+++ b/thirdparty/freetype/src/gxvalid/gxvmorx.h
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT common definition for morx table (specification). */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmorx0.c b/thirdparty/freetype/src/gxvalid/gxvmorx0.c
index e0a0a92438..302261b7fa 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmorx0.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmorx0.c
@@ -5,7 +5,7 @@
/* TrueTypeGX/AAT morx table validation */
/* body for type0 (Indic Script Rearrangement) subtable. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmorx1.c b/thirdparty/freetype/src/gxvalid/gxvmorx1.c
index 9afebdbfa8..890ca74b1d 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmorx1.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmorx1.c
@@ -5,7 +5,7 @@
/* TrueTypeGX/AAT morx table validation */
/* body for type1 (Contextual Substitution) subtable. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmorx2.c b/thirdparty/freetype/src/gxvalid/gxvmorx2.c
index 3a60cf68a4..3135031d45 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmorx2.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmorx2.c
@@ -5,7 +5,7 @@
/* TrueTypeGX/AAT morx table validation */
/* body for type2 (Ligature Substitution) subtable. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmorx4.c b/thirdparty/freetype/src/gxvalid/gxvmorx4.c
index 29555685af..1e2397b0c5 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmorx4.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmorx4.c
@@ -5,7 +5,7 @@
/* TrueTypeGX/AAT morx table validation */
/* body for "morx" type4 (Non-Contextual Glyph Substitution) subtable. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvmorx5.c b/thirdparty/freetype/src/gxvalid/gxvmorx5.c
index 05c11417ef..db4f9290c8 100644
--- a/thirdparty/freetype/src/gxvalid/gxvmorx5.c
+++ b/thirdparty/freetype/src/gxvalid/gxvmorx5.c
@@ -5,7 +5,7 @@
/* TrueTypeGX/AAT morx table validation */
/* body for type5 (Contextual Glyph Insertion) subtable. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvopbd.c b/thirdparty/freetype/src/gxvalid/gxvopbd.c
index 11580d8b2a..e2c167ea59 100644
--- a/thirdparty/freetype/src/gxvalid/gxvopbd.c
+++ b/thirdparty/freetype/src/gxvalid/gxvopbd.c
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT opbd table validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvprop.c b/thirdparty/freetype/src/gxvalid/gxvprop.c
index 7d398b7e66..a67b6bdd00 100644
--- a/thirdparty/freetype/src/gxvalid/gxvprop.c
+++ b/thirdparty/freetype/src/gxvalid/gxvprop.c
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT prop table validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/gxvtrak.c b/thirdparty/freetype/src/gxvalid/gxvtrak.c
index dd49825565..d501b5014b 100644
--- a/thirdparty/freetype/src/gxvalid/gxvtrak.c
+++ b/thirdparty/freetype/src/gxvalid/gxvtrak.c
@@ -4,7 +4,7 @@
/* */
/* TrueTypeGX/AAT trak table validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
diff --git a/thirdparty/freetype/src/gxvalid/module.mk b/thirdparty/freetype/src/gxvalid/module.mk
index 7f87e10812..b64879dcee 100644
--- a/thirdparty/freetype/src/gxvalid/module.mk
+++ b/thirdparty/freetype/src/gxvalid/module.mk
@@ -2,7 +2,7 @@
# FreeType 2 gxvalid module definition
#
-# Copyright 2004-2017 by
+# Copyright 2004-2018 by
# suzuki toshiya, Masatake YAMATO, Red Hat K.K.,
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
diff --git a/thirdparty/freetype/src/gxvalid/rules.mk b/thirdparty/freetype/src/gxvalid/rules.mk
index 10ec08c5b0..3a17c030a6 100644
--- a/thirdparty/freetype/src/gxvalid/rules.mk
+++ b/thirdparty/freetype/src/gxvalid/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2004-2017 by
+# Copyright 2004-2018 by
# suzuki toshiya, Masatake YAMATO, Red Hat K.K.,
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
diff --git a/thirdparty/freetype/src/gzip/ftgzip.c b/thirdparty/freetype/src/gzip/ftgzip.c
index c487786d99..f8011c2dd8 100644
--- a/thirdparty/freetype/src/gzip/ftgzip.c
+++ b/thirdparty/freetype/src/gzip/ftgzip.c
@@ -8,7 +8,7 @@
/* parse compressed PCF fonts, as found with many X11 server */
/* distributions. */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/gzip/rules.mk b/thirdparty/freetype/src/gzip/rules.mk
index bc7d5fa631..1a2e48bebd 100644
--- a/thirdparty/freetype/src/gzip/rules.mk
+++ b/thirdparty/freetype/src/gzip/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2002-2017 by
+# Copyright 2002-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/lzw/ftlzw.c b/thirdparty/freetype/src/lzw/ftlzw.c
index 941f6cef4c..cb46f93c68 100644
--- a/thirdparty/freetype/src/lzw/ftlzw.c
+++ b/thirdparty/freetype/src/lzw/ftlzw.c
@@ -8,7 +8,7 @@
/* be used to parse compressed PCF fonts, as found with many X11 server */
/* distributions. */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* Albert Chin-A-Young. */
/* */
/* based on code in `src/gzip/ftgzip.c' */
diff --git a/thirdparty/freetype/src/lzw/ftzopen.c b/thirdparty/freetype/src/lzw/ftzopen.c
index 486c546c14..2b868ba9f2 100644
--- a/thirdparty/freetype/src/lzw/ftzopen.c
+++ b/thirdparty/freetype/src/lzw/ftzopen.c
@@ -8,7 +8,7 @@
/* be used to parse compressed PCF fonts, as found with many X11 server */
/* distributions. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* David Turner. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/lzw/ftzopen.h b/thirdparty/freetype/src/lzw/ftzopen.h
index a108862c0a..4fd267eb90 100644
--- a/thirdparty/freetype/src/lzw/ftzopen.h
+++ b/thirdparty/freetype/src/lzw/ftzopen.h
@@ -8,7 +8,7 @@
/* be used to parse compressed PCF fonts, as found with many X11 server */
/* distributions. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* David Turner. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/lzw/rules.mk b/thirdparty/freetype/src/lzw/rules.mk
index e7bf68a065..18933c41c2 100644
--- a/thirdparty/freetype/src/lzw/rules.mk
+++ b/thirdparty/freetype/src/lzw/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2004-2017 by
+# Copyright 2004-2018 by
# Albert Chin-A-Young.
#
# based on `src/lzw/rules.mk'
diff --git a/thirdparty/freetype/src/otvalid/module.mk b/thirdparty/freetype/src/otvalid/module.mk
index 5ee1265db8..34f3dab32f 100644
--- a/thirdparty/freetype/src/otvalid/module.mk
+++ b/thirdparty/freetype/src/otvalid/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2004-2017 by
+# Copyright 2004-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/otvalid/otvalid.c b/thirdparty/freetype/src/otvalid/otvalid.c
index 312751a1f4..4423ca1012 100644
--- a/thirdparty/freetype/src/otvalid/otvalid.c
+++ b/thirdparty/freetype/src/otvalid/otvalid.c
@@ -4,7 +4,7 @@
/* */
/* FreeType validator for OpenType tables (body only). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/otvalid/otvalid.h b/thirdparty/freetype/src/otvalid/otvalid.h
index f2969ccccc..d7801abae5 100644
--- a/thirdparty/freetype/src/otvalid/otvalid.h
+++ b/thirdparty/freetype/src/otvalid/otvalid.h
@@ -4,7 +4,7 @@
/* */
/* OpenType table validation (specification only). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/otvalid/otvbase.c b/thirdparty/freetype/src/otvalid/otvbase.c
index 3adad8439c..a01d45c707 100644
--- a/thirdparty/freetype/src/otvalid/otvbase.c
+++ b/thirdparty/freetype/src/otvalid/otvbase.c
@@ -4,7 +4,7 @@
/* */
/* OpenType BASE table validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -284,22 +284,41 @@
OTV_Validator otvalid = &otvalidrec;
FT_Bytes p = table;
FT_UInt table_size;
+ FT_UShort version;
OTV_OPTIONAL_TABLE( HorizAxis );
OTV_OPTIONAL_TABLE( VertAxis );
+ OTV_OPTIONAL_TABLE32( itemVarStore );
+
otvalid->root = ftvalid;
FT_TRACE3(( "validating BASE table\n" ));
OTV_INIT;
- OTV_LIMIT_CHECK( 6 );
+ OTV_LIMIT_CHECK( 4 );
- if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */
+ if ( FT_NEXT_USHORT( p ) != 1 ) /* majorVersion */
FT_INVALID_FORMAT;
- table_size = 6;
+ version = FT_NEXT_USHORT( p ); /* minorVersion */
+
+ table_size = 8;
+ switch ( version )
+ {
+ case 0:
+ OTV_LIMIT_CHECK( 4 );
+ break;
+
+ case 1:
+ OTV_LIMIT_CHECK( 8 );
+ table_size += 4;
+ break;
+
+ default:
+ FT_INVALID_FORMAT;
+ }
OTV_OPTIONAL_OFFSET( HorizAxis );
OTV_SIZE_CHECK( HorizAxis );
@@ -311,6 +330,14 @@
if ( VertAxis )
otv_Axis_validate( table + VertAxis, otvalid );
+ if ( version > 0 )
+ {
+ OTV_OPTIONAL_OFFSET32( itemVarStore );
+ OTV_SIZE_CHECK32( itemVarStore );
+ if ( itemVarStore )
+ OTV_TRACE(( " [omitting itemVarStore validation]\n" )); /* XXX */
+ }
+
FT_TRACE4(( "\n" ));
}
diff --git a/thirdparty/freetype/src/otvalid/otvcommn.c b/thirdparty/freetype/src/otvalid/otvcommn.c
index 3407d2ad4a..0ccfb03c08 100644
--- a/thirdparty/freetype/src/otvalid/otvcommn.c
+++ b/thirdparty/freetype/src/otvalid/otvcommn.c
@@ -4,7 +4,7 @@
/* */
/* OpenType common tables validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -313,19 +313,26 @@
OTV_NAME_ENTER( "Device" );
- OTV_LIMIT_CHECK( 8 );
+ OTV_LIMIT_CHECK( 6 );
StartSize = FT_NEXT_USHORT( p );
EndSize = FT_NEXT_USHORT( p );
DeltaFormat = FT_NEXT_USHORT( p );
- if ( DeltaFormat < 1 || DeltaFormat > 3 )
- FT_INVALID_FORMAT;
+ if ( DeltaFormat == 0x8000U )
+ {
+ /* VariationIndex, nothing to do */
+ }
+ else
+ {
+ if ( DeltaFormat < 1 || DeltaFormat > 3 )
+ FT_INVALID_FORMAT;
- if ( EndSize < StartSize )
- FT_INVALID_DATA;
+ if ( EndSize < StartSize )
+ FT_INVALID_DATA;
- count = EndSize - StartSize + 1;
- OTV_LIMIT_CHECK( ( 1 << DeltaFormat ) * count / 8 ); /* DeltaValue */
+ count = EndSize - StartSize + 1;
+ OTV_LIMIT_CHECK( ( 1 << DeltaFormat ) * count / 8 ); /* DeltaValue */
+ }
OTV_EXIT;
}
@@ -347,7 +354,7 @@
OTV_Validator otvalid )
{
FT_Bytes p = table;
- FT_UInt LookupType, SubTableCount;
+ FT_UInt LookupType, LookupFlag, SubTableCount;
OTV_Validate_Func validate;
@@ -355,7 +362,7 @@
OTV_LIMIT_CHECK( 6 );
LookupType = FT_NEXT_USHORT( p );
- p += 2; /* skip LookupFlag */
+ LookupFlag = FT_NEXT_USHORT( p );
SubTableCount = FT_NEXT_USHORT( p );
OTV_TRACE(( " (type %d)\n", LookupType ));
@@ -373,6 +380,9 @@
for ( ; SubTableCount > 0; SubTableCount-- )
validate( table + FT_NEXT_USHORT( p ), otvalid );
+ if ( LookupFlag & 0x10 )
+ OTV_LIMIT_CHECK( 2 ); /* MarkFilteringSet */
+
OTV_EXIT;
}
diff --git a/thirdparty/freetype/src/otvalid/otvcommn.h b/thirdparty/freetype/src/otvalid/otvcommn.h
index 10a603ebb9..a392784cf1 100644
--- a/thirdparty/freetype/src/otvalid/otvcommn.h
+++ b/thirdparty/freetype/src/otvalid/otvcommn.h
@@ -4,7 +4,7 @@
/* */
/* OpenType common tables validation (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -67,29 +67,38 @@ FT_BEGIN_HEADER
#undef FT_INVALID_
-#define FT_INVALID_( _error ) \
+#define FT_INVALID_( _error ) \
ft_validator_error( otvalid->root, FT_THROW( _error ) )
#define OTV_OPTIONAL_TABLE( _table ) FT_UShort _table; \
FT_Bytes _table ## _p
+#define OTV_OPTIONAL_TABLE32( _table ) FT_ULong _table; \
+ FT_Bytes _table ## _p
+
#define OTV_OPTIONAL_OFFSET( _offset ) \
FT_BEGIN_STMNT \
_offset ## _p = p; \
_offset = FT_NEXT_USHORT( p ); \
FT_END_STMNT
-#define OTV_LIMIT_CHECK( _count ) \
- FT_BEGIN_STMNT \
+#define OTV_OPTIONAL_OFFSET32( _offset ) \
+ FT_BEGIN_STMNT \
+ _offset ## _p = p; \
+ _offset = FT_NEXT_ULONG( p ); \
+ FT_END_STMNT
+
+#define OTV_LIMIT_CHECK( _count ) \
+ FT_BEGIN_STMNT \
if ( p + (_count) > otvalid->root->limit ) \
- FT_INVALID_TOO_SHORT; \
+ FT_INVALID_TOO_SHORT; \
FT_END_STMNT
#define OTV_SIZE_CHECK( _size ) \
FT_BEGIN_STMNT \
if ( _size > 0 && _size < table_size ) \
{ \
- if ( otvalid->root->level == FT_VALIDATE_PARANOID ) \
+ if ( otvalid->root->level == FT_VALIDATE_PARANOID ) \
FT_INVALID_OFFSET; \
else \
{ \
@@ -102,12 +111,33 @@ FT_BEGIN_HEADER
" set to zero.\n" \
"\n", #_size )); \
\
- /* always assume 16bit entities */ \
_size = pp[0] = pp[1] = 0; \
} \
} \
FT_END_STMNT
+#define OTV_SIZE_CHECK32( _size ) \
+ FT_BEGIN_STMNT \
+ if ( _size > 0 && _size < table_size ) \
+ { \
+ if ( otvalid->root->level == FT_VALIDATE_PARANOID ) \
+ FT_INVALID_OFFSET; \
+ else \
+ { \
+ /* strip off `const' */ \
+ FT_Byte* pp = (FT_Byte*)_size ## _p; \
+ \
+ \
+ FT_TRACE3(( "\n" \
+ "Invalid offset to optional table `%s'" \
+ " set to zero.\n" \
+ "\n", #_size )); \
+ \
+ _size = pp[0] = pp[1] = pp[2] = pp[3] = 0; \
+ } \
+ } \
+ FT_END_STMNT
+
#define OTV_NAME_(x) #x
#define OTV_NAME(x) OTV_NAME_(x)
@@ -146,11 +176,11 @@ FT_BEGIN_HEADER
#define OTV_INIT otvalid->debug_indent = 0
-#define OTV_ENTER \
- FT_BEGIN_STMNT \
- otvalid->debug_indent += 2; \
- FT_TRACE4(( "%*.s", otvalid->debug_indent, 0 )); \
- FT_TRACE4(( "%s table\n", \
+#define OTV_ENTER \
+ FT_BEGIN_STMNT \
+ otvalid->debug_indent += 2; \
+ FT_TRACE4(( "%*.s", otvalid->debug_indent, 0 )); \
+ FT_TRACE4(( "%s table\n", \
otvalid->debug_function_name[otvalid->nesting_level] )); \
FT_END_STMNT
diff --git a/thirdparty/freetype/src/otvalid/otverror.h b/thirdparty/freetype/src/otvalid/otverror.h
index 699903987c..2fcf42e387 100644
--- a/thirdparty/freetype/src/otvalid/otverror.h
+++ b/thirdparty/freetype/src/otvalid/otverror.h
@@ -4,7 +4,7 @@
/* */
/* OpenType validation module error codes (specification only). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/otvalid/otvgdef.c b/thirdparty/freetype/src/otvalid/otvgdef.c
index 27b9a69ca8..08f3171541 100644
--- a/thirdparty/freetype/src/otvalid/otvgdef.c
+++ b/thirdparty/freetype/src/otvalid/otvgdef.c
@@ -4,7 +4,7 @@
/* */
/* OpenType GDEF table validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -68,7 +68,7 @@
OTV_LIMIT_CHECK( GlyphCount * 2 );
otvalid->nesting_level++;
- func = otvalid->func[otvalid->nesting_level];
+ func = otvalid->func[otvalid->nesting_level];
otvalid->extra1 = 0;
for ( ; GlyphCount > 0; GlyphCount-- )
@@ -136,6 +136,40 @@
/*************************************************************************/
/*************************************************************************/
/***** *****/
+ /***** MARK GLYPH SETS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ otv_MarkGlyphSets_validate( FT_Bytes table,
+ OTV_Validator otvalid )
+ {
+ FT_Bytes p = table;
+ FT_UInt MarkGlyphSetCount;
+
+
+ OTV_NAME_ENTER( "MarkGlyphSets" );
+
+ p += 2; /* skip Format */
+
+ OTV_LIMIT_CHECK( 2 );
+ MarkGlyphSetCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (MarkGlyphSetCount = %d)\n", MarkGlyphSetCount ));
+
+ OTV_LIMIT_CHECK( MarkGlyphSetCount * 4 ); /* CoverageOffsets */
+
+ for ( ; MarkGlyphSetCount > 0; MarkGlyphSetCount-- )
+ otv_Coverage_validate( table + FT_NEXT_ULONG( p ), otvalid, -1 );
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
/***** GDEF TABLE *****/
/***** *****/
/*************************************************************************/
@@ -152,14 +186,18 @@
{
OTV_ValidatorRec otvalidrec;
OTV_Validator otvalid = &otvalidrec;
- FT_Bytes p = table;
+ FT_Bytes p = table;
FT_UInt table_size;
- FT_Bool need_MarkAttachClassDef;
+ FT_UShort version;
+ FT_Bool need_MarkAttachClassDef = 1;
OTV_OPTIONAL_TABLE( GlyphClassDef );
OTV_OPTIONAL_TABLE( AttachListOffset );
OTV_OPTIONAL_TABLE( LigCaretListOffset );
OTV_OPTIONAL_TABLE( MarkAttachClassDef );
+ OTV_OPTIONAL_TABLE( MarkGlyphSetsDef );
+
+ OTV_OPTIONAL_TABLE32( itemVarStore );
otvalid->root = ftvalid;
@@ -167,24 +205,49 @@
FT_TRACE3(( "validating GDEF table\n" ));
OTV_INIT;
- OTV_LIMIT_CHECK( 12 );
+ OTV_LIMIT_CHECK( 4 );
- if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */
+ if ( FT_NEXT_USHORT( p ) != 1 ) /* majorVersion */
FT_INVALID_FORMAT;
- /* MarkAttachClassDef has been added to the OpenType */
- /* specification without increasing GDEF's version, */
- /* so we use this ugly hack to find out whether the */
- /* table is needed actually. */
+ version = FT_NEXT_USHORT( p ); /* minorVersion */
- need_MarkAttachClassDef = FT_BOOL(
- otv_GSUBGPOS_have_MarkAttachmentType_flag( gsub ) ||
- otv_GSUBGPOS_have_MarkAttachmentType_flag( gpos ) );
+ table_size = 10;
+ switch ( version )
+ {
+ case 0:
+ /* MarkAttachClassDef has been added to the OpenType */
+ /* specification without increasing GDEF's version, */
+ /* so we use this ugly hack to find out whether the */
+ /* table is needed actually. */
+
+ need_MarkAttachClassDef = FT_BOOL(
+ otv_GSUBGPOS_have_MarkAttachmentType_flag( gsub ) ||
+ otv_GSUBGPOS_have_MarkAttachmentType_flag( gpos ) );
+
+ if ( need_MarkAttachClassDef )
+ {
+ OTV_LIMIT_CHECK( 8 );
+ table_size += 2;
+ }
+ else
+ OTV_LIMIT_CHECK( 6 ); /* OpenType < 1.2 */
- if ( need_MarkAttachClassDef )
- table_size = 12; /* OpenType >= 1.2 */
- else
- table_size = 10; /* OpenType < 1.2 */
+ break;
+
+ case 2:
+ OTV_LIMIT_CHECK( 10 );
+ table_size += 4;
+ break;
+
+ case 3:
+ OTV_LIMIT_CHECK( 14 );
+ table_size += 8;
+ break;
+
+ default:
+ FT_INVALID_FORMAT;
+ }
otvalid->glyph_count = glyph_count;
@@ -217,6 +280,22 @@
otv_ClassDef_validate( table + MarkAttachClassDef, otvalid );
}
+ if ( version > 0 )
+ {
+ OTV_OPTIONAL_OFFSET( MarkGlyphSetsDef );
+ OTV_SIZE_CHECK( MarkGlyphSetsDef );
+ if ( MarkGlyphSetsDef )
+ otv_MarkGlyphSets_validate( table + MarkGlyphSetsDef, otvalid );
+ }
+
+ if ( version > 2 )
+ {
+ OTV_OPTIONAL_OFFSET32( itemVarStore );
+ OTV_SIZE_CHECK32( itemVarStore );
+ if ( itemVarStore )
+ OTV_TRACE(( " [omitting itemVarStore validation]\n" )); /* XXX */
+ }
+
FT_TRACE4(( "\n" ));
}
diff --git a/thirdparty/freetype/src/otvalid/otvgpos.c b/thirdparty/freetype/src/otvalid/otvgpos.c
index 0fbcc2077c..696b35cae6 100644
--- a/thirdparty/freetype/src/otvalid/otvgpos.c
+++ b/thirdparty/freetype/src/otvalid/otvgpos.c
@@ -4,7 +4,7 @@
/* */
/* OpenType GPOS table validation (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -130,7 +130,7 @@
otv_MarkArray_validate( table + Array1, otvalid );
otvalid->nesting_level++;
- func = otvalid->func[otvalid->nesting_level];
+ func = otvalid->func[otvalid->nesting_level];
otvalid->extra1 = ClassCount;
func( table + Array2, otvalid );
@@ -218,10 +218,6 @@
OTV_LIMIT_CHECK( 2 );
OTV_OPTIONAL_OFFSET( device );
- /* XXX: this value is usually too small, especially if the current */
- /* ValueRecord is part of an array -- getting the correct table */
- /* size is probably not worth the trouble */
-
table_size = p - otvalid->extra3;
OTV_SIZE_CHECK( device );
@@ -271,7 +267,7 @@
case 3:
{
- FT_UInt table_size;
+ FT_UInt table_size;
OTV_OPTIONAL_TABLE( XDeviceTable );
OTV_OPTIONAL_TABLE( YDeviceTable );
@@ -426,6 +422,8 @@
/*************************************************************************/
/*************************************************************************/
+ /* sets otvalid->extra3 (pointer to base table) */
+
static void
otv_PairSet_validate( FT_Bytes table,
FT_UInt format1,
@@ -438,6 +436,8 @@
OTV_NAME_ENTER( "PairSet" );
+ otvalid->extra3 = table;
+
OTV_LIMIT_CHECK( 2 );
PairValueCount = FT_NEXT_USHORT( p );
@@ -483,8 +483,6 @@
OTV_TRACE(( " (format %d)\n", PosFormat ));
- otvalid->extra3 = table;
-
switch ( PosFormat )
{
case 1: /* PairPosFormat1 */
@@ -537,7 +535,9 @@
otv_ClassDef_validate( table + ClassDef2, otvalid );
OTV_LIMIT_CHECK( ClassCount1 * ClassCount2 *
- ( len_value1 + len_value2 ) );
+ ( len_value1 + len_value2 ) );
+
+ otvalid->extra3 = table;
/* Class1Record */
for ( ; ClassCount1 > 0; ClassCount1-- )
@@ -985,20 +985,42 @@
{
OTV_ValidatorRec validrec;
OTV_Validator otvalid = &validrec;
- FT_Bytes p = table;
+ FT_Bytes p = table;
+ FT_UInt table_size;
+ FT_UShort version;
FT_UInt ScriptList, FeatureList, LookupList;
+ OTV_OPTIONAL_TABLE32( featureVariations );
+
otvalid->root = ftvalid;
FT_TRACE3(( "validating GPOS table\n" ));
OTV_INIT;
- OTV_LIMIT_CHECK( 10 );
+ OTV_LIMIT_CHECK( 4 );
- if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */
+ if ( FT_NEXT_USHORT( p ) != 1 ) /* majorVersion */
FT_INVALID_FORMAT;
+ version = FT_NEXT_USHORT( p ); /* minorVersion */
+
+ table_size = 10;
+ switch ( version )
+ {
+ case 0:
+ OTV_LIMIT_CHECK( 6 );
+ break;
+
+ case 1:
+ OTV_LIMIT_CHECK( 10 );
+ table_size += 4;
+ break;
+
+ default:
+ FT_INVALID_FORMAT;
+ }
+
ScriptList = FT_NEXT_USHORT( p );
FeatureList = FT_NEXT_USHORT( p );
LookupList = FT_NEXT_USHORT( p );
@@ -1014,6 +1036,14 @@
otv_ScriptList_validate( table + ScriptList, table + FeatureList,
otvalid );
+ if ( version > 0 )
+ {
+ OTV_OPTIONAL_OFFSET32( featureVariations );
+ OTV_SIZE_CHECK32( featureVariations );
+ if ( featureVariations )
+ OTV_TRACE(( " [omitting featureVariations validation]\n" )); /* XXX */
+ }
+
FT_TRACE4(( "\n" ));
}
diff --git a/thirdparty/freetype/src/otvalid/otvgpos.h b/thirdparty/freetype/src/otvalid/otvgpos.h
index 99b0ad3915..95f9ac3ee8 100644
--- a/thirdparty/freetype/src/otvalid/otvgpos.h
+++ b/thirdparty/freetype/src/otvalid/otvgpos.h
@@ -4,7 +4,7 @@
/* */
/* OpenType GPOS table validator (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/otvalid/otvgsub.c b/thirdparty/freetype/src/otvalid/otvgsub.c
index f9bd8dc5d8..d35ea67f30 100644
--- a/thirdparty/freetype/src/otvalid/otvgsub.c
+++ b/thirdparty/freetype/src/otvalid/otvgsub.c
@@ -4,7 +4,7 @@
/* */
/* OpenType GSUB table validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -552,18 +552,40 @@
OTV_ValidatorRec otvalidrec;
OTV_Validator otvalid = &otvalidrec;
FT_Bytes p = table;
+ FT_UInt table_size;
+ FT_UShort version;
FT_UInt ScriptList, FeatureList, LookupList;
+ OTV_OPTIONAL_TABLE32( featureVariations );
+
otvalid->root = ftvalid;
FT_TRACE3(( "validating GSUB table\n" ));
OTV_INIT;
- OTV_LIMIT_CHECK( 10 );
+ OTV_LIMIT_CHECK( 4 );
+
+ if ( FT_NEXT_USHORT( p ) != 1 ) /* majorVersion */
+ FT_INVALID_FORMAT;
+
+ version = FT_NEXT_USHORT( p ); /* minorVersion */
+
+ table_size = 10;
+ switch ( version )
+ {
+ case 0:
+ OTV_LIMIT_CHECK( 6 );
+ break;
+
+ case 1:
+ OTV_LIMIT_CHECK( 10 );
+ table_size += 4;
+ break;
- if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */
+ default:
FT_INVALID_FORMAT;
+ }
ScriptList = FT_NEXT_USHORT( p );
FeatureList = FT_NEXT_USHORT( p );
@@ -580,6 +602,14 @@
otv_ScriptList_validate( table + ScriptList, table + FeatureList,
otvalid );
+ if ( version > 0 )
+ {
+ OTV_OPTIONAL_OFFSET32( featureVariations );
+ OTV_SIZE_CHECK32( featureVariations );
+ if ( featureVariations )
+ OTV_TRACE(( " [omitting featureVariations validation]\n" )); /* XXX */
+ }
+
FT_TRACE4(( "\n" ));
}
diff --git a/thirdparty/freetype/src/otvalid/otvjstf.c b/thirdparty/freetype/src/otvalid/otvjstf.c
index 57a38f95c9..94d4af90f6 100644
--- a/thirdparty/freetype/src/otvalid/otvjstf.c
+++ b/thirdparty/freetype/src/otvalid/otvjstf.c
@@ -4,7 +4,7 @@
/* */
/* OpenType JSTF table validation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/otvalid/otvmath.c b/thirdparty/freetype/src/otvalid/otvmath.c
index a14d369700..b9800f60a2 100644
--- a/thirdparty/freetype/src/otvalid/otvmath.c
+++ b/thirdparty/freetype/src/otvalid/otvmath.c
@@ -4,7 +4,7 @@
/* */
/* OpenType MATH table validation (body). */
/* */
-/* Copyright 2007-2017 by */
+/* Copyright 2007-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* Written by George Williams. */
diff --git a/thirdparty/freetype/src/otvalid/otvmod.c b/thirdparty/freetype/src/otvalid/otvmod.c
index 35ffc43d3a..89ee449d16 100644
--- a/thirdparty/freetype/src/otvalid/otvmod.c
+++ b/thirdparty/freetype/src/otvalid/otvmod.c
@@ -4,7 +4,7 @@
/* */
/* FreeType's OpenType validation module implementation (body). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/otvalid/otvmod.h b/thirdparty/freetype/src/otvalid/otvmod.h
index 30d401ddca..6917bccee5 100644
--- a/thirdparty/freetype/src/otvalid/otvmod.h
+++ b/thirdparty/freetype/src/otvalid/otvmod.h
@@ -5,7 +5,7 @@
/* FreeType's OpenType validation module implementation */
/* (specification). */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/otvalid/rules.mk b/thirdparty/freetype/src/otvalid/rules.mk
index 10329a96ea..d4fc723740 100644
--- a/thirdparty/freetype/src/otvalid/rules.mk
+++ b/thirdparty/freetype/src/otvalid/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2004-2017 by
+# Copyright 2004-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/pcf/pcfdrivr.c b/thirdparty/freetype/src/pcf/pcfdrivr.c
index 169f75e950..0119d94853 100644
--- a/thirdparty/freetype/src/pcf/pcfdrivr.c
+++ b/thirdparty/freetype/src/pcf/pcfdrivr.c
@@ -50,7 +50,7 @@ THE SOFTWARE.
#include FT_SERVICE_BDF_H
#include FT_SERVICE_FONT_FORMAT_H
#include FT_SERVICE_PROPERTIES_H
-#include FT_PCF_DRIVER_H
+#include FT_DRIVER_H
/*************************************************************************/
diff --git a/thirdparty/freetype/src/pcf/pcfread.c b/thirdparty/freetype/src/pcf/pcfread.c
index da216b05f4..537da0dc79 100644
--- a/thirdparty/freetype/src/pcf/pcfread.c
+++ b/thirdparty/freetype/src/pcf/pcfread.c
@@ -840,7 +840,7 @@ THE SOFTWARE.
FT_TRACE4(( "pcf_get_bitmaps:\n"
" format: 0x%lX\n"
" (%s, %s,\n"
- " padding=%d bits, scanning=%d bits)\n",
+ " padding=%d bit%s, scanning=%d bit%s)\n",
format,
PCF_BYTE_ORDER( format ) == MSBFirst
? "most significant byte first"
@@ -849,7 +849,9 @@ THE SOFTWARE.
? "most significant bit first"
: "least significant bit first",
8 << PCF_GLYPH_PAD_INDEX( format ),
- 8 << PCF_SCAN_UNIT_INDEX( format ) ));
+ ( 8 << PCF_GLYPH_PAD_INDEX( format ) ) == 1 ? "" : "s",
+ 8 << PCF_SCAN_UNIT_INDEX( format ),
+ ( 8 << PCF_SCAN_UNIT_INDEX( format ) ) == 1 ? "" : "s" ));
if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
return FT_THROW( Invalid_File_Format );
diff --git a/thirdparty/freetype/src/pfr/module.mk b/thirdparty/freetype/src/pfr/module.mk
index 7b84da9708..27fec8e5f6 100644
--- a/thirdparty/freetype/src/pfr/module.mk
+++ b/thirdparty/freetype/src/pfr/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2002-2017 by
+# Copyright 2002-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/pfr/pfr.c b/thirdparty/freetype/src/pfr/pfr.c
index 4f31f5d9bc..1760882fcd 100644
--- a/thirdparty/freetype/src/pfr/pfr.c
+++ b/thirdparty/freetype/src/pfr/pfr.c
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR driver component. */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrcmap.c b/thirdparty/freetype/src/pfr/pfrcmap.c
index 1d6b15be48..60643780a1 100644
--- a/thirdparty/freetype/src/pfr/pfrcmap.c
+++ b/thirdparty/freetype/src/pfr/pfrcmap.c
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR cmap handling (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrcmap.h b/thirdparty/freetype/src/pfr/pfrcmap.h
index 957bf65b4c..c70a0c83c5 100644
--- a/thirdparty/freetype/src/pfr/pfrcmap.h
+++ b/thirdparty/freetype/src/pfr/pfrcmap.h
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR cmap handling (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrdrivr.c b/thirdparty/freetype/src/pfr/pfrdrivr.c
index 195cdb76ac..6c7e50128a 100644
--- a/thirdparty/freetype/src/pfr/pfrdrivr.c
+++ b/thirdparty/freetype/src/pfr/pfrdrivr.c
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR driver interface (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrdrivr.h b/thirdparty/freetype/src/pfr/pfrdrivr.h
index b81d56017e..cab852789b 100644
--- a/thirdparty/freetype/src/pfr/pfrdrivr.h
+++ b/thirdparty/freetype/src/pfr/pfrdrivr.h
@@ -4,7 +4,7 @@
/* */
/* High-level Type PFR driver interface (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrerror.h b/thirdparty/freetype/src/pfr/pfrerror.h
index ef044e32c9..7027c818e8 100644
--- a/thirdparty/freetype/src/pfr/pfrerror.h
+++ b/thirdparty/freetype/src/pfr/pfrerror.h
@@ -4,7 +4,7 @@
/* */
/* PFR error codes (specification only). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrgload.c b/thirdparty/freetype/src/pfr/pfrgload.c
index 93f5fc7090..b7990196b6 100644
--- a/thirdparty/freetype/src/pfr/pfrgload.c
+++ b/thirdparty/freetype/src/pfr/pfrgload.c
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR glyph loader (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -756,8 +756,10 @@
count = glyph->num_subs - old_count;
- FT_TRACE4(( "compound glyph with %d elements (offset %lu):\n",
- count, offset ));
+ FT_TRACE4(( "compound glyph with %d element%s (offset %lu):\n",
+ count,
+ count == 1 ? "" : "s",
+ offset ));
/* now, load each individual glyph */
for ( n = 0; n < count; n++ )
@@ -810,7 +812,9 @@
/* proceed to next sub-glyph */
}
- FT_TRACE4(( "end compound glyph with %d elements\n", count ));
+ FT_TRACE4(( "end compound glyph with %d element%s\n",
+ count,
+ count == 1 ? "" : "s" ));
}
else
{
diff --git a/thirdparty/freetype/src/pfr/pfrgload.h b/thirdparty/freetype/src/pfr/pfrgload.h
index 6612323871..01f48d7706 100644
--- a/thirdparty/freetype/src/pfr/pfrgload.h
+++ b/thirdparty/freetype/src/pfr/pfrgload.h
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR glyph loader (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrload.c b/thirdparty/freetype/src/pfr/pfrload.c
index 4f84165828..2776da462a 100644
--- a/thirdparty/freetype/src/pfr/pfrload.c
+++ b/thirdparty/freetype/src/pfr/pfrload.c
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR loader (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrload.h b/thirdparty/freetype/src/pfr/pfrload.h
index f9475ae062..36e809a762 100644
--- a/thirdparty/freetype/src/pfr/pfrload.h
+++ b/thirdparty/freetype/src/pfr/pfrload.h
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR loader (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrobjs.c b/thirdparty/freetype/src/pfr/pfrobjs.c
index 514af8050d..737b97b5ff 100644
--- a/thirdparty/freetype/src/pfr/pfrobjs.c
+++ b/thirdparty/freetype/src/pfr/pfrobjs.c
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR object methods (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrobjs.h b/thirdparty/freetype/src/pfr/pfrobjs.h
index d6ad6562df..59c709f58d 100644
--- a/thirdparty/freetype/src/pfr/pfrobjs.h
+++ b/thirdparty/freetype/src/pfr/pfrobjs.h
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR object methods (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrsbit.c b/thirdparty/freetype/src/pfr/pfrsbit.c
index 54e7d0e6cc..ba909ddca7 100644
--- a/thirdparty/freetype/src/pfr/pfrsbit.c
+++ b/thirdparty/freetype/src/pfr/pfrsbit.c
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR bitmap loader (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrsbit.h b/thirdparty/freetype/src/pfr/pfrsbit.h
index fc270f50a8..07b27bc06c 100644
--- a/thirdparty/freetype/src/pfr/pfrsbit.h
+++ b/thirdparty/freetype/src/pfr/pfrsbit.h
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR bitmap loader (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/pfrtypes.h b/thirdparty/freetype/src/pfr/pfrtypes.h
index c3d542c209..058d6aadc9 100644
--- a/thirdparty/freetype/src/pfr/pfrtypes.h
+++ b/thirdparty/freetype/src/pfr/pfrtypes.h
@@ -4,7 +4,7 @@
/* */
/* FreeType PFR data structures (specification only). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pfr/rules.mk b/thirdparty/freetype/src/pfr/rules.mk
index 9940f62286..3acb795696 100644
--- a/thirdparty/freetype/src/pfr/rules.mk
+++ b/thirdparty/freetype/src/pfr/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2002-2017 by
+# Copyright 2002-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/psaux/afmparse.c b/thirdparty/freetype/src/psaux/afmparse.c
index ff2cc8cf08..0c33d5949b 100644
--- a/thirdparty/freetype/src/psaux/afmparse.c
+++ b/thirdparty/freetype/src/psaux/afmparse.c
@@ -4,7 +4,7 @@
/* */
/* AFM parser (body). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psaux/afmparse.h b/thirdparty/freetype/src/psaux/afmparse.h
index cd2beb7804..86f852a247 100644
--- a/thirdparty/freetype/src/psaux/afmparse.h
+++ b/thirdparty/freetype/src/psaux/afmparse.h
@@ -4,7 +4,7 @@
/* */
/* AFM parser (specification). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psaux/cffdecode.c b/thirdparty/freetype/src/psaux/cffdecode.c
new file mode 100644
index 0000000000..80d622c0e1
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/cffdecode.c
@@ -0,0 +1,2370 @@
+/***************************************************************************/
+/* */
+/* cffdecode.c */
+/* */
+/* PostScript CFF (Type 2) decoding routines (body). */
+/* */
+/* Copyright 2017-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_SERVICE_H
+#include FT_SERVICE_CFF_TABLE_LOAD_H
+
+#include "cffdecode.h"
+#include "psobjs.h"
+
+#include "psauxerr.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cffdecode
+
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+
+ typedef enum CFF_Operator_
+ {
+ cff_op_unknown = 0,
+
+ cff_op_rmoveto,
+ cff_op_hmoveto,
+ cff_op_vmoveto,
+
+ cff_op_rlineto,
+ cff_op_hlineto,
+ cff_op_vlineto,
+
+ cff_op_rrcurveto,
+ cff_op_hhcurveto,
+ cff_op_hvcurveto,
+ cff_op_rcurveline,
+ cff_op_rlinecurve,
+ cff_op_vhcurveto,
+ cff_op_vvcurveto,
+
+ cff_op_flex,
+ cff_op_hflex,
+ cff_op_hflex1,
+ cff_op_flex1,
+
+ cff_op_endchar,
+
+ cff_op_hstem,
+ cff_op_vstem,
+ cff_op_hstemhm,
+ cff_op_vstemhm,
+
+ cff_op_hintmask,
+ cff_op_cntrmask,
+ cff_op_dotsection, /* deprecated, acts as no-op */
+
+ cff_op_abs,
+ cff_op_add,
+ cff_op_sub,
+ cff_op_div,
+ cff_op_neg,
+ cff_op_random,
+ cff_op_mul,
+ cff_op_sqrt,
+
+ cff_op_blend,
+
+ cff_op_drop,
+ cff_op_exch,
+ cff_op_index,
+ cff_op_roll,
+ cff_op_dup,
+
+ cff_op_put,
+ cff_op_get,
+ cff_op_store,
+ cff_op_load,
+
+ cff_op_and,
+ cff_op_or,
+ cff_op_not,
+ cff_op_eq,
+ cff_op_ifelse,
+
+ cff_op_callsubr,
+ cff_op_callgsubr,
+ cff_op_return,
+
+ /* Type 1 opcodes: invalid but seen in real life */
+ cff_op_hsbw,
+ cff_op_closepath,
+ cff_op_callothersubr,
+ cff_op_pop,
+ cff_op_seac,
+ cff_op_sbw,
+ cff_op_setcurrentpoint,
+
+ /* do not remove */
+ cff_op_max
+
+ } CFF_Operator;
+
+
+#define CFF_COUNT_CHECK_WIDTH 0x80
+#define CFF_COUNT_EXACT 0x40
+#define CFF_COUNT_CLEAR_STACK 0x20
+
+ /* count values which have the `CFF_COUNT_CHECK_WIDTH' flag set are */
+ /* used for checking the width and requested numbers of arguments */
+ /* only; they are set to zero afterwards */
+
+ /* the other two flags are informative only and unused currently */
+
+ static const FT_Byte cff_argument_counts[] =
+ {
+ 0, /* unknown */
+
+ 2 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT, /* rmoveto */
+ 1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT,
+ 1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT,
+
+ 0 | CFF_COUNT_CLEAR_STACK, /* rlineto */
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+
+ 0 | CFF_COUNT_CLEAR_STACK, /* rrcurveto */
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+
+ 13, /* flex */
+ 7,
+ 9,
+ 11,
+
+ 0 | CFF_COUNT_CHECK_WIDTH, /* endchar */
+
+ 2 | CFF_COUNT_CHECK_WIDTH, /* hstem */
+ 2 | CFF_COUNT_CHECK_WIDTH,
+ 2 | CFF_COUNT_CHECK_WIDTH,
+ 2 | CFF_COUNT_CHECK_WIDTH,
+
+ 0 | CFF_COUNT_CHECK_WIDTH, /* hintmask */
+ 0 | CFF_COUNT_CHECK_WIDTH, /* cntrmask */
+ 0, /* dotsection */
+
+ 1, /* abs */
+ 2,
+ 2,
+ 2,
+ 1,
+ 0,
+ 2,
+ 1,
+
+ 1, /* blend */
+
+ 1, /* drop */
+ 2,
+ 1,
+ 2,
+ 1,
+
+ 2, /* put */
+ 1,
+ 4,
+ 3,
+
+ 2, /* and */
+ 2,
+ 1,
+ 2,
+ 4,
+
+ 1, /* callsubr */
+ 1,
+ 0,
+
+ 2, /* hsbw */
+ 0,
+ 0,
+ 0,
+ 5, /* seac */
+ 4, /* sbw */
+ 2 /* setcurrentpoint */
+ };
+
+
+ static FT_Error
+ cff_operator_seac( CFF_Decoder* decoder,
+ FT_Pos asb,
+ FT_Pos adx,
+ FT_Pos ady,
+ FT_Int bchar,
+ FT_Int achar )
+ {
+ FT_Error error;
+ CFF_Builder* builder = &decoder->builder;
+ FT_Int bchar_index, achar_index;
+ TT_Face face = decoder->builder.face;
+ FT_Vector left_bearing, advance;
+ FT_Byte* charstring;
+ FT_ULong charstring_len;
+ FT_Pos glyph_width;
+
+
+ if ( decoder->seac )
+ {
+ FT_ERROR(( "cff_operator_seac: invalid nested seac\n" ));
+ return FT_THROW( Syntax_Error );
+ }
+
+ adx += decoder->builder.left_bearing.x;
+ ady += decoder->builder.left_bearing.y;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ /* Incremental fonts don't necessarily have valid charsets. */
+ /* They use the character code, not the glyph index, in this case. */
+ if ( face->root.internal->incremental_interface )
+ {
+ bchar_index = bchar;
+ achar_index = achar;
+ }
+ else
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+ {
+ CFF_Font cff = (CFF_Font)(face->extra.data);
+
+
+ bchar_index = cff_lookup_glyph_by_stdcharcode( cff, bchar );
+ achar_index = cff_lookup_glyph_by_stdcharcode( cff, achar );
+ }
+
+ if ( bchar_index < 0 || achar_index < 0 )
+ {
+ FT_ERROR(( "cff_operator_seac:"
+ " invalid seac character code arguments\n" ));
+ return FT_THROW( Syntax_Error );
+ }
+
+ /* If we are trying to load a composite glyph, do not load the */
+ /* accent character and return the array of subglyphs. */
+ if ( builder->no_recurse )
+ {
+ FT_GlyphSlot glyph = (FT_GlyphSlot)builder->glyph;
+ FT_GlyphLoader loader = glyph->internal->loader;
+ FT_SubGlyph subg;
+
+
+ /* reallocate subglyph array if necessary */
+ error = FT_GlyphLoader_CheckSubGlyphs( loader, 2 );
+ if ( error )
+ goto Exit;
+
+ subg = loader->current.subglyphs;
+
+ /* subglyph 0 = base character */
+ subg->index = bchar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES |
+ FT_SUBGLYPH_FLAG_USE_MY_METRICS;
+ subg->arg1 = 0;
+ subg->arg2 = 0;
+ subg++;
+
+ /* subglyph 1 = accent character */
+ subg->index = achar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES;
+ subg->arg1 = (FT_Int)( adx >> 16 );
+ subg->arg2 = (FT_Int)( ady >> 16 );
+
+ /* set up remaining glyph fields */
+ glyph->num_subglyphs = 2;
+ glyph->subglyphs = loader->base.subglyphs;
+ glyph->format = FT_GLYPH_FORMAT_COMPOSITE;
+
+ loader->current.num_subglyphs = 2;
+ }
+
+ FT_GlyphLoader_Prepare( builder->loader );
+
+ /* First load `bchar' in builder */
+ error = decoder->get_glyph_callback( face, (FT_UInt)bchar_index,
+ &charstring, &charstring_len );
+ if ( !error )
+ {
+ /* the seac operator must not be nested */
+ decoder->seac = TRUE;
+ error = cff_decoder_parse_charstrings( decoder, charstring,
+ charstring_len, 0 );
+ decoder->seac = FALSE;
+
+ decoder->free_glyph_callback( face, &charstring, charstring_len );
+
+ if ( error )
+ goto Exit;
+ }
+
+ /* Save the left bearing, advance and glyph width of the base */
+ /* character as they will be erased by the next load. */
+
+ left_bearing = builder->left_bearing;
+ advance = builder->advance;
+ glyph_width = decoder->glyph_width;
+
+ builder->left_bearing.x = 0;
+ builder->left_bearing.y = 0;
+
+ builder->pos_x = adx - asb;
+ builder->pos_y = ady;
+
+ /* Now load `achar' on top of the base outline. */
+ error = decoder->get_glyph_callback( face, (FT_UInt)achar_index,
+ &charstring, &charstring_len );
+ if ( !error )
+ {
+ /* the seac operator must not be nested */
+ decoder->seac = TRUE;
+ error = cff_decoder_parse_charstrings( decoder, charstring,
+ charstring_len, 0 );
+ decoder->seac = FALSE;
+
+ decoder->free_glyph_callback( face, &charstring, charstring_len );
+
+ if ( error )
+ goto Exit;
+ }
+
+ /* Restore the left side bearing, advance and glyph width */
+ /* of the base character. */
+ builder->left_bearing = left_bearing;
+ builder->advance = advance;
+ decoder->glyph_width = glyph_width;
+
+ builder->pos_x = 0;
+ builder->pos_y = 0;
+
+ Exit:
+ return error;
+ }
+
+#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /********** *********/
+ /********** *********/
+ /********** GENERIC CHARSTRING PARSING *********/
+ /********** *********/
+ /********** *********/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cff_compute_bias */
+ /* */
+ /* <Description> */
+ /* Computes the bias value in dependence of the number of glyph */
+ /* subroutines. */
+ /* */
+ /* <Input> */
+ /* in_charstring_type :: The `CharstringType' value of the top DICT */
+ /* dictionary. */
+ /* */
+ /* num_subrs :: The number of glyph subroutines. */
+ /* */
+ /* <Return> */
+ /* The bias value. */
+ static FT_Int
+ cff_compute_bias( FT_Int in_charstring_type,
+ FT_UInt num_subrs )
+ {
+ FT_Int result;
+
+
+ if ( in_charstring_type == 1 )
+ result = 0;
+ else if ( num_subrs < 1240 )
+ result = 107;
+ else if ( num_subrs < 33900U )
+ result = 1131;
+ else
+ result = 32768U;
+
+ return result;
+ }
+
+
+ FT_LOCAL_DEF( FT_Int )
+ cff_lookup_glyph_by_stdcharcode( CFF_Font cff,
+ FT_Int charcode )
+ {
+ FT_UInt n;
+ FT_UShort glyph_sid;
+
+ FT_Service_CFFLoad cffload;
+
+
+ /* CID-keyed fonts don't have glyph names */
+ if ( !cff->charset.sids )
+ return -1;
+
+ /* check range of standard char code */
+ if ( charcode < 0 || charcode > 255 )
+ return -1;
+
+#if 0
+ /* retrieve cffload from list of current modules */
+ FT_Service_CFFLoad cffload;
+
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, cffload, CFF_LOAD );
+ if ( !cffload )
+ {
+ FT_ERROR(( "cff_lookup_glyph_by_stdcharcode:"
+ " the `cffload' module is not available\n" ));
+ return FT_THROW( Unimplemented_Feature );
+ }
+#endif
+
+ cffload = (FT_Service_CFFLoad)cff->cffload;
+
+ /* Get code to SID mapping from `cff_standard_encoding'. */
+ glyph_sid = cffload->get_standard_encoding( (FT_UInt)charcode );
+
+ for ( n = 0; n < cff->num_glyphs; n++ )
+ {
+ if ( cff->charset.sids[n] == glyph_sid )
+ return (FT_Int)n;
+ }
+
+ return -1;
+ }
+
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cff_decoder_parse_charstrings */
+ /* */
+ /* <Description> */
+ /* Parses a given Type 2 charstrings program. */
+ /* */
+ /* <InOut> */
+ /* decoder :: The current Type 1 decoder. */
+ /* */
+ /* <Input> */
+ /* charstring_base :: The base of the charstring stream. */
+ /* */
+ /* charstring_len :: The length in bytes of the charstring stream. */
+ /* */
+ /* in_dict :: Set to 1 if function is called from top or */
+ /* private DICT (needed for Multiple Master CFFs). */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ cff_decoder_parse_charstrings( CFF_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len,
+ FT_Bool in_dict )
+ {
+ FT_Error error;
+ CFF_Decoder_Zone* zone;
+ FT_Byte* ip;
+ FT_Byte* limit;
+ CFF_Builder* builder = &decoder->builder;
+ FT_Pos x, y;
+ FT_Fixed* stack;
+ FT_Int charstring_type =
+ decoder->cff->top_font.font_dict.charstring_type;
+ FT_UShort num_designs =
+ decoder->cff->top_font.font_dict.num_designs;
+ FT_UShort num_axes =
+ decoder->cff->top_font.font_dict.num_axes;
+
+ T2_Hints_Funcs hinter;
+
+
+ /* set default width */
+ decoder->num_hints = 0;
+ decoder->read_width = 1;
+
+ /* initialize the decoder */
+ decoder->top = decoder->stack;
+ decoder->zone = decoder->zones;
+ zone = decoder->zones;
+ stack = decoder->top;
+
+ hinter = (T2_Hints_Funcs)builder->hints_funcs;
+
+ builder->path_begun = 0;
+
+ zone->base = charstring_base;
+ limit = zone->limit = charstring_base + charstring_len;
+ ip = zone->cursor = zone->base;
+
+ error = FT_Err_Ok;
+
+ x = builder->pos_x;
+ y = builder->pos_y;
+
+ /* begin hints recording session, if any */
+ if ( hinter )
+ hinter->open( hinter->hints );
+
+ /* now execute loop */
+ while ( ip < limit )
+ {
+ CFF_Operator op;
+ FT_Byte v;
+
+
+ /********************************************************************/
+ /* */
+ /* Decode operator or operand */
+ /* */
+ v = *ip++;
+ if ( v >= 32 || v == 28 )
+ {
+ FT_Int shift = 16;
+ FT_Int32 val;
+
+
+ /* this is an operand, push it on the stack */
+
+ /* if we use shifts, all computations are done with unsigned */
+ /* values; the conversion to a signed value is the last step */
+ if ( v == 28 )
+ {
+ if ( ip + 1 >= limit )
+ goto Syntax_Error;
+ val = (FT_Short)( ( (FT_UShort)ip[0] << 8 ) | ip[1] );
+ ip += 2;
+ }
+ else if ( v < 247 )
+ val = (FT_Int32)v - 139;
+ else if ( v < 251 )
+ {
+ if ( ip >= limit )
+ goto Syntax_Error;
+ val = ( (FT_Int32)v - 247 ) * 256 + *ip++ + 108;
+ }
+ else if ( v < 255 )
+ {
+ if ( ip >= limit )
+ goto Syntax_Error;
+ val = -( (FT_Int32)v - 251 ) * 256 - *ip++ - 108;
+ }
+ else
+ {
+ if ( ip + 3 >= limit )
+ goto Syntax_Error;
+ val = (FT_Int32)( ( (FT_UInt32)ip[0] << 24 ) |
+ ( (FT_UInt32)ip[1] << 16 ) |
+ ( (FT_UInt32)ip[2] << 8 ) |
+ (FT_UInt32)ip[3] );
+ ip += 4;
+ if ( charstring_type == 2 )
+ shift = 0;
+ }
+ if ( decoder->top - stack >= CFF_MAX_OPERANDS )
+ goto Stack_Overflow;
+
+ val = (FT_Int32)( (FT_UInt32)val << shift );
+ *decoder->top++ = val;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( !( val & 0xFFFFL ) )
+ FT_TRACE4(( " %hd", (FT_Short)( (FT_UInt32)val >> 16 ) ));
+ else
+ FT_TRACE4(( " %.5f", val / 65536.0 ));
+#endif
+
+ }
+ else
+ {
+ /* The specification says that normally arguments are to be taken */
+ /* from the bottom of the stack. However, this seems not to be */
+ /* correct, at least for Acroread 7.0.8 on GNU/Linux: It pops the */
+ /* arguments similar to a PS interpreter. */
+
+ FT_Fixed* args = decoder->top;
+ FT_Int num_args = (FT_Int)( args - decoder->stack );
+ FT_Int req_args;
+
+
+ /* find operator */
+ op = cff_op_unknown;
+
+ switch ( v )
+ {
+ case 1:
+ op = cff_op_hstem;
+ break;
+ case 3:
+ op = cff_op_vstem;
+ break;
+ case 4:
+ op = cff_op_vmoveto;
+ break;
+ case 5:
+ op = cff_op_rlineto;
+ break;
+ case 6:
+ op = cff_op_hlineto;
+ break;
+ case 7:
+ op = cff_op_vlineto;
+ break;
+ case 8:
+ op = cff_op_rrcurveto;
+ break;
+ case 9:
+ op = cff_op_closepath;
+ break;
+ case 10:
+ op = cff_op_callsubr;
+ break;
+ case 11:
+ op = cff_op_return;
+ break;
+ case 12:
+ if ( ip >= limit )
+ goto Syntax_Error;
+ v = *ip++;
+
+ switch ( v )
+ {
+ case 0:
+ op = cff_op_dotsection;
+ break;
+ case 1: /* this is actually the Type1 vstem3 operator */
+ op = cff_op_vstem;
+ break;
+ case 2: /* this is actually the Type1 hstem3 operator */
+ op = cff_op_hstem;
+ break;
+ case 3:
+ op = cff_op_and;
+ break;
+ case 4:
+ op = cff_op_or;
+ break;
+ case 5:
+ op = cff_op_not;
+ break;
+ case 6:
+ op = cff_op_seac;
+ break;
+ case 7:
+ op = cff_op_sbw;
+ break;
+ case 8:
+ op = cff_op_store;
+ break;
+ case 9:
+ op = cff_op_abs;
+ break;
+ case 10:
+ op = cff_op_add;
+ break;
+ case 11:
+ op = cff_op_sub;
+ break;
+ case 12:
+ op = cff_op_div;
+ break;
+ case 13:
+ op = cff_op_load;
+ break;
+ case 14:
+ op = cff_op_neg;
+ break;
+ case 15:
+ op = cff_op_eq;
+ break;
+ case 16:
+ op = cff_op_callothersubr;
+ break;
+ case 17:
+ op = cff_op_pop;
+ break;
+ case 18:
+ op = cff_op_drop;
+ break;
+ case 20:
+ op = cff_op_put;
+ break;
+ case 21:
+ op = cff_op_get;
+ break;
+ case 22:
+ op = cff_op_ifelse;
+ break;
+ case 23:
+ op = cff_op_random;
+ break;
+ case 24:
+ op = cff_op_mul;
+ break;
+ case 26:
+ op = cff_op_sqrt;
+ break;
+ case 27:
+ op = cff_op_dup;
+ break;
+ case 28:
+ op = cff_op_exch;
+ break;
+ case 29:
+ op = cff_op_index;
+ break;
+ case 30:
+ op = cff_op_roll;
+ break;
+ case 33:
+ op = cff_op_setcurrentpoint;
+ break;
+ case 34:
+ op = cff_op_hflex;
+ break;
+ case 35:
+ op = cff_op_flex;
+ break;
+ case 36:
+ op = cff_op_hflex1;
+ break;
+ case 37:
+ op = cff_op_flex1;
+ break;
+ default:
+ FT_TRACE4(( " unknown op (12, %d)\n", v ));
+ break;
+ }
+ break;
+ case 13:
+ op = cff_op_hsbw;
+ break;
+ case 14:
+ op = cff_op_endchar;
+ break;
+ case 16:
+ op = cff_op_blend;
+ break;
+ case 18:
+ op = cff_op_hstemhm;
+ break;
+ case 19:
+ op = cff_op_hintmask;
+ break;
+ case 20:
+ op = cff_op_cntrmask;
+ break;
+ case 21:
+ op = cff_op_rmoveto;
+ break;
+ case 22:
+ op = cff_op_hmoveto;
+ break;
+ case 23:
+ op = cff_op_vstemhm;
+ break;
+ case 24:
+ op = cff_op_rcurveline;
+ break;
+ case 25:
+ op = cff_op_rlinecurve;
+ break;
+ case 26:
+ op = cff_op_vvcurveto;
+ break;
+ case 27:
+ op = cff_op_hhcurveto;
+ break;
+ case 29:
+ op = cff_op_callgsubr;
+ break;
+ case 30:
+ op = cff_op_vhcurveto;
+ break;
+ case 31:
+ op = cff_op_hvcurveto;
+ break;
+ default:
+ FT_TRACE4(( " unknown op (%d)\n", v ));
+ break;
+ }
+
+ if ( op == cff_op_unknown )
+ continue;
+
+ /* in Multiple Master CFFs, T2 charstrings can appear in */
+ /* dictionaries, but some operators are prohibited */
+ if ( in_dict )
+ {
+ switch ( op )
+ {
+ case cff_op_hstem:
+ case cff_op_vstem:
+ case cff_op_vmoveto:
+ case cff_op_rlineto:
+ case cff_op_hlineto:
+ case cff_op_vlineto:
+ case cff_op_rrcurveto:
+ case cff_op_hstemhm:
+ case cff_op_hintmask:
+ case cff_op_cntrmask:
+ case cff_op_rmoveto:
+ case cff_op_hmoveto:
+ case cff_op_vstemhm:
+ case cff_op_rcurveline:
+ case cff_op_rlinecurve:
+ case cff_op_vvcurveto:
+ case cff_op_hhcurveto:
+ case cff_op_vhcurveto:
+ case cff_op_hvcurveto:
+ case cff_op_hflex:
+ case cff_op_flex:
+ case cff_op_hflex1:
+ case cff_op_flex1:
+ case cff_op_callsubr:
+ case cff_op_callgsubr:
+ goto MM_Error;
+
+ default:
+ break;
+ }
+ }
+
+ /* check arguments */
+ req_args = cff_argument_counts[op];
+ if ( req_args & CFF_COUNT_CHECK_WIDTH )
+ {
+ if ( num_args > 0 && decoder->read_width )
+ {
+ /* If `nominal_width' is non-zero, the number is really a */
+ /* difference against `nominal_width'. Else, the number here */
+ /* is truly a width, not a difference against `nominal_width'. */
+ /* If the font does not set `nominal_width', then */
+ /* `nominal_width' defaults to zero, and so we can set */
+ /* `glyph_width' to `nominal_width' plus number on the stack */
+ /* -- for either case. */
+
+ FT_Int set_width_ok;
+
+
+ switch ( op )
+ {
+ case cff_op_hmoveto:
+ case cff_op_vmoveto:
+ set_width_ok = num_args & 2;
+ break;
+
+ case cff_op_hstem:
+ case cff_op_vstem:
+ case cff_op_hstemhm:
+ case cff_op_vstemhm:
+ case cff_op_rmoveto:
+ case cff_op_hintmask:
+ case cff_op_cntrmask:
+ set_width_ok = num_args & 1;
+ break;
+
+ case cff_op_endchar:
+ /* If there is a width specified for endchar, we either have */
+ /* 1 argument or 5 arguments. We like to argue. */
+ set_width_ok = in_dict
+ ? 0
+ : ( ( num_args == 5 ) || ( num_args == 1 ) );
+ break;
+
+ default:
+ set_width_ok = 0;
+ break;
+ }
+
+ if ( set_width_ok )
+ {
+ decoder->glyph_width = decoder->nominal_width +
+ ( stack[0] >> 16 );
+
+ if ( decoder->width_only )
+ {
+ /* we only want the advance width; stop here */
+ break;
+ }
+
+ /* Consumed an argument. */
+ num_args--;
+ }
+ }
+
+ decoder->read_width = 0;
+ req_args = 0;
+ }
+
+ req_args &= 0x000F;
+ if ( num_args < req_args )
+ goto Stack_Underflow;
+ args -= req_args;
+ num_args -= req_args;
+
+ /* At this point, `args' points to the first argument of the */
+ /* operand in case `req_args' isn't zero. Otherwise, we have */
+ /* to adjust `args' manually. */
+
+ /* Note that we only pop arguments from the stack which we */
+ /* really need and can digest so that we can continue in case */
+ /* of superfluous stack elements. */
+
+ switch ( op )
+ {
+ case cff_op_hstem:
+ case cff_op_vstem:
+ case cff_op_hstemhm:
+ case cff_op_vstemhm:
+ /* the number of arguments is always even here */
+ FT_TRACE4((
+ op == cff_op_hstem ? " hstem\n" :
+ ( op == cff_op_vstem ? " vstem\n" :
+ ( op == cff_op_hstemhm ? " hstemhm\n" : " vstemhm\n" ) ) ));
+
+ if ( hinter )
+ hinter->stems( hinter->hints,
+ ( op == cff_op_hstem || op == cff_op_hstemhm ),
+ num_args / 2,
+ args - ( num_args & ~1 ) );
+
+ decoder->num_hints += num_args / 2;
+ args = stack;
+ break;
+
+ case cff_op_hintmask:
+ case cff_op_cntrmask:
+ FT_TRACE4(( op == cff_op_hintmask ? " hintmask" : " cntrmask" ));
+
+ /* implement vstem when needed -- */
+ /* the specification doesn't say it, but this also works */
+ /* with the 'cntrmask' operator */
+ /* */
+ if ( num_args > 0 )
+ {
+ if ( hinter )
+ hinter->stems( hinter->hints,
+ 0,
+ num_args / 2,
+ args - ( num_args & ~1 ) );
+
+ decoder->num_hints += num_args / 2;
+ }
+
+ /* In a valid charstring there must be at least one byte */
+ /* after `hintmask' or `cntrmask' (e.g., for a `return' */
+ /* instruction). Additionally, there must be space for */
+ /* `num_hints' bits. */
+
+ if ( ( ip + ( ( decoder->num_hints + 7 ) >> 3 ) ) >= limit )
+ goto Syntax_Error;
+
+ if ( hinter )
+ {
+ if ( op == cff_op_hintmask )
+ hinter->hintmask( hinter->hints,
+ (FT_UInt)builder->current->n_points,
+ (FT_UInt)decoder->num_hints,
+ ip );
+ else
+ hinter->counter( hinter->hints,
+ (FT_UInt)decoder->num_hints,
+ ip );
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ {
+ FT_UInt maskbyte;
+
+
+ FT_TRACE4(( " (maskbytes:" ));
+
+ for ( maskbyte = 0;
+ maskbyte < (FT_UInt)( ( decoder->num_hints + 7 ) >> 3 );
+ maskbyte++, ip++ )
+ FT_TRACE4(( " 0x%02X", *ip ));
+
+ FT_TRACE4(( ")\n" ));
+ }
+#else
+ ip += ( decoder->num_hints + 7 ) >> 3;
+#endif
+ args = stack;
+ break;
+
+ case cff_op_rmoveto:
+ FT_TRACE4(( " rmoveto\n" ));
+
+ cff_builder_close_contour( builder );
+ builder->path_begun = 0;
+ x = ADD_LONG( x, args[-2] );
+ y = ADD_LONG( y, args[-1] );
+ args = stack;
+ break;
+
+ case cff_op_vmoveto:
+ FT_TRACE4(( " vmoveto\n" ));
+
+ cff_builder_close_contour( builder );
+ builder->path_begun = 0;
+ y = ADD_LONG( y, args[-1] );
+ args = stack;
+ break;
+
+ case cff_op_hmoveto:
+ FT_TRACE4(( " hmoveto\n" ));
+
+ cff_builder_close_contour( builder );
+ builder->path_begun = 0;
+ x = ADD_LONG( x, args[-1] );
+ args = stack;
+ break;
+
+ case cff_op_rlineto:
+ FT_TRACE4(( " rlineto\n" ));
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, num_args / 2 ) )
+ goto Fail;
+
+ if ( num_args < 2 )
+ goto Stack_Underflow;
+
+ args -= num_args & ~1;
+ while ( args < decoder->top )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 1 );
+ args += 2;
+ }
+ args = stack;
+ break;
+
+ case cff_op_hlineto:
+ case cff_op_vlineto:
+ {
+ FT_Int phase = ( op == cff_op_hlineto );
+
+
+ FT_TRACE4(( op == cff_op_hlineto ? " hlineto\n"
+ : " vlineto\n" ));
+
+ if ( num_args < 0 )
+ goto Stack_Underflow;
+
+ /* there exist subsetted fonts (found in PDFs) */
+ /* which call `hlineto' without arguments */
+ if ( num_args == 0 )
+ break;
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, num_args ) )
+ goto Fail;
+
+ args = stack;
+ while ( args < decoder->top )
+ {
+ if ( phase )
+ x = ADD_LONG( x, args[0] );
+ else
+ y = ADD_LONG( y, args[0] );
+
+ if ( cff_builder_add_point1( builder, x, y ) )
+ goto Fail;
+
+ args++;
+ phase ^= 1;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_rrcurveto:
+ {
+ FT_Int nargs;
+
+
+ FT_TRACE4(( " rrcurveto\n" ));
+
+ if ( num_args < 6 )
+ goto Stack_Underflow;
+
+ nargs = num_args - num_args % 6;
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, nargs / 2 ) )
+ goto Fail;
+
+ args -= nargs;
+ while ( args < decoder->top )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[2] );
+ y = ADD_LONG( y, args[3] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[4] );
+ y = ADD_LONG( y, args[5] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args += 6;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_vvcurveto:
+ {
+ FT_Int nargs;
+
+
+ FT_TRACE4(( " vvcurveto\n" ));
+
+ if ( num_args < 4 )
+ goto Stack_Underflow;
+
+ /* if num_args isn't of the form 4n or 4n+1, */
+ /* we enforce it by clearing the second bit */
+
+ nargs = num_args & ~2;
+
+ if ( cff_builder_start_point( builder, x, y ) )
+ goto Fail;
+
+ args -= nargs;
+
+ if ( nargs & 1 )
+ {
+ x = ADD_LONG( x, args[0] );
+ args++;
+ nargs--;
+ }
+
+ if ( cff_check_points( builder, 3 * ( nargs / 4 ) ) )
+ goto Fail;
+
+ while ( args < decoder->top )
+ {
+ y = ADD_LONG( y, args[0] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[1] );
+ y = ADD_LONG( y, args[2] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ y = ADD_LONG( y, args[3] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args += 4;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_hhcurveto:
+ {
+ FT_Int nargs;
+
+
+ FT_TRACE4(( " hhcurveto\n" ));
+
+ if ( num_args < 4 )
+ goto Stack_Underflow;
+
+ /* if num_args isn't of the form 4n or 4n+1, */
+ /* we enforce it by clearing the second bit */
+
+ nargs = num_args & ~2;
+
+ if ( cff_builder_start_point( builder, x, y ) )
+ goto Fail;
+
+ args -= nargs;
+ if ( nargs & 1 )
+ {
+ y = ADD_LONG( y, args[0] );
+ args++;
+ nargs--;
+ }
+
+ if ( cff_check_points( builder, 3 * ( nargs / 4 ) ) )
+ goto Fail;
+
+ while ( args < decoder->top )
+ {
+ x = ADD_LONG( x, args[0] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[1] );
+ y = ADD_LONG( y, args[2] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[3] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args += 4;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_vhcurveto:
+ case cff_op_hvcurveto:
+ {
+ FT_Int phase;
+ FT_Int nargs;
+
+
+ FT_TRACE4(( op == cff_op_vhcurveto ? " vhcurveto\n"
+ : " hvcurveto\n" ));
+
+ if ( cff_builder_start_point( builder, x, y ) )
+ goto Fail;
+
+ if ( num_args < 4 )
+ goto Stack_Underflow;
+
+ /* if num_args isn't of the form 8n, 8n+1, 8n+4, or 8n+5, */
+ /* we enforce it by clearing the second bit */
+
+ nargs = num_args & ~2;
+
+ args -= nargs;
+ if ( cff_check_points( builder, ( nargs / 4 ) * 3 ) )
+ goto Stack_Underflow;
+
+ phase = ( op == cff_op_hvcurveto );
+
+ while ( nargs >= 4 )
+ {
+ nargs -= 4;
+ if ( phase )
+ {
+ x = ADD_LONG( x, args[0] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[1] );
+ y = ADD_LONG( y, args[2] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ y = ADD_LONG( y, args[3] );
+ if ( nargs == 1 )
+ x = ADD_LONG( x, args[4] );
+ cff_builder_add_point( builder, x, y, 1 );
+ }
+ else
+ {
+ y = ADD_LONG( y, args[0] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[1] );
+ y = ADD_LONG( y, args[2] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[3] );
+ if ( nargs == 1 )
+ y = ADD_LONG( y, args[4] );
+ cff_builder_add_point( builder, x, y, 1 );
+ }
+ args += 4;
+ phase ^= 1;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_rlinecurve:
+ {
+ FT_Int num_lines;
+ FT_Int nargs;
+
+
+ FT_TRACE4(( " rlinecurve\n" ));
+
+ if ( num_args < 8 )
+ goto Stack_Underflow;
+
+ nargs = num_args & ~1;
+ num_lines = ( nargs - 6 ) / 2;
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, num_lines + 3 ) )
+ goto Fail;
+
+ args -= nargs;
+
+ /* first, add the line segments */
+ while ( num_lines > 0 )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args += 2;
+ num_lines--;
+ }
+
+ /* then the curve */
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[2] );
+ y = ADD_LONG( y, args[3] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[4] );
+ y = ADD_LONG( y, args[5] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ }
+ break;
+
+ case cff_op_rcurveline:
+ {
+ FT_Int num_curves;
+ FT_Int nargs;
+
+
+ FT_TRACE4(( " rcurveline\n" ));
+
+ if ( num_args < 8 )
+ goto Stack_Underflow;
+
+ nargs = num_args - 2;
+ nargs = nargs - nargs % 6 + 2;
+ num_curves = ( nargs - 2 ) / 6;
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, num_curves * 3 + 2 ) )
+ goto Fail;
+
+ args -= nargs;
+
+ /* first, add the curves */
+ while ( num_curves > 0 )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[2] );
+ y = ADD_LONG( y, args[3] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[4] );
+ y = ADD_LONG( y, args[5] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args += 6;
+ num_curves--;
+ }
+
+ /* then the final line */
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ }
+ break;
+
+ case cff_op_hflex1:
+ {
+ FT_Pos start_y;
+
+
+ FT_TRACE4(( " hflex1\n" ));
+
+ /* adding five more points: 4 control points, 1 on-curve point */
+ /* -- make sure we have enough space for the start point if it */
+ /* needs to be added */
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, 6 ) )
+ goto Fail;
+
+ /* record the starting point's y position for later use */
+ start_y = y;
+
+ /* first control point */
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* second control point */
+ x = ADD_LONG( x, args[2] );
+ y = ADD_LONG( y, args[3] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* join point; on curve, with y-value the same as the last */
+ /* control point's y-value */
+ x = ADD_LONG( x, args[4] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ /* third control point, with y-value the same as the join */
+ /* point's y-value */
+ x = ADD_LONG( x, args[5] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* fourth control point */
+ x = ADD_LONG( x, args[6] );
+ y = ADD_LONG( y, args[7] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* ending point, with y-value the same as the start */
+ x = ADD_LONG( x, args[8] );
+ y = start_y;
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ break;
+ }
+
+ case cff_op_hflex:
+ {
+ FT_Pos start_y;
+
+
+ FT_TRACE4(( " hflex\n" ));
+
+ /* adding six more points; 4 control points, 2 on-curve points */
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, 6 ) )
+ goto Fail;
+
+ /* record the starting point's y-position for later use */
+ start_y = y;
+
+ /* first control point */
+ x = ADD_LONG( x, args[0] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* second control point */
+ x = ADD_LONG( x, args[1] );
+ y = ADD_LONG( y, args[2] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* join point; on curve, with y-value the same as the last */
+ /* control point's y-value */
+ x = ADD_LONG( x, args[3] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ /* third control point, with y-value the same as the join */
+ /* point's y-value */
+ x = ADD_LONG( x, args[4] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* fourth control point */
+ x = ADD_LONG( x, args[5] );
+ y = start_y;
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* ending point, with y-value the same as the start point's */
+ /* y-value -- we don't add this point, though */
+ x = ADD_LONG( x, args[6] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ break;
+ }
+
+ case cff_op_flex1:
+ {
+ FT_Pos start_x, start_y; /* record start x, y values for */
+ /* alter use */
+ FT_Fixed dx = 0, dy = 0; /* used in horizontal/vertical */
+ /* algorithm below */
+ FT_Int horizontal, count;
+ FT_Fixed* temp;
+
+
+ FT_TRACE4(( " flex1\n" ));
+
+ /* adding six more points; 4 control points, 2 on-curve points */
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, 6 ) )
+ goto Fail;
+
+ /* record the starting point's x, y position for later use */
+ start_x = x;
+ start_y = y;
+
+ /* XXX: figure out whether this is supposed to be a horizontal */
+ /* or vertical flex; the Type 2 specification is vague... */
+
+ temp = args;
+
+ /* grab up to the last argument */
+ for ( count = 5; count > 0; count-- )
+ {
+ dx = ADD_LONG( dx, temp[0] );
+ dy = ADD_LONG( dy, temp[1] );
+ temp += 2;
+ }
+
+ if ( dx < 0 )
+ dx = -dx;
+ if ( dy < 0 )
+ dy = -dy;
+
+ /* strange test, but here it is... */
+ horizontal = ( dx > dy );
+
+ for ( count = 5; count > 0; count-- )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y,
+ (FT_Bool)( count == 3 ) );
+ args += 2;
+ }
+
+ /* is last operand an x- or y-delta? */
+ if ( horizontal )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = start_y;
+ }
+ else
+ {
+ x = start_x;
+ y = ADD_LONG( y, args[0] );
+ }
+
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ break;
+ }
+
+ case cff_op_flex:
+ {
+ FT_UInt count;
+
+
+ FT_TRACE4(( " flex\n" ));
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, 6 ) )
+ goto Fail;
+
+ for ( count = 6; count > 0; count-- )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y,
+ (FT_Bool)( count == 4 || count == 1 ) );
+ args += 2;
+ }
+
+ args = stack;
+ }
+ break;
+
+ case cff_op_seac:
+ FT_TRACE4(( " seac\n" ));
+
+ error = cff_operator_seac( decoder,
+ args[0], args[1], args[2],
+ (FT_Int)( args[3] >> 16 ),
+ (FT_Int)( args[4] >> 16 ) );
+
+ /* add current outline to the glyph slot */
+ FT_GlyphLoader_Add( builder->loader );
+
+ /* return now! */
+ FT_TRACE4(( "\n" ));
+ return error;
+
+ case cff_op_endchar:
+ /* in dictionaries, `endchar' simply indicates end of data */
+ if ( in_dict )
+ return error;
+
+ FT_TRACE4(( " endchar\n" ));
+
+ /* We are going to emulate the seac operator. */
+ if ( num_args >= 4 )
+ {
+ /* Save glyph width so that the subglyphs don't overwrite it. */
+ FT_Pos glyph_width = decoder->glyph_width;
+
+
+ error = cff_operator_seac( decoder,
+ 0L, args[-4], args[-3],
+ (FT_Int)( args[-2] >> 16 ),
+ (FT_Int)( args[-1] >> 16 ) );
+
+ decoder->glyph_width = glyph_width;
+ }
+ else
+ {
+ cff_builder_close_contour( builder );
+
+ /* close hints recording session */
+ if ( hinter )
+ {
+ if ( hinter->close( hinter->hints,
+ (FT_UInt)builder->current->n_points ) )
+ goto Syntax_Error;
+
+ /* apply hints to the loaded glyph outline now */
+ error = hinter->apply( hinter->hints,
+ builder->current,
+ (PSH_Globals)builder->hints_globals,
+ decoder->hint_mode );
+ if ( error )
+ goto Fail;
+ }
+
+ /* add current outline to the glyph slot */
+ FT_GlyphLoader_Add( builder->loader );
+ }
+
+ /* return now! */
+ FT_TRACE4(( "\n" ));
+ return error;
+
+ case cff_op_abs:
+ FT_TRACE4(( " abs\n" ));
+
+ if ( args[0] < 0 )
+ {
+ if ( args[0] == FT_LONG_MIN )
+ args[0] = FT_LONG_MAX;
+ else
+ args[0] = -args[0];
+ }
+ args++;
+ break;
+
+ case cff_op_add:
+ FT_TRACE4(( " add\n" ));
+
+ args[0] = ADD_LONG( args[0], args[1] );
+ args++;
+ break;
+
+ case cff_op_sub:
+ FT_TRACE4(( " sub\n" ));
+
+ args[0] = SUB_LONG( args[0], args[1] );
+ args++;
+ break;
+
+ case cff_op_div:
+ FT_TRACE4(( " div\n" ));
+
+ args[0] = FT_DivFix( args[0], args[1] );
+ args++;
+ break;
+
+ case cff_op_neg:
+ FT_TRACE4(( " neg\n" ));
+
+ if ( args[0] == FT_LONG_MIN )
+ args[0] = FT_LONG_MAX;
+ args[0] = -args[0];
+ args++;
+ break;
+
+ case cff_op_random:
+ FT_TRACE4(( " random\n" ));
+
+ /* only use the lower 16 bits of `random' */
+ /* to generate a number in the range (0;1] */
+ args[0] = (FT_Fixed)
+ ( ( decoder->current_subfont->random & 0xFFFF ) + 1 );
+ args++;
+
+ decoder->current_subfont->random =
+ cff_random( decoder->current_subfont->random );
+ break;
+
+ case cff_op_mul:
+ FT_TRACE4(( " mul\n" ));
+
+ args[0] = FT_MulFix( args[0], args[1] );
+ args++;
+ break;
+
+ case cff_op_sqrt:
+ FT_TRACE4(( " sqrt\n" ));
+
+ if ( args[0] > 0 )
+ {
+ FT_Fixed root = args[0];
+ FT_Fixed new_root;
+
+
+ for (;;)
+ {
+ new_root = ( root + FT_DivFix( args[0], root ) + 1 ) >> 1;
+ if ( new_root == root )
+ break;
+ root = new_root;
+ }
+ args[0] = new_root;
+ }
+ else
+ args[0] = 0;
+ args++;
+ break;
+
+ case cff_op_drop:
+ /* nothing */
+ FT_TRACE4(( " drop\n" ));
+
+ break;
+
+ case cff_op_exch:
+ {
+ FT_Fixed tmp;
+
+
+ FT_TRACE4(( " exch\n" ));
+
+ tmp = args[0];
+ args[0] = args[1];
+ args[1] = tmp;
+ args += 2;
+ }
+ break;
+
+ case cff_op_index:
+ {
+ FT_Int idx = (FT_Int)( args[0] >> 16 );
+
+
+ FT_TRACE4(( " index\n" ));
+
+ if ( idx < 0 )
+ idx = 0;
+ else if ( idx > num_args - 2 )
+ idx = num_args - 2;
+ args[0] = args[-( idx + 1 )];
+ args++;
+ }
+ break;
+
+ case cff_op_roll:
+ {
+ FT_Int count = (FT_Int)( args[0] >> 16 );
+ FT_Int idx = (FT_Int)( args[1] >> 16 );
+
+
+ FT_TRACE4(( " roll\n" ));
+
+ if ( count <= 0 )
+ count = 1;
+
+ args -= count;
+ if ( args < stack )
+ goto Stack_Underflow;
+
+ if ( idx >= 0 )
+ {
+ while ( idx > 0 )
+ {
+ FT_Fixed tmp = args[count - 1];
+ FT_Int i;
+
+
+ for ( i = count - 2; i >= 0; i-- )
+ args[i + 1] = args[i];
+ args[0] = tmp;
+ idx--;
+ }
+ }
+ else
+ {
+ while ( idx < 0 )
+ {
+ FT_Fixed tmp = args[0];
+ FT_Int i;
+
+
+ for ( i = 0; i < count - 1; i++ )
+ args[i] = args[i + 1];
+ args[count - 1] = tmp;
+ idx++;
+ }
+ }
+ args += count;
+ }
+ break;
+
+ case cff_op_dup:
+ FT_TRACE4(( " dup\n" ));
+
+ args[1] = args[0];
+ args += 2;
+ break;
+
+ case cff_op_put:
+ {
+ FT_Fixed val = args[0];
+ FT_Int idx = (FT_Int)( args[1] >> 16 );
+
+
+ FT_TRACE4(( " put\n" ));
+
+ /* the Type2 specification before version 16-March-2000 */
+ /* didn't give a hard-coded size limit of the temporary */
+ /* storage array; instead, an argument of the */
+ /* `MultipleMaster' operator set the size */
+ if ( idx >= 0 && idx < CFF_MAX_TRANS_ELEMENTS )
+ decoder->buildchar[idx] = val;
+ }
+ break;
+
+ case cff_op_get:
+ {
+ FT_Int idx = (FT_Int)( args[0] >> 16 );
+ FT_Fixed val = 0;
+
+
+ FT_TRACE4(( " get\n" ));
+
+ if ( idx >= 0 && idx < CFF_MAX_TRANS_ELEMENTS )
+ val = decoder->buildchar[idx];
+
+ args[0] = val;
+ args++;
+ }
+ break;
+
+ case cff_op_store:
+ /* this operator was removed from the Type2 specification */
+ /* in version 16-March-2000 */
+
+ /* since we currently don't handle interpolation of multiple */
+ /* master fonts, this is a no-op */
+ FT_TRACE4(( " store\n" ));
+ break;
+
+ case cff_op_load:
+ /* this operator was removed from the Type2 specification */
+ /* in version 16-March-2000 */
+ {
+ FT_Int reg_idx = (FT_Int)args[0];
+ FT_Int idx = (FT_Int)args[1];
+ FT_Int count = (FT_Int)args[2];
+
+
+ FT_TRACE4(( " load\n" ));
+
+ /* since we currently don't handle interpolation of multiple */
+ /* master fonts, we store a vector [1 0 0 ...] in the */
+ /* temporary storage array regardless of the Registry index */
+ if ( reg_idx >= 0 && reg_idx <= 2 &&
+ idx >= 0 && idx < CFF_MAX_TRANS_ELEMENTS &&
+ count >= 0 && count <= num_axes )
+ {
+ FT_Int end, i;
+
+
+ end = FT_MIN( idx + count, CFF_MAX_TRANS_ELEMENTS );
+
+ if ( idx < end )
+ decoder->buildchar[idx] = 1 << 16;
+
+ for ( i = idx + 1; i < end; i++ )
+ decoder->buildchar[i] = 0;
+ }
+ }
+ break;
+
+ case cff_op_blend:
+ /* this operator was removed from the Type2 specification */
+ /* in version 16-March-2000 */
+ {
+ FT_Int num_results = (FT_Int)( args[0] >> 16 );
+
+
+ FT_TRACE4(( " blend\n" ));
+
+ if ( num_results < 0 )
+ goto Syntax_Error;
+
+ if ( num_results * (FT_Int)num_designs > num_args )
+ goto Stack_Underflow;
+
+ /* since we currently don't handle interpolation of multiple */
+ /* master fonts, return the `num_results' values of the */
+ /* first master */
+ args -= num_results * ( num_designs - 1 );
+ num_args -= num_results * ( num_designs - 1 );
+ }
+ break;
+
+ case cff_op_dotsection:
+ /* this operator is deprecated and ignored by the parser */
+ FT_TRACE4(( " dotsection\n" ));
+ break;
+
+ case cff_op_closepath:
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from probably */
+ /* Type 1 to CFF, and some parsers seem to accept it */
+
+ FT_TRACE4(( " closepath (invalid op)\n" ));
+
+ args = stack;
+ break;
+
+ case cff_op_hsbw:
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from probably */
+ /* Type 1 to CFF, and some parsers seem to accept it */
+
+ FT_TRACE4(( " hsbw (invalid op)\n" ));
+
+ decoder->glyph_width =
+ ADD_LONG( decoder->nominal_width, ( args[1] >> 16 ) );
+
+ decoder->builder.left_bearing.x = args[0];
+ decoder->builder.left_bearing.y = 0;
+
+ x = ADD_LONG( decoder->builder.pos_x, args[0] );
+ y = decoder->builder.pos_y;
+ args = stack;
+ break;
+
+ case cff_op_sbw:
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from probably */
+ /* Type 1 to CFF, and some parsers seem to accept it */
+
+ FT_TRACE4(( " sbw (invalid op)\n" ));
+
+ decoder->glyph_width =
+ ADD_LONG( decoder->nominal_width, ( args[2] >> 16 ) );
+
+ decoder->builder.left_bearing.x = args[0];
+ decoder->builder.left_bearing.y = args[1];
+
+ x = ADD_LONG( decoder->builder.pos_x, args[0] );
+ y = ADD_LONG( decoder->builder.pos_y, args[1] );
+ args = stack;
+ break;
+
+ case cff_op_setcurrentpoint:
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from probably */
+ /* Type 1 to CFF, and some parsers seem to accept it */
+
+ FT_TRACE4(( " setcurrentpoint (invalid op)\n" ));
+
+ x = ADD_LONG( decoder->builder.pos_x, args[0] );
+ y = ADD_LONG( decoder->builder.pos_y, args[1] );
+ args = stack;
+ break;
+
+ case cff_op_callothersubr:
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from probably */
+ /* Type 1 to CFF, and some parsers seem to accept it */
+
+ FT_TRACE4(( " callothersubr (invalid op)\n" ));
+
+ /* subsequent `pop' operands should add the arguments, */
+ /* this is the implementation described for `unknown' other */
+ /* subroutines in the Type1 spec. */
+ /* */
+ /* XXX Fix return arguments (see discussion below). */
+ args -= 2 + ( args[-2] >> 16 );
+ if ( args < stack )
+ goto Stack_Underflow;
+ break;
+
+ case cff_op_pop:
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from probably */
+ /* Type 1 to CFF, and some parsers seem to accept it */
+
+ FT_TRACE4(( " pop (invalid op)\n" ));
+
+ /* XXX Increasing `args' is wrong: After a certain number of */
+ /* `pop's we get a stack overflow. Reason for doing it is */
+ /* code like this (actually found in a CFF font): */
+ /* */
+ /* 17 1 3 callothersubr */
+ /* pop */
+ /* callsubr */
+ /* */
+ /* Since we handle `callothersubr' as a no-op, and */
+ /* `callsubr' needs at least one argument, `pop' can't be a */
+ /* no-op too as it basically should be. */
+ /* */
+ /* The right solution would be to provide real support for */
+ /* `callothersubr' as done in `t1decode.c', however, given */
+ /* the fact that CFF fonts with `pop' are invalid, it is */
+ /* questionable whether it is worth the time. */
+ args++;
+ break;
+
+ case cff_op_and:
+ {
+ FT_Fixed cond = ( args[0] && args[1] );
+
+
+ FT_TRACE4(( " and\n" ));
+
+ args[0] = cond ? 0x10000L : 0;
+ args++;
+ }
+ break;
+
+ case cff_op_or:
+ {
+ FT_Fixed cond = ( args[0] || args[1] );
+
+
+ FT_TRACE4(( " or\n" ));
+
+ args[0] = cond ? 0x10000L : 0;
+ args++;
+ }
+ break;
+
+ case cff_op_not:
+ {
+ FT_Fixed cond = !args[0];
+
+
+ FT_TRACE4(( " not\n" ));
+
+ args[0] = cond ? 0x10000L : 0;
+ args++;
+ }
+ break;
+
+ case cff_op_eq:
+ {
+ FT_Fixed cond = ( args[0] == args[1] );
+
+
+ FT_TRACE4(( " eq\n" ));
+
+ args[0] = cond ? 0x10000L : 0;
+ args++;
+ }
+ break;
+
+ case cff_op_ifelse:
+ {
+ FT_Fixed cond = ( args[2] <= args[3] );
+
+
+ FT_TRACE4(( " ifelse\n" ));
+
+ if ( !cond )
+ args[0] = args[1];
+ args++;
+ }
+ break;
+
+ case cff_op_callsubr:
+ {
+ FT_UInt idx = (FT_UInt)( ( args[0] >> 16 ) +
+ decoder->locals_bias );
+
+
+ FT_TRACE4(( " callsubr (idx %d, entering level %d)\n",
+ idx,
+ zone - decoder->zones + 1 ));
+
+ if ( idx >= decoder->num_locals )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " invalid local subr index\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " too many nested subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ zone->cursor = ip; /* save current instruction pointer */
+
+ zone++;
+ zone->base = decoder->locals[idx];
+ zone->limit = decoder->locals[idx + 1];
+ zone->cursor = zone->base;
+
+ if ( !zone->base || zone->limit == zone->base )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " invoking empty subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone = zone;
+ ip = zone->base;
+ limit = zone->limit;
+ }
+ break;
+
+ case cff_op_callgsubr:
+ {
+ FT_UInt idx = (FT_UInt)( ( args[0] >> 16 ) +
+ decoder->globals_bias );
+
+
+ FT_TRACE4(( " callgsubr (idx %d, entering level %d)\n",
+ idx,
+ zone - decoder->zones + 1 ));
+
+ if ( idx >= decoder->num_globals )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " invalid global subr index\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " too many nested subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ zone->cursor = ip; /* save current instruction pointer */
+
+ zone++;
+ zone->base = decoder->globals[idx];
+ zone->limit = decoder->globals[idx + 1];
+ zone->cursor = zone->base;
+
+ if ( !zone->base || zone->limit == zone->base )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " invoking empty subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone = zone;
+ ip = zone->base;
+ limit = zone->limit;
+ }
+ break;
+
+ case cff_op_return:
+ FT_TRACE4(( " return (leaving level %d)\n",
+ decoder->zone - decoder->zones ));
+
+ if ( decoder->zone <= decoder->zones )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " unexpected return\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone--;
+ zone = decoder->zone;
+ ip = zone->cursor;
+ limit = zone->limit;
+ break;
+
+ default:
+ FT_ERROR(( "Unimplemented opcode: %d", ip[-1] ));
+
+ if ( ip[-1] == 12 )
+ FT_ERROR(( " %d", ip[0] ));
+ FT_ERROR(( "\n" ));
+
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+ decoder->top = args;
+
+ if ( decoder->top - stack >= CFF_MAX_OPERANDS )
+ goto Stack_Overflow;
+
+ } /* general operator processing */
+
+ } /* while ip < limit */
+
+ FT_TRACE4(( "..end..\n\n" ));
+
+ Fail:
+ return error;
+
+ MM_Error:
+ FT_TRACE4(( "cff_decoder_parse_charstrings:"
+ " invalid opcode found in top DICT charstring\n"));
+ return FT_THROW( Invalid_File_Format );
+
+ Syntax_Error:
+ FT_TRACE4(( "cff_decoder_parse_charstrings: syntax error\n" ));
+ return FT_THROW( Invalid_File_Format );
+
+ Stack_Underflow:
+ FT_TRACE4(( "cff_decoder_parse_charstrings: stack underflow\n" ));
+ return FT_THROW( Too_Few_Arguments );
+
+ Stack_Overflow:
+ FT_TRACE4(( "cff_decoder_parse_charstrings: stack overflow\n" ));
+ return FT_THROW( Stack_Overflow );
+ }
+
+#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cff_decoder_init */
+ /* */
+ /* <Description> */
+ /* Initializes a given glyph decoder. */
+ /* */
+ /* <InOut> */
+ /* decoder :: A pointer to the glyph builder to initialize. */
+ /* */
+ /* <Input> */
+ /* face :: The current face object. */
+ /* */
+ /* size :: The current size object. */
+ /* */
+ /* slot :: The current glyph object. */
+ /* */
+ /* hinting :: Whether hinting is active. */
+ /* */
+ /* hint_mode :: The hinting mode. */
+ /* */
+ FT_LOCAL_DEF( void )
+ cff_decoder_init( CFF_Decoder* decoder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot slot,
+ FT_Bool hinting,
+ FT_Render_Mode hint_mode,
+ CFF_Decoder_Get_Glyph_Callback get_callback,
+ CFF_Decoder_Free_Glyph_Callback free_callback )
+ {
+ CFF_Font cff = (CFF_Font)face->extra.data;
+
+
+ /* clear everything */
+ FT_ZERO( decoder );
+
+ /* initialize builder */
+ cff_builder_init( &decoder->builder, face, size, slot, hinting );
+
+ /* initialize Type2 decoder */
+ decoder->cff = cff;
+ decoder->num_globals = cff->global_subrs_index.count;
+ decoder->globals = cff->global_subrs;
+ decoder->globals_bias = cff_compute_bias(
+ cff->top_font.font_dict.charstring_type,
+ decoder->num_globals );
+
+ decoder->hint_mode = hint_mode;
+
+ decoder->get_glyph_callback = get_callback;
+ decoder->free_glyph_callback = free_callback;
+ }
+
+
+ /* this function is used to select the subfont */
+ /* and the locals subrs array */
+ FT_LOCAL_DEF( FT_Error )
+ cff_decoder_prepare( CFF_Decoder* decoder,
+ CFF_Size size,
+ FT_UInt glyph_index )
+ {
+ CFF_Builder *builder = &decoder->builder;
+ CFF_Font cff = (CFF_Font)builder->face->extra.data;
+ CFF_SubFont sub = &cff->top_font;
+ FT_Error error = FT_Err_Ok;
+
+ FT_Service_CFFLoad cffload = (FT_Service_CFFLoad)cff->cffload;
+
+
+ /* manage CID fonts */
+ if ( cff->num_subfonts )
+ {
+ FT_Byte fd_index = cffload->fd_select_get( &cff->fd_select,
+ glyph_index );
+
+
+ if ( fd_index >= cff->num_subfonts )
+ {
+ FT_TRACE4(( "cff_decoder_prepare: invalid CID subfont index\n" ));
+ error = FT_THROW( Invalid_File_Format );
+ goto Exit;
+ }
+
+ FT_TRACE3(( " in subfont %d:\n", fd_index ));
+
+ sub = cff->subfonts[fd_index];
+
+ if ( builder->hints_funcs && size )
+ {
+ FT_Size ftsize = FT_SIZE( size );
+ CFF_Internal internal = (CFF_Internal)ftsize->internal->module_data;
+
+
+ /* for CFFs without subfonts, this value has already been set */
+ builder->hints_globals = (void *)internal->subfonts[fd_index];
+ }
+ }
+
+ decoder->num_locals = sub->local_subrs_index.count;
+ decoder->locals = sub->local_subrs;
+ decoder->locals_bias = cff_compute_bias(
+ decoder->cff->top_font.font_dict.charstring_type,
+ decoder->num_locals );
+
+ decoder->glyph_width = sub->private_dict.default_width;
+ decoder->nominal_width = sub->private_dict.nominal_width;
+
+ decoder->current_subfont = sub;
+
+ Exit:
+ return error;
+ }
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/cffdecode.h b/thirdparty/freetype/src/psaux/cffdecode.h
new file mode 100644
index 0000000000..0d4f5fef63
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/cffdecode.h
@@ -0,0 +1,64 @@
+/***************************************************************************/
+/* */
+/* cffdecode.h */
+/* */
+/* PostScript CFF (Type 2) decoding routines (specification). */
+/* */
+/* Copyright 2017-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef CFFDECODE_H_
+#define CFFDECODE_H_
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+
+FT_BEGIN_HEADER
+
+ FT_LOCAL( void )
+ cff_decoder_init( CFF_Decoder* decoder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot slot,
+ FT_Bool hinting,
+ FT_Render_Mode hint_mode,
+ CFF_Decoder_Get_Glyph_Callback get_callback,
+ CFF_Decoder_Free_Glyph_Callback free_callback );
+
+ FT_LOCAL( FT_Error )
+ cff_decoder_prepare( CFF_Decoder* decoder,
+ CFF_Size size,
+ FT_UInt glyph_index );
+
+
+ FT_LOCAL( FT_Int )
+ cff_lookup_glyph_by_stdcharcode( CFF_Font cff,
+ FT_Int charcode );
+
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ FT_LOCAL( FT_Error )
+ cff_decoder_parse_charstrings( CFF_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len,
+ FT_Bool in_dict );
+#endif
+
+
+FT_END_HEADER
+
+#endif
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/module.mk b/thirdparty/freetype/src/psaux/module.mk
index c70a227166..6584d075a2 100644
--- a/thirdparty/freetype/src/psaux/module.mk
+++ b/thirdparty/freetype/src/psaux/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/psaux/psarrst.c b/thirdparty/freetype/src/psaux/psarrst.c
new file mode 100644
index 0000000000..a8780947f9
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psarrst.c
@@ -0,0 +1,241 @@
+/***************************************************************************/
+/* */
+/* psarrst.c */
+/* */
+/* Adobe's code for Array Stacks (body). */
+/* */
+/* Copyright 2007-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#include "psft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "psglue.h"
+#include "psarrst.h"
+
+#include "pserror.h"
+
+
+ /*
+ * CF2_ArrStack uses an error pointer, to enable shared errors.
+ * Shared errors are necessary when multiple objects allow the program
+ * to continue after detecting errors. Only the first error should be
+ * recorded.
+ */
+
+ FT_LOCAL_DEF( void )
+ cf2_arrstack_init( CF2_ArrStack arrstack,
+ FT_Memory memory,
+ FT_Error* error,
+ size_t sizeItem )
+ {
+ FT_ASSERT( arrstack );
+
+ /* initialize the structure */
+ arrstack->memory = memory;
+ arrstack->error = error;
+ arrstack->sizeItem = sizeItem;
+ arrstack->allocated = 0;
+ arrstack->chunk = 10; /* chunks of 10 items */
+ arrstack->count = 0;
+ arrstack->totalSize = 0;
+ arrstack->ptr = NULL;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_arrstack_finalize( CF2_ArrStack arrstack )
+ {
+ FT_Memory memory = arrstack->memory; /* for FT_FREE */
+
+
+ FT_ASSERT( arrstack );
+
+ arrstack->allocated = 0;
+ arrstack->count = 0;
+ arrstack->totalSize = 0;
+
+ /* free the data buffer */
+ FT_FREE( arrstack->ptr );
+ }
+
+
+ /* allocate or reallocate the buffer size; */
+ /* return false on memory error */
+ static FT_Bool
+ cf2_arrstack_setNumElements( CF2_ArrStack arrstack,
+ size_t numElements )
+ {
+ FT_ASSERT( arrstack );
+
+ {
+ FT_Error error = FT_Err_Ok; /* for FT_REALLOC */
+ FT_Memory memory = arrstack->memory; /* for FT_REALLOC */
+
+ size_t newSize = numElements * arrstack->sizeItem;
+
+
+ if ( numElements > FT_LONG_MAX / arrstack->sizeItem )
+ goto exit;
+
+
+ FT_ASSERT( newSize > 0 ); /* avoid realloc with zero size */
+
+ if ( !FT_REALLOC( arrstack->ptr, arrstack->totalSize, newSize ) )
+ {
+ arrstack->allocated = numElements;
+ arrstack->totalSize = newSize;
+
+ if ( arrstack->count > numElements )
+ {
+ /* we truncated the list! */
+ CF2_SET_ERROR( arrstack->error, Stack_Overflow );
+ arrstack->count = numElements;
+ return FALSE;
+ }
+
+ return TRUE; /* success */
+ }
+ }
+
+ exit:
+ /* if there's not already an error, store this one */
+ CF2_SET_ERROR( arrstack->error, Out_Of_Memory );
+
+ return FALSE;
+ }
+
+
+ /* set the count, ensuring allocation is sufficient */
+ FT_LOCAL_DEF( void )
+ cf2_arrstack_setCount( CF2_ArrStack arrstack,
+ size_t numElements )
+ {
+ FT_ASSERT( arrstack );
+
+ if ( numElements > arrstack->allocated )
+ {
+ /* expand the allocation first */
+ if ( !cf2_arrstack_setNumElements( arrstack, numElements ) )
+ return;
+ }
+
+ arrstack->count = numElements;
+ }
+
+
+ /* clear the count */
+ FT_LOCAL_DEF( void )
+ cf2_arrstack_clear( CF2_ArrStack arrstack )
+ {
+ FT_ASSERT( arrstack );
+
+ arrstack->count = 0;
+ }
+
+
+ /* current number of items */
+ FT_LOCAL_DEF( size_t )
+ cf2_arrstack_size( const CF2_ArrStack arrstack )
+ {
+ FT_ASSERT( arrstack );
+
+ return arrstack->count;
+ }
+
+
+ FT_LOCAL_DEF( void* )
+ cf2_arrstack_getBuffer( const CF2_ArrStack arrstack )
+ {
+ FT_ASSERT( arrstack );
+
+ return arrstack->ptr;
+ }
+
+
+ /* return pointer to the given element */
+ FT_LOCAL_DEF( void* )
+ cf2_arrstack_getPointer( const CF2_ArrStack arrstack,
+ size_t idx )
+ {
+ void* newPtr;
+
+
+ FT_ASSERT( arrstack );
+
+ if ( idx >= arrstack->count )
+ {
+ /* overflow */
+ CF2_SET_ERROR( arrstack->error, Stack_Overflow );
+ idx = 0; /* choose safe default */
+ }
+
+ newPtr = (FT_Byte*)arrstack->ptr + idx * arrstack->sizeItem;
+
+ return newPtr;
+ }
+
+
+ /* push (append) an element at the end of the list; */
+ /* return false on memory error */
+ /* TODO: should there be a length param for extra checking? */
+ FT_LOCAL_DEF( void )
+ cf2_arrstack_push( CF2_ArrStack arrstack,
+ const void* ptr )
+ {
+ FT_ASSERT( arrstack );
+
+ if ( arrstack->count == arrstack->allocated )
+ {
+ /* grow the buffer by one chunk */
+ if ( !cf2_arrstack_setNumElements(
+ arrstack, arrstack->allocated + arrstack->chunk ) )
+ {
+ /* on error, ignore the push */
+ return;
+ }
+ }
+
+ FT_ASSERT( ptr );
+
+ {
+ size_t offset = arrstack->count * arrstack->sizeItem;
+ void* newPtr = (FT_Byte*)arrstack->ptr + offset;
+
+
+ FT_MEM_COPY( newPtr, ptr, arrstack->sizeItem );
+ arrstack->count += 1;
+ }
+ }
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psarrst.h b/thirdparty/freetype/src/psaux/psarrst.h
new file mode 100644
index 0000000000..b3568eb61f
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psarrst.h
@@ -0,0 +1,100 @@
+/***************************************************************************/
+/* */
+/* psarrst.h */
+/* */
+/* Adobe's code for Array Stacks (specification). */
+/* */
+/* Copyright 2007-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef PSARRST_H_
+#define PSARRST_H_
+
+
+#include "pserror.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /* need to define the struct here (not opaque) so it can be allocated by */
+ /* clients */
+ typedef struct CF2_ArrStackRec_
+ {
+ FT_Memory memory;
+ FT_Error* error;
+
+ size_t sizeItem; /* bytes per element */
+ size_t allocated; /* items allocated */
+ size_t chunk; /* allocation increment in items */
+ size_t count; /* number of elements allocated */
+ size_t totalSize; /* total bytes allocated */
+
+ void* ptr; /* ptr to data */
+
+ } CF2_ArrStackRec, *CF2_ArrStack;
+
+
+ FT_LOCAL( void )
+ cf2_arrstack_init( CF2_ArrStack arrstack,
+ FT_Memory memory,
+ FT_Error* error,
+ size_t sizeItem );
+ FT_LOCAL( void )
+ cf2_arrstack_finalize( CF2_ArrStack arrstack );
+
+ FT_LOCAL( void )
+ cf2_arrstack_setCount( CF2_ArrStack arrstack,
+ size_t numElements );
+ FT_LOCAL( void )
+ cf2_arrstack_clear( CF2_ArrStack arrstack );
+ FT_LOCAL( size_t )
+ cf2_arrstack_size( const CF2_ArrStack arrstack );
+
+ FT_LOCAL( void* )
+ cf2_arrstack_getBuffer( const CF2_ArrStack arrstack );
+ FT_LOCAL( void* )
+ cf2_arrstack_getPointer( const CF2_ArrStack arrstack,
+ size_t idx );
+
+ FT_LOCAL( void )
+ cf2_arrstack_push( CF2_ArrStack arrstack,
+ const void* ptr );
+
+
+FT_END_HEADER
+
+
+#endif /* PSARRST_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psaux.c b/thirdparty/freetype/src/psaux/psaux.c
index c373aa7d5b..fb447fcdbb 100644
--- a/thirdparty/freetype/src/psaux/psaux.c
+++ b/thirdparty/freetype/src/psaux/psaux.c
@@ -4,7 +4,7 @@
/* */
/* FreeType auxiliary PostScript driver component (body only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -25,6 +25,17 @@
#include "psobjs.c"
#include "t1cmap.c"
#include "t1decode.c"
+#include "cffdecode.c"
+
+#include "psarrst.c"
+#include "psblues.c"
+#include "pserror.c"
+#include "psfont.c"
+#include "psft.c"
+#include "pshints.c"
+#include "psintrp.c"
+#include "psread.c"
+#include "psstack.c"
/* END */
diff --git a/thirdparty/freetype/src/psaux/psauxerr.h b/thirdparty/freetype/src/psaux/psauxerr.h
index 1d7ac6001b..cc33fd2eea 100644
--- a/thirdparty/freetype/src/psaux/psauxerr.h
+++ b/thirdparty/freetype/src/psaux/psauxerr.h
@@ -4,7 +4,7 @@
/* */
/* PS auxiliary module error codes (specification only). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psaux/psauxmod.c b/thirdparty/freetype/src/psaux/psauxmod.c
index 1f589cefc2..ee497085cc 100644
--- a/thirdparty/freetype/src/psaux/psauxmod.c
+++ b/thirdparty/freetype/src/psaux/psauxmod.c
@@ -4,7 +4,7 @@
/* */
/* FreeType auxiliary PostScript module implementation (body). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -21,6 +21,8 @@
#include "psobjs.h"
#include "t1decode.h"
#include "t1cmap.h"
+#include "psft.h"
+#include "cffdecode.h"
#ifndef T1_CONFIG_OPTION_NO_AFM
#include "afmparse.h"
@@ -60,6 +62,14 @@
FT_CALLBACK_TABLE_DEF
+ const PS_Builder_FuncsRec ps_builder_funcs =
+ {
+ ps_builder_init, /* init */
+ ps_builder_done /* done */
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
const T1_Builder_FuncsRec t1_builder_funcs =
{
t1_builder_init, /* init */
@@ -77,9 +87,14 @@
FT_CALLBACK_TABLE_DEF
const T1_Decoder_FuncsRec t1_decoder_funcs =
{
- t1_decoder_init, /* init */
- t1_decoder_done, /* done */
- t1_decoder_parse_charstrings /* parse_charstrings */
+ t1_decoder_init, /* init */
+ t1_decoder_done, /* done */
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ t1_decoder_parse_charstrings, /* parse_charstrings_old */
+#else
+ t1_decoder_parse_metrics, /* parse_metrics */
+#endif
+ cf2_decoder_parse_charstrings /* parse_charstrings */
};
@@ -104,6 +119,34 @@
};
+ FT_CALLBACK_TABLE_DEF
+ const CFF_Builder_FuncsRec cff_builder_funcs =
+ {
+ cff_builder_init, /* init */
+ cff_builder_done, /* done */
+
+ cff_check_points, /* check_points */
+ cff_builder_add_point, /* add_point */
+ cff_builder_add_point1, /* add_point1 */
+ cff_builder_add_contour, /* add_contour */
+ cff_builder_start_point, /* start_point */
+ cff_builder_close_contour /* close_contour */
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const CFF_Decoder_FuncsRec cff_decoder_funcs =
+ {
+ cff_decoder_init, /* init */
+ cff_decoder_prepare, /* prepare */
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ cff_decoder_parse_charstrings, /* parse_charstrings_old */
+#endif
+ cf2_decoder_parse_charstrings /* parse_charstrings */
+ };
+
+
static
const PSAux_Interface psaux_interface =
{
@@ -112,6 +155,9 @@
&t1_builder_funcs,
&t1_decoder_funcs,
t1_decrypt,
+ cff_random,
+ ps_decoder_init,
+ t1_make_subfont,
(const T1_CMap_ClassesRec*) &t1_cmap_classes,
@@ -120,6 +166,8 @@
#else
0,
#endif
+
+ &cff_decoder_funcs,
};
diff --git a/thirdparty/freetype/src/psaux/psauxmod.h b/thirdparty/freetype/src/psaux/psauxmod.h
index 926f37eba5..f30978f022 100644
--- a/thirdparty/freetype/src/psaux/psauxmod.h
+++ b/thirdparty/freetype/src/psaux/psauxmod.h
@@ -4,7 +4,7 @@
/* */
/* FreeType auxiliary PostScript module implementation (specification). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -23,6 +23,8 @@
#include <ft2build.h>
#include FT_MODULE_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
FT_BEGIN_HEADER
@@ -31,6 +33,13 @@ FT_BEGIN_HEADER
#endif
+ FT_CALLBACK_TABLE
+ const CFF_Builder_FuncsRec cff_builder_funcs;
+
+ FT_CALLBACK_TABLE
+ const PS_Builder_FuncsRec ps_builder_funcs;
+
+
FT_EXPORT_VAR( const FT_Module_Class ) psaux_driver_class;
diff --git a/thirdparty/freetype/src/psaux/psblues.c b/thirdparty/freetype/src/psaux/psblues.c
new file mode 100644
index 0000000000..ae39d03c77
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psblues.c
@@ -0,0 +1,582 @@
+/***************************************************************************/
+/* */
+/* psblues.c */
+/* */
+/* Adobe's code for handling Blue Zones (body). */
+/* */
+/* Copyright 2009-2014 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#include "psft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "psblues.h"
+#include "pshints.h"
+#include "psfont.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cf2blues
+
+
+ /*
+ * For blue values, the FreeType parser produces an array of integers,
+ * while the Adobe CFF engine produces an array of fixed.
+ * Define a macro to convert FreeType to fixed.
+ */
+#define cf2_blueToFixed( x ) cf2_intToFixed( x )
+
+
+ FT_LOCAL_DEF( void )
+ cf2_blues_init( CF2_Blues blues,
+ CF2_Font font )
+ {
+ /* pointer to parsed font object */
+ PS_Decoder* decoder = font->decoder;
+
+ CF2_Fixed zoneHeight;
+ CF2_Fixed maxZoneHeight = 0;
+ CF2_Fixed csUnitsPerPixel;
+
+ size_t numBlueValues;
+ size_t numOtherBlues;
+ size_t numFamilyBlues;
+ size_t numFamilyOtherBlues;
+
+ FT_Pos* blueValues;
+ FT_Pos* otherBlues;
+ FT_Pos* familyBlues;
+ FT_Pos* familyOtherBlues;
+
+ size_t i;
+ CF2_Fixed emBoxBottom, emBoxTop;
+
+#if 0
+ CF2_Int unitsPerEm = font->unitsPerEm;
+
+
+ if ( unitsPerEm == 0 )
+ unitsPerEm = 1000;
+#endif
+
+ FT_ZERO( blues );
+ blues->scale = font->innerTransform.d;
+
+ cf2_getBlueMetrics( decoder,
+ &blues->blueScale,
+ &blues->blueShift,
+ &blues->blueFuzz );
+
+ cf2_getBlueValues( decoder, &numBlueValues, &blueValues );
+ cf2_getOtherBlues( decoder, &numOtherBlues, &otherBlues );
+ cf2_getFamilyBlues( decoder, &numFamilyBlues, &familyBlues );
+ cf2_getFamilyOtherBlues( decoder, &numFamilyOtherBlues, &familyOtherBlues );
+
+ /*
+ * synthetic em box hint heuristic
+ *
+ * Apply this when ideographic dictionary (LanguageGroup 1) has no
+ * real alignment zones. Adobe tools generate dummy zones at -250 and
+ * 1100 for a 1000 unit em. Fonts with ICF-based alignment zones
+ * should not enable the heuristic. When the heuristic is enabled,
+ * the font's blue zones are ignored.
+ *
+ */
+
+ /* get em box from OS/2 typoAscender/Descender */
+ /* TODO: FreeType does not parse these metrics. Skip them for now. */
+#if 0
+ FCM_getHorizontalLineMetrics( &e,
+ font->font,
+ &ascender,
+ &descender,
+ &linegap );
+ if ( ascender - descender == unitsPerEm )
+ {
+ emBoxBottom = cf2_intToFixed( descender );
+ emBoxTop = cf2_intToFixed( ascender );
+ }
+ else
+#endif
+ {
+ emBoxBottom = CF2_ICF_Bottom;
+ emBoxTop = CF2_ICF_Top;
+ }
+
+ if ( cf2_getLanguageGroup( decoder ) == 1 &&
+ ( numBlueValues == 0 ||
+ ( numBlueValues == 4 &&
+ cf2_blueToFixed( blueValues[0] ) < emBoxBottom &&
+ cf2_blueToFixed( blueValues[1] ) < emBoxBottom &&
+ cf2_blueToFixed( blueValues[2] ) > emBoxTop &&
+ cf2_blueToFixed( blueValues[3] ) > emBoxTop ) ) )
+ {
+ /*
+ * Construct hint edges suitable for synthetic ghost hints at top
+ * and bottom of em box. +-CF2_MIN_COUNTER allows for unhinted
+ * features above or below the last hinted edge. This also gives a
+ * net 1 pixel boost to the height of ideographic glyphs.
+ *
+ * Note: Adjust synthetic hints outward by epsilon (0x.0001) to
+ * avoid interference. E.g., some fonts have real hints at
+ * 880 and -120.
+ */
+
+ blues->emBoxBottomEdge.csCoord = emBoxBottom - CF2_FIXED_EPSILON;
+ blues->emBoxBottomEdge.dsCoord = cf2_fixedRound(
+ FT_MulFix(
+ blues->emBoxBottomEdge.csCoord,
+ blues->scale ) ) -
+ CF2_MIN_COUNTER;
+ blues->emBoxBottomEdge.scale = blues->scale;
+ blues->emBoxBottomEdge.flags = CF2_GhostBottom |
+ CF2_Locked |
+ CF2_Synthetic;
+
+ blues->emBoxTopEdge.csCoord = emBoxTop + CF2_FIXED_EPSILON +
+ 2 * font->darkenY;
+ blues->emBoxTopEdge.dsCoord = cf2_fixedRound(
+ FT_MulFix(
+ blues->emBoxTopEdge.csCoord,
+ blues->scale ) ) +
+ CF2_MIN_COUNTER;
+ blues->emBoxTopEdge.scale = blues->scale;
+ blues->emBoxTopEdge.flags = CF2_GhostTop |
+ CF2_Locked |
+ CF2_Synthetic;
+
+ blues->doEmBoxHints = TRUE; /* enable the heuristic */
+
+ return;
+ }
+
+ /* copy `BlueValues' and `OtherBlues' to a combined array of top and */
+ /* bottom zones */
+ for ( i = 0; i < numBlueValues; i += 2 )
+ {
+ blues->zone[blues->count].csBottomEdge =
+ cf2_blueToFixed( blueValues[i] );
+ blues->zone[blues->count].csTopEdge =
+ cf2_blueToFixed( blueValues[i + 1] );
+
+ zoneHeight = SUB_INT32( blues->zone[blues->count].csTopEdge,
+ blues->zone[blues->count].csBottomEdge );
+
+ if ( zoneHeight < 0 )
+ {
+ FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" ));
+ continue; /* reject this zone */
+ }
+
+ if ( zoneHeight > maxZoneHeight )
+ {
+ /* take maximum before darkening adjustment */
+ /* so overshoot suppression point doesn't change */
+ maxZoneHeight = zoneHeight;
+ }
+
+ /* adjust both edges of top zone upward by twice darkening amount */
+ if ( i != 0 )
+ {
+ blues->zone[blues->count].csTopEdge += 2 * font->darkenY;
+ blues->zone[blues->count].csBottomEdge += 2 * font->darkenY;
+ }
+
+ /* first `BlueValue' is bottom zone; others are top */
+ if ( i == 0 )
+ {
+ blues->zone[blues->count].bottomZone =
+ TRUE;
+ blues->zone[blues->count].csFlatEdge =
+ blues->zone[blues->count].csTopEdge;
+ }
+ else
+ {
+ blues->zone[blues->count].bottomZone =
+ FALSE;
+ blues->zone[blues->count].csFlatEdge =
+ blues->zone[blues->count].csBottomEdge;
+ }
+
+ blues->count += 1;
+ }
+
+ for ( i = 0; i < numOtherBlues; i += 2 )
+ {
+ blues->zone[blues->count].csBottomEdge =
+ cf2_blueToFixed( otherBlues[i] );
+ blues->zone[blues->count].csTopEdge =
+ cf2_blueToFixed( otherBlues[i + 1] );
+
+ zoneHeight = SUB_INT32( blues->zone[blues->count].csTopEdge,
+ blues->zone[blues->count].csBottomEdge );
+
+ if ( zoneHeight < 0 )
+ {
+ FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" ));
+ continue; /* reject this zone */
+ }
+
+ if ( zoneHeight > maxZoneHeight )
+ {
+ /* take maximum before darkening adjustment */
+ /* so overshoot suppression point doesn't change */
+ maxZoneHeight = zoneHeight;
+ }
+
+ /* Note: bottom zones are not adjusted for darkening amount */
+
+ /* all OtherBlues are bottom zone */
+ blues->zone[blues->count].bottomZone =
+ TRUE;
+ blues->zone[blues->count].csFlatEdge =
+ blues->zone[blues->count].csTopEdge;
+
+ blues->count += 1;
+ }
+
+ /* Adjust for FamilyBlues */
+
+ /* Search for the nearest flat edge in `FamilyBlues' or */
+ /* `FamilyOtherBlues'. According to the Black Book, any matching edge */
+ /* must be within one device pixel */
+
+ csUnitsPerPixel = FT_DivFix( cf2_intToFixed( 1 ), blues->scale );
+
+ /* loop on all zones in this font */
+ for ( i = 0; i < blues->count; i++ )
+ {
+ size_t j;
+ CF2_Fixed minDiff;
+ CF2_Fixed flatFamilyEdge, diff;
+ /* value for this font */
+ CF2_Fixed flatEdge = blues->zone[i].csFlatEdge;
+
+
+ if ( blues->zone[i].bottomZone )
+ {
+ /* In a bottom zone, the top edge is the flat edge. */
+ /* Search `FamilyOtherBlues' for bottom zones; look for closest */
+ /* Family edge that is within the one pixel threshold. */
+
+ minDiff = CF2_FIXED_MAX;
+
+ for ( j = 0; j < numFamilyOtherBlues; j += 2 )
+ {
+ /* top edge */
+ flatFamilyEdge = cf2_blueToFixed( familyOtherBlues[j + 1] );
+
+ diff = cf2_fixedAbs( SUB_INT32( flatEdge, flatFamilyEdge ) );
+
+ if ( diff < minDiff && diff < csUnitsPerPixel )
+ {
+ blues->zone[i].csFlatEdge = flatFamilyEdge;
+ minDiff = diff;
+
+ if ( diff == 0 )
+ break;
+ }
+ }
+
+ /* check the first member of FamilyBlues, which is a bottom zone */
+ if ( numFamilyBlues >= 2 )
+ {
+ /* top edge */
+ flatFamilyEdge = cf2_blueToFixed( familyBlues[1] );
+
+ diff = cf2_fixedAbs( SUB_INT32( flatEdge, flatFamilyEdge ) );
+
+ if ( diff < minDiff && diff < csUnitsPerPixel )
+ blues->zone[i].csFlatEdge = flatFamilyEdge;
+ }
+ }
+ else
+ {
+ /* In a top zone, the bottom edge is the flat edge. */
+ /* Search `FamilyBlues' for top zones; skip first zone, which is a */
+ /* bottom zone; look for closest Family edge that is within the */
+ /* one pixel threshold */
+
+ minDiff = CF2_FIXED_MAX;
+
+ for ( j = 2; j < numFamilyBlues; j += 2 )
+ {
+ /* bottom edge */
+ flatFamilyEdge = cf2_blueToFixed( familyBlues[j] );
+
+ /* adjust edges of top zone upward by twice darkening amount */
+ flatFamilyEdge += 2 * font->darkenY; /* bottom edge */
+
+ diff = cf2_fixedAbs( SUB_INT32( flatEdge, flatFamilyEdge ) );
+
+ if ( diff < minDiff && diff < csUnitsPerPixel )
+ {
+ blues->zone[i].csFlatEdge = flatFamilyEdge;
+ minDiff = diff;
+
+ if ( diff == 0 )
+ break;
+ }
+ }
+ }
+ }
+
+ /* TODO: enforce separation of zones, including BlueFuzz */
+
+ /* Adjust BlueScale; similar to AdjustBlueScale() in coretype */
+ /* `bcsetup.c'. */
+
+ if ( maxZoneHeight > 0 )
+ {
+ if ( blues->blueScale > FT_DivFix( cf2_intToFixed( 1 ),
+ maxZoneHeight ) )
+ {
+ /* clamp at maximum scale */
+ blues->blueScale = FT_DivFix( cf2_intToFixed( 1 ),
+ maxZoneHeight );
+ }
+
+ /*
+ * TODO: Revisit the bug fix for 613448. The minimum scale
+ * requirement catches a number of library fonts. For
+ * example, with default BlueScale (.039625) and 0.4 minimum,
+ * the test below catches any font with maxZoneHeight < 10.1.
+ * There are library fonts ranging from 2 to 10 that get
+ * caught, including e.g., Eurostile LT Std Medium with
+ * maxZoneHeight of 6.
+ *
+ */
+#if 0
+ if ( blueScale < .4 / maxZoneHeight )
+ {
+ tetraphilia_assert( 0 );
+ /* clamp at minimum scale, per bug 0613448 fix */
+ blueScale = .4 / maxZoneHeight;
+ }
+#endif
+
+ }
+
+ /*
+ * Suppress overshoot and boost blue zones at small sizes. Boost
+ * amount varies linearly from 0.5 pixel near 0 to 0 pixel at
+ * blueScale cutoff.
+ * Note: This boost amount is different from the coretype heuristic.
+ *
+ */
+
+ if ( blues->scale < blues->blueScale )
+ {
+ blues->suppressOvershoot = TRUE;
+
+ /* Change rounding threshold for `dsFlatEdge'. */
+ /* Note: constant changed from 0.5 to 0.6 to avoid a problem with */
+ /* 10ppem Arial */
+
+ blues->boost = cf2_doubleToFixed( .6 ) -
+ FT_MulDiv( cf2_doubleToFixed ( .6 ),
+ blues->scale,
+ blues->blueScale );
+ if ( blues->boost > 0x7FFF )
+ {
+ /* boost must remain less than 0.5, or baseline could go negative */
+ blues->boost = 0x7FFF;
+ }
+ }
+
+ /* boost and darkening have similar effects; don't do both */
+ if ( font->stemDarkened )
+ blues->boost = 0;
+
+ /* set device space alignment for each zone; */
+ /* apply boost amount before rounding flat edge */
+
+ for ( i = 0; i < blues->count; i++ )
+ {
+ if ( blues->zone[i].bottomZone )
+ blues->zone[i].dsFlatEdge = cf2_fixedRound(
+ FT_MulFix(
+ blues->zone[i].csFlatEdge,
+ blues->scale ) -
+ blues->boost );
+ else
+ blues->zone[i].dsFlatEdge = cf2_fixedRound(
+ FT_MulFix(
+ blues->zone[i].csFlatEdge,
+ blues->scale ) +
+ blues->boost );
+ }
+ }
+
+
+ /*
+ * Check whether `stemHint' is captured by one of the blue zones.
+ *
+ * Zero, one or both edges may be valid; only valid edges can be
+ * captured. For compatibility with CoolType, search top and bottom
+ * zones in the same pass (see `BlueLock'). If a hint is captured,
+ * return true and position the edge(s) in one of 3 ways:
+ *
+ * 1) If `BlueScale' suppresses overshoot, position the captured edge
+ * at the flat edge of the zone.
+ * 2) If overshoot is not suppressed and `BlueShift' requires
+ * overshoot, position the captured edge a minimum of 1 device pixel
+ * from the flat edge.
+ * 3) If overshoot is not suppressed or required, position the captured
+ * edge at the nearest device pixel.
+ *
+ */
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_blues_capture( const CF2_Blues blues,
+ CF2_Hint bottomHintEdge,
+ CF2_Hint topHintEdge )
+ {
+ /* TODO: validate? */
+ CF2_Fixed csFuzz = blues->blueFuzz;
+
+ /* new position of captured edge */
+ CF2_Fixed dsNew;
+
+ /* amount that hint is moved when positioned */
+ CF2_Fixed dsMove = 0;
+
+ FT_Bool captured = FALSE;
+ CF2_UInt i;
+
+
+ /* assert edge flags are consistent */
+ FT_ASSERT( !cf2_hint_isTop( bottomHintEdge ) &&
+ !cf2_hint_isBottom( topHintEdge ) );
+
+ /* TODO: search once without blue fuzz for compatibility with coretype? */
+ for ( i = 0; i < blues->count; i++ )
+ {
+ if ( blues->zone[i].bottomZone &&
+ cf2_hint_isBottom( bottomHintEdge ) )
+ {
+ if ( SUB_INT32( blues->zone[i].csBottomEdge, csFuzz ) <=
+ bottomHintEdge->csCoord &&
+ bottomHintEdge->csCoord <=
+ ADD_INT32( blues->zone[i].csTopEdge, csFuzz ) )
+ {
+ /* bottom edge captured by bottom zone */
+
+ if ( blues->suppressOvershoot )
+ dsNew = blues->zone[i].dsFlatEdge;
+
+ else if ( SUB_INT32( blues->zone[i].csTopEdge,
+ bottomHintEdge->csCoord ) >=
+ blues->blueShift )
+ {
+ /* guarantee minimum of 1 pixel overshoot */
+ dsNew = FT_MIN(
+ cf2_fixedRound( bottomHintEdge->dsCoord ),
+ blues->zone[i].dsFlatEdge - cf2_intToFixed( 1 ) );
+ }
+
+ else
+ {
+ /* simply round captured edge */
+ dsNew = cf2_fixedRound( bottomHintEdge->dsCoord );
+ }
+
+ dsMove = SUB_INT32( dsNew, bottomHintEdge->dsCoord );
+ captured = TRUE;
+
+ break;
+ }
+ }
+
+ if ( !blues->zone[i].bottomZone && cf2_hint_isTop( topHintEdge ) )
+ {
+ if ( SUB_INT32( blues->zone[i].csBottomEdge, csFuzz ) <=
+ topHintEdge->csCoord &&
+ topHintEdge->csCoord <=
+ ADD_INT32( blues->zone[i].csTopEdge, csFuzz ) )
+ {
+ /* top edge captured by top zone */
+
+ if ( blues->suppressOvershoot )
+ dsNew = blues->zone[i].dsFlatEdge;
+
+ else if ( SUB_INT32( topHintEdge->csCoord,
+ blues->zone[i].csBottomEdge ) >=
+ blues->blueShift )
+ {
+ /* guarantee minimum of 1 pixel overshoot */
+ dsNew = FT_MAX(
+ cf2_fixedRound( topHintEdge->dsCoord ),
+ blues->zone[i].dsFlatEdge + cf2_intToFixed( 1 ) );
+ }
+
+ else
+ {
+ /* simply round captured edge */
+ dsNew = cf2_fixedRound( topHintEdge->dsCoord );
+ }
+
+ dsMove = SUB_INT32( dsNew, topHintEdge->dsCoord );
+ captured = TRUE;
+
+ break;
+ }
+ }
+ }
+
+ if ( captured )
+ {
+ /* move both edges and flag them `locked' */
+ if ( cf2_hint_isValid( bottomHintEdge ) )
+ {
+ bottomHintEdge->dsCoord = ADD_INT32( bottomHintEdge->dsCoord,
+ dsMove );
+ cf2_hint_lock( bottomHintEdge );
+ }
+
+ if ( cf2_hint_isValid( topHintEdge ) )
+ {
+ topHintEdge->dsCoord = ADD_INT32( topHintEdge->dsCoord, dsMove );
+ cf2_hint_lock( topHintEdge );
+ }
+ }
+
+ return captured;
+ }
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psblues.h b/thirdparty/freetype/src/psaux/psblues.h
new file mode 100644
index 0000000000..25ef6849c7
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psblues.h
@@ -0,0 +1,185 @@
+/***************************************************************************/
+/* */
+/* psblues.h */
+/* */
+/* Adobe's code for handling Blue Zones (specification). */
+/* */
+/* Copyright 2009-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+ /*
+ * A `CF2_Blues' object stores the blue zones (horizontal alignment
+ * zones) of a font. These are specified in the CFF private dictionary
+ * by `BlueValues', `OtherBlues', `FamilyBlues', and `FamilyOtherBlues'.
+ * Each zone is defined by a top and bottom edge in character space.
+ * Further, each zone is either a top zone or a bottom zone, as recorded
+ * by `bottomZone'.
+ *
+ * The maximum number of `BlueValues' and `FamilyBlues' is 7 each.
+ * However, these are combined to produce a total of 7 zones.
+ * Similarly, the maximum number of `OtherBlues' and `FamilyOtherBlues'
+ * is 5 and these are combined to produce an additional 5 zones.
+ *
+ * Blue zones are used to `capture' hints and force them to a common
+ * alignment point. This alignment is recorded in device space in
+ * `dsFlatEdge'. Except for this value, a `CF2_Blues' object could be
+ * constructed independently of scaling. Construction may occur once
+ * the matrix is known. Other features implemented in the Capture
+ * method are overshoot suppression, overshoot enforcement, and Blue
+ * Boost.
+ *
+ * Capture is determined by `BlueValues' and `OtherBlues', but the
+ * alignment point may be adjusted to the scaled flat edge of
+ * `FamilyBlues' or `FamilyOtherBlues'. No alignment is done to the
+ * curved edge of a zone.
+ *
+ */
+
+
+#ifndef PSBLUES_H_
+#define PSBLUES_H_
+
+
+#include "psglue.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /*
+ * `CF2_Hint' is shared by `cf2hints.h' and
+ * `cf2blues.h', but `cf2blues.h' depends on
+ * `cf2hints.h', so define it here. Note: The typedef is in
+ * `cf2glue.h'.
+ *
+ */
+ enum
+ {
+ CF2_GhostBottom = 0x1, /* a single bottom edge */
+ CF2_GhostTop = 0x2, /* a single top edge */
+ CF2_PairBottom = 0x4, /* the bottom edge of a stem hint */
+ CF2_PairTop = 0x8, /* the top edge of a stem hint */
+ CF2_Locked = 0x10, /* this edge has been aligned */
+ /* by a blue zone */
+ CF2_Synthetic = 0x20 /* this edge was synthesized */
+ };
+
+
+ /*
+ * Default value for OS/2 typoAscender/Descender when their difference
+ * is not equal to `unitsPerEm'. The default is based on -250 and 1100
+ * in `CF2_Blues', assuming 1000 units per em here.
+ *
+ */
+ enum
+ {
+ CF2_ICF_Top = cf2_intToFixed( 880 ),
+ CF2_ICF_Bottom = cf2_intToFixed( -120 )
+ };
+
+
+ /*
+ * Constant used for hint adjustment and for synthetic em box hint
+ * placement.
+ */
+#define CF2_MIN_COUNTER cf2_doubleToFixed( 0.5 )
+
+
+ /* shared typedef is in cf2glue.h */
+ struct CF2_HintRec_
+ {
+ CF2_UInt flags; /* attributes of the edge */
+ size_t index; /* index in original stem hint array */
+ /* (if not synthetic) */
+ CF2_Fixed csCoord;
+ CF2_Fixed dsCoord;
+ CF2_Fixed scale;
+ };
+
+
+ typedef struct CF2_BlueRec_
+ {
+ CF2_Fixed csBottomEdge;
+ CF2_Fixed csTopEdge;
+ CF2_Fixed csFlatEdge; /* may be from either local or Family zones */
+ CF2_Fixed dsFlatEdge; /* top edge of bottom zone or bottom edge */
+ /* of top zone (rounded) */
+ FT_Bool bottomZone;
+
+ } CF2_BlueRec;
+
+
+ /* max total blue zones is 12 */
+ enum
+ {
+ CF2_MAX_BLUES = 7,
+ CF2_MAX_OTHERBLUES = 5
+ };
+
+
+ typedef struct CF2_BluesRec_
+ {
+ CF2_Fixed scale;
+ CF2_UInt count;
+ FT_Bool suppressOvershoot;
+ FT_Bool doEmBoxHints;
+
+ CF2_Fixed blueScale;
+ CF2_Fixed blueShift;
+ CF2_Fixed blueFuzz;
+
+ CF2_Fixed boost;
+
+ CF2_HintRec emBoxTopEdge;
+ CF2_HintRec emBoxBottomEdge;
+
+ CF2_BlueRec zone[CF2_MAX_BLUES + CF2_MAX_OTHERBLUES];
+
+ } CF2_BluesRec, *CF2_Blues;
+
+
+ FT_LOCAL( void )
+ cf2_blues_init( CF2_Blues blues,
+ CF2_Font font );
+ FT_LOCAL( FT_Bool )
+ cf2_blues_capture( const CF2_Blues blues,
+ CF2_Hint bottomHintEdge,
+ CF2_Hint topHintEdge );
+
+
+FT_END_HEADER
+
+
+#endif /* PSBLUES_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psconv.c b/thirdparty/freetype/src/psaux/psconv.c
index d125b0834a..a03385000d 100644
--- a/thirdparty/freetype/src/psaux/psconv.c
+++ b/thirdparty/freetype/src/psaux/psconv.c
@@ -4,7 +4,7 @@
/* */
/* Some convenience conversions (body). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psaux/psconv.h b/thirdparty/freetype/src/psaux/psconv.h
index cab254ac5a..d643ffcfc2 100644
--- a/thirdparty/freetype/src/psaux/psconv.h
+++ b/thirdparty/freetype/src/psaux/psconv.h
@@ -4,7 +4,7 @@
/* */
/* Some convenience conversions (specification). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psaux/pserror.c b/thirdparty/freetype/src/psaux/pserror.c
new file mode 100644
index 0000000000..9169e5222d
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/pserror.c
@@ -0,0 +1,52 @@
+/***************************************************************************/
+/* */
+/* pserror.c */
+/* */
+/* Adobe's code for error handling (body). */
+/* */
+/* Copyright 2006-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#include "psft.h"
+#include "pserror.h"
+
+
+ FT_LOCAL_DEF( void )
+ cf2_setError( FT_Error* error,
+ FT_Error value )
+ {
+ if ( error && !*error )
+ *error = value;
+ }
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/pserror.h b/thirdparty/freetype/src/psaux/pserror.h
new file mode 100644
index 0000000000..13d52062bf
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/pserror.h
@@ -0,0 +1,119 @@
+/***************************************************************************/
+/* */
+/* pserror.h */
+/* */
+/* Adobe's code for error handling (specification). */
+/* */
+/* Copyright 2006-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef PSERROR_H_
+#define PSERROR_H_
+
+
+#include FT_MODULE_ERRORS_H
+
+#undef FTERRORS_H_
+
+#undef FT_ERR_PREFIX
+#define FT_ERR_PREFIX CF2_Err_
+#define FT_ERR_BASE FT_Mod_Err_CF2
+
+
+#include FT_ERRORS_H
+#include "psft.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /*
+ * A poor-man error facility.
+ *
+ * This code being written in vanilla C, doesn't have the luxury of a
+ * language-supported exception mechanism such as the one available in
+ * Java. Instead, we are stuck with using error codes that must be
+ * carefully managed and preserved. However, it is convenient for us to
+ * model our error mechanism on a Java-like exception mechanism.
+ * When we assign an error code we are thus `throwing' an error.
+ *
+ * The preservation of an error code is done by coding convention.
+ * Upon a function call if the error code is anything other than
+ * `FT_Err_Ok', which is guaranteed to be zero, we
+ * will return without altering that error. This will allow the
+ * error to propagate and be handled at the appropriate location in
+ * the code.
+ *
+ * This allows a style of code where the error code is initialized
+ * up front and a block of calls are made with the error code only
+ * being checked after the block. If a new error occurs, the original
+ * error will be preserved and a functional no-op should result in any
+ * subsequent function that has an initial error code not equal to
+ * `FT_Err_Ok'.
+ *
+ * Errors are encoded by calling the `FT_THROW' macro. For example,
+ *
+ * {
+ * FT_Error e;
+ *
+ *
+ * ...
+ * e = FT_THROW( Out_Of_Memory );
+ * }
+ *
+ */
+
+
+ /* Set error code to a particular value. */
+ FT_LOCAL( void )
+ cf2_setError( FT_Error* error,
+ FT_Error value );
+
+
+ /*
+ * A macro that conditionally sets an error code.
+ *
+ * This macro will first check whether `error' is set;
+ * if not, it will set it to `e'.
+ *
+ */
+#define CF2_SET_ERROR( error, e ) \
+ cf2_setError( error, FT_THROW( e ) )
+
+
+FT_END_HEADER
+
+
+#endif /* PSERROR_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psfixed.h b/thirdparty/freetype/src/psaux/psfixed.h
new file mode 100644
index 0000000000..219589e7fc
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psfixed.h
@@ -0,0 +1,95 @@
+/***************************************************************************/
+/* */
+/* psfixed.h */
+/* */
+/* Adobe's code for Fixed Point Mathematics (specification only). */
+/* */
+/* Copyright 2007-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef PSFIXED_H_
+#define PSFIXED_H_
+
+
+FT_BEGIN_HEADER
+
+
+ /* rasterizer integer and fixed point arithmetic must be 32-bit */
+
+#define CF2_Fixed CF2_F16Dot16
+ typedef FT_Int32 CF2_Frac; /* 2.30 fixed point */
+
+
+#define CF2_FIXED_MAX ( (CF2_Fixed)0x7FFFFFFFL )
+#define CF2_FIXED_MIN ( (CF2_Fixed)0x80000000L )
+#define CF2_FIXED_ONE ( (CF2_Fixed)0x10000L )
+#define CF2_FIXED_EPSILON ( (CF2_Fixed)0x0001 )
+
+ /* in C 89, left and right shift of negative numbers is */
+ /* implementation specific behaviour in the general case */
+
+#define cf2_intToFixed( i ) \
+ ( (CF2_Fixed)( (FT_UInt32)(i) << 16 ) )
+#define cf2_fixedToInt( x ) \
+ ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
+#define cf2_fixedRound( x ) \
+ ( (CF2_Fixed)( ( (FT_UInt32)(x) + 0x8000U ) & 0xFFFF0000UL ) )
+#define cf2_doubleToFixed( f ) \
+ ( (CF2_Fixed)( (f) * 65536.0 + 0.5 ) )
+#define cf2_fixedAbs( x ) \
+ ( (x) < 0 ? NEG_INT32( x ) : (x) )
+#define cf2_fixedFloor( x ) \
+ ( (CF2_Fixed)( (FT_UInt32)(x) & 0xFFFF0000UL ) )
+#define cf2_fixedFraction( x ) \
+ ( (x) - cf2_fixedFloor( x ) )
+#define cf2_fracToFixed( x ) \
+ ( (x) < 0 ? -( ( -(x) + 0x2000 ) >> 14 ) \
+ : ( ( (x) + 0x2000 ) >> 14 ) )
+
+
+ /* signed numeric types */
+ typedef enum CF2_NumberType_
+ {
+ CF2_NumberFixed, /* 16.16 */
+ CF2_NumberFrac, /* 2.30 */
+ CF2_NumberInt /* 32.0 */
+
+ } CF2_NumberType;
+
+
+FT_END_HEADER
+
+
+#endif /* PSFIXED_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psfont.c b/thirdparty/freetype/src/psaux/psfont.c
new file mode 100644
index 0000000000..dde67a739d
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psfont.c
@@ -0,0 +1,567 @@
+/***************************************************************************/
+/* */
+/* psfont.c */
+/* */
+/* Adobe's code for font instances (body). */
+/* */
+/* Copyright 2007-2014 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_CALC_H
+
+#include "psft.h"
+
+#include "psglue.h"
+#include "psfont.h"
+#include "pserror.h"
+#include "psintrp.h"
+
+
+ /* Compute a stem darkening amount in character space. */
+ static void
+ cf2_computeDarkening( CF2_Fixed emRatio,
+ CF2_Fixed ppem,
+ CF2_Fixed stemWidth,
+ CF2_Fixed* darkenAmount,
+ CF2_Fixed boldenAmount,
+ FT_Bool stemDarkened,
+ FT_Int* darkenParams )
+ {
+ /*
+ * Total darkening amount is computed in 1000 unit character space
+ * using the modified 5 part curve as Adobe's Avalon rasterizer.
+ * The darkening amount is smaller for thicker stems.
+ * It becomes zero when the stem is thicker than 2.333 pixels.
+ *
+ * By default, we use
+ *
+ * darkenAmount = 0.4 pixels if scaledStem <= 0.5 pixels,
+ * darkenAmount = 0.275 pixels if 1 <= scaledStem <= 1.667 pixels,
+ * darkenAmount = 0 pixel if scaledStem >= 2.333 pixels,
+ *
+ * and piecewise linear in-between:
+ *
+ *
+ * darkening
+ * ^
+ * |
+ * | (x1,y1)
+ * |--------+
+ * | \
+ * | \
+ * | \ (x3,y3)
+ * | +----------+
+ * | (x2,y2) \
+ * | \
+ * | \
+ * | +-----------------
+ * | (x4,y4)
+ * +---------------------------------------------> stem
+ * thickness
+ *
+ *
+ * This corresponds to the following values for the
+ * `darkening-parameters' property:
+ *
+ * (x1, y1) = (500, 400)
+ * (x2, y2) = (1000, 275)
+ * (x3, y3) = (1667, 275)
+ * (x4, y4) = (2333, 0)
+ *
+ */
+
+ /* Internal calculations are done in units per thousand for */
+ /* convenience. The x axis is scaled stem width in */
+ /* thousandths of a pixel. That is, 1000 is 1 pixel. */
+ /* The y axis is darkening amount in thousandths of a pixel.*/
+ /* In the code, below, dividing by ppem and */
+ /* adjusting for emRatio converts darkenAmount to character */
+ /* space (font units). */
+ CF2_Fixed stemWidthPer1000, scaledStem;
+ FT_Int logBase2;
+
+
+ *darkenAmount = 0;
+
+ if ( boldenAmount == 0 && !stemDarkened )
+ return;
+
+ /* protect against range problems and divide by zero */
+ if ( emRatio < cf2_doubleToFixed( .01 ) )
+ return;
+
+ if ( stemDarkened )
+ {
+ FT_Int x1 = darkenParams[0];
+ FT_Int y1 = darkenParams[1];
+ FT_Int x2 = darkenParams[2];
+ FT_Int y2 = darkenParams[3];
+ FT_Int x3 = darkenParams[4];
+ FT_Int y3 = darkenParams[5];
+ FT_Int x4 = darkenParams[6];
+ FT_Int y4 = darkenParams[7];
+
+
+ /* convert from true character space to 1000 unit character space; */
+ /* add synthetic emboldening effect */
+
+ /* `stemWidthPer1000' will not overflow for a legitimate font */
+
+ stemWidthPer1000 = FT_MulFix( stemWidth + boldenAmount, emRatio );
+
+ /* `scaledStem' can easily overflow, so we must clamp its maximum */
+ /* value; the test doesn't need to be precise, but must be */
+ /* conservative. The clamp value (default 2333) where */
+ /* `darkenAmount' is zero is well below the overflow value of */
+ /* 32767. */
+ /* */
+ /* FT_MSB computes the integer part of the base 2 logarithm. The */
+ /* number of bits for the product is 1 or 2 more than the sum of */
+ /* logarithms; remembering that the 16 lowest bits of the fraction */
+ /* are dropped this is correct to within a factor of almost 4. */
+ /* For example, 0x80.0000 * 0x80.0000 = 0x4000.0000 is 23+23 and */
+ /* is flagged as possible overflow because 0xFF.FFFF * 0xFF.FFFF = */
+ /* 0xFFFF.FE00 is also 23+23. */
+
+ logBase2 = FT_MSB( (FT_UInt32)stemWidthPer1000 ) +
+ FT_MSB( (FT_UInt32)ppem );
+
+ if ( logBase2 >= 46 )
+ /* possible overflow */
+ scaledStem = cf2_intToFixed( x4 );
+ else
+ scaledStem = FT_MulFix( stemWidthPer1000, ppem );
+
+ /* now apply the darkening parameters */
+
+ if ( scaledStem < cf2_intToFixed( x1 ) )
+ *darkenAmount = FT_DivFix( cf2_intToFixed( y1 ), ppem );
+
+ else if ( scaledStem < cf2_intToFixed( x2 ) )
+ {
+ FT_Int xdelta = x2 - x1;
+ FT_Int ydelta = y2 - y1;
+ FT_Int x = stemWidthPer1000 -
+ FT_DivFix( cf2_intToFixed( x1 ), ppem );
+
+
+ if ( !xdelta )
+ goto Try_x3;
+
+ *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
+ FT_DivFix( cf2_intToFixed( y1 ), ppem );
+ }
+
+ else if ( scaledStem < cf2_intToFixed( x3 ) )
+ {
+ Try_x3:
+ {
+ FT_Int xdelta = x3 - x2;
+ FT_Int ydelta = y3 - y2;
+ FT_Int x = stemWidthPer1000 -
+ FT_DivFix( cf2_intToFixed( x2 ), ppem );
+
+
+ if ( !xdelta )
+ goto Try_x4;
+
+ *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
+ FT_DivFix( cf2_intToFixed( y2 ), ppem );
+ }
+ }
+
+ else if ( scaledStem < cf2_intToFixed( x4 ) )
+ {
+ Try_x4:
+ {
+ FT_Int xdelta = x4 - x3;
+ FT_Int ydelta = y4 - y3;
+ FT_Int x = stemWidthPer1000 -
+ FT_DivFix( cf2_intToFixed( x3 ), ppem );
+
+
+ if ( !xdelta )
+ goto Use_y4;
+
+ *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
+ FT_DivFix( cf2_intToFixed( y3 ), ppem );
+ }
+ }
+
+ else
+ {
+ Use_y4:
+ *darkenAmount = FT_DivFix( cf2_intToFixed( y4 ), ppem );
+ }
+
+ /* use half the amount on each side and convert back to true */
+ /* character space */
+ *darkenAmount = FT_DivFix( *darkenAmount, 2 * emRatio );
+ }
+
+ /* add synthetic emboldening effect in character space */
+ *darkenAmount += boldenAmount / 2;
+ }
+
+
+ /* set up values for the current FontDict and matrix; */
+ /* called for each glyph to be rendered */
+
+ /* caller's transform is adjusted for subpixel positioning */
+ static void
+ cf2_font_setup( CF2_Font font,
+ const CF2_Matrix* transform )
+ {
+ /* pointer to parsed font object */
+ PS_Decoder* decoder = font->decoder;
+
+ FT_Bool needExtraSetup = FALSE;
+
+ CFF_VStoreRec* vstore;
+ FT_Bool hasVariations = FALSE;
+
+ /* character space units */
+ CF2_Fixed boldenX = font->syntheticEmboldeningAmountX;
+ CF2_Fixed boldenY = font->syntheticEmboldeningAmountY;
+
+ CFF_SubFont subFont;
+ CF2_Fixed ppem;
+
+ CF2_UInt lenNormalizedV = 0;
+ FT_Fixed* normalizedV = NULL;
+
+ /* clear previous error */
+ font->error = FT_Err_Ok;
+
+ /* if a CID fontDict has changed, we need to recompute some cached */
+ /* data */
+ subFont = cf2_getSubfont( decoder );
+ if ( font->lastSubfont != subFont )
+ {
+ font->lastSubfont = subFont;
+ needExtraSetup = TRUE;
+ }
+
+ if ( !font->isT1 )
+ {
+ FT_Service_CFFLoad cffload = (FT_Service_CFFLoad)font->cffload;
+
+
+ /* check for variation vectors */
+ vstore = cf2_getVStore( decoder );
+ hasVariations = ( vstore->dataCount != 0 );
+
+ if ( hasVariations )
+ {
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ /* check whether Private DICT in this subfont needs to be reparsed */
+ font->error = cf2_getNormalizedVector( decoder,
+ &lenNormalizedV,
+ &normalizedV );
+ if ( font->error )
+ return;
+
+ if ( cffload->blend_check_vector( &subFont->blend,
+ subFont->private_dict.vsindex,
+ lenNormalizedV,
+ normalizedV ) )
+ {
+ /* blend has changed, reparse */
+ cffload->load_private_dict( decoder->cff,
+ subFont,
+ lenNormalizedV,
+ normalizedV );
+ needExtraSetup = TRUE;
+ }
+#endif
+
+ /* copy from subfont */
+ font->blend.font = subFont->blend.font;
+
+ /* clear state of charstring blend */
+ font->blend.usedBV = FALSE;
+
+ /* initialize value for charstring */
+ font->vsindex = subFont->private_dict.vsindex;
+
+ /* store vector inputs for blends in charstring */
+ font->lenNDV = lenNormalizedV;
+ font->NDV = normalizedV;
+ }
+ }
+
+ /* if ppem has changed, we need to recompute some cached data */
+ /* note: because of CID font matrix concatenation, ppem and transform */
+ /* do not necessarily track. */
+ ppem = cf2_getPpemY( decoder );
+ if ( font->ppem != ppem )
+ {
+ font->ppem = ppem;
+ needExtraSetup = TRUE;
+ }
+
+ /* copy hinted flag on each call */
+ font->hinted = (FT_Bool)( font->renderingFlags & CF2_FlagsHinted );
+
+ /* determine if transform has changed; */
+ /* include Fontmatrix but ignore translation */
+ if ( ft_memcmp( transform,
+ &font->currentTransform,
+ 4 * sizeof ( CF2_Fixed ) ) != 0 )
+ {
+ /* save `key' information for `cache of one' matrix data; */
+ /* save client transform, without the translation */
+ font->currentTransform = *transform;
+ font->currentTransform.tx =
+ font->currentTransform.ty = cf2_intToFixed( 0 );
+
+ /* TODO: FreeType transform is simple scalar; for now, use identity */
+ /* for outer */
+ font->innerTransform = *transform;
+ font->outerTransform.a =
+ font->outerTransform.d = cf2_intToFixed( 1 );
+ font->outerTransform.b =
+ font->outerTransform.c = cf2_intToFixed( 0 );
+
+ needExtraSetup = TRUE;
+ }
+
+ /*
+ * font->darkened is set to true if there is a stem darkening request or
+ * the font is synthetic emboldened.
+ * font->darkened controls whether to adjust blue zones, winding order,
+ * and hinting.
+ *
+ */
+ if ( font->stemDarkened != ( font->renderingFlags & CF2_FlagsDarkened ) )
+ {
+ font->stemDarkened =
+ (FT_Bool)( font->renderingFlags & CF2_FlagsDarkened );
+
+ /* blue zones depend on darkened flag */
+ needExtraSetup = TRUE;
+ }
+
+ /* recompute variables that are dependent on transform or FontDict or */
+ /* darken flag */
+ if ( needExtraSetup )
+ {
+ /* StdVW is found in the private dictionary; */
+ /* recompute darkening amounts whenever private dictionary or */
+ /* transform change */
+ /* Note: a rendering flag turns darkening on or off, so we want to */
+ /* store the `on' amounts; */
+ /* darkening amount is computed in character space */
+ /* TODO: testing size-dependent darkening here; */
+ /* what to do for rotations? */
+
+ CF2_Fixed emRatio;
+ CF2_Fixed stdHW;
+ CF2_Int unitsPerEm = font->unitsPerEm;
+
+
+ if ( unitsPerEm == 0 )
+ unitsPerEm = 1000;
+
+ ppem = FT_MAX( cf2_intToFixed( 4 ),
+ font->ppem ); /* use minimum ppem of 4 */
+
+#if 0
+ /* since vstem is measured in the x-direction, we use the `a' member */
+ /* of the fontMatrix */
+ emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->a );
+#endif
+
+ /* Freetype does not preserve the fontMatrix when parsing; use */
+ /* unitsPerEm instead. */
+ /* TODO: check precision of this */
+ emRatio = cf2_intToFixed( 1000 ) / unitsPerEm;
+ font->stdVW = cf2_getStdVW( decoder );
+
+ if ( font->stdVW <= 0 )
+ font->stdVW = FT_DivFix( cf2_intToFixed( 75 ), emRatio );
+
+ if ( boldenX > 0 )
+ {
+ /* Ensure that boldenX is at least 1 pixel for synthetic bold font */
+ /* (similar to what Avalon does) */
+ boldenX = FT_MAX( boldenX,
+ FT_DivFix( cf2_intToFixed( unitsPerEm ), ppem ) );
+
+ /* Synthetic emboldening adds at least 1 pixel to darkenX, while */
+ /* stem darkening adds at most half pixel. Since the purpose of */
+ /* stem darkening (readability at small sizes) is met with */
+ /* synthetic emboldening, no need to add stem darkening for a */
+ /* synthetic bold font. */
+ cf2_computeDarkening( emRatio,
+ ppem,
+ font->stdVW,
+ &font->darkenX,
+ boldenX,
+ FALSE,
+ font->darkenParams );
+ }
+ else
+ cf2_computeDarkening( emRatio,
+ ppem,
+ font->stdVW,
+ &font->darkenX,
+ 0,
+ font->stemDarkened,
+ font->darkenParams );
+
+#if 0
+ /* since hstem is measured in the y-direction, we use the `d' member */
+ /* of the fontMatrix */
+ /* TODO: use the same units per em as above; check this */
+ emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->d );
+#endif
+
+ /* set the default stem width, because it must be the same for all */
+ /* family members; */
+ /* choose a constant for StdHW that depends on font contrast */
+ stdHW = cf2_getStdHW( decoder );
+
+ if ( stdHW > 0 && font->stdVW > MUL_INT32( 2, stdHW ) )
+ font->stdHW = FT_DivFix( cf2_intToFixed( 75 ), emRatio );
+ else
+ {
+ /* low contrast font gets less hstem darkening */
+ font->stdHW = FT_DivFix( cf2_intToFixed( 110 ), emRatio );
+ }
+
+ cf2_computeDarkening( emRatio,
+ ppem,
+ font->stdHW,
+ &font->darkenY,
+ boldenY,
+ font->stemDarkened,
+ font->darkenParams );
+
+ if ( font->darkenX != 0 || font->darkenY != 0 )
+ font->darkened = TRUE;
+ else
+ font->darkened = FALSE;
+
+ font->reverseWinding = FALSE; /* initial expectation is CCW */
+
+ /* compute blue zones for this instance */
+ cf2_blues_init( &font->blues, font );
+
+ } /* needExtraSetup */
+ }
+
+
+ /* equivalent to AdobeGetOutline */
+ FT_LOCAL_DEF( FT_Error )
+ cf2_getGlyphOutline( CF2_Font font,
+ CF2_Buffer charstring,
+ const CF2_Matrix* transform,
+ CF2_F16Dot16* glyphWidth )
+ {
+ FT_Error lastError = FT_Err_Ok;
+
+ FT_Vector translation;
+
+#if 0
+ FT_Vector advancePoint;
+#endif
+
+ CF2_Fixed advWidth = 0;
+ FT_Bool needWinding;
+
+
+ /* Note: use both integer and fraction for outlines. This allows bbox */
+ /* to come out directly. */
+
+ translation.x = transform->tx;
+ translation.y = transform->ty;
+
+ /* set up values based on transform */
+ cf2_font_setup( font, transform );
+ if ( font->error )
+ goto exit; /* setup encountered an error */
+
+ /* reset darken direction */
+ font->reverseWinding = FALSE;
+
+ /* winding order only affects darkening */
+ needWinding = font->darkened;
+
+ while ( 1 )
+ {
+ /* reset output buffer */
+ cf2_outline_reset( &font->outline );
+
+ /* build the outline, passing the full translation */
+ cf2_interpT2CharString( font,
+ charstring,
+ (CF2_OutlineCallbacks)&font->outline,
+ &translation,
+ FALSE,
+ 0,
+ 0,
+ &advWidth );
+
+ if ( font->error )
+ goto exit;
+
+ if ( !needWinding )
+ break;
+
+ /* check winding order */
+ if ( font->outline.root.windingMomentum >= 0 ) /* CFF is CCW */
+ break;
+
+ /* invert darkening and render again */
+ /* TODO: this should be a parameter to getOutline-computeOffset */
+ font->reverseWinding = TRUE;
+
+ needWinding = FALSE; /* exit after next iteration */
+ }
+
+ /* finish storing client outline */
+ cf2_outline_close( &font->outline );
+
+ exit:
+ /* FreeType just wants the advance width; there is no translation */
+ *glyphWidth = advWidth;
+
+ /* free resources and collect errors from objects we've used */
+ cf2_setError( &font->error, lastError );
+
+ return font->error;
+ }
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psfont.h b/thirdparty/freetype/src/psaux/psfont.h
new file mode 100644
index 0000000000..e611ac4bdc
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psfont.h
@@ -0,0 +1,134 @@
+/***************************************************************************/
+/* */
+/* psfont.h */
+/* */
+/* Adobe's code for font instances (specification). */
+/* */
+/* Copyright 2007-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef PSFONT_H_
+#define PSFONT_H_
+
+
+#include FT_SERVICE_CFF_TABLE_LOAD_H
+
+#include "psft.h"
+#include "psblues.h"
+
+
+FT_BEGIN_HEADER
+
+
+#define CF2_OPERAND_STACK_SIZE 48
+#define CF2_MAX_SUBR 16 /* maximum subroutine nesting; */
+ /* only 10 are allowed but there exist */
+ /* fonts like `HiraKakuProN-W3.ttf' */
+ /* (Hiragino Kaku Gothic ProN W3; */
+ /* 8.2d6e1; 2014-12-19) that exceed */
+ /* this limit */
+#define CF2_STORAGE_SIZE 32
+
+
+ /* typedef is in `cf2glue.h' */
+ struct CF2_FontRec_
+ {
+ FT_Memory memory;
+ FT_Error error; /* shared error for this instance */
+
+ FT_Bool isT1;
+ FT_Bool isCFF2;
+ CF2_RenderingFlags renderingFlags;
+
+ /* variables that depend on Transform: */
+ /* the following have zero translation; */
+ /* inner * outer = font * original */
+
+ CF2_Matrix currentTransform; /* original client matrix */
+ CF2_Matrix innerTransform; /* for hinting; erect, scaled */
+ CF2_Matrix outerTransform; /* post hinting; includes rotations */
+ CF2_Fixed ppem; /* transform-dependent */
+
+ /* variation data */
+ CFF_BlendRec blend; /* cached charstring blend vector */
+ CF2_UInt vsindex; /* current vsindex */
+ CF2_UInt lenNDV; /* current length NDV or zero */
+ FT_Fixed* NDV; /* ptr to current NDV or NULL */
+
+ CF2_Int unitsPerEm;
+
+ CF2_Fixed syntheticEmboldeningAmountX; /* character space units */
+ CF2_Fixed syntheticEmboldeningAmountY; /* character space units */
+
+ /* FreeType related members */
+ CF2_OutlineRec outline; /* freetype glyph outline functions */
+ PS_Decoder* decoder;
+ CFF_SubFont lastSubfont; /* FreeType parsed data; */
+ /* top font or subfont */
+
+ /* these flags can vary from one call to the next */
+ FT_Bool hinted;
+ FT_Bool darkened; /* true if stemDarkened or synthetic bold */
+ /* i.e. darkenX != 0 || darkenY != 0 */
+ FT_Bool stemDarkened;
+
+ FT_Int darkenParams[8]; /* 1000 unit character space */
+
+ /* variables that depend on both FontDict and Transform */
+ CF2_Fixed stdVW; /* in character space; depends on dict entry */
+ CF2_Fixed stdHW; /* in character space; depends on dict entry */
+ CF2_Fixed darkenX; /* character space units */
+ CF2_Fixed darkenY; /* depends on transform */
+ /* and private dict (StdVW) */
+ FT_Bool reverseWinding; /* darken assuming */
+ /* counterclockwise winding */
+
+ CF2_BluesRec blues; /* computed zone data */
+
+ FT_Service_CFFLoad cffload; /* pointer to cff functions */
+ };
+
+
+ FT_LOCAL( FT_Error )
+ cf2_getGlyphOutline( CF2_Font font,
+ CF2_Buffer charstring,
+ const CF2_Matrix* transform,
+ CF2_F16Dot16* glyphWidth );
+
+
+FT_END_HEADER
+
+
+#endif /* PSFONT_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psft.c b/thirdparty/freetype/src/psaux/psft.c
new file mode 100644
index 0000000000..1f750174a1
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psft.c
@@ -0,0 +1,890 @@
+/***************************************************************************/
+/* */
+/* psft.c */
+/* */
+/* FreeType Glue Component to Adobe's Interpreter (body). */
+/* */
+/* Copyright 2013-2014 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#include "psft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "psfont.h"
+#include "pserror.h"
+#include "psobjs.h"
+#include "cffdecode.h"
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include FT_MULTIPLE_MASTERS_H
+#include FT_SERVICE_MULTIPLE_MASTERS_H
+#endif
+
+#include FT_SERVICE_CFF_TABLE_LOAD_H
+
+
+#define CF2_MAX_SIZE cf2_intToFixed( 2000 ) /* max ppem */
+
+
+ /*
+ * This check should avoid most internal overflow cases. Clients should
+ * generally respond to `Glyph_Too_Big' by getting a glyph outline
+ * at EM size, scaling it and filling it as a graphics operation.
+ *
+ */
+ static FT_Error
+ cf2_checkTransform( const CF2_Matrix* transform,
+ CF2_Int unitsPerEm )
+ {
+ CF2_Fixed maxScale;
+
+
+ FT_ASSERT( unitsPerEm > 0 );
+
+ if ( transform->a <= 0 || transform->d <= 0 )
+ return FT_THROW( Invalid_Size_Handle );
+
+ FT_ASSERT( transform->b == 0 && transform->c == 0 );
+ FT_ASSERT( transform->tx == 0 && transform->ty == 0 );
+
+ if ( unitsPerEm > 0x7FFF )
+ return FT_THROW( Glyph_Too_Big );
+
+ maxScale = FT_DivFix( CF2_MAX_SIZE, cf2_intToFixed( unitsPerEm ) );
+
+ if ( transform->a > maxScale || transform->d > maxScale )
+ return FT_THROW( Glyph_Too_Big );
+
+ return FT_Err_Ok;
+ }
+
+
+ static void
+ cf2_setGlyphWidth( CF2_Outline outline,
+ CF2_Fixed width )
+ {
+ PS_Decoder* decoder = outline->decoder;
+
+
+ FT_ASSERT( decoder );
+
+ if ( !decoder->builder.is_t1 )
+ *decoder->glyph_width = cf2_fixedToInt( width );
+ }
+
+
+ /* Clean up font instance. */
+ static void
+ cf2_free_instance( void* ptr )
+ {
+ CF2_Font font = (CF2_Font)ptr;
+
+
+ if ( font )
+ {
+ FT_Memory memory = font->memory;
+
+
+ FT_FREE( font->blend.lastNDV );
+ FT_FREE( font->blend.BV );
+ }
+ }
+
+
+ /********************************************/
+ /* */
+ /* functions for handling client outline; */
+ /* FreeType uses coordinates in 26.6 format */
+ /* */
+ /********************************************/
+
+ static void
+ cf2_builder_moveTo( CF2_OutlineCallbacks callbacks,
+ const CF2_CallbackParams params )
+ {
+ /* downcast the object pointer */
+ CF2_Outline outline = (CF2_Outline)callbacks;
+ PS_Builder* builder;
+
+ (void)params; /* only used in debug mode */
+
+
+ FT_ASSERT( outline && outline->decoder );
+ FT_ASSERT( params->op == CF2_PathOpMoveTo );
+
+ builder = &outline->decoder->builder;
+
+ /* note: two successive moves simply close the contour twice */
+ ps_builder_close_contour( builder );
+ builder->path_begun = 0;
+ }
+
+
+ static void
+ cf2_builder_lineTo( CF2_OutlineCallbacks callbacks,
+ const CF2_CallbackParams params )
+ {
+ FT_Error error;
+
+ /* downcast the object pointer */
+ CF2_Outline outline = (CF2_Outline)callbacks;
+ PS_Builder* builder;
+
+
+ FT_ASSERT( outline && outline->decoder );
+ FT_ASSERT( params->op == CF2_PathOpLineTo );
+
+ builder = &outline->decoder->builder;
+
+ if ( !builder->path_begun )
+ {
+ /* record the move before the line; also check points and set */
+ /* `path_begun' */
+ error = ps_builder_start_point( builder,
+ params->pt0.x,
+ params->pt0.y );
+ if ( error )
+ {
+ if ( !*callbacks->error )
+ *callbacks->error = error;
+ return;
+ }
+ }
+
+ /* `ps_builder_add_point1' includes a check_points call for one point */
+ error = ps_builder_add_point1( builder,
+ params->pt1.x,
+ params->pt1.y );
+ if ( error )
+ {
+ if ( !*callbacks->error )
+ *callbacks->error = error;
+ return;
+ }
+ }
+
+
+ static void
+ cf2_builder_cubeTo( CF2_OutlineCallbacks callbacks,
+ const CF2_CallbackParams params )
+ {
+ FT_Error error;
+
+ /* downcast the object pointer */
+ CF2_Outline outline = (CF2_Outline)callbacks;
+ PS_Builder* builder;
+
+
+ FT_ASSERT( outline && outline->decoder );
+ FT_ASSERT( params->op == CF2_PathOpCubeTo );
+
+ builder = &outline->decoder->builder;
+
+ if ( !builder->path_begun )
+ {
+ /* record the move before the line; also check points and set */
+ /* `path_begun' */
+ error = ps_builder_start_point( builder,
+ params->pt0.x,
+ params->pt0.y );
+ if ( error )
+ {
+ if ( !*callbacks->error )
+ *callbacks->error = error;
+ return;
+ }
+ }
+
+ /* prepare room for 3 points: 2 off-curve, 1 on-curve */
+ error = ps_builder_check_points( builder, 3 );
+ if ( error )
+ {
+ if ( !*callbacks->error )
+ *callbacks->error = error;
+ return;
+ }
+
+ ps_builder_add_point( builder,
+ params->pt1.x,
+ params->pt1.y, 0 );
+ ps_builder_add_point( builder,
+ params->pt2.x,
+ params->pt2.y, 0 );
+ ps_builder_add_point( builder,
+ params->pt3.x,
+ params->pt3.y, 1 );
+ }
+
+
+ static void
+ cf2_outline_init( CF2_Outline outline,
+ FT_Memory memory,
+ FT_Error* error )
+ {
+ FT_ZERO( outline );
+
+ outline->root.memory = memory;
+ outline->root.error = error;
+
+ outline->root.moveTo = cf2_builder_moveTo;
+ outline->root.lineTo = cf2_builder_lineTo;
+ outline->root.cubeTo = cf2_builder_cubeTo;
+ }
+
+
+ /* get scaling and hint flag from GlyphSlot */
+ static void
+ cf2_getScaleAndHintFlag( PS_Decoder* decoder,
+ CF2_Fixed* x_scale,
+ CF2_Fixed* y_scale,
+ FT_Bool* hinted,
+ FT_Bool* scaled )
+ {
+ FT_ASSERT( decoder && decoder->builder.glyph );
+
+ /* note: FreeType scale includes a factor of 64 */
+ *hinted = decoder->builder.glyph->hint;
+ *scaled = decoder->builder.glyph->scaled;
+
+ if ( *hinted )
+ {
+ *x_scale = ADD_INT32( decoder->builder.glyph->x_scale, 32 ) / 64;
+ *y_scale = ADD_INT32( decoder->builder.glyph->y_scale, 32 ) / 64;
+ }
+ else
+ {
+ /* for unhinted outlines, `cff_slot_load' does the scaling, */
+ /* thus render at `unity' scale */
+
+ *x_scale = 0x0400; /* 1/64 as 16.16 */
+ *y_scale = 0x0400;
+ }
+ }
+
+
+ /* get units per em from `FT_Face' */
+ /* TODO: should handle font matrix concatenation? */
+ static FT_UShort
+ cf2_getUnitsPerEm( PS_Decoder* decoder )
+ {
+ FT_ASSERT( decoder && decoder->builder.face );
+ FT_ASSERT( decoder->builder.face->units_per_EM );
+
+ return decoder->builder.face->units_per_EM;
+ }
+
+
+ /* Main entry point: Render one glyph. */
+ FT_LOCAL_DEF( FT_Error )
+ cf2_decoder_parse_charstrings( PS_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len )
+ {
+ FT_Memory memory;
+ FT_Error error = FT_Err_Ok;
+ CF2_Font font;
+
+ FT_Bool is_t1 = decoder->builder.is_t1;
+
+
+ FT_ASSERT( decoder &&
+ ( is_t1 || decoder->cff ) );
+
+ if ( is_t1 && !decoder->current_subfont )
+ {
+ FT_ERROR(( "cf2_decoder_parse_charstrings (Type 1): "
+ "SubFont missing. Use `t1_make_subfont' first\n" ));
+ return FT_THROW( Invalid_Table );
+ }
+
+ memory = decoder->builder.memory;
+
+ /* CF2 data is saved here across glyphs */
+ font = (CF2_Font)decoder->cf2_instance->data;
+
+ /* on first glyph, allocate instance structure */
+ if ( !decoder->cf2_instance->data )
+ {
+ decoder->cf2_instance->finalizer =
+ (FT_Generic_Finalizer)cf2_free_instance;
+
+ if ( FT_ALLOC( decoder->cf2_instance->data,
+ sizeof ( CF2_FontRec ) ) )
+ return FT_THROW( Out_Of_Memory );
+
+ font = (CF2_Font)decoder->cf2_instance->data;
+
+ font->memory = memory;
+
+ if ( !is_t1 )
+ font->cffload = (FT_Service_CFFLoad)decoder->cff->cffload;
+
+ /* initialize a client outline, to be shared by each glyph rendered */
+ cf2_outline_init( &font->outline, font->memory, &font->error );
+ }
+
+ /* save decoder; it is a stack variable and will be different on each */
+ /* call */
+ font->decoder = decoder;
+ font->outline.decoder = decoder;
+
+ {
+ /* build parameters for Adobe engine */
+
+ PS_Builder* builder = &decoder->builder;
+ PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( builder->face );
+
+ FT_Bool no_stem_darkening_driver =
+ driver->no_stem_darkening;
+ FT_Char no_stem_darkening_font =
+ builder->face->internal->no_stem_darkening;
+
+ /* local error */
+ FT_Error error2 = FT_Err_Ok;
+ CF2_BufferRec buf;
+ CF2_Matrix transform;
+ CF2_F16Dot16 glyphWidth;
+
+ FT_Bool hinted;
+ FT_Bool scaled;
+
+
+ /* FreeType has already looked up the GID; convert to */
+ /* `RegionBuffer', assuming that the input has been validated */
+ FT_ASSERT( charstring_base + charstring_len >= charstring_base );
+
+ FT_ZERO( &buf );
+ buf.start =
+ buf.ptr = charstring_base;
+ buf.end = charstring_base + charstring_len;
+
+ FT_ZERO( &transform );
+
+ cf2_getScaleAndHintFlag( decoder,
+ &transform.a,
+ &transform.d,
+ &hinted,
+ &scaled );
+
+ if ( is_t1 )
+ font->isCFF2 = FALSE;
+ else
+ {
+ /* copy isCFF2 boolean from TT_Face to CF2_Font */
+ font->isCFF2 = ((TT_Face)builder->face)->is_cff2;
+ }
+ font->isT1 = is_t1;
+
+ font->renderingFlags = 0;
+ if ( hinted )
+ font->renderingFlags |= CF2_FlagsHinted;
+ if ( scaled && ( !no_stem_darkening_font ||
+ ( no_stem_darkening_font < 0 &&
+ !no_stem_darkening_driver ) ) )
+ font->renderingFlags |= CF2_FlagsDarkened;
+
+ font->darkenParams[0] = driver->darken_params[0];
+ font->darkenParams[1] = driver->darken_params[1];
+ font->darkenParams[2] = driver->darken_params[2];
+ font->darkenParams[3] = driver->darken_params[3];
+ font->darkenParams[4] = driver->darken_params[4];
+ font->darkenParams[5] = driver->darken_params[5];
+ font->darkenParams[6] = driver->darken_params[6];
+ font->darkenParams[7] = driver->darken_params[7];
+
+ /* now get an outline for this glyph; */
+ /* also get units per em to validate scale */
+ font->unitsPerEm = (CF2_Int)cf2_getUnitsPerEm( decoder );
+
+ if ( scaled )
+ {
+ error2 = cf2_checkTransform( &transform, font->unitsPerEm );
+ if ( error2 )
+ return error2;
+ }
+
+ error2 = cf2_getGlyphOutline( font, &buf, &transform, &glyphWidth );
+ if ( error2 )
+ return FT_ERR( Invalid_File_Format );
+
+ cf2_setGlyphWidth( &font->outline, glyphWidth );
+
+ return FT_Err_Ok;
+ }
+ }
+
+
+ /* get pointer to current FreeType subfont (based on current glyphID) */
+ FT_LOCAL_DEF( CFF_SubFont )
+ cf2_getSubfont( PS_Decoder* decoder )
+ {
+ FT_ASSERT( decoder && decoder->current_subfont );
+
+ return decoder->current_subfont;
+ }
+
+
+ /* get pointer to VStore structure */
+ FT_LOCAL_DEF( CFF_VStore )
+ cf2_getVStore( PS_Decoder* decoder )
+ {
+ FT_ASSERT( decoder && decoder->cff );
+
+ return &decoder->cff->vstore;
+ }
+
+
+ /* get maxstack value from CFF2 Top DICT */
+ FT_LOCAL_DEF( FT_UInt )
+ cf2_getMaxstack( PS_Decoder* decoder )
+ {
+ FT_ASSERT( decoder && decoder->cff );
+
+ return decoder->cff->top_font.font_dict.maxstack;
+ }
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ /* Get normalized design vector for current render request; */
+ /* return pointer and length. */
+ /* */
+ /* Note: Uses FT_Fixed not CF2_Fixed for the vector. */
+ FT_LOCAL_DEF( FT_Error )
+ cf2_getNormalizedVector( PS_Decoder* decoder,
+ CF2_UInt *len,
+ FT_Fixed* *vec )
+ {
+ TT_Face face;
+ FT_Service_MultiMasters mm;
+
+
+ FT_ASSERT( decoder && decoder->builder.face );
+ FT_ASSERT( vec && len );
+ FT_ASSERT( !decoder->builder.is_t1 );
+
+ face = (TT_Face)decoder->builder.face;
+ mm = (FT_Service_MultiMasters)face->mm;
+
+ return mm->get_var_blend( FT_FACE( face ), len, NULL, vec, NULL );
+ }
+#endif
+
+
+ /* get `y_ppem' from `CFF_Size' */
+ FT_LOCAL_DEF( CF2_Fixed )
+ cf2_getPpemY( PS_Decoder* decoder )
+ {
+ FT_ASSERT( decoder &&
+ decoder->builder.face &&
+ decoder->builder.face->size );
+
+ /*
+ * Note that `y_ppem' can be zero if there wasn't a call to
+ * `FT_Set_Char_Size' or something similar. However, this isn't a
+ * problem since we come to this place in the code only if
+ * FT_LOAD_NO_SCALE is set (the other case gets caught by
+ * `cf2_checkTransform'). The ppem value is needed to compute the stem
+ * darkening, which is disabled for getting the unscaled outline.
+ *
+ */
+ return cf2_intToFixed(
+ decoder->builder.face->size->metrics.y_ppem );
+ }
+
+
+ /* get standard stem widths for the current subfont; */
+ /* FreeType stores these as integer font units */
+ /* (note: variable names seem swapped) */
+ FT_LOCAL_DEF( CF2_Fixed )
+ cf2_getStdVW( PS_Decoder* decoder )
+ {
+ FT_ASSERT( decoder && decoder->current_subfont );
+
+ return cf2_intToFixed(
+ decoder->current_subfont->private_dict.standard_height );
+ }
+
+
+ FT_LOCAL_DEF( CF2_Fixed )
+ cf2_getStdHW( PS_Decoder* decoder )
+ {
+ FT_ASSERT( decoder && decoder->current_subfont );
+
+ return cf2_intToFixed(
+ decoder->current_subfont->private_dict.standard_width );
+ }
+
+
+ /* note: FreeType stores 1000 times the actual value for `BlueScale' */
+ FT_LOCAL_DEF( void )
+ cf2_getBlueMetrics( PS_Decoder* decoder,
+ CF2_Fixed* blueScale,
+ CF2_Fixed* blueShift,
+ CF2_Fixed* blueFuzz )
+ {
+ FT_ASSERT( decoder && decoder->current_subfont );
+
+ *blueScale = FT_DivFix(
+ decoder->current_subfont->private_dict.blue_scale,
+ cf2_intToFixed( 1000 ) );
+ *blueShift = cf2_intToFixed(
+ decoder->current_subfont->private_dict.blue_shift );
+ *blueFuzz = cf2_intToFixed(
+ decoder->current_subfont->private_dict.blue_fuzz );
+ }
+
+
+ /* get blue values counts and arrays; the FreeType parser has validated */
+ /* the counts and verified that each is an even number */
+ FT_LOCAL_DEF( void )
+ cf2_getBlueValues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data )
+ {
+ FT_ASSERT( decoder && decoder->current_subfont );
+
+ *count = decoder->current_subfont->private_dict.num_blue_values;
+ *data = (FT_Pos*)
+ &decoder->current_subfont->private_dict.blue_values;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_getOtherBlues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data )
+ {
+ FT_ASSERT( decoder && decoder->current_subfont );
+
+ *count = decoder->current_subfont->private_dict.num_other_blues;
+ *data = (FT_Pos*)
+ &decoder->current_subfont->private_dict.other_blues;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_getFamilyBlues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data )
+ {
+ FT_ASSERT( decoder && decoder->current_subfont );
+
+ *count = decoder->current_subfont->private_dict.num_family_blues;
+ *data = (FT_Pos*)
+ &decoder->current_subfont->private_dict.family_blues;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_getFamilyOtherBlues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data )
+ {
+ FT_ASSERT( decoder && decoder->current_subfont );
+
+ *count = decoder->current_subfont->private_dict.num_family_other_blues;
+ *data = (FT_Pos*)
+ &decoder->current_subfont->private_dict.family_other_blues;
+ }
+
+
+ FT_LOCAL_DEF( CF2_Int )
+ cf2_getLanguageGroup( PS_Decoder* decoder )
+ {
+ FT_ASSERT( decoder && decoder->current_subfont );
+
+ return decoder->current_subfont->private_dict.language_group;
+ }
+
+
+ /* convert unbiased subroutine index to `CF2_Buffer' and */
+ /* return 0 on success */
+ FT_LOCAL_DEF( CF2_Int )
+ cf2_initGlobalRegionBuffer( PS_Decoder* decoder,
+ CF2_Int subrNum,
+ CF2_Buffer buf )
+ {
+ CF2_UInt idx;
+
+
+ FT_ASSERT( decoder );
+
+ FT_ZERO( buf );
+
+ idx = (CF2_UInt)( subrNum + decoder->globals_bias );
+ if ( idx >= decoder->num_globals )
+ return TRUE; /* error */
+
+ FT_ASSERT( decoder->globals );
+
+ buf->start =
+ buf->ptr = decoder->globals[idx];
+ buf->end = decoder->globals[idx + 1];
+
+ return FALSE; /* success */
+ }
+
+
+ /* convert AdobeStandardEncoding code to CF2_Buffer; */
+ /* used for seac component */
+ FT_LOCAL_DEF( FT_Error )
+ cf2_getSeacComponent( PS_Decoder* decoder,
+ CF2_Int code,
+ CF2_Buffer buf )
+ {
+ CF2_Int gid;
+ FT_Byte* charstring;
+ FT_ULong len;
+ FT_Error error;
+
+
+ FT_ASSERT( decoder );
+ FT_ASSERT( !decoder->builder.is_t1 );
+
+ FT_ZERO( buf );
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ /* Incremental fonts don't necessarily have valid charsets. */
+ /* They use the character code, not the glyph index, in this case. */
+ if ( decoder->builder.face->internal->incremental_interface )
+ gid = code;
+ else
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+ {
+ gid = cff_lookup_glyph_by_stdcharcode( decoder->cff, code );
+ if ( gid < 0 )
+ return FT_THROW( Invalid_Glyph_Format );
+ }
+
+ error = decoder->get_glyph_callback( (TT_Face)decoder->builder.face,
+ (CF2_UInt)gid,
+ &charstring,
+ &len );
+ /* TODO: for now, just pass the FreeType error through */
+ if ( error )
+ return error;
+
+ /* assume input has been validated */
+ FT_ASSERT( charstring + len >= charstring );
+
+ buf->start = charstring;
+ buf->end = charstring + len;
+ buf->ptr = buf->start;
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_freeSeacComponent( PS_Decoder* decoder,
+ CF2_Buffer buf )
+ {
+ FT_ASSERT( decoder );
+ FT_ASSERT( !decoder->builder.is_t1 );
+
+ decoder->free_glyph_callback( (TT_Face)decoder->builder.face,
+ (FT_Byte**)&buf->start,
+ (FT_ULong)( buf->end - buf->start ) );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cf2_getT1SeacComponent( PS_Decoder* decoder,
+ FT_UInt glyph_index,
+ CF2_Buffer buf )
+ {
+ FT_Data glyph_data;
+ FT_Error error = FT_Err_Ok;
+ T1_Face face = (T1_Face)decoder->builder.face;
+ T1_Font type1 = &face->type1;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ FT_Incremental_InterfaceRec *inc =
+ face->root.internal->incremental_interface;
+
+
+ /* For incremental fonts get the character data using the */
+ /* callback function. */
+ if ( inc )
+ error = inc->funcs->get_glyph_data( inc->object,
+ glyph_index, &glyph_data );
+ else
+#endif
+ /* For ordinary fonts get the character data stored in the face record. */
+ {
+ glyph_data.pointer = type1->charstrings[glyph_index];
+ glyph_data.length = (FT_Int)type1->charstrings_len[glyph_index];
+ }
+
+ if ( !error )
+ {
+ FT_Byte* charstring_base = (FT_Byte*)glyph_data.pointer;
+ FT_ULong charstring_len = (FT_ULong)glyph_data.length;
+
+
+ FT_ASSERT( charstring_base + charstring_len >= charstring_base );
+
+ FT_ZERO( buf );
+ buf->start =
+ buf->ptr = charstring_base;
+ buf->end = charstring_base + charstring_len;
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_freeT1SeacComponent( PS_Decoder* decoder,
+ CF2_Buffer buf )
+ {
+ T1_Face face;
+ FT_Data data;
+
+
+ FT_ASSERT( decoder );
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ face = (T1_Face)decoder->builder.face;
+
+ data.pointer = buf->start;
+ data.length = (FT_Int)( buf->end - buf->start );
+
+ if ( face->root.internal->incremental_interface )
+ face->root.internal->incremental_interface->funcs->free_glyph_data(
+ face->root.internal->incremental_interface->object,
+ &data );
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+ }
+
+
+ FT_LOCAL_DEF( CF2_Int )
+ cf2_initLocalRegionBuffer( PS_Decoder* decoder,
+ CF2_Int subrNum,
+ CF2_Buffer buf )
+ {
+ CF2_UInt idx;
+
+
+ FT_ASSERT( decoder );
+
+ FT_ZERO( buf );
+
+ idx = (CF2_UInt)( subrNum + decoder->locals_bias );
+ if ( idx >= decoder->num_locals )
+ return TRUE; /* error */
+
+ FT_ASSERT( decoder->locals );
+
+ buf->start = decoder->locals[idx];
+
+ if ( decoder->builder.is_t1 )
+ {
+ /* The Type 1 driver stores subroutines without the seed bytes. */
+ /* The CID driver stores subroutines with seed bytes. This */
+ /* case is taken care of when decoder->subrs_len == 0. */
+ if ( decoder->locals_len )
+ buf->end = buf->start + decoder->locals_len[idx];
+ else
+ {
+ /* We are using subroutines from a CID font. We must adjust */
+ /* for the seed bytes. */
+ buf->start += ( decoder->lenIV >= 0 ? decoder->lenIV : 0 );
+ buf->end = decoder->locals[idx + 1];
+ }
+
+ if ( !buf->start )
+ {
+ FT_ERROR(( "cf2_initLocalRegionBuffer (Type 1 mode):"
+ " invoking empty subrs\n" ));
+ }
+ }
+ else
+ {
+ buf->end = decoder->locals[idx + 1];
+ }
+
+ buf->ptr = buf->start;
+
+ return FALSE; /* success */
+ }
+
+
+ FT_LOCAL_DEF( CF2_Fixed )
+ cf2_getDefaultWidthX( PS_Decoder* decoder )
+ {
+ FT_ASSERT( decoder && decoder->current_subfont );
+
+ return cf2_intToFixed(
+ decoder->current_subfont->private_dict.default_width );
+ }
+
+
+ FT_LOCAL_DEF( CF2_Fixed )
+ cf2_getNominalWidthX( PS_Decoder* decoder )
+ {
+ FT_ASSERT( decoder && decoder->current_subfont );
+
+ return cf2_intToFixed(
+ decoder->current_subfont->private_dict.nominal_width );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_outline_reset( CF2_Outline outline )
+ {
+ PS_Decoder* decoder = outline->decoder;
+
+
+ FT_ASSERT( decoder );
+
+ outline->root.windingMomentum = 0;
+
+ FT_GlyphLoader_Rewind( decoder->builder.loader );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_outline_close( CF2_Outline outline )
+ {
+ PS_Decoder* decoder = outline->decoder;
+
+
+ FT_ASSERT( decoder );
+
+ ps_builder_close_contour( &decoder->builder );
+
+ FT_GlyphLoader_Add( decoder->builder.loader );
+ }
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psft.h b/thirdparty/freetype/src/psaux/psft.h
new file mode 100644
index 0000000000..ab172110bb
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psft.h
@@ -0,0 +1,167 @@
+/***************************************************************************/
+/* */
+/* psft.h */
+/* */
+/* FreeType Glue Component to Adobe's Interpreter (specification). */
+/* */
+/* Copyright 2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef PSFT_H_
+#define PSFT_H_
+
+
+#include "pstypes.h"
+
+
+ /* TODO: disable asserts for now */
+#define CF2_NDEBUG
+
+
+#include FT_SYSTEM_H
+
+#include "psglue.h"
+#include FT_INTERNAL_POSTSCRIPT_AUX_H /* for PS_Decoder */
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ cf2_decoder_parse_charstrings( PS_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len );
+
+ FT_LOCAL( CFF_SubFont )
+ cf2_getSubfont( PS_Decoder* decoder );
+
+ FT_LOCAL( CFF_VStore )
+ cf2_getVStore( PS_Decoder* decoder );
+
+ FT_LOCAL( FT_UInt )
+ cf2_getMaxstack( PS_Decoder* decoder );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ FT_LOCAL( FT_Error )
+ cf2_getNormalizedVector( PS_Decoder* decoder,
+ CF2_UInt *len,
+ FT_Fixed* *vec );
+#endif
+
+ FT_LOCAL( CF2_Fixed )
+ cf2_getPpemY( PS_Decoder* decoder );
+ FT_LOCAL( CF2_Fixed )
+ cf2_getStdVW( PS_Decoder* decoder );
+ FT_LOCAL( CF2_Fixed )
+ cf2_getStdHW( PS_Decoder* decoder );
+
+ FT_LOCAL( void )
+ cf2_getBlueMetrics( PS_Decoder* decoder,
+ CF2_Fixed* blueScale,
+ CF2_Fixed* blueShift,
+ CF2_Fixed* blueFuzz );
+ FT_LOCAL( void )
+ cf2_getBlueValues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data );
+ FT_LOCAL( void )
+ cf2_getOtherBlues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data );
+ FT_LOCAL( void )
+ cf2_getFamilyBlues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data );
+ FT_LOCAL( void )
+ cf2_getFamilyOtherBlues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data );
+
+ FT_LOCAL( CF2_Int )
+ cf2_getLanguageGroup( PS_Decoder* decoder );
+
+ FT_LOCAL( CF2_Int )
+ cf2_initGlobalRegionBuffer( PS_Decoder* decoder,
+ CF2_Int subrNum,
+ CF2_Buffer buf );
+ FT_LOCAL( FT_Error )
+ cf2_getSeacComponent( PS_Decoder* decoder,
+ CF2_Int code,
+ CF2_Buffer buf );
+ FT_LOCAL( void )
+ cf2_freeSeacComponent( PS_Decoder* decoder,
+ CF2_Buffer buf );
+ FT_LOCAL( CF2_Int )
+ cf2_initLocalRegionBuffer( PS_Decoder* decoder,
+ CF2_Int subrNum,
+ CF2_Buffer buf );
+
+ FT_LOCAL( CF2_Fixed )
+ cf2_getDefaultWidthX( PS_Decoder* decoder );
+ FT_LOCAL( CF2_Fixed )
+ cf2_getNominalWidthX( PS_Decoder* decoder );
+
+
+ FT_LOCAL( FT_Error )
+ cf2_getT1SeacComponent( PS_Decoder* decoder,
+ FT_UInt glyph_index,
+ CF2_Buffer buf );
+ FT_LOCAL( void )
+ cf2_freeT1SeacComponent( PS_Decoder* decoder,
+ CF2_Buffer buf );
+
+ /*
+ * FreeType client outline
+ *
+ * process output from the charstring interpreter
+ */
+ typedef struct CF2_OutlineRec_
+ {
+ CF2_OutlineCallbacksRec root; /* base class must be first */
+ PS_Decoder* decoder;
+
+ } CF2_OutlineRec, *CF2_Outline;
+
+
+ FT_LOCAL( void )
+ cf2_outline_reset( CF2_Outline outline );
+ FT_LOCAL( void )
+ cf2_outline_close( CF2_Outline outline );
+
+
+FT_END_HEADER
+
+
+#endif /* PSFT_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psglue.h b/thirdparty/freetype/src/psaux/psglue.h
new file mode 100644
index 0000000000..5545e12a5b
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psglue.h
@@ -0,0 +1,144 @@
+/***************************************************************************/
+/* */
+/* psglue.h */
+/* */
+/* Adobe's code for shared stuff (specification only). */
+/* */
+/* Copyright 2007-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef PSGLUE_H_
+#define PSGLUE_H_
+
+
+/* common includes for other modules */
+#include "pserror.h"
+#include "psfixed.h"
+#include "psarrst.h"
+#include "psread.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /* rendering parameters */
+
+ /* apply hints to rendered glyphs */
+#define CF2_FlagsHinted 1
+ /* for testing */
+#define CF2_FlagsDarkened 2
+
+ /* type for holding the flags */
+ typedef CF2_Int CF2_RenderingFlags;
+
+
+ /* elements of a glyph outline */
+ typedef enum CF2_PathOp_
+ {
+ CF2_PathOpMoveTo = 1, /* change the current point */
+ CF2_PathOpLineTo = 2, /* line */
+ CF2_PathOpQuadTo = 3, /* quadratic curve */
+ CF2_PathOpCubeTo = 4 /* cubic curve */
+
+ } CF2_PathOp;
+
+
+ /* a matrix of fixed point values */
+ typedef struct CF2_Matrix_
+ {
+ CF2_F16Dot16 a;
+ CF2_F16Dot16 b;
+ CF2_F16Dot16 c;
+ CF2_F16Dot16 d;
+ CF2_F16Dot16 tx;
+ CF2_F16Dot16 ty;
+
+ } CF2_Matrix;
+
+
+ /* these typedefs are needed by more than one header file */
+ /* and gcc compiler doesn't allow redefinition */
+ typedef struct CF2_FontRec_ CF2_FontRec, *CF2_Font;
+ typedef struct CF2_HintRec_ CF2_HintRec, *CF2_Hint;
+
+
+ /* A common structure for all callback parameters. */
+ /* */
+ /* Some members may be unused. For example, `pt0' is not used for */
+ /* `moveTo' and `pt3' is not used for `quadTo'. The initial point `pt0' */
+ /* is included for each path element for generality; curve conversions */
+ /* need it. The `op' parameter allows one function to handle multiple */
+ /* element types. */
+
+ typedef struct CF2_CallbackParamsRec_
+ {
+ FT_Vector pt0;
+ FT_Vector pt1;
+ FT_Vector pt2;
+ FT_Vector pt3;
+
+ CF2_Int op;
+
+ } CF2_CallbackParamsRec, *CF2_CallbackParams;
+
+
+ /* forward reference */
+ typedef struct CF2_OutlineCallbacksRec_ CF2_OutlineCallbacksRec,
+ *CF2_OutlineCallbacks;
+
+ /* callback function pointers */
+ typedef void
+ (*CF2_Callback_Type)( CF2_OutlineCallbacks callbacks,
+ const CF2_CallbackParams params );
+
+
+ struct CF2_OutlineCallbacksRec_
+ {
+ CF2_Callback_Type moveTo;
+ CF2_Callback_Type lineTo;
+ CF2_Callback_Type quadTo;
+ CF2_Callback_Type cubeTo;
+
+ CF2_Int windingMomentum; /* for winding order detection */
+
+ FT_Memory memory;
+ FT_Error* error;
+ };
+
+
+FT_END_HEADER
+
+
+#endif /* PSGLUE_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/pshints.c b/thirdparty/freetype/src/psaux/pshints.c
new file mode 100644
index 0000000000..3615196425
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/pshints.c
@@ -0,0 +1,1939 @@
+/***************************************************************************/
+/* */
+/* pshints.c */
+/* */
+/* Adobe's code for handling CFF hints (body). */
+/* */
+/* Copyright 2007-2014 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#include "psft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "psglue.h"
+#include "psfont.h"
+#include "pshints.h"
+#include "psintrp.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cf2hints
+
+
+ typedef struct CF2_HintMoveRec_
+ {
+ size_t j; /* index of upper hint map edge */
+ CF2_Fixed moveUp; /* adjustment to optimum position */
+
+ } CF2_HintMoveRec, *CF2_HintMove;
+
+
+ /* Compute angular momentum for winding order detection. It is called */
+ /* for all lines and curves, but not necessarily in element order. */
+ static CF2_Int
+ cf2_getWindingMomentum( CF2_Fixed x1,
+ CF2_Fixed y1,
+ CF2_Fixed x2,
+ CF2_Fixed y2 )
+ {
+ /* cross product of pt1 position from origin with pt2 position from */
+ /* pt1; we reduce the precision so that the result fits into 32 bits */
+
+ return ( x1 >> 16 ) * ( SUB_INT32( y2, y1 ) >> 16 ) -
+ ( y1 >> 16 ) * ( SUB_INT32( x2, x1 ) >> 16 );
+ }
+
+
+ /*
+ * Construct from a StemHint; this is used as a parameter to
+ * `cf2_blues_capture'.
+ * `hintOrigin' is the character space displacement of a seac accent.
+ * Adjust stem hint for darkening here.
+ *
+ */
+ static void
+ cf2_hint_init( CF2_Hint hint,
+ const CF2_ArrStack stemHintArray,
+ size_t indexStemHint,
+ const CF2_Font font,
+ CF2_Fixed hintOrigin,
+ CF2_Fixed scale,
+ FT_Bool bottom )
+ {
+ CF2_Fixed width;
+ const CF2_StemHintRec* stemHint;
+
+
+ FT_ZERO( hint );
+
+ stemHint = (const CF2_StemHintRec*)cf2_arrstack_getPointer(
+ stemHintArray,
+ indexStemHint );
+
+ width = SUB_INT32( stemHint->max, stemHint->min );
+
+ if ( width == cf2_intToFixed( -21 ) )
+ {
+ /* ghost bottom */
+
+ if ( bottom )
+ {
+ hint->csCoord = stemHint->max;
+ hint->flags = CF2_GhostBottom;
+ }
+ else
+ hint->flags = 0;
+ }
+
+ else if ( width == cf2_intToFixed( -20 ) )
+ {
+ /* ghost top */
+
+ if ( bottom )
+ hint->flags = 0;
+ else
+ {
+ hint->csCoord = stemHint->min;
+ hint->flags = CF2_GhostTop;
+ }
+ }
+
+ else if ( width < 0 )
+ {
+ /* inverted pair */
+
+ /*
+ * Hints with negative widths were produced by an early version of a
+ * non-Adobe font tool. The Type 2 spec allows edge (ghost) hints
+ * with negative widths, but says
+ *
+ * All other negative widths have undefined meaning.
+ *
+ * CoolType has a silent workaround that negates the hint width; for
+ * permissive mode, we do the same here.
+ *
+ * Note: Such fonts cannot use ghost hints, but should otherwise work.
+ * Note: Some poor hints in our faux fonts can produce negative
+ * widths at some blends. For example, see a light weight of
+ * `u' in ASerifMM.
+ *
+ */
+ if ( bottom )
+ {
+ hint->csCoord = stemHint->max;
+ hint->flags = CF2_PairBottom;
+ }
+ else
+ {
+ hint->csCoord = stemHint->min;
+ hint->flags = CF2_PairTop;
+ }
+ }
+
+ else
+ {
+ /* normal pair */
+
+ if ( bottom )
+ {
+ hint->csCoord = stemHint->min;
+ hint->flags = CF2_PairBottom;
+ }
+ else
+ {
+ hint->csCoord = stemHint->max;
+ hint->flags = CF2_PairTop;
+ }
+ }
+
+ /* Now that ghost hints have been detected, adjust this edge for */
+ /* darkening. Bottoms are not changed; tops are incremented by twice */
+ /* `darkenY'. */
+ if ( cf2_hint_isTop( hint ) )
+ hint->csCoord = ADD_INT32( hint->csCoord, 2 * font->darkenY );
+
+ hint->csCoord = ADD_INT32( hint->csCoord, hintOrigin );
+ hint->scale = scale;
+ hint->index = indexStemHint; /* index in original stem hint array */
+
+ /* if original stem hint has been used, use the same position */
+ if ( hint->flags != 0 && stemHint->used )
+ {
+ if ( cf2_hint_isTop( hint ) )
+ hint->dsCoord = stemHint->maxDS;
+ else
+ hint->dsCoord = stemHint->minDS;
+
+ cf2_hint_lock( hint );
+ }
+ else
+ hint->dsCoord = FT_MulFix( hint->csCoord, scale );
+ }
+
+
+ /* initialize an invalid hint map element */
+ static void
+ cf2_hint_initZero( CF2_Hint hint )
+ {
+ FT_ZERO( hint );
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_hint_isValid( const CF2_Hint hint )
+ {
+ return (FT_Bool)( hint->flags != 0 );
+ }
+
+
+ static FT_Bool
+ cf2_hint_isPair( const CF2_Hint hint )
+ {
+ return (FT_Bool)( ( hint->flags &
+ ( CF2_PairBottom | CF2_PairTop ) ) != 0 );
+ }
+
+
+ static FT_Bool
+ cf2_hint_isPairTop( const CF2_Hint hint )
+ {
+ return (FT_Bool)( ( hint->flags & CF2_PairTop ) != 0 );
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_hint_isTop( const CF2_Hint hint )
+ {
+ return (FT_Bool)( ( hint->flags &
+ ( CF2_PairTop | CF2_GhostTop ) ) != 0 );
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_hint_isBottom( const CF2_Hint hint )
+ {
+ return (FT_Bool)( ( hint->flags &
+ ( CF2_PairBottom | CF2_GhostBottom ) ) != 0 );
+ }
+
+
+ static FT_Bool
+ cf2_hint_isLocked( const CF2_Hint hint )
+ {
+ return (FT_Bool)( ( hint->flags & CF2_Locked ) != 0 );
+ }
+
+
+ static FT_Bool
+ cf2_hint_isSynthetic( const CF2_Hint hint )
+ {
+ return (FT_Bool)( ( hint->flags & CF2_Synthetic ) != 0 );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_hint_lock( CF2_Hint hint )
+ {
+ hint->flags |= CF2_Locked;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_hintmap_init( CF2_HintMap hintmap,
+ CF2_Font font,
+ CF2_HintMap initialMap,
+ CF2_ArrStack hintMoves,
+ CF2_Fixed scale )
+ {
+ FT_ZERO( hintmap );
+
+ /* copy parameters from font instance */
+ hintmap->hinted = font->hinted;
+ hintmap->scale = scale;
+ hintmap->font = font;
+ hintmap->initialHintMap = initialMap;
+ /* will clear in `cf2_hintmap_adjustHints' */
+ hintmap->hintMoves = hintMoves;
+ }
+
+
+ static FT_Bool
+ cf2_hintmap_isValid( const CF2_HintMap hintmap )
+ {
+ return hintmap->isValid;
+ }
+
+
+ static void
+ cf2_hintmap_dump( CF2_HintMap hintmap )
+ {
+#ifdef FT_DEBUG_LEVEL_TRACE
+ CF2_UInt i;
+
+
+ FT_TRACE6(( " index csCoord dsCoord scale flags\n" ));
+
+ for ( i = 0; i < hintmap->count; i++ )
+ {
+ CF2_Hint hint = &hintmap->edge[i];
+
+
+ FT_TRACE6(( " %3d %7.2f %7.2f %5d %s%s%s%s\n",
+ hint->index,
+ hint->csCoord / 65536.0,
+ hint->dsCoord / ( hint->scale * 1.0 ),
+ hint->scale,
+ ( cf2_hint_isPair( hint ) ? "p" : "g" ),
+ ( cf2_hint_isTop( hint ) ? "t" : "b" ),
+ ( cf2_hint_isLocked( hint ) ? "L" : ""),
+ ( cf2_hint_isSynthetic( hint ) ? "S" : "" ) ));
+ }
+#else
+ FT_UNUSED( hintmap );
+#endif
+ }
+
+
+ /* transform character space coordinate to device space using hint map */
+ static CF2_Fixed
+ cf2_hintmap_map( CF2_HintMap hintmap,
+ CF2_Fixed csCoord )
+ {
+ if ( hintmap->count == 0 || ! hintmap->hinted )
+ {
+ /* there are no hints; use uniform scale and zero offset */
+ return FT_MulFix( csCoord, hintmap->scale );
+ }
+ else
+ {
+ /* start linear search from last hit */
+ CF2_UInt i = hintmap->lastIndex;
+
+
+ FT_ASSERT( hintmap->lastIndex < CF2_MAX_HINT_EDGES );
+
+ /* search up */
+ while ( i < hintmap->count - 1 &&
+ csCoord >= hintmap->edge[i + 1].csCoord )
+ i += 1;
+
+ /* search down */
+ while ( i > 0 && csCoord < hintmap->edge[i].csCoord )
+ i -= 1;
+
+ hintmap->lastIndex = i;
+
+ if ( i == 0 && csCoord < hintmap->edge[0].csCoord )
+ {
+ /* special case for points below first edge: use uniform scale */
+ return ADD_INT32( FT_MulFix( SUB_INT32( csCoord,
+ hintmap->edge[0].csCoord ),
+ hintmap->scale ),
+ hintmap->edge[0].dsCoord );
+ }
+ else
+ {
+ /*
+ * Note: entries with duplicate csCoord are allowed.
+ * Use edge[i], the highest entry where csCoord >= entry[i].csCoord
+ */
+ return ADD_INT32( FT_MulFix( SUB_INT32( csCoord,
+ hintmap->edge[i].csCoord ),
+ hintmap->edge[i].scale ),
+ hintmap->edge[i].dsCoord );
+ }
+ }
+ }
+
+
+ /*
+ * This hinting policy moves a hint pair in device space so that one of
+ * its two edges is on a device pixel boundary (its fractional part is
+ * zero). `cf2_hintmap_insertHint' guarantees no overlap in CS
+ * space. Ensure here that there is no overlap in DS.
+ *
+ * In the first pass, edges are adjusted relative to adjacent hints.
+ * Those that are below have already been adjusted. Those that are
+ * above have not yet been adjusted. If a hint above blocks an
+ * adjustment to an optimal position, we will try again in a second
+ * pass. The second pass is top-down.
+ *
+ */
+
+ static void
+ cf2_hintmap_adjustHints( CF2_HintMap hintmap )
+ {
+ size_t i, j;
+
+
+ cf2_arrstack_clear( hintmap->hintMoves ); /* working storage */
+
+ /*
+ * First pass is bottom-up (font hint order) without look-ahead.
+ * Locked edges are already adjusted.
+ * Unlocked edges begin with dsCoord from `initialHintMap'.
+ * Save edges that are not optimally adjusted in `hintMoves' array,
+ * and process them in second pass.
+ */
+
+ for ( i = 0; i < hintmap->count; i++ )
+ {
+ FT_Bool isPair = cf2_hint_isPair( &hintmap->edge[i] );
+
+
+ /* index of upper edge (same value for ghost hint) */
+ j = isPair ? i + 1 : i;
+
+ FT_ASSERT( j < hintmap->count );
+ FT_ASSERT( cf2_hint_isValid( &hintmap->edge[i] ) );
+ FT_ASSERT( cf2_hint_isValid( &hintmap->edge[j] ) );
+ FT_ASSERT( cf2_hint_isLocked( &hintmap->edge[i] ) ==
+ cf2_hint_isLocked( &hintmap->edge[j] ) );
+
+ if ( !cf2_hint_isLocked( &hintmap->edge[i] ) )
+ {
+ /* hint edge is not locked, we can adjust it */
+ CF2_Fixed fracDown = cf2_fixedFraction( hintmap->edge[i].dsCoord );
+ CF2_Fixed fracUp = cf2_fixedFraction( hintmap->edge[j].dsCoord );
+
+ /* calculate all four possibilities; moves down are negative */
+ CF2_Fixed downMoveDown = 0 - fracDown;
+ CF2_Fixed upMoveDown = 0 - fracUp;
+ CF2_Fixed downMoveUp = ( fracDown == 0 )
+ ? 0
+ : cf2_intToFixed( 1 ) - fracDown;
+ CF2_Fixed upMoveUp = ( fracUp == 0 )
+ ? 0
+ : cf2_intToFixed( 1 ) - fracUp;
+
+ /* smallest move up */
+ CF2_Fixed moveUp = FT_MIN( downMoveUp, upMoveUp );
+ /* smallest move down */
+ CF2_Fixed moveDown = FT_MAX( downMoveDown, upMoveDown );
+
+ /* final amount to move edge or edge pair */
+ CF2_Fixed move;
+
+ CF2_Fixed downMinCounter = CF2_MIN_COUNTER;
+ CF2_Fixed upMinCounter = CF2_MIN_COUNTER;
+ FT_Bool saveEdge = FALSE;
+
+
+ /* minimum counter constraint doesn't apply when adjacent edges */
+ /* are synthetic */
+ /* TODO: doesn't seem a big effect; for now, reduce the code */
+#if 0
+ if ( i == 0 ||
+ cf2_hint_isSynthetic( &hintmap->edge[i - 1] ) )
+ downMinCounter = 0;
+
+ if ( j >= hintmap->count - 1 ||
+ cf2_hint_isSynthetic( &hintmap->edge[j + 1] ) )
+ upMinCounter = 0;
+#endif
+
+ /* is there room to move up? */
+ /* there is if we are at top of array or the next edge is at or */
+ /* beyond proposed move up? */
+ if ( j >= hintmap->count - 1 ||
+ hintmap->edge[j + 1].dsCoord >=
+ ADD_INT32( hintmap->edge[j].dsCoord,
+ moveUp + upMinCounter ) )
+ {
+ /* there is room to move up; is there also room to move down? */
+ if ( i == 0 ||
+ hintmap->edge[i - 1].dsCoord <=
+ ADD_INT32( hintmap->edge[i].dsCoord,
+ moveDown - downMinCounter ) )
+ {
+ /* move smaller absolute amount */
+ move = ( -moveDown < moveUp ) ? moveDown : moveUp; /* optimum */
+ }
+ else
+ move = moveUp;
+ }
+ else
+ {
+ /* is there room to move down? */
+ if ( i == 0 ||
+ hintmap->edge[i - 1].dsCoord <=
+ ADD_INT32( hintmap->edge[i].dsCoord,
+ moveDown - downMinCounter ) )
+ {
+ move = moveDown;
+ /* true if non-optimum move */
+ saveEdge = (FT_Bool)( moveUp < -moveDown );
+ }
+ else
+ {
+ /* no room to move either way without overlapping or reducing */
+ /* the counter too much */
+ move = 0;
+ saveEdge = TRUE;
+ }
+ }
+
+ /* Identify non-moves and moves down that aren't optimal, and save */
+ /* them for second pass. */
+ /* Do this only if there is an unlocked edge above (which could */
+ /* possibly move). */
+ if ( saveEdge &&
+ j < hintmap->count - 1 &&
+ !cf2_hint_isLocked( &hintmap->edge[j + 1] ) )
+ {
+ CF2_HintMoveRec savedMove;
+
+
+ savedMove.j = j;
+ /* desired adjustment in second pass */
+ savedMove.moveUp = moveUp - move;
+
+ cf2_arrstack_push( hintmap->hintMoves, &savedMove );
+ }
+
+ /* move the edge(s) */
+ hintmap->edge[i].dsCoord = ADD_INT32( hintmap->edge[i].dsCoord,
+ move );
+ if ( isPair )
+ hintmap->edge[j].dsCoord = ADD_INT32( hintmap->edge[j].dsCoord,
+ move );
+ }
+
+ /* assert there are no overlaps in device space */
+ FT_ASSERT( i == 0 ||
+ hintmap->edge[i - 1].dsCoord <= hintmap->edge[i].dsCoord );
+ FT_ASSERT( i < j ||
+ hintmap->edge[i].dsCoord <= hintmap->edge[j].dsCoord );
+
+ /* adjust the scales, avoiding divide by zero */
+ if ( i > 0 )
+ {
+ if ( hintmap->edge[i].csCoord != hintmap->edge[i - 1].csCoord )
+ hintmap->edge[i - 1].scale =
+ FT_DivFix( SUB_INT32( hintmap->edge[i].dsCoord,
+ hintmap->edge[i - 1].dsCoord ),
+ SUB_INT32( hintmap->edge[i].csCoord,
+ hintmap->edge[i - 1].csCoord ) );
+ }
+
+ if ( isPair )
+ {
+ if ( hintmap->edge[j].csCoord != hintmap->edge[j - 1].csCoord )
+ hintmap->edge[j - 1].scale =
+ FT_DivFix( SUB_INT32( hintmap->edge[j].dsCoord,
+ hintmap->edge[j - 1].dsCoord ),
+ SUB_INT32( hintmap->edge[j].csCoord,
+ hintmap->edge[j - 1].csCoord ) );
+
+ i += 1; /* skip upper edge on next loop */
+ }
+ }
+
+ /* second pass tries to move non-optimal hints up, in case there is */
+ /* room now */
+ for ( i = cf2_arrstack_size( hintmap->hintMoves ); i > 0; i-- )
+ {
+ CF2_HintMove hintMove = (CF2_HintMove)
+ cf2_arrstack_getPointer( hintmap->hintMoves, i - 1 );
+
+
+ j = hintMove->j;
+
+ /* this was tested before the push, above */
+ FT_ASSERT( j < hintmap->count - 1 );
+
+ /* is there room to move up? */
+ if ( hintmap->edge[j + 1].dsCoord >=
+ ADD_INT32( hintmap->edge[j].dsCoord,
+ hintMove->moveUp + CF2_MIN_COUNTER ) )
+ {
+ /* there is more room now, move edge up */
+ hintmap->edge[j].dsCoord = ADD_INT32( hintmap->edge[j].dsCoord,
+ hintMove->moveUp );
+
+ if ( cf2_hint_isPair( &hintmap->edge[j] ) )
+ {
+ FT_ASSERT( j > 0 );
+ hintmap->edge[j - 1].dsCoord =
+ ADD_INT32( hintmap->edge[j - 1].dsCoord, hintMove->moveUp );
+ }
+ }
+ }
+ }
+
+
+ /* insert hint edges into map, sorted by csCoord */
+ static void
+ cf2_hintmap_insertHint( CF2_HintMap hintmap,
+ CF2_Hint bottomHintEdge,
+ CF2_Hint topHintEdge )
+ {
+ CF2_UInt indexInsert;
+
+ /* set default values, then check for edge hints */
+ FT_Bool isPair = TRUE;
+ CF2_Hint firstHintEdge = bottomHintEdge;
+ CF2_Hint secondHintEdge = topHintEdge;
+
+
+ /* one or none of the input params may be invalid when dealing with */
+ /* edge hints; at least one edge must be valid */
+ FT_ASSERT( cf2_hint_isValid( bottomHintEdge ) ||
+ cf2_hint_isValid( topHintEdge ) );
+
+ /* determine how many and which edges to insert */
+ if ( !cf2_hint_isValid( bottomHintEdge ) )
+ {
+ /* insert only the top edge */
+ firstHintEdge = topHintEdge;
+ isPair = FALSE;
+ }
+ else if ( !cf2_hint_isValid( topHintEdge ) )
+ {
+ /* insert only the bottom edge */
+ isPair = FALSE;
+ }
+
+ /* paired edges must be in proper order */
+ if ( isPair &&
+ topHintEdge->csCoord < bottomHintEdge->csCoord )
+ return;
+
+ /* linear search to find index value of insertion point */
+ indexInsert = 0;
+ for ( ; indexInsert < hintmap->count; indexInsert++ )
+ {
+ if ( hintmap->edge[indexInsert].csCoord >= firstHintEdge->csCoord )
+ break;
+ }
+
+ FT_TRACE7(( " Got hint at %.2f (%.2f)\n",
+ firstHintEdge->csCoord / 65536.0,
+ firstHintEdge->dsCoord / 65536.0 ));
+ if ( isPair )
+ FT_TRACE7(( " Got hint at %.2f (%.2f)\n",
+ secondHintEdge->csCoord / 65536.0,
+ secondHintEdge->dsCoord / 65536.0 ));
+
+ /*
+ * Discard any hints that overlap in character space. Most often, this
+ * is while building the initial map, where captured hints from all
+ * zones are combined. Define overlap to include hints that `touch'
+ * (overlap zero). Hiragino Sans/Gothic fonts have numerous hints that
+ * touch. Some fonts have non-ideographic glyphs that overlap our
+ * synthetic hints.
+ *
+ * Overlap also occurs when darkening stem hints that are close.
+ *
+ */
+ if ( indexInsert < hintmap->count )
+ {
+ /* we are inserting before an existing edge: */
+ /* verify that an existing edge is not the same */
+ if ( hintmap->edge[indexInsert].csCoord == firstHintEdge->csCoord )
+ return; /* ignore overlapping stem hint */
+
+ /* verify that a new pair does not straddle the next edge */
+ if ( isPair &&
+ hintmap->edge[indexInsert].csCoord <= secondHintEdge->csCoord )
+ return; /* ignore overlapping stem hint */
+
+ /* verify that we are not inserting between paired edges */
+ if ( cf2_hint_isPairTop( &hintmap->edge[indexInsert] ) )
+ return; /* ignore overlapping stem hint */
+ }
+
+ /* recompute device space locations using initial hint map */
+ if ( cf2_hintmap_isValid( hintmap->initialHintMap ) &&
+ !cf2_hint_isLocked( firstHintEdge ) )
+ {
+ if ( isPair )
+ {
+ /* Use hint map to position the center of stem, and nominal scale */
+ /* to position the two edges. This preserves the stem width. */
+ CF2_Fixed midpoint =
+ cf2_hintmap_map(
+ hintmap->initialHintMap,
+ ADD_INT32( secondHintEdge->csCoord,
+ firstHintEdge->csCoord ) / 2 );
+ CF2_Fixed halfWidth =
+ FT_MulFix( SUB_INT32( secondHintEdge->csCoord,
+ firstHintEdge->csCoord ) / 2,
+ hintmap->scale );
+
+
+ firstHintEdge->dsCoord = SUB_INT32( midpoint, halfWidth );
+ secondHintEdge->dsCoord = ADD_INT32( midpoint, halfWidth );
+ }
+ else
+ firstHintEdge->dsCoord = cf2_hintmap_map( hintmap->initialHintMap,
+ firstHintEdge->csCoord );
+ }
+
+ /*
+ * Discard any hints that overlap in device space; this can occur
+ * because locked hints have been moved to align with blue zones.
+ *
+ * TODO: Although we might correct this later during adjustment, we
+ * don't currently have a way to delete a conflicting hint once it has
+ * been inserted. See v2.030 MinionPro-Regular, 12 ppem darkened,
+ * initial hint map for second path, glyph 945 (the perispomeni (tilde)
+ * in U+1F6E, Greek omega with psili and perispomeni). Darkening is
+ * 25. Pair 667,747 initially conflicts in design space with top edge
+ * 660. This is because 667 maps to 7.87, and the top edge was
+ * captured by a zone at 8.0. The pair is later successfully inserted
+ * in a zone without the top edge. In this zone it is adjusted to 8.0,
+ * and no longer conflicts with the top edge in design space. This
+ * means it can be included in yet a later zone which does have the top
+ * edge hint. This produces a small mismatch between the first and
+ * last points of this path, even though the hint masks are the same.
+ * The density map difference is tiny (1/256).
+ *
+ */
+
+ if ( indexInsert > 0 )
+ {
+ /* we are inserting after an existing edge */
+ if ( firstHintEdge->dsCoord < hintmap->edge[indexInsert - 1].dsCoord )
+ return;
+ }
+
+ if ( indexInsert < hintmap->count )
+ {
+ /* we are inserting before an existing edge */
+ if ( isPair )
+ {
+ if ( secondHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord )
+ return;
+ }
+ else
+ {
+ if ( firstHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord )
+ return;
+ }
+ }
+
+ /* make room to insert */
+ {
+ CF2_UInt iSrc = hintmap->count - 1;
+ CF2_UInt iDst = isPair ? hintmap->count + 1 : hintmap->count;
+
+ CF2_UInt count = hintmap->count - indexInsert;
+
+
+ if ( iDst >= CF2_MAX_HINT_EDGES )
+ {
+ FT_TRACE4(( "cf2_hintmap_insertHint: too many hintmaps\n" ));
+ return;
+ }
+
+ while ( count-- )
+ hintmap->edge[iDst--] = hintmap->edge[iSrc--];
+
+ /* insert first edge */
+ hintmap->edge[indexInsert] = *firstHintEdge; /* copy struct */
+ hintmap->count += 1;
+
+ FT_TRACE7(( " Inserting hint %.2f (%.2f)\n",
+ firstHintEdge->csCoord / 65536.0,
+ firstHintEdge->dsCoord / 65536.0 ));
+
+ if ( isPair )
+ {
+ /* insert second edge */
+ hintmap->edge[indexInsert + 1] = *secondHintEdge; /* copy struct */
+ hintmap->count += 1;
+
+ FT_TRACE7(( " Inserting hint %.2f (%.2f)\n",
+ secondHintEdge->csCoord / 65536.0,
+ secondHintEdge->dsCoord / 65536.0 ));
+
+ }
+ }
+
+ return;
+ }
+
+
+ /*
+ * Build a map from hints and mask.
+ *
+ * This function may recur one level if `hintmap->initialHintMap' is not yet
+ * valid.
+ * If `initialMap' is true, simply build initial map.
+ *
+ * Synthetic hints are used in two ways. A hint at zero is inserted, if
+ * needed, in the initial hint map, to prevent translations from
+ * propagating across the origin. If synthetic em box hints are enabled
+ * for ideographic dictionaries, then they are inserted in all hint
+ * maps, including the initial one.
+ *
+ */
+ FT_LOCAL_DEF( void )
+ cf2_hintmap_build( CF2_HintMap hintmap,
+ CF2_ArrStack hStemHintArray,
+ CF2_ArrStack vStemHintArray,
+ CF2_HintMask hintMask,
+ CF2_Fixed hintOrigin,
+ FT_Bool initialMap )
+ {
+ FT_Byte* maskPtr;
+
+ CF2_Font font = hintmap->font;
+ CF2_HintMaskRec tempHintMask;
+
+ size_t bitCount, i;
+ FT_Byte maskByte;
+
+
+ /* check whether initial map is constructed */
+ if ( !initialMap && !cf2_hintmap_isValid( hintmap->initialHintMap ) )
+ {
+ /* make recursive call with initialHintMap and temporary mask; */
+ /* temporary mask will get all bits set, below */
+ cf2_hintmask_init( &tempHintMask, hintMask->error );
+ cf2_hintmap_build( hintmap->initialHintMap,
+ hStemHintArray,
+ vStemHintArray,
+ &tempHintMask,
+ hintOrigin,
+ TRUE );
+ }
+
+ if ( !cf2_hintmask_isValid( hintMask ) )
+ {
+ /* without a hint mask, assume all hints are active */
+ cf2_hintmask_setAll( hintMask,
+ cf2_arrstack_size( hStemHintArray ) +
+ cf2_arrstack_size( vStemHintArray ) );
+ if ( !cf2_hintmask_isValid( hintMask ) )
+ {
+ if ( font->isT1 )
+ {
+ /* no error, just continue unhinted */
+ *hintMask->error = FT_Err_Ok;
+ hintmap->hinted = FALSE;
+ }
+ return; /* too many stem hints */
+ }
+ }
+
+ /* begin by clearing the map */
+ hintmap->count = 0;
+ hintmap->lastIndex = 0;
+
+ /* make a copy of the hint mask so we can modify it */
+ tempHintMask = *hintMask;
+ maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask );
+
+ /* use the hStem hints only, which are first in the mask */
+ bitCount = cf2_arrstack_size( hStemHintArray );
+
+ /* Defense-in-depth. Should never return here. */
+ if ( bitCount > hintMask->bitCount )
+ return;
+
+ /* synthetic embox hints get highest priority */
+ if ( font->blues.doEmBoxHints )
+ {
+ CF2_HintRec dummy;
+
+
+ cf2_hint_initZero( &dummy ); /* invalid hint map element */
+
+ /* ghost bottom */
+ cf2_hintmap_insertHint( hintmap,
+ &font->blues.emBoxBottomEdge,
+ &dummy );
+ /* ghost top */
+ cf2_hintmap_insertHint( hintmap,
+ &dummy,
+ &font->blues.emBoxTopEdge );
+ }
+
+ /* insert hints captured by a blue zone or already locked (higher */
+ /* priority) */
+ for ( i = 0, maskByte = 0x80; i < bitCount; i++ )
+ {
+ if ( maskByte & *maskPtr )
+ {
+ /* expand StemHint into two `CF2_Hint' elements */
+ CF2_HintRec bottomHintEdge, topHintEdge;
+
+
+ cf2_hint_init( &bottomHintEdge,
+ hStemHintArray,
+ i,
+ font,
+ hintOrigin,
+ hintmap->scale,
+ TRUE /* bottom */ );
+ cf2_hint_init( &topHintEdge,
+ hStemHintArray,
+ i,
+ font,
+ hintOrigin,
+ hintmap->scale,
+ FALSE /* top */ );
+
+ if ( cf2_hint_isLocked( &bottomHintEdge ) ||
+ cf2_hint_isLocked( &topHintEdge ) ||
+ cf2_blues_capture( &font->blues,
+ &bottomHintEdge,
+ &topHintEdge ) )
+ {
+ /* insert captured hint into map */
+ cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge );
+
+ *maskPtr &= ~maskByte; /* turn off the bit for this hint */
+ }
+ }
+
+ if ( ( i & 7 ) == 7 )
+ {
+ /* move to next mask byte */
+ maskPtr++;
+ maskByte = 0x80;
+ }
+ else
+ maskByte >>= 1;
+ }
+
+ /* initial hint map includes only captured hints plus maybe one at 0 */
+
+ /*
+ * TODO: There is a problem here because we are trying to build a
+ * single hint map containing all captured hints. It is
+ * possible for there to be conflicts between captured hints,
+ * either because of darkening or because the hints are in
+ * separate hint zones (we are ignoring hint zones for the
+ * initial map). An example of the latter is MinionPro-Regular
+ * v2.030 glyph 883 (Greek Capital Alpha with Psili) at 15ppem.
+ * A stem hint for the psili conflicts with the top edge hint
+ * for the base character. The stem hint gets priority because
+ * of its sort order. In glyph 884 (Greek Capital Alpha with
+ * Psili and Oxia), the top of the base character gets a stem
+ * hint, and the psili does not. This creates different initial
+ * maps for the two glyphs resulting in different renderings of
+ * the base character. Will probably defer this either as not
+ * worth the cost or as a font bug. I don't think there is any
+ * good reason for an accent to be captured by an alignment
+ * zone. -darnold 2/12/10
+ */
+
+ if ( initialMap )
+ {
+ /* Apply a heuristic that inserts a point for (0,0), unless it's */
+ /* already covered by a mapping. This locks the baseline for glyphs */
+ /* that have no baseline hints. */
+
+ if ( hintmap->count == 0 ||
+ hintmap->edge[0].csCoord > 0 ||
+ hintmap->edge[hintmap->count - 1].csCoord < 0 )
+ {
+ /* all edges are above 0 or all edges are below 0; */
+ /* construct a locked edge hint at 0 */
+
+ CF2_HintRec edge, invalid;
+
+
+ cf2_hint_initZero( &edge );
+
+ edge.flags = CF2_GhostBottom |
+ CF2_Locked |
+ CF2_Synthetic;
+ edge.scale = hintmap->scale;
+
+ cf2_hint_initZero( &invalid );
+ cf2_hintmap_insertHint( hintmap, &edge, &invalid );
+ }
+ }
+ else
+ {
+ /* insert remaining hints */
+
+ maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask );
+
+ for ( i = 0, maskByte = 0x80; i < bitCount; i++ )
+ {
+ if ( maskByte & *maskPtr )
+ {
+ CF2_HintRec bottomHintEdge, topHintEdge;
+
+
+ cf2_hint_init( &bottomHintEdge,
+ hStemHintArray,
+ i,
+ font,
+ hintOrigin,
+ hintmap->scale,
+ TRUE /* bottom */ );
+ cf2_hint_init( &topHintEdge,
+ hStemHintArray,
+ i,
+ font,
+ hintOrigin,
+ hintmap->scale,
+ FALSE /* top */ );
+
+ cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge );
+ }
+
+ if ( ( i & 7 ) == 7 )
+ {
+ /* move to next mask byte */
+ maskPtr++;
+ maskByte = 0x80;
+ }
+ else
+ maskByte >>= 1;
+ }
+ }
+
+ FT_TRACE6(( initialMap ? "flags: [p]air [g]host [t]op "
+ "[b]ottom [L]ocked [S]ynthetic\n"
+ "Initial hintmap\n"
+ : "Hints:\n" ));
+ cf2_hintmap_dump( hintmap );
+
+ /*
+ * Note: The following line is a convenient place to break when
+ * debugging hinting. Examine `hintmap->edge' for the list of
+ * enabled hints, then step over the call to see the effect of
+ * adjustment. We stop here first on the recursive call that
+ * creates the initial map, and then on each counter group and
+ * hint zone.
+ */
+
+ /* adjust positions of hint edges that are not locked to blue zones */
+ cf2_hintmap_adjustHints( hintmap );
+
+ FT_TRACE6(( "(adjusted)\n" ));
+ cf2_hintmap_dump( hintmap );
+
+ /* save the position of all hints that were used in this hint map; */
+ /* if we use them again, we'll locate them in the same position */
+ if ( !initialMap )
+ {
+ for ( i = 0; i < hintmap->count; i++ )
+ {
+ if ( !cf2_hint_isSynthetic( &hintmap->edge[i] ) )
+ {
+ /* Note: include both valid and invalid edges */
+ /* Note: top and bottom edges are copied back separately */
+ CF2_StemHint stemhint = (CF2_StemHint)
+ cf2_arrstack_getPointer( hStemHintArray,
+ hintmap->edge[i].index );
+
+
+ if ( cf2_hint_isTop( &hintmap->edge[i] ) )
+ stemhint->maxDS = hintmap->edge[i].dsCoord;
+ else
+ stemhint->minDS = hintmap->edge[i].dsCoord;
+
+ stemhint->used = TRUE;
+ }
+ }
+ }
+
+ /* hint map is ready to use */
+ hintmap->isValid = TRUE;
+
+ /* remember this mask has been used */
+ cf2_hintmask_setNew( hintMask, FALSE );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_init( CF2_GlyphPath glyphpath,
+ CF2_Font font,
+ CF2_OutlineCallbacks callbacks,
+ CF2_Fixed scaleY,
+ /* CF2_Fixed hShift, */
+ CF2_ArrStack hStemHintArray,
+ CF2_ArrStack vStemHintArray,
+ CF2_HintMask hintMask,
+ CF2_Fixed hintOriginY,
+ const CF2_Blues blues,
+ const FT_Vector* fractionalTranslation )
+ {
+ FT_ZERO( glyphpath );
+
+ glyphpath->font = font;
+ glyphpath->callbacks = callbacks;
+
+ cf2_arrstack_init( &glyphpath->hintMoves,
+ font->memory,
+ &font->error,
+ sizeof ( CF2_HintMoveRec ) );
+
+ cf2_hintmap_init( &glyphpath->initialHintMap,
+ font,
+ &glyphpath->initialHintMap,
+ &glyphpath->hintMoves,
+ scaleY );
+ cf2_hintmap_init( &glyphpath->firstHintMap,
+ font,
+ &glyphpath->initialHintMap,
+ &glyphpath->hintMoves,
+ scaleY );
+ cf2_hintmap_init( &glyphpath->hintMap,
+ font,
+ &glyphpath->initialHintMap,
+ &glyphpath->hintMoves,
+ scaleY );
+
+ glyphpath->scaleX = font->innerTransform.a;
+ glyphpath->scaleC = font->innerTransform.c;
+ glyphpath->scaleY = font->innerTransform.d;
+
+ glyphpath->fractionalTranslation = *fractionalTranslation;
+
+#if 0
+ glyphpath->hShift = hShift; /* for fauxing */
+#endif
+
+ glyphpath->hStemHintArray = hStemHintArray;
+ glyphpath->vStemHintArray = vStemHintArray;
+ glyphpath->hintMask = hintMask; /* ptr to current mask */
+ glyphpath->hintOriginY = hintOriginY;
+ glyphpath->blues = blues;
+ glyphpath->darken = font->darkened; /* TODO: should we make copies? */
+ glyphpath->xOffset = font->darkenX;
+ glyphpath->yOffset = font->darkenY;
+ glyphpath->miterLimit = 2 * FT_MAX(
+ cf2_fixedAbs( glyphpath->xOffset ),
+ cf2_fixedAbs( glyphpath->yOffset ) );
+
+ /* .1 character space unit */
+ glyphpath->snapThreshold = cf2_doubleToFixed( 0.1 );
+
+ glyphpath->moveIsPending = TRUE;
+ glyphpath->pathIsOpen = FALSE;
+ glyphpath->pathIsClosing = FALSE;
+ glyphpath->elemIsQueued = FALSE;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_finalize( CF2_GlyphPath glyphpath )
+ {
+ cf2_arrstack_finalize( &glyphpath->hintMoves );
+ }
+
+
+ /*
+ * Hint point in y-direction and apply outerTransform.
+ * Input `current' hint map (which is actually delayed by one element).
+ * Input x,y point in Character Space.
+ * Output x,y point in Device Space, including translation.
+ */
+ static void
+ cf2_glyphpath_hintPoint( CF2_GlyphPath glyphpath,
+ CF2_HintMap hintmap,
+ FT_Vector* ppt,
+ CF2_Fixed x,
+ CF2_Fixed y )
+ {
+ FT_Vector pt; /* hinted point in upright DS */
+
+
+ pt.x = ADD_INT32( FT_MulFix( glyphpath->scaleX, x ),
+ FT_MulFix( glyphpath->scaleC, y ) );
+ pt.y = cf2_hintmap_map( hintmap, y );
+
+ ppt->x = ADD_INT32(
+ FT_MulFix( glyphpath->font->outerTransform.a, pt.x ),
+ ADD_INT32(
+ FT_MulFix( glyphpath->font->outerTransform.c, pt.y ),
+ glyphpath->fractionalTranslation.x ) );
+ ppt->y = ADD_INT32(
+ FT_MulFix( glyphpath->font->outerTransform.b, pt.x ),
+ ADD_INT32(
+ FT_MulFix( glyphpath->font->outerTransform.d, pt.y ),
+ glyphpath->fractionalTranslation.y ) );
+ }
+
+
+ /*
+ * From two line segments, (u1,u2) and (v1,v2), compute a point of
+ * intersection on the corresponding lines.
+ * Return false if no intersection is found, or if the intersection is
+ * too far away from the ends of the line segments, u2 and v1.
+ *
+ */
+ static FT_Bool
+ cf2_glyphpath_computeIntersection( CF2_GlyphPath glyphpath,
+ const FT_Vector* u1,
+ const FT_Vector* u2,
+ const FT_Vector* v1,
+ const FT_Vector* v2,
+ FT_Vector* intersection )
+ {
+ /*
+ * Let `u' be a zero-based vector from the first segment, `v' from the
+ * second segment.
+ * Let `w 'be the zero-based vector from `u1' to `v1'.
+ * `perp' is the `perpendicular dot product'; see
+ * https://mathworld.wolfram.com/PerpDotProduct.html.
+ * `s' is the parameter for the parametric line for the first segment
+ * (`u').
+ *
+ * See notation in
+ * http://softsurfer.com/Archive/algorithm_0104/algorithm_0104B.htm.
+ * Calculations are done in 16.16, but must handle the squaring of
+ * line lengths in character space. We scale all vectors by 1/32 to
+ * avoid overflow. This allows values up to 4095 to be squared. The
+ * scale factor cancels in the divide.
+ *
+ * TODO: the scale factor could be computed from UnitsPerEm.
+ *
+ */
+
+#define cf2_perp( a, b ) \
+ ( FT_MulFix( a.x, b.y ) - FT_MulFix( a.y, b.x ) )
+
+ /* round and divide by 32 */
+#define CF2_CS_SCALE( x ) \
+ ( ( (x) + 0x10 ) >> 5 )
+
+ FT_Vector u, v, w; /* scaled vectors */
+ CF2_Fixed denominator, s;
+
+
+ u.x = CF2_CS_SCALE( SUB_INT32( u2->x, u1->x ) );
+ u.y = CF2_CS_SCALE( SUB_INT32( u2->y, u1->y ) );
+ v.x = CF2_CS_SCALE( SUB_INT32( v2->x, v1->x ) );
+ v.y = CF2_CS_SCALE( SUB_INT32( v2->y, v1->y ) );
+ w.x = CF2_CS_SCALE( SUB_INT32( v1->x, u1->x ) );
+ w.y = CF2_CS_SCALE( SUB_INT32( v1->y, u1->y ) );
+
+ denominator = cf2_perp( u, v );
+
+ if ( denominator == 0 )
+ return FALSE; /* parallel or coincident lines */
+
+ s = FT_DivFix( cf2_perp( w, v ), denominator );
+
+ intersection->x = ADD_INT32( u1->x,
+ FT_MulFix( s, SUB_INT32( u2->x, u1->x ) ) );
+ intersection->y = ADD_INT32( u1->y,
+ FT_MulFix( s, SUB_INT32( u2->y, u1->y ) ) );
+
+
+ /*
+ * Special case snapping for horizontal and vertical lines.
+ * This cleans up intersections and reduces problems with winding
+ * order detection.
+ * Sample case is sbc cd KozGoPr6N-Medium.otf 20 16685.
+ * Note: these calculations are in character space.
+ *
+ */
+
+ if ( u1->x == u2->x &&
+ cf2_fixedAbs( SUB_INT32( intersection->x,
+ u1->x ) ) < glyphpath->snapThreshold )
+ intersection->x = u1->x;
+ if ( u1->y == u2->y &&
+ cf2_fixedAbs( SUB_INT32( intersection->y,
+ u1->y ) ) < glyphpath->snapThreshold )
+ intersection->y = u1->y;
+
+ if ( v1->x == v2->x &&
+ cf2_fixedAbs( SUB_INT32( intersection->x,
+ v1->x ) ) < glyphpath->snapThreshold )
+ intersection->x = v1->x;
+ if ( v1->y == v2->y &&
+ cf2_fixedAbs( SUB_INT32( intersection->y,
+ v1->y ) ) < glyphpath->snapThreshold )
+ intersection->y = v1->y;
+
+ /* limit the intersection distance from midpoint of u2 and v1 */
+ if ( cf2_fixedAbs( intersection->x - ADD_INT32( u2->x, v1->x ) / 2 ) >
+ glyphpath->miterLimit ||
+ cf2_fixedAbs( intersection->y - ADD_INT32( u2->y, v1->y ) / 2 ) >
+ glyphpath->miterLimit )
+ return FALSE;
+
+ return TRUE;
+ }
+
+
+ /*
+ * Push the cached element (glyphpath->prevElem*) to the outline
+ * consumer. When a darkening offset is used, the end point of the
+ * cached element may be adjusted to an intersection point or we may
+ * synthesize a connecting line to the current element. If we are
+ * closing a subpath, we may also generate a connecting line to the start
+ * point.
+ *
+ * This is where Character Space (CS) is converted to Device Space (DS)
+ * using a hint map. This calculation must use a HintMap that was valid
+ * at the time the element was saved. For the first point in a subpath,
+ * that is a saved HintMap. For most elements, it just means the caller
+ * has delayed building a HintMap from the current HintMask.
+ *
+ * Transform each point with outerTransform and call the outline
+ * callbacks. This is a general 3x3 transform:
+ *
+ * x' = a*x + c*y + tx, y' = b*x + d*y + ty
+ *
+ * but it uses 4 elements from CF2_Font and the translation part
+ * from CF2_GlyphPath.
+ *
+ */
+ static void
+ cf2_glyphpath_pushPrevElem( CF2_GlyphPath glyphpath,
+ CF2_HintMap hintmap,
+ FT_Vector* nextP0,
+ FT_Vector nextP1,
+ FT_Bool close )
+ {
+ CF2_CallbackParamsRec params;
+
+ FT_Vector* prevP0;
+ FT_Vector* prevP1;
+
+ FT_Vector intersection = { 0, 0 };
+ FT_Bool useIntersection = FALSE;
+
+
+ FT_ASSERT( glyphpath->prevElemOp == CF2_PathOpLineTo ||
+ glyphpath->prevElemOp == CF2_PathOpCubeTo );
+
+ if ( glyphpath->prevElemOp == CF2_PathOpLineTo )
+ {
+ prevP0 = &glyphpath->prevElemP0;
+ prevP1 = &glyphpath->prevElemP1;
+ }
+ else
+ {
+ prevP0 = &glyphpath->prevElemP2;
+ prevP1 = &glyphpath->prevElemP3;
+ }
+
+ /* optimization: if previous and next elements are offset by the same */
+ /* amount, then there will be no gap, and no need to compute an */
+ /* intersection. */
+ if ( prevP1->x != nextP0->x || prevP1->y != nextP0->y )
+ {
+ /* previous element does not join next element: */
+ /* adjust end point of previous element to the intersection */
+ useIntersection = cf2_glyphpath_computeIntersection( glyphpath,
+ prevP0,
+ prevP1,
+ nextP0,
+ &nextP1,
+ &intersection );
+ if ( useIntersection )
+ {
+ /* modify the last point of the cached element (either line or */
+ /* curve) */
+ *prevP1 = intersection;
+ }
+ }
+
+ params.pt0 = glyphpath->currentDS;
+
+ switch( glyphpath->prevElemOp )
+ {
+ case CF2_PathOpLineTo:
+ params.op = CF2_PathOpLineTo;
+
+ /* note: pt2 and pt3 are unused */
+
+ if ( close )
+ {
+ /* use first hint map if closing */
+ cf2_glyphpath_hintPoint( glyphpath,
+ &glyphpath->firstHintMap,
+ &params.pt1,
+ glyphpath->prevElemP1.x,
+ glyphpath->prevElemP1.y );
+ }
+ else
+ {
+ cf2_glyphpath_hintPoint( glyphpath,
+ hintmap,
+ &params.pt1,
+ glyphpath->prevElemP1.x,
+ glyphpath->prevElemP1.y );
+ }
+
+ /* output only non-zero length lines */
+ if ( params.pt0.x != params.pt1.x || params.pt0.y != params.pt1.y )
+ {
+ glyphpath->callbacks->lineTo( glyphpath->callbacks, &params );
+
+ glyphpath->currentDS = params.pt1;
+ }
+ break;
+
+ case CF2_PathOpCubeTo:
+ params.op = CF2_PathOpCubeTo;
+
+ /* TODO: should we intersect the interior joins (p1-p2 and p2-p3)? */
+ cf2_glyphpath_hintPoint( glyphpath,
+ hintmap,
+ &params.pt1,
+ glyphpath->prevElemP1.x,
+ glyphpath->prevElemP1.y );
+ cf2_glyphpath_hintPoint( glyphpath,
+ hintmap,
+ &params.pt2,
+ glyphpath->prevElemP2.x,
+ glyphpath->prevElemP2.y );
+ cf2_glyphpath_hintPoint( glyphpath,
+ hintmap,
+ &params.pt3,
+ glyphpath->prevElemP3.x,
+ glyphpath->prevElemP3.y );
+
+ glyphpath->callbacks->cubeTo( glyphpath->callbacks, &params );
+
+ glyphpath->currentDS = params.pt3;
+
+ break;
+ }
+
+ if ( !useIntersection || close )
+ {
+ /* insert connecting line between end of previous element and start */
+ /* of current one */
+ /* note: at the end of a subpath, we might do both, so use `nextP0' */
+ /* before we change it, below */
+
+ if ( close )
+ {
+ /* if we are closing the subpath, then nextP0 is in the first */
+ /* hint zone */
+ cf2_glyphpath_hintPoint( glyphpath,
+ &glyphpath->firstHintMap,
+ &params.pt1,
+ nextP0->x,
+ nextP0->y );
+ }
+ else
+ {
+ cf2_glyphpath_hintPoint( glyphpath,
+ hintmap,
+ &params.pt1,
+ nextP0->x,
+ nextP0->y );
+ }
+
+ if ( params.pt1.x != glyphpath->currentDS.x ||
+ params.pt1.y != glyphpath->currentDS.y )
+ {
+ /* length is nonzero */
+ params.op = CF2_PathOpLineTo;
+ params.pt0 = glyphpath->currentDS;
+
+ /* note: pt2 and pt3 are unused */
+ glyphpath->callbacks->lineTo( glyphpath->callbacks, &params );
+
+ glyphpath->currentDS = params.pt1;
+ }
+ }
+
+ if ( useIntersection )
+ {
+ /* return intersection point to caller */
+ *nextP0 = intersection;
+ }
+ }
+
+
+ /* push a MoveTo element based on current point and offset of current */
+ /* element */
+ static void
+ cf2_glyphpath_pushMove( CF2_GlyphPath glyphpath,
+ FT_Vector start )
+ {
+ CF2_CallbackParamsRec params;
+
+
+ params.op = CF2_PathOpMoveTo;
+ params.pt0 = glyphpath->currentDS;
+
+ /* Test if move has really happened yet; it would have called */
+ /* `cf2_hintmap_build' to set `isValid'. */
+ if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) )
+ {
+ /* we are here iff first subpath is missing a moveto operator: */
+ /* synthesize first moveTo to finish initialization of hintMap */
+ cf2_glyphpath_moveTo( glyphpath,
+ glyphpath->start.x,
+ glyphpath->start.y );
+ }
+
+ cf2_glyphpath_hintPoint( glyphpath,
+ &glyphpath->hintMap,
+ &params.pt1,
+ start.x,
+ start.y );
+
+ /* note: pt2 and pt3 are unused */
+ glyphpath->callbacks->moveTo( glyphpath->callbacks, &params );
+
+ glyphpath->currentDS = params.pt1;
+ glyphpath->offsetStart0 = start;
+ }
+
+
+ /*
+ * All coordinates are in character space.
+ * On input, (x1, y1) and (x2, y2) give line segment.
+ * On output, (x, y) give offset vector.
+ * We use a piecewise approximation to trig functions.
+ *
+ * TODO: Offset true perpendicular and proper length
+ * supply the y-translation for hinting here, too,
+ * that adds yOffset unconditionally to *y.
+ */
+ static void
+ cf2_glyphpath_computeOffset( CF2_GlyphPath glyphpath,
+ CF2_Fixed x1,
+ CF2_Fixed y1,
+ CF2_Fixed x2,
+ CF2_Fixed y2,
+ CF2_Fixed* x,
+ CF2_Fixed* y )
+ {
+ CF2_Fixed dx = SUB_INT32( x2, x1 );
+ CF2_Fixed dy = SUB_INT32( y2, y1 );
+
+
+ /* note: negative offsets don't work here; negate deltas to change */
+ /* quadrants, below */
+ if ( glyphpath->font->reverseWinding )
+ {
+ dx = NEG_INT32( dx );
+ dy = NEG_INT32( dy );
+ }
+
+ *x = *y = 0;
+
+ if ( !glyphpath->darken )
+ return;
+
+ /* add momentum for this path element */
+ glyphpath->callbacks->windingMomentum =
+ ADD_INT32( glyphpath->callbacks->windingMomentum,
+ cf2_getWindingMomentum( x1, y1, x2, y2 ) );
+
+ /* note: allow mixed integer and fixed multiplication here */
+ if ( dx >= 0 )
+ {
+ if ( dy >= 0 )
+ {
+ /* first quadrant, +x +y */
+
+ if ( dx > MUL_INT32( 2, dy ) )
+ {
+ /* +x */
+ *x = 0;
+ *y = 0;
+ }
+ else if ( dy > MUL_INT32( 2, dx ) )
+ {
+ /* +y */
+ *x = glyphpath->xOffset;
+ *y = glyphpath->yOffset;
+ }
+ else
+ {
+ /* +x +y */
+ *x = FT_MulFix( cf2_doubleToFixed( 0.7 ),
+ glyphpath->xOffset );
+ *y = FT_MulFix( cf2_doubleToFixed( 1.0 - 0.7 ),
+ glyphpath->yOffset );
+ }
+ }
+ else
+ {
+ /* fourth quadrant, +x -y */
+
+ if ( dx > MUL_INT32( -2, dy ) )
+ {
+ /* +x */
+ *x = 0;
+ *y = 0;
+ }
+ else if ( NEG_INT32( dy ) > MUL_INT32( 2, dx ) )
+ {
+ /* -y */
+ *x = NEG_INT32( glyphpath->xOffset );
+ *y = glyphpath->yOffset;
+ }
+ else
+ {
+ /* +x -y */
+ *x = FT_MulFix( cf2_doubleToFixed( -0.7 ),
+ glyphpath->xOffset );
+ *y = FT_MulFix( cf2_doubleToFixed( 1.0 - 0.7 ),
+ glyphpath->yOffset );
+ }
+ }
+ }
+ else
+ {
+ if ( dy >= 0 )
+ {
+ /* second quadrant, -x +y */
+
+ if ( NEG_INT32( dx ) > MUL_INT32( 2, dy ) )
+ {
+ /* -x */
+ *x = 0;
+ *y = MUL_INT32( 2, glyphpath->yOffset );
+ }
+ else if ( dy > MUL_INT32( -2, dx ) )
+ {
+ /* +y */
+ *x = glyphpath->xOffset;
+ *y = glyphpath->yOffset;
+ }
+ else
+ {
+ /* -x +y */
+ *x = FT_MulFix( cf2_doubleToFixed( 0.7 ),
+ glyphpath->xOffset );
+ *y = FT_MulFix( cf2_doubleToFixed( 1.0 + 0.7 ),
+ glyphpath->yOffset );
+ }
+ }
+ else
+ {
+ /* third quadrant, -x -y */
+
+ if ( NEG_INT32( dx ) > MUL_INT32( -2, dy ) )
+ {
+ /* -x */
+ *x = 0;
+ *y = MUL_INT32( 2, glyphpath->yOffset );
+ }
+ else if ( NEG_INT32( dy ) > MUL_INT32( -2, dx ) )
+ {
+ /* -y */
+ *x = NEG_INT32( glyphpath->xOffset );
+ *y = glyphpath->yOffset;
+ }
+ else
+ {
+ /* -x -y */
+ *x = FT_MulFix( cf2_doubleToFixed( -0.7 ),
+ glyphpath->xOffset );
+ *y = FT_MulFix( cf2_doubleToFixed( 1.0 + 0.7 ),
+ glyphpath->yOffset );
+ }
+ }
+ }
+ }
+
+
+ /*
+ * The functions cf2_glyphpath_{moveTo,lineTo,curveTo,closeOpenPath} are
+ * called by the interpreter with Character Space (CS) coordinates. Each
+ * path element is placed into a queue of length one to await the
+ * calculation of the following element. At that time, the darkening
+ * offset of the following element is known and joins can be computed,
+ * including possible modification of this element, before mapping to
+ * Device Space (DS) and passing it on to the outline consumer.
+ *
+ */
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_moveTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x,
+ CF2_Fixed y )
+ {
+ cf2_glyphpath_closeOpenPath( glyphpath );
+
+ /* save the parameters of the move for later, when we'll know how to */
+ /* offset it; */
+ /* also save last move point */
+ glyphpath->currentCS.x = glyphpath->start.x = x;
+ glyphpath->currentCS.y = glyphpath->start.y = y;
+
+ glyphpath->moveIsPending = TRUE;
+
+ /* ensure we have a valid map with current mask */
+ if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) ||
+ cf2_hintmask_isNew( glyphpath->hintMask ) )
+ cf2_hintmap_build( &glyphpath->hintMap,
+ glyphpath->hStemHintArray,
+ glyphpath->vStemHintArray,
+ glyphpath->hintMask,
+ glyphpath->hintOriginY,
+ FALSE );
+
+ /* save a copy of current HintMap to use when drawing initial point */
+ glyphpath->firstHintMap = glyphpath->hintMap; /* structure copy */
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_lineTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x,
+ CF2_Fixed y )
+ {
+ CF2_Fixed xOffset, yOffset;
+ FT_Vector P0, P1;
+ FT_Bool newHintMap;
+
+ /*
+ * New hints will be applied after cf2_glyphpath_pushPrevElem has run.
+ * In case this is a synthesized closing line, any new hints should be
+ * delayed until this path is closed (`cf2_hintmask_isNew' will be
+ * called again before the next line or curve).
+ */
+
+ /* true if new hint map not on close */
+ newHintMap = cf2_hintmask_isNew( glyphpath->hintMask ) &&
+ !glyphpath->pathIsClosing;
+
+ /*
+ * Zero-length lines may occur in the charstring. Because we cannot
+ * compute darkening offsets or intersections from zero-length lines,
+ * it is best to remove them and avoid artifacts. However, zero-length
+ * lines in CS at the start of a new hint map can generate non-zero
+ * lines in DS due to hint substitution. We detect a change in hint
+ * map here and pass those zero-length lines along.
+ */
+
+ /*
+ * Note: Find explicitly closed paths here with a conditional
+ * breakpoint using
+ *
+ * !gp->pathIsClosing && gp->start.x == x && gp->start.y == y
+ *
+ */
+
+ if ( glyphpath->currentCS.x == x &&
+ glyphpath->currentCS.y == y &&
+ !newHintMap )
+ /*
+ * Ignore zero-length lines in CS where the hint map is the same
+ * because the line in DS will also be zero length.
+ *
+ * Ignore zero-length lines when we synthesize a closing line because
+ * the close will be handled in cf2_glyphPath_pushPrevElem.
+ */
+ return;
+
+ cf2_glyphpath_computeOffset( glyphpath,
+ glyphpath->currentCS.x,
+ glyphpath->currentCS.y,
+ x,
+ y,
+ &xOffset,
+ &yOffset );
+
+ /* construct offset points */
+ P0.x = ADD_INT32( glyphpath->currentCS.x, xOffset );
+ P0.y = ADD_INT32( glyphpath->currentCS.y, yOffset );
+ P1.x = ADD_INT32( x, xOffset );
+ P1.y = ADD_INT32( y, yOffset );
+
+ if ( glyphpath->moveIsPending )
+ {
+ /* emit offset 1st point as MoveTo */
+ cf2_glyphpath_pushMove( glyphpath, P0 );
+
+ glyphpath->moveIsPending = FALSE; /* adjust state machine */
+ glyphpath->pathIsOpen = TRUE;
+
+ glyphpath->offsetStart1 = P1; /* record second point */
+ }
+
+ if ( glyphpath->elemIsQueued )
+ {
+ FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) ||
+ glyphpath->hintMap.count == 0 );
+
+ cf2_glyphpath_pushPrevElem( glyphpath,
+ &glyphpath->hintMap,
+ &P0,
+ P1,
+ FALSE );
+ }
+
+ /* queue the current element with offset points */
+ glyphpath->elemIsQueued = TRUE;
+ glyphpath->prevElemOp = CF2_PathOpLineTo;
+ glyphpath->prevElemP0 = P0;
+ glyphpath->prevElemP1 = P1;
+
+ /* update current map */
+ if ( newHintMap )
+ cf2_hintmap_build( &glyphpath->hintMap,
+ glyphpath->hStemHintArray,
+ glyphpath->vStemHintArray,
+ glyphpath->hintMask,
+ glyphpath->hintOriginY,
+ FALSE );
+
+ glyphpath->currentCS.x = x; /* pre-offset current point */
+ glyphpath->currentCS.y = y;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_curveTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x1,
+ CF2_Fixed y1,
+ CF2_Fixed x2,
+ CF2_Fixed y2,
+ CF2_Fixed x3,
+ CF2_Fixed y3 )
+ {
+ CF2_Fixed xOffset1, yOffset1, xOffset3, yOffset3;
+ FT_Vector P0, P1, P2, P3;
+
+
+ /* TODO: ignore zero length portions of curve?? */
+ cf2_glyphpath_computeOffset( glyphpath,
+ glyphpath->currentCS.x,
+ glyphpath->currentCS.y,
+ x1,
+ y1,
+ &xOffset1,
+ &yOffset1 );
+ cf2_glyphpath_computeOffset( glyphpath,
+ x2,
+ y2,
+ x3,
+ y3,
+ &xOffset3,
+ &yOffset3 );
+
+ /* add momentum from the middle segment */
+ glyphpath->callbacks->windingMomentum =
+ ADD_INT32( glyphpath->callbacks->windingMomentum,
+ cf2_getWindingMomentum( x1, y1, x2, y2 ) );
+
+ /* construct offset points */
+ P0.x = ADD_INT32( glyphpath->currentCS.x, xOffset1 );
+ P0.y = ADD_INT32( glyphpath->currentCS.y, yOffset1 );
+ P1.x = ADD_INT32( x1, xOffset1 );
+ P1.y = ADD_INT32( y1, yOffset1 );
+ /* note: preserve angle of final segment by using offset3 at both ends */
+ P2.x = ADD_INT32( x2, xOffset3 );
+ P2.y = ADD_INT32( y2, yOffset3 );
+ P3.x = ADD_INT32( x3, xOffset3 );
+ P3.y = ADD_INT32( y3, yOffset3 );
+
+ if ( glyphpath->moveIsPending )
+ {
+ /* emit offset 1st point as MoveTo */
+ cf2_glyphpath_pushMove( glyphpath, P0 );
+
+ glyphpath->moveIsPending = FALSE;
+ glyphpath->pathIsOpen = TRUE;
+
+ glyphpath->offsetStart1 = P1; /* record second point */
+ }
+
+ if ( glyphpath->elemIsQueued )
+ {
+ FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) ||
+ glyphpath->hintMap.count == 0 );
+
+ cf2_glyphpath_pushPrevElem( glyphpath,
+ &glyphpath->hintMap,
+ &P0,
+ P1,
+ FALSE );
+ }
+
+ /* queue the current element with offset points */
+ glyphpath->elemIsQueued = TRUE;
+ glyphpath->prevElemOp = CF2_PathOpCubeTo;
+ glyphpath->prevElemP0 = P0;
+ glyphpath->prevElemP1 = P1;
+ glyphpath->prevElemP2 = P2;
+ glyphpath->prevElemP3 = P3;
+
+ /* update current map */
+ if ( cf2_hintmask_isNew( glyphpath->hintMask ) )
+ cf2_hintmap_build( &glyphpath->hintMap,
+ glyphpath->hStemHintArray,
+ glyphpath->vStemHintArray,
+ glyphpath->hintMask,
+ glyphpath->hintOriginY,
+ FALSE );
+
+ glyphpath->currentCS.x = x3; /* pre-offset current point */
+ glyphpath->currentCS.y = y3;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_closeOpenPath( CF2_GlyphPath glyphpath )
+ {
+ if ( glyphpath->pathIsOpen )
+ {
+ /*
+ * A closing line in Character Space line is always generated below
+ * with `cf2_glyphPath_lineTo'. It may be ignored later if it turns
+ * out to be zero length in Device Space.
+ */
+ glyphpath->pathIsClosing = TRUE;
+
+ cf2_glyphpath_lineTo( glyphpath,
+ glyphpath->start.x,
+ glyphpath->start.y );
+
+ /* empty the final element from the queue and close the path */
+ if ( glyphpath->elemIsQueued )
+ cf2_glyphpath_pushPrevElem( glyphpath,
+ &glyphpath->hintMap,
+ &glyphpath->offsetStart0,
+ glyphpath->offsetStart1,
+ TRUE );
+
+ /* reset state machine */
+ glyphpath->moveIsPending = TRUE;
+ glyphpath->pathIsOpen = FALSE;
+ glyphpath->pathIsClosing = FALSE;
+ glyphpath->elemIsQueued = FALSE;
+ }
+ }
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/pshints.h b/thirdparty/freetype/src/psaux/pshints.h
new file mode 100644
index 0000000000..92e37e98ae
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/pshints.h
@@ -0,0 +1,288 @@
+/***************************************************************************/
+/* */
+/* pshints.h */
+/* */
+/* Adobe's code for handling CFF hints (body). */
+/* */
+/* Copyright 2007-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef PSHINT_H_
+#define PSHINT_H_
+
+FT_BEGIN_HEADER
+
+
+ enum
+ {
+ CF2_MAX_HINTS = 96 /* maximum # of hints */
+ };
+
+
+ /*
+ * A HintMask object stores a bit mask that specifies which hints in the
+ * charstring are active at a given time. Hints in CFF must be declared
+ * at the start, before any drawing operators, with horizontal hints
+ * preceding vertical hints. The HintMask is ordered the same way, with
+ * horizontal hints immediately followed by vertical hints. Clients are
+ * responsible for knowing how many of each type are present.
+ *
+ * The maximum total number of hints is 96, as specified by the CFF
+ * specification.
+ *
+ * A HintMask is built 0 or more times while interpreting a charstring, by
+ * the HintMask operator. There is only one HintMask, but it is built or
+ * rebuilt each time there is a hint substitution (HintMask operator) in
+ * the charstring. A default HintMask with all bits set is built if there
+ * has been no HintMask operator prior to the first drawing operator.
+ *
+ */
+
+ typedef struct CF2_HintMaskRec_
+ {
+ FT_Error* error;
+
+ FT_Bool isValid;
+ FT_Bool isNew;
+
+ size_t bitCount;
+ size_t byteCount;
+
+ FT_Byte mask[( CF2_MAX_HINTS + 7 ) / 8];
+
+ } CF2_HintMaskRec, *CF2_HintMask;
+
+
+ typedef struct CF2_StemHintRec_
+ {
+ FT_Bool used; /* DS positions are valid */
+
+ CF2_Fixed min; /* original character space value */
+ CF2_Fixed max;
+
+ CF2_Fixed minDS; /* DS position after first use */
+ CF2_Fixed maxDS;
+
+ } CF2_StemHintRec, *CF2_StemHint;
+
+
+ /*
+ * A HintMap object stores a piecewise linear function for mapping
+ * y-coordinates from character space to device space, providing
+ * appropriate pixel alignment to stem edges.
+ *
+ * The map is implemented as an array of `CF2_Hint' elements, each
+ * representing an edge. When edges are paired, as from stem hints, the
+ * bottom edge must immediately precede the top edge in the array.
+ * Element character space AND device space positions must both increase
+ * monotonically in the array. `CF2_Hint' elements are also used as
+ * parameters to `cf2_blues_capture'.
+ *
+ * The `cf2_hintmap_build' method must be called before any drawing
+ * operation (beginning with a Move operator) and at each hint
+ * substitution (HintMask operator).
+ *
+ * The `cf2_hintmap_map' method is called to transform y-coordinates at
+ * each drawing operation (move, line, curve).
+ *
+ */
+
+ /* TODO: make this a CF2_ArrStack and add a deep copy method */
+ enum
+ {
+ CF2_MAX_HINT_EDGES = CF2_MAX_HINTS * 2
+ };
+
+
+ typedef struct CF2_HintMapRec_
+ {
+ CF2_Font font;
+
+ /* initial map based on blue zones */
+ struct CF2_HintMapRec_* initialHintMap;
+
+ /* working storage for 2nd pass adjustHints */
+ CF2_ArrStack hintMoves;
+
+ FT_Bool isValid;
+ FT_Bool hinted;
+
+ CF2_Fixed scale;
+ CF2_UInt count;
+
+ /* start search from this index */
+ CF2_UInt lastIndex;
+
+ CF2_HintRec edge[CF2_MAX_HINT_EDGES]; /* 192 */
+
+ } CF2_HintMapRec, *CF2_HintMap;
+
+
+ FT_LOCAL( FT_Bool )
+ cf2_hint_isValid( const CF2_Hint hint );
+ FT_LOCAL( FT_Bool )
+ cf2_hint_isTop( const CF2_Hint hint );
+ FT_LOCAL( FT_Bool )
+ cf2_hint_isBottom( const CF2_Hint hint );
+ FT_LOCAL( void )
+ cf2_hint_lock( CF2_Hint hint );
+
+
+ FT_LOCAL( void )
+ cf2_hintmap_init( CF2_HintMap hintmap,
+ CF2_Font font,
+ CF2_HintMap initialMap,
+ CF2_ArrStack hintMoves,
+ CF2_Fixed scale );
+ FT_LOCAL( void )
+ cf2_hintmap_build( CF2_HintMap hintmap,
+ CF2_ArrStack hStemHintArray,
+ CF2_ArrStack vStemHintArray,
+ CF2_HintMask hintMask,
+ CF2_Fixed hintOrigin,
+ FT_Bool initialMap );
+
+
+ /*
+ * GlyphPath is a wrapper for drawing operations that scales the
+ * coordinates according to the render matrix and HintMap. It also tracks
+ * open paths to control ClosePath and to insert MoveTo for broken fonts.
+ *
+ */
+ typedef struct CF2_GlyphPathRec_
+ {
+ /* TODO: gather some of these into a hinting context */
+
+ CF2_Font font; /* font instance */
+ CF2_OutlineCallbacks callbacks; /* outline consumer */
+
+
+ CF2_HintMapRec hintMap; /* current hint map */
+ CF2_HintMapRec firstHintMap; /* saved copy */
+ CF2_HintMapRec initialHintMap; /* based on all captured hints */
+
+ CF2_ArrStackRec hintMoves; /* list of hint moves for 2nd pass */
+
+ CF2_Fixed scaleX; /* matrix a */
+ CF2_Fixed scaleC; /* matrix c */
+ CF2_Fixed scaleY; /* matrix d */
+
+ FT_Vector fractionalTranslation; /* including deviceXScale */
+#if 0
+ CF2_Fixed hShift; /* character space horizontal shift */
+ /* (for fauxing) */
+#endif
+
+ FT_Bool pathIsOpen; /* true after MoveTo */
+ FT_Bool pathIsClosing; /* true when synthesizing closepath line */
+ FT_Bool darken; /* true if stem darkening */
+ FT_Bool moveIsPending; /* true between MoveTo and offset MoveTo */
+
+ /* references used to call `cf2_hintmap_build', if necessary */
+ CF2_ArrStack hStemHintArray;
+ CF2_ArrStack vStemHintArray;
+ CF2_HintMask hintMask; /* ptr to the current mask */
+ CF2_Fixed hintOriginY; /* copy of current origin */
+ const CF2_BluesRec* blues;
+
+ CF2_Fixed xOffset; /* character space offsets */
+ CF2_Fixed yOffset;
+
+ /* character space miter limit threshold */
+ CF2_Fixed miterLimit;
+ /* vertical/horizontal snap distance in character space */
+ CF2_Fixed snapThreshold;
+
+ FT_Vector offsetStart0; /* first and second points of first */
+ FT_Vector offsetStart1; /* element with offset applied */
+
+ /* current point, character space, before offset */
+ FT_Vector currentCS;
+ /* current point, device space */
+ FT_Vector currentDS;
+ /* start point of subpath, character space */
+ FT_Vector start;
+
+ /* the following members constitute the `queue' of one element */
+ FT_Bool elemIsQueued;
+ CF2_Int prevElemOp;
+
+ FT_Vector prevElemP0;
+ FT_Vector prevElemP1;
+ FT_Vector prevElemP2;
+ FT_Vector prevElemP3;
+
+ } CF2_GlyphPathRec, *CF2_GlyphPath;
+
+
+ FT_LOCAL( void )
+ cf2_glyphpath_init( CF2_GlyphPath glyphpath,
+ CF2_Font font,
+ CF2_OutlineCallbacks callbacks,
+ CF2_Fixed scaleY,
+ /* CF2_Fixed hShift, */
+ CF2_ArrStack hStemHintArray,
+ CF2_ArrStack vStemHintArray,
+ CF2_HintMask hintMask,
+ CF2_Fixed hintOrigin,
+ const CF2_Blues blues,
+ const FT_Vector* fractionalTranslation );
+ FT_LOCAL( void )
+ cf2_glyphpath_finalize( CF2_GlyphPath glyphpath );
+
+ FT_LOCAL( void )
+ cf2_glyphpath_moveTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x,
+ CF2_Fixed y );
+ FT_LOCAL( void )
+ cf2_glyphpath_lineTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x,
+ CF2_Fixed y );
+ FT_LOCAL( void )
+ cf2_glyphpath_curveTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x1,
+ CF2_Fixed y1,
+ CF2_Fixed x2,
+ CF2_Fixed y2,
+ CF2_Fixed x3,
+ CF2_Fixed y3 );
+ FT_LOCAL( void )
+ cf2_glyphpath_closeOpenPath( CF2_GlyphPath glyphpath );
+
+
+FT_END_HEADER
+
+
+#endif /* PSHINT_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psintrp.c b/thirdparty/freetype/src/psaux/psintrp.c
new file mode 100644
index 0000000000..da5a8dad1d
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psintrp.c
@@ -0,0 +1,3040 @@
+/***************************************************************************/
+/* */
+/* psintrp.c */
+/* */
+/* Adobe's CFF Interpreter (body). */
+/* */
+/* Copyright 2007-2014 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#include "psft.h"
+#include FT_INTERNAL_DEBUG_H
+#include FT_SERVICE_CFF_TABLE_LOAD_H
+
+#include "psglue.h"
+#include "psfont.h"
+#include "psstack.h"
+#include "pshints.h"
+#include "psintrp.h"
+
+#include "pserror.h"
+
+#include "psobjs.h" /* for cff_random */
+#include "t1decode.h" /* for t1 seac */
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cf2interp
+
+
+ FT_LOCAL_DEF( void )
+ cf2_hintmask_init( CF2_HintMask hintmask,
+ FT_Error* error )
+ {
+ FT_ZERO( hintmask );
+
+ hintmask->error = error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_hintmask_isValid( const CF2_HintMask hintmask )
+ {
+ return hintmask->isValid;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_hintmask_isNew( const CF2_HintMask hintmask )
+ {
+ return hintmask->isNew;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_hintmask_setNew( CF2_HintMask hintmask,
+ FT_Bool val )
+ {
+ hintmask->isNew = val;
+ }
+
+
+ /* clients call `getMaskPtr' in order to iterate */
+ /* through hint mask */
+
+ FT_LOCAL_DEF( FT_Byte* )
+ cf2_hintmask_getMaskPtr( CF2_HintMask hintmask )
+ {
+ return hintmask->mask;
+ }
+
+
+ static size_t
+ cf2_hintmask_setCounts( CF2_HintMask hintmask,
+ size_t bitCount )
+ {
+ if ( bitCount > CF2_MAX_HINTS )
+ {
+ /* total of h and v stems must be <= 96 */
+ CF2_SET_ERROR( hintmask->error, Invalid_Glyph_Format );
+ return 0;
+ }
+
+ hintmask->bitCount = bitCount;
+ hintmask->byteCount = ( hintmask->bitCount + 7 ) / 8;
+
+ hintmask->isValid = TRUE;
+ hintmask->isNew = TRUE;
+
+ return bitCount;
+ }
+
+
+ /* consume the hintmask bytes from the charstring, advancing the src */
+ /* pointer */
+ static void
+ cf2_hintmask_read( CF2_HintMask hintmask,
+ CF2_Buffer charstring,
+ size_t bitCount )
+ {
+ size_t i;
+
+#ifndef CF2_NDEBUG
+ /* these are the bits in the final mask byte that should be zero */
+ /* Note: this variable is only used in an assert expression below */
+ /* and then only if CF2_NDEBUG is not defined */
+ CF2_UInt mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1;
+#endif
+
+
+ /* initialize counts and isValid */
+ if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 )
+ return;
+
+ FT_ASSERT( hintmask->byteCount > 0 );
+
+ FT_TRACE4(( " (maskbytes:" ));
+
+ /* set mask and advance interpreter's charstring pointer */
+ for ( i = 0; i < hintmask->byteCount; i++ )
+ {
+ hintmask->mask[i] = (FT_Byte)cf2_buf_readByte( charstring );
+ FT_TRACE4(( " 0x%02X", hintmask->mask[i] ));
+ }
+
+ FT_TRACE4(( ")\n" ));
+
+ /* assert any unused bits in last byte are zero unless there's a prior */
+ /* error */
+ /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1 */
+#ifndef CF2_NDEBUG
+ FT_ASSERT( ( hintmask->mask[hintmask->byteCount - 1] & mask ) == 0 ||
+ *hintmask->error );
+#endif
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_hintmask_setAll( CF2_HintMask hintmask,
+ size_t bitCount )
+ {
+ size_t i;
+ CF2_UInt mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1;
+
+
+ /* initialize counts and isValid */
+ if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 )
+ return;
+
+ FT_ASSERT( hintmask->byteCount > 0 );
+ FT_ASSERT( hintmask->byteCount <=
+ sizeof ( hintmask->mask ) / sizeof ( hintmask->mask[0] ) );
+
+ /* set mask to all ones */
+ for ( i = 0; i < hintmask->byteCount; i++ )
+ hintmask->mask[i] = 0xFF;
+
+ /* clear unused bits */
+ /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1 */
+ hintmask->mask[hintmask->byteCount - 1] &= ~mask;
+ }
+
+
+ /* Type2 charstring opcodes */
+ enum
+ {
+ cf2_cmdRESERVED_0, /* 0 */
+ cf2_cmdHSTEM, /* 1 */
+ cf2_cmdRESERVED_2, /* 2 */
+ cf2_cmdVSTEM, /* 3 */
+ cf2_cmdVMOVETO, /* 4 */
+ cf2_cmdRLINETO, /* 5 */
+ cf2_cmdHLINETO, /* 6 */
+ cf2_cmdVLINETO, /* 7 */
+ cf2_cmdRRCURVETO, /* 8 */
+ cf2_cmdCLOSEPATH, /* 9 T1 only */
+ cf2_cmdCALLSUBR, /* 10 */
+ cf2_cmdRETURN, /* 11 */
+ cf2_cmdESC, /* 12 */
+ cf2_cmdHSBW, /* 13 T1 only */
+ cf2_cmdENDCHAR, /* 14 */
+ cf2_cmdVSINDEX, /* 15 */
+ cf2_cmdBLEND, /* 16 */
+ cf2_cmdRESERVED_17, /* 17 */
+ cf2_cmdHSTEMHM, /* 18 */
+ cf2_cmdHINTMASK, /* 19 */
+ cf2_cmdCNTRMASK, /* 20 */
+ cf2_cmdRMOVETO, /* 21 */
+ cf2_cmdHMOVETO, /* 22 */
+ cf2_cmdVSTEMHM, /* 23 */
+ cf2_cmdRCURVELINE, /* 24 */
+ cf2_cmdRLINECURVE, /* 25 */
+ cf2_cmdVVCURVETO, /* 26 */
+ cf2_cmdHHCURVETO, /* 27 */
+ cf2_cmdEXTENDEDNMBR, /* 28 */
+ cf2_cmdCALLGSUBR, /* 29 */
+ cf2_cmdVHCURVETO, /* 30 */
+ cf2_cmdHVCURVETO /* 31 */
+ };
+
+ enum
+ {
+ cf2_escDOTSECTION, /* 0 */
+ cf2_escVSTEM3, /* 1 T1 only */
+ cf2_escHSTEM3, /* 2 T1 only */
+ cf2_escAND, /* 3 */
+ cf2_escOR, /* 4 */
+ cf2_escNOT, /* 5 */
+ cf2_escSEAC, /* 6 T1 only */
+ cf2_escSBW, /* 7 T1 only */
+ cf2_escRESERVED_8, /* 8 */
+ cf2_escABS, /* 9 */
+ cf2_escADD, /* 10 like otherADD */
+ cf2_escSUB, /* 11 like otherSUB */
+ cf2_escDIV, /* 12 */
+ cf2_escRESERVED_13, /* 13 */
+ cf2_escNEG, /* 14 */
+ cf2_escEQ, /* 15 */
+ cf2_escCALLOTHERSUBR,/* 16 T1 only */
+ cf2_escPOP, /* 17 T1 only */
+ cf2_escDROP, /* 18 */
+ cf2_escRESERVED_19, /* 19 */
+ cf2_escPUT, /* 20 like otherPUT */
+ cf2_escGET, /* 21 like otherGET */
+ cf2_escIFELSE, /* 22 like otherIFELSE */
+ cf2_escRANDOM, /* 23 like otherRANDOM */
+ cf2_escMUL, /* 24 like otherMUL */
+ cf2_escRESERVED_25, /* 25 */
+ cf2_escSQRT, /* 26 */
+ cf2_escDUP, /* 27 like otherDUP */
+ cf2_escEXCH, /* 28 like otherEXCH */
+ cf2_escINDEX, /* 29 */
+ cf2_escROLL, /* 30 */
+ cf2_escRESERVED_31, /* 31 */
+ cf2_escRESERVED_32, /* 32 */
+ cf2_escSETCURRENTPT, /* 33 T1 only */
+ cf2_escHFLEX, /* 34 */
+ cf2_escFLEX, /* 35 */
+ cf2_escHFLEX1, /* 36 */
+ cf2_escFLEX1, /* 37 */
+ cf2_escRESERVED_38 /* 38 & all higher */
+ };
+
+
+ /* `stemHintArray' does not change once we start drawing the outline. */
+ static void
+ cf2_doStems( const CF2_Font font,
+ CF2_Stack opStack,
+ CF2_ArrStack stemHintArray,
+ CF2_Fixed* width,
+ FT_Bool* haveWidth,
+ CF2_Fixed hintOffset )
+ {
+ CF2_UInt i;
+ CF2_UInt count = cf2_stack_count( opStack );
+ FT_Bool hasWidthArg = (FT_Bool)( count & 1 );
+
+ /* variable accumulates delta values from operand stack */
+ CF2_Fixed position = hintOffset;
+
+ if ( font->isT1 && !font->decoder->flex_state && !*haveWidth )
+ FT_ERROR(( "cf2_doStems (Type 1 mode):"
+ " No width. Use hsbw/sbw as first op\n" ));
+
+ if ( !font->isT1 && hasWidthArg && !*haveWidth )
+ *width = ADD_INT32( cf2_stack_getReal( opStack, 0 ),
+ cf2_getNominalWidthX( font->decoder ) );
+
+ if ( font->decoder->width_only )
+ goto exit;
+
+ for ( i = hasWidthArg ? 1 : 0; i < count; i += 2 )
+ {
+ /* construct a CF2_StemHint and push it onto the list */
+ CF2_StemHintRec stemhint;
+
+
+ stemhint.min =
+ position = ADD_INT32( position,
+ cf2_stack_getReal( opStack, i ) );
+ stemhint.max =
+ position = ADD_INT32( position,
+ cf2_stack_getReal( opStack, i + 1 ) );
+
+ stemhint.used = FALSE;
+ stemhint.maxDS =
+ stemhint.minDS = 0;
+
+ cf2_arrstack_push( stemHintArray, &stemhint ); /* defer error check */
+ }
+
+ cf2_stack_clear( opStack );
+
+ exit:
+ /* cf2_doStems must define a width (may be default) */
+ *haveWidth = TRUE;
+ }
+
+
+ static void
+ cf2_doFlex( CF2_Stack opStack,
+ CF2_Fixed* curX,
+ CF2_Fixed* curY,
+ CF2_GlyphPath glyphPath,
+ const FT_Bool* readFromStack,
+ FT_Bool doConditionalLastRead )
+ {
+ CF2_Fixed vals[14];
+ CF2_UInt idx;
+ FT_Bool isHFlex;
+ CF2_Int top, i, j;
+
+
+ vals[0] = *curX;
+ vals[1] = *curY;
+ idx = 0;
+ isHFlex = FT_BOOL( readFromStack[9] == FALSE );
+ top = isHFlex ? 9 : 10;
+
+ for ( i = 0; i < top; i++ )
+ {
+ vals[i + 2] = vals[i];
+ if ( readFromStack[i] )
+ vals[i + 2] = ADD_INT32( vals[i + 2], cf2_stack_getReal( opStack,
+ idx++ ) );
+ }
+
+ if ( isHFlex )
+ vals[9 + 2] = *curY;
+
+ if ( doConditionalLastRead )
+ {
+ FT_Bool lastIsX = (FT_Bool)(
+ cf2_fixedAbs( SUB_INT32( vals[10], *curX ) ) >
+ cf2_fixedAbs( SUB_INT32( vals[11], *curY ) ) );
+ CF2_Fixed lastVal = cf2_stack_getReal( opStack, idx );
+
+
+ if ( lastIsX )
+ {
+ vals[12] = ADD_INT32( vals[10], lastVal );
+ vals[13] = *curY;
+ }
+ else
+ {
+ vals[12] = *curX;
+ vals[13] = ADD_INT32( vals[11], lastVal );
+ }
+ }
+ else
+ {
+ if ( readFromStack[10] )
+ vals[12] = ADD_INT32( vals[10],
+ cf2_stack_getReal( opStack, idx++ ) );
+ else
+ vals[12] = *curX;
+
+ if ( readFromStack[11] )
+ vals[13] = ADD_INT32( vals[11],
+ cf2_stack_getReal( opStack, idx ) );
+ else
+ vals[13] = *curY;
+ }
+
+ for ( j = 0; j < 2; j++ )
+ cf2_glyphpath_curveTo( glyphPath, vals[j * 6 + 2],
+ vals[j * 6 + 3],
+ vals[j * 6 + 4],
+ vals[j * 6 + 5],
+ vals[j * 6 + 6],
+ vals[j * 6 + 7] );
+
+ cf2_stack_clear( opStack );
+
+ *curX = vals[12];
+ *curY = vals[13];
+ }
+
+
+ /* Blend numOperands on the stack, */
+ /* store results into the first numBlends values, */
+ /* then pop remaining arguments. */
+ static void
+ cf2_doBlend( const CFF_Blend blend,
+ CF2_Stack opStack,
+ CF2_UInt numBlends )
+ {
+ CF2_UInt delta;
+ CF2_UInt base;
+ CF2_UInt i, j;
+ CF2_UInt numOperands = (CF2_UInt)( numBlends * blend->lenBV );
+
+
+ base = cf2_stack_count( opStack ) - numOperands;
+ delta = base + numBlends;
+
+ for ( i = 0; i < numBlends; i++ )
+ {
+ const CF2_Fixed* weight = &blend->BV[1];
+
+ /* start with first term */
+ CF2_Fixed sum = cf2_stack_getReal( opStack, i + base );
+
+
+ for ( j = 1; j < blend->lenBV; j++ )
+ sum = ADD_INT32( sum,
+ FT_MulFix( *weight++,
+ cf2_stack_getReal( opStack,
+ delta++ ) ) );
+
+ /* store blended result */
+ cf2_stack_setReal( opStack, i + base, sum );
+ }
+
+ /* leave only `numBlends' results on stack */
+ cf2_stack_pop( opStack, numOperands - numBlends );
+ }
+
+
+ /*
+ * `error' is a shared error code used by many objects in this
+ * routine. Before the code continues from an error, it must check and
+ * record the error in `*error'. The idea is that this shared
+ * error code will record the first error encountered. If testing
+ * for an error anyway, the cost of `goto exit' is small, so we do it,
+ * even if continuing would be safe. In this case, `lastError' is
+ * set, so the testing and storing can be done in one place, at `exit'.
+ *
+ * Continuing after an error is intended for objects which do their own
+ * testing of `*error', e.g., array stack functions. This allows us to
+ * avoid an extra test after the call.
+ *
+ * Unimplemented opcodes are ignored.
+ *
+ */
+ FT_LOCAL_DEF( void )
+ cf2_interpT2CharString( CF2_Font font,
+ CF2_Buffer buf,
+ CF2_OutlineCallbacks callbacks,
+ const FT_Vector* translation,
+ FT_Bool doingSeac,
+ CF2_Fixed curX,
+ CF2_Fixed curY,
+ CF2_Fixed* width )
+ {
+ /* lastError is used for errors that are immediately tested */
+ FT_Error lastError = FT_Err_Ok;
+
+ /* pointer to parsed font object */
+ PS_Decoder* decoder = font->decoder;
+
+ FT_Error* error = &font->error;
+ FT_Memory memory = font->memory;
+
+ CF2_Fixed scaleY = font->innerTransform.d;
+ CF2_Fixed nominalWidthX = cf2_getNominalWidthX( decoder );
+
+ /* stuff for Type 1 */
+ FT_Int known_othersubr_result_cnt = 0;
+ FT_Bool large_int = FALSE;
+ FT_Bool initial_map_ready = FALSE;
+
+#define PS_STORAGE_SIZE 3
+ CF2_F16Dot16 results[PS_STORAGE_SIZE]; /* for othersubr results */
+ FT_Int result_cnt = 0;
+
+ /* save this for hinting seac accents */
+ CF2_Fixed hintOriginY = curY;
+
+ CF2_Stack opStack = NULL;
+ FT_UInt stackSize;
+ FT_Byte op1; /* first opcode byte */
+
+ CF2_F16Dot16 storage[CF2_STORAGE_SIZE]; /* for `put' and `get' */
+ CF2_F16Dot16 flexStore[6]; /* for Type 1 flex */
+
+ /* instruction limit; 20,000,000 matches Avalon */
+ FT_UInt32 instructionLimit = 20000000UL;
+
+ CF2_ArrStackRec subrStack;
+
+ FT_Bool haveWidth;
+ CF2_Buffer charstring = NULL;
+
+ CF2_Int charstringIndex = -1; /* initialize to empty */
+
+ /* TODO: placeholders for hint structures */
+
+ /* objects used for hinting */
+ CF2_ArrStackRec hStemHintArray;
+ CF2_ArrStackRec vStemHintArray;
+
+ CF2_HintMaskRec hintMask;
+ CF2_GlyphPathRec glyphPath;
+
+
+ FT_ZERO( &storage );
+ FT_ZERO( &results );
+ FT_ZERO( &flexStore );
+
+ /* initialize the remaining objects */
+ cf2_arrstack_init( &subrStack,
+ memory,
+ error,
+ sizeof ( CF2_BufferRec ) );
+ cf2_arrstack_init( &hStemHintArray,
+ memory,
+ error,
+ sizeof ( CF2_StemHintRec ) );
+ cf2_arrstack_init( &vStemHintArray,
+ memory,
+ error,
+ sizeof ( CF2_StemHintRec ) );
+
+ /* initialize CF2_StemHint arrays */
+ cf2_hintmask_init( &hintMask, error );
+
+ /* initialize path map to manage drawing operations */
+
+ /* Note: last 4 params are used to handle `MoveToPermissive', which */
+ /* may need to call `hintMap.Build' */
+ /* TODO: MoveToPermissive is gone; are these still needed? */
+ cf2_glyphpath_init( &glyphPath,
+ font,
+ callbacks,
+ scaleY,
+ /* hShift, */
+ &hStemHintArray,
+ &vStemHintArray,
+ &hintMask,
+ hintOriginY,
+ &font->blues,
+ translation );
+
+ /*
+ * Initialize state for width parsing. From the CFF Spec:
+ *
+ * The first stack-clearing operator, which must be one of hstem,
+ * hstemhm, vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto,
+ * rmoveto, or endchar, takes an additional argument - the width (as
+ * described earlier), which may be expressed as zero or one numeric
+ * argument.
+ *
+ * What we implement here uses the first validly specified width, but
+ * does not detect errors for specifying more than one width.
+ *
+ * If one of the above operators occurs without explicitly specifying
+ * a width, we assume the default width.
+ *
+ * CFF2 charstrings always return the default width (0).
+ *
+ */
+ haveWidth = font->isCFF2 ? TRUE : FALSE;
+ *width = cf2_getDefaultWidthX( decoder );
+
+ /*
+ * Note: At this point, all pointers to resources must be NULL
+ * and all local objects must be initialized.
+ * There must be no branches to `exit:' above this point.
+ *
+ */
+
+ /* allocate an operand stack */
+ stackSize = font->isCFF2 ? cf2_getMaxstack( decoder )
+ : CF2_OPERAND_STACK_SIZE;
+ opStack = cf2_stack_init( memory, error, stackSize );
+
+ if ( !opStack )
+ {
+ lastError = FT_THROW( Out_Of_Memory );
+ goto exit;
+ }
+
+ /* initialize subroutine stack by placing top level charstring as */
+ /* first element (max depth plus one for the charstring) */
+ /* Note: Caller owns and must finalize the first charstring. */
+ /* Our copy of it does not change that requirement. */
+ cf2_arrstack_setCount( &subrStack, CF2_MAX_SUBR + 1 );
+
+ charstring = (CF2_Buffer)cf2_arrstack_getBuffer( &subrStack );
+ *charstring = *buf; /* structure copy */
+
+ charstringIndex = 0; /* entry is valid now */
+
+ /* catch errors so far */
+ if ( *error )
+ goto exit;
+
+ /* main interpreter loop */
+ while ( 1 )
+ {
+ if ( font->isT1 )
+ FT_ASSERT( known_othersubr_result_cnt == 0 ||
+ result_cnt == 0 );
+
+ if ( cf2_buf_isEnd( charstring ) )
+ {
+ /* If we've reached the end of the charstring, simulate a */
+ /* cf2_cmdRETURN or cf2_cmdENDCHAR. */
+ /* We do this for both CFF and CFF2. */
+ if ( charstringIndex )
+ op1 = cf2_cmdRETURN; /* end of buffer for subroutine */
+ else
+ op1 = cf2_cmdENDCHAR; /* end of buffer for top level charstring */
+ }
+ else
+ {
+ op1 = (FT_Byte)cf2_buf_readByte( charstring );
+
+ /* Explicit RETURN and ENDCHAR in CFF2 should be ignored. */
+ /* Note: Trace message will report 0 instead of 11 or 14. */
+ if ( ( op1 == cf2_cmdRETURN || op1 == cf2_cmdENDCHAR ) &&
+ font->isCFF2 )
+ op1 = cf2_cmdRESERVED_0;
+ }
+
+ if ( font->isT1 )
+ {
+ if ( !initial_map_ready &&
+ !( op1 == cf2_cmdHSTEM ||
+ op1 == cf2_cmdVSTEM ||
+ op1 == cf2_cmdHSBW ||
+ op1 == cf2_cmdCALLSUBR ||
+ op1 == cf2_cmdRETURN ||
+ op1 == cf2_cmdESC ||
+ op1 == cf2_cmdENDCHAR ||
+ op1 >= 32 /* Numbers */ ) )
+ {
+ /* Skip outline commands first time round. */
+ /* `endchar' will trigger initial hintmap build */
+ /* and rewind the charstring. */
+ cf2_stack_clear( opStack );
+ continue;
+ }
+
+ if ( result_cnt > 0 &&
+ !( op1 == cf2_cmdCALLSUBR ||
+ op1 == cf2_cmdRETURN ||
+ op1 == cf2_cmdESC ||
+ op1 >= 32 /* Numbers */ ) )
+ {
+ /* all operands have been transferred by previous pops */
+ result_cnt = 0;
+ }
+
+ if ( large_int && !( op1 >= 32 || op1 == cf2_escDIV ) )
+ {
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " no `div' after large integer\n" ));
+
+ large_int = FALSE;
+ }
+ }
+
+ /* check for errors once per loop */
+ if ( *error )
+ goto exit;
+
+ instructionLimit--;
+ if ( instructionLimit == 0 )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ switch( op1 )
+ {
+ case cf2_cmdRESERVED_0:
+ case cf2_cmdRESERVED_2:
+ case cf2_cmdRESERVED_17:
+ /* we may get here if we have a prior error */
+ FT_TRACE4(( " unknown op (%d)\n", op1 ));
+ break;
+
+ case cf2_cmdVSINDEX:
+ FT_TRACE4(( " vsindex\n" ));
+
+ if ( !font->isCFF2 )
+ break; /* clear stack & ignore */
+
+ if ( font->blend.usedBV )
+ {
+ /* vsindex not allowed after blend */
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ {
+ FT_Int temp = cf2_stack_popInt( opStack );
+
+
+ if ( temp >= 0 )
+ font->vsindex = (FT_UInt)temp;
+ }
+ break;
+
+ case cf2_cmdBLEND:
+ {
+ FT_UInt numBlends;
+
+
+ FT_TRACE4(( " blend\n" ));
+
+ if ( !font->isCFF2 )
+ break; /* clear stack & ignore */
+
+ /* do we have a `blend' op in a non-variant font? */
+ if ( !font->blend.font )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* check cached blend vector */
+ if ( font->cffload->blend_check_vector( &font->blend,
+ font->vsindex,
+ font->lenNDV,
+ font->NDV ) )
+ {
+ lastError = font->cffload->blend_build_vector( &font->blend,
+ font->vsindex,
+ font->lenNDV,
+ font->NDV );
+ if ( lastError )
+ goto exit;
+ }
+
+ /* do the blend */
+ numBlends = (FT_UInt)cf2_stack_popInt( opStack );
+ if ( numBlends > stackSize )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ cf2_doBlend( &font->blend, opStack, numBlends );
+
+ font->blend.usedBV = TRUE;
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_cmdHSTEMHM:
+ case cf2_cmdHSTEM:
+ FT_TRACE4(( op1 == cf2_cmdHSTEMHM ? " hstemhm\n" : " hstem\n" ));
+
+ if ( !font->isT1 )
+ {
+ /* never add hints after the mask is computed */
+ /* except if in Type 1 mode (no hintmask op) */
+ if ( cf2_hintmask_isValid( &hintMask ) )
+ {
+ FT_TRACE4(( "cf2_interpT2CharString:"
+ " invalid horizontal hint mask\n" ));
+ break;
+ }
+ }
+
+ /* add left-sidebearing correction in Type 1 mode */
+ cf2_doStems( font,
+ opStack,
+ &hStemHintArray,
+ width,
+ &haveWidth,
+ font->isT1 ? decoder->builder.left_bearing->y
+ : 0 );
+
+ if ( decoder->width_only )
+ goto exit;
+
+ break;
+
+ case cf2_cmdVSTEMHM:
+ case cf2_cmdVSTEM:
+ FT_TRACE4(( op1 == cf2_cmdVSTEMHM ? " vstemhm\n" : " vstem\n" ));
+
+ if ( !font->isT1 )
+ {
+ /* never add hints after the mask is computed */
+ /* except if in Type 1 mode (no hintmask op) */
+ if ( cf2_hintmask_isValid( &hintMask ) )
+ {
+ FT_TRACE4(( "cf2_interpT2CharString:"
+ " invalid vertical hint mask\n" ));
+ break;
+ }
+ }
+
+ /* add left-sidebearing correction in Type 1 mode */
+ cf2_doStems( font,
+ opStack,
+ &vStemHintArray,
+ width,
+ &haveWidth,
+ font->isT1 ? decoder->builder.left_bearing->x
+ : 0 );
+
+ if ( decoder->width_only )
+ goto exit;
+
+ break;
+
+ case cf2_cmdVMOVETO:
+ FT_TRACE4(( " vmoveto\n" ));
+
+ if ( font->isT1 && !decoder->flex_state && !haveWidth )
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " No width. Use hsbw/sbw as first op\n" ));
+
+ if ( cf2_stack_count( opStack ) > 1 && !haveWidth )
+ *width = ADD_INT32( cf2_stack_getReal( opStack, 0 ),
+ nominalWidthX );
+
+ /* width is defined or default after this */
+ haveWidth = TRUE;
+
+ if ( decoder->width_only )
+ goto exit;
+
+ curY = ADD_INT32( curY, cf2_stack_popFixed( opStack ) );
+
+ if ( !decoder->flex_state )
+ cf2_glyphpath_moveTo( &glyphPath, curX, curY );
+
+ break;
+
+ case cf2_cmdRLINETO:
+ {
+ CF2_UInt idx;
+ CF2_UInt count = cf2_stack_count( opStack );
+
+
+ FT_TRACE4(( " rlineto\n" ));
+
+ for ( idx = 0; idx < count; idx += 2 )
+ {
+ curX = ADD_INT32( curX, cf2_stack_getReal( opStack,
+ idx + 0 ) );
+ curY = ADD_INT32( curY, cf2_stack_getReal( opStack,
+ idx + 1 ) );
+
+ cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdHLINETO:
+ case cf2_cmdVLINETO:
+ {
+ CF2_UInt idx;
+ CF2_UInt count = cf2_stack_count( opStack );
+
+ FT_Bool isX = FT_BOOL( op1 == cf2_cmdHLINETO );
+
+
+ FT_TRACE4(( isX ? " hlineto\n" : " vlineto\n" ));
+
+ for ( idx = 0; idx < count; idx++ )
+ {
+ CF2_Fixed v = cf2_stack_getReal( opStack, idx );
+
+
+ if ( isX )
+ curX = ADD_INT32( curX, v );
+ else
+ curY = ADD_INT32( curY, v );
+
+ isX = !isX;
+
+ cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue;
+
+ case cf2_cmdRCURVELINE:
+ case cf2_cmdRRCURVETO:
+ {
+ CF2_UInt count = cf2_stack_count( opStack );
+ CF2_UInt idx = 0;
+
+
+ FT_TRACE4(( op1 == cf2_cmdRCURVELINE ? " rcurveline\n"
+ : " rrcurveto\n" ));
+
+ while ( idx + 6 <= count )
+ {
+ CF2_Fixed x1, y1, x2, y2, x3, y3;
+
+
+ x1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curX );
+ y1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), curY );
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), y1 );
+ x3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 4 ), x2 );
+ y3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 5 ), y2 );
+
+ cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+ curX = x3;
+ curY = y3;
+ idx += 6;
+ }
+
+ if ( op1 == cf2_cmdRCURVELINE )
+ {
+ curX = ADD_INT32( curX, cf2_stack_getReal( opStack,
+ idx + 0 ) );
+ curY = ADD_INT32( curY, cf2_stack_getReal( opStack,
+ idx + 1 ) );
+
+ cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdCLOSEPATH:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (%d)\n", op1 ));
+ else
+ {
+ FT_TRACE4(( " closepath" ));
+
+ /* if there is no path, `closepath' is a no-op */
+ ps_builder_close_contour( &decoder->builder );
+
+ haveWidth = TRUE;
+ }
+ break;
+
+ case cf2_cmdCALLGSUBR:
+ case cf2_cmdCALLSUBR:
+ {
+ CF2_Int subrNum;
+
+
+ FT_TRACE4(( op1 == cf2_cmdCALLGSUBR ? " callgsubr"
+ : " callsubr" ));
+
+ if ( ( !font->isT1 && charstringIndex > CF2_MAX_SUBR ) ||
+ ( font->isT1 && charstringIndex > T1_MAX_SUBRS_CALLS ) )
+ {
+ /* max subr plus one for charstring */
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* overflow of stack */
+ }
+
+ /* push our current CFF charstring region on subrStack */
+ charstring = (CF2_Buffer)
+ cf2_arrstack_getPointer(
+ &subrStack,
+ (size_t)charstringIndex + 1 );
+
+ /* set up the new CFF region and pointer */
+ subrNum = cf2_stack_popInt( opStack );
+
+ if ( font->isT1 && decoder->locals_hash )
+ {
+ size_t* val = ft_hash_num_lookup( subrNum,
+ decoder->locals_hash );
+
+
+ if ( val )
+ subrNum = *val;
+ else
+ subrNum = -1;
+ }
+
+ switch ( op1 )
+ {
+ case cf2_cmdCALLGSUBR:
+ FT_TRACE4(( " (idx %d, entering level %d)\n",
+ subrNum + decoder->globals_bias,
+ charstringIndex + 1 ));
+
+ if ( cf2_initGlobalRegionBuffer( decoder,
+ subrNum,
+ charstring ) )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* subroutine lookup or stream error */
+ }
+ break;
+
+ default:
+ /* cf2_cmdCALLSUBR */
+ FT_TRACE4(( " (idx %d, entering level %d)\n",
+ subrNum + decoder->locals_bias,
+ charstringIndex + 1 ));
+
+ if ( cf2_initLocalRegionBuffer( decoder,
+ subrNum,
+ charstring ) )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* subroutine lookup or stream error */
+ }
+ }
+
+ charstringIndex += 1; /* entry is valid now */
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_cmdRETURN:
+ FT_TRACE4(( " return (leaving level %d)\n", charstringIndex ));
+
+ if ( charstringIndex < 1 )
+ {
+ /* Note: cannot return from top charstring */
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* underflow of stack */
+ }
+
+ /* restore position in previous charstring */
+ charstring = (CF2_Buffer)
+ cf2_arrstack_getPointer(
+ &subrStack,
+ (CF2_UInt)--charstringIndex );
+ continue; /* do not clear the stack */
+
+ case cf2_cmdESC:
+ {
+ FT_Byte op2 = (FT_Byte)cf2_buf_readByte( charstring );
+
+
+ /* first switch for 2-byte operators handles CFF2 */
+ /* and opcodes that are reserved for both CFF and CFF2 */
+ switch ( op2 )
+ {
+ case cf2_escHFLEX:
+ {
+ static const FT_Bool readFromStack[12] =
+ {
+ TRUE /* dx1 */, FALSE /* dy1 */,
+ TRUE /* dx2 */, TRUE /* dy2 */,
+ TRUE /* dx3 */, FALSE /* dy3 */,
+ TRUE /* dx4 */, FALSE /* dy4 */,
+ TRUE /* dx5 */, FALSE /* dy5 */,
+ TRUE /* dx6 */, FALSE /* dy6 */
+ };
+
+
+ FT_TRACE4(( " hflex\n" ));
+
+ cf2_doFlex( opStack,
+ &curX,
+ &curY,
+ &glyphPath,
+ readFromStack,
+ FALSE /* doConditionalLastRead */ );
+ }
+ continue;
+
+ case cf2_escFLEX:
+ {
+ static const FT_Bool readFromStack[12] =
+ {
+ TRUE /* dx1 */, TRUE /* dy1 */,
+ TRUE /* dx2 */, TRUE /* dy2 */,
+ TRUE /* dx3 */, TRUE /* dy3 */,
+ TRUE /* dx4 */, TRUE /* dy4 */,
+ TRUE /* dx5 */, TRUE /* dy5 */,
+ TRUE /* dx6 */, TRUE /* dy6 */
+ };
+
+
+ FT_TRACE4(( " flex\n" ));
+
+ cf2_doFlex( opStack,
+ &curX,
+ &curY,
+ &glyphPath,
+ readFromStack,
+ FALSE /* doConditionalLastRead */ );
+ }
+ break; /* TODO: why is this not a continue? */
+
+ case cf2_escHFLEX1:
+ {
+ static const FT_Bool readFromStack[12] =
+ {
+ TRUE /* dx1 */, TRUE /* dy1 */,
+ TRUE /* dx2 */, TRUE /* dy2 */,
+ TRUE /* dx3 */, FALSE /* dy3 */,
+ TRUE /* dx4 */, FALSE /* dy4 */,
+ TRUE /* dx5 */, TRUE /* dy5 */,
+ TRUE /* dx6 */, FALSE /* dy6 */
+ };
+
+
+ FT_TRACE4(( " hflex1\n" ));
+
+ cf2_doFlex( opStack,
+ &curX,
+ &curY,
+ &glyphPath,
+ readFromStack,
+ FALSE /* doConditionalLastRead */ );
+ }
+ continue;
+
+ case cf2_escFLEX1:
+ {
+ static const FT_Bool readFromStack[12] =
+ {
+ TRUE /* dx1 */, TRUE /* dy1 */,
+ TRUE /* dx2 */, TRUE /* dy2 */,
+ TRUE /* dx3 */, TRUE /* dy3 */,
+ TRUE /* dx4 */, TRUE /* dy4 */,
+ TRUE /* dx5 */, TRUE /* dy5 */,
+ FALSE /* dx6 */, FALSE /* dy6 */
+ };
+
+
+ FT_TRACE4(( " flex1\n" ));
+
+ cf2_doFlex( opStack,
+ &curX,
+ &curY,
+ &glyphPath,
+ readFromStack,
+ TRUE /* doConditionalLastRead */ );
+ }
+ continue;
+
+ /* these opcodes are always reserved */
+ case cf2_escRESERVED_8:
+ case cf2_escRESERVED_13:
+ case cf2_escRESERVED_19:
+ case cf2_escRESERVED_25:
+ case cf2_escRESERVED_31:
+ case cf2_escRESERVED_32:
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ break;
+
+ default:
+ {
+ if ( font->isCFF2 || op2 >= cf2_escRESERVED_38 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else if ( font->isT1 && result_cnt > 0 && op2 != cf2_escPOP )
+ {
+ /* all operands have been transferred by previous pops */
+ result_cnt = 0;
+ }
+ else
+ {
+ /* second switch for 2-byte operators handles */
+ /* CFF and Type 1 */
+ switch ( op2 )
+ {
+
+ case cf2_escDOTSECTION:
+ /* something about `flip type of locking' -- ignore it */
+ FT_TRACE4(( " dotsection\n" ));
+
+ break;
+
+ case cf2_escVSTEM3:
+ case cf2_escHSTEM3:
+ /*
+ * Type 1: Type 2:
+ * x0 dx0 x1 dx1 x2 dx2 vstem3 x dx {dxa dxb}* vstem
+ * y0 dy0 y1 dy1 y2 dy2 hstem3 y dy {dya dyb}* hstem
+ * relative to lsb point relative to zero
+ *
+ */
+ {
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ CF2_F16Dot16 v0, v1, v2;
+
+ FT_Bool isV = FT_BOOL( op2 == cf2_escVSTEM3 );
+
+
+ FT_TRACE4(( isV ? " vstem3\n"
+ : " hstem3\n" ));
+
+ FT_ASSERT( cf2_stack_count( opStack ) == 6 );
+
+ v0 = cf2_stack_getReal( opStack, 0 );
+ v1 = cf2_stack_getReal( opStack, 2 );
+ v2 = cf2_stack_getReal( opStack, 4 );
+
+ cf2_stack_setReal(
+ opStack, 2,
+ SUB_INT32( SUB_INT32( v1, v0 ),
+ cf2_stack_getReal( opStack, 1 ) ) );
+ cf2_stack_setReal(
+ opStack, 4,
+ SUB_INT32( SUB_INT32( v2, v1 ),
+ cf2_stack_getReal( opStack, 3 ) ) );
+
+ /* add left-sidebearing correction */
+ cf2_doStems( font,
+ opStack,
+ isV ? &vStemHintArray : &hStemHintArray,
+ width,
+ &haveWidth,
+ isV ? decoder->builder.left_bearing->x
+ : decoder->builder.left_bearing->y );
+
+ if ( decoder->width_only )
+ goto exit;
+ }
+ }
+ break;
+
+ case cf2_escAND:
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+
+
+ FT_TRACE4(( " and\n" ));
+
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushInt( opStack, arg1 && arg2 );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escOR:
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+
+
+ FT_TRACE4(( " or\n" ));
+
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushInt( opStack, arg1 || arg2 );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escNOT:
+ {
+ CF2_F16Dot16 arg;
+
+
+ FT_TRACE4(( " not\n" ));
+
+ arg = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushInt( opStack, !arg );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escSEAC:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ FT_Error error2;
+ CF2_Int bchar_index, achar_index;
+ FT_Vector left_bearing, advance;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ T1_Face face = (T1_Face)decoder->builder.face;
+#endif
+ CF2_BufferRec component;
+ CF2_Fixed dummyWidth;
+
+ CF2_Int achar = cf2_stack_popInt( opStack );
+ CF2_Int bchar = cf2_stack_popInt( opStack );
+
+ FT_Pos ady = cf2_stack_popFixed ( opStack );
+ FT_Pos adx = cf2_stack_popFixed ( opStack );
+ FT_Pos asb = cf2_stack_popFixed ( opStack );
+
+
+ FT_TRACE4(( " seac\n" ));
+
+ if ( doingSeac )
+ {
+ FT_ERROR(( " nested seac\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* nested seac */
+ }
+
+ if ( decoder->builder.metrics_only )
+ {
+ FT_ERROR(( " unexpected seac\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* unexpected seac */
+ }
+
+ /* `glyph_names' is set to 0 for CID fonts which do */
+ /* not include an encoding. How can we deal with */
+ /* these? */
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ if ( decoder->glyph_names == 0 &&
+ !face->root.internal->incremental_interface )
+#else
+ if ( decoder->glyph_names == 0 )
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+ {
+ FT_ERROR((
+ "cf2_interpT2CharString: (Type 1 seac)"
+ " glyph names table not available in this font\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* seac weirdness */
+ adx += decoder->builder.left_bearing->x;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ if ( face->root.internal->incremental_interface )
+ {
+ /* the caller must handle the font encoding also */
+ bchar_index = bchar;
+ achar_index = achar;
+ }
+ else
+#endif
+ {
+ bchar_index = t1_lookup_glyph_by_stdcharcode_ps(
+ decoder, bchar );
+ achar_index = t1_lookup_glyph_by_stdcharcode_ps(
+ decoder, achar );
+ }
+
+ if ( bchar_index < 0 || achar_index < 0 )
+ {
+ FT_ERROR((
+ "cf2_interpT2CharString: (Type 1 seac)"
+ " invalid seac character code arguments\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* if we are trying to load a composite glyph, */
+ /* do not load the accent character and return */
+ /* the array of subglyphs. */
+ if ( decoder->builder.no_recurse )
+ {
+ FT_GlyphSlot glyph = (FT_GlyphSlot)decoder->builder.glyph;
+ FT_GlyphLoader loader = glyph->internal->loader;
+ FT_SubGlyph subg;
+
+
+ /* reallocate subglyph array if necessary */
+ error2 = FT_GlyphLoader_CheckSubGlyphs( loader, 2 );
+ if ( error2 )
+ {
+ lastError = error2; /* pass FreeType error through */
+ goto exit;
+ }
+
+ subg = loader->current.subglyphs;
+
+ /* subglyph 0 = base character */
+ subg->index = bchar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES |
+ FT_SUBGLYPH_FLAG_USE_MY_METRICS;
+ subg->arg1 = 0;
+ subg->arg2 = 0;
+ subg++;
+
+ /* subglyph 1 = accent character */
+ subg->index = achar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES;
+ subg->arg1 = (FT_Int)FIXED_TO_INT( adx - asb );
+ subg->arg2 = (FT_Int)FIXED_TO_INT( ady );
+
+ /* set up remaining glyph fields */
+ glyph->num_subglyphs = 2;
+ glyph->subglyphs = loader->base.subglyphs;
+ glyph->format = FT_GLYPH_FORMAT_COMPOSITE;
+
+ loader->current.num_subglyphs = 2;
+
+ goto exit;
+ }
+
+ /* First load `bchar' in builder */
+ /* now load the unscaled outline */
+
+ /* prepare loader */
+ FT_GlyphLoader_Prepare( decoder->builder.loader );
+
+ error2 = cf2_getT1SeacComponent( decoder,
+ (FT_UInt)bchar_index,
+ &component );
+ if ( error2 )
+ {
+ lastError = error2; /* pass FreeType error through */
+ goto exit;
+ }
+ cf2_interpT2CharString( font,
+ &component,
+ callbacks,
+ translation,
+ TRUE,
+ 0,
+ 0,
+ &dummyWidth );
+ cf2_freeT1SeacComponent( decoder, &component );
+
+ /* save the left bearing and width of the base */
+ /* character as they will be erased by the next load */
+
+ left_bearing = *decoder->builder.left_bearing;
+ advance = *decoder->builder.advance;
+
+ decoder->builder.left_bearing->x = 0;
+ decoder->builder.left_bearing->y = 0;
+
+ /* Now load `achar' on top of */
+ /* the base outline */
+
+ error2 = cf2_getT1SeacComponent( decoder,
+ (FT_UInt)achar_index,
+ &component );
+ if ( error2 )
+ {
+ lastError = error2; /* pass FreeType error through */
+ goto exit;
+ }
+ cf2_interpT2CharString( font,
+ &component,
+ callbacks,
+ translation,
+ TRUE,
+ adx - asb,
+ ady,
+ &dummyWidth );
+ cf2_freeT1SeacComponent( decoder, &component );
+
+ /* restore the left side bearing and */
+ /* advance width of the base character */
+
+ *decoder->builder.left_bearing = left_bearing;
+ *decoder->builder.advance = advance;
+
+ goto exit;
+ }
+ break;
+
+ case cf2_escSBW:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ CF2_Fixed lsb_x, lsb_y;
+ PS_Builder* builder;
+
+
+ FT_TRACE4(( " sbw" ));
+
+ builder = &decoder->builder;
+
+ builder->advance->y = cf2_stack_popFixed( opStack );
+ builder->advance->x = cf2_stack_popFixed( opStack );
+
+ lsb_y = cf2_stack_popFixed( opStack );
+ lsb_x = cf2_stack_popFixed( opStack );
+
+ builder->left_bearing->x =
+ ADD_INT32( builder->left_bearing->x, lsb_x );
+ builder->left_bearing->y =
+ ADD_INT32( builder->left_bearing->y, lsb_y );
+
+ haveWidth = TRUE;
+
+ /* the `metrics_only' indicates that we only want */
+ /* to compute the glyph's metrics (lsb + advance */
+ /* width), not load the rest of it; so exit */
+ /* immediately */
+ if ( builder->metrics_only )
+ goto exit;
+
+ if ( initial_map_ready )
+ {
+ curX = ADD_INT32( curX, lsb_x );
+ curY = ADD_INT32( curY, lsb_y );
+ }
+ }
+ break;
+
+ case cf2_escABS:
+ {
+ CF2_F16Dot16 arg;
+
+
+ FT_TRACE4(( " abs\n" ));
+
+ arg = cf2_stack_popFixed( opStack );
+
+ if ( arg < -CF2_FIXED_MAX )
+ cf2_stack_pushFixed( opStack, CF2_FIXED_MAX );
+ else
+ cf2_stack_pushFixed( opStack, FT_ABS( arg ) );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escADD:
+ {
+ CF2_F16Dot16 summand1;
+ CF2_F16Dot16 summand2;
+
+
+ FT_TRACE4(( " add\n" ));
+
+ summand2 = cf2_stack_popFixed( opStack );
+ summand1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ ADD_INT32( summand1,
+ summand2 ) );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escSUB:
+ {
+ CF2_F16Dot16 minuend;
+ CF2_F16Dot16 subtrahend;
+
+
+ FT_TRACE4(( " sub\n" ));
+
+ subtrahend = cf2_stack_popFixed( opStack );
+ minuend = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ SUB_INT32( minuend, subtrahend ) );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escDIV:
+ {
+ CF2_F16Dot16 dividend;
+ CF2_F16Dot16 divisor;
+
+
+ FT_TRACE4(( " div\n" ));
+
+ if ( font->isT1 && large_int )
+ {
+ divisor = (CF2_F16Dot16)cf2_stack_popInt( opStack );
+ dividend = (CF2_F16Dot16)cf2_stack_popInt( opStack );
+
+ large_int = FALSE;
+ }
+ else
+ {
+ divisor = cf2_stack_popFixed( opStack );
+ dividend = cf2_stack_popFixed( opStack );
+ }
+
+ cf2_stack_pushFixed( opStack,
+ FT_DivFix( dividend, divisor ) );
+
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escNEG:
+ {
+ CF2_F16Dot16 arg;
+
+
+ FT_TRACE4(( " neg\n" ));
+
+ arg = cf2_stack_popFixed( opStack );
+
+ if ( arg < -CF2_FIXED_MAX )
+ cf2_stack_pushFixed( opStack, CF2_FIXED_MAX );
+ else
+ cf2_stack_pushFixed( opStack, -arg );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escEQ:
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+
+
+ FT_TRACE4(( " eq\n" ));
+
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushInt( opStack, arg1 == arg2 );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escCALLOTHERSUBR:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ CF2_Int subr_no;
+ CF2_Int arg_cnt;
+ CF2_UInt count;
+ CF2_UInt opIdx = 0;
+
+
+ FT_TRACE4(( " callothersubr\n" ));
+
+ subr_no = cf2_stack_popInt( opStack );
+ arg_cnt = cf2_stack_popInt( opStack );
+
+ /*******************************************************/
+ /* */
+ /* remove all operands to callothersubr from the stack */
+ /* */
+ /* for handled othersubrs, where we know the number of */
+ /* arguments, we increase the stack by the value of */
+ /* known_othersubr_result_cnt */
+ /* */
+ /* for unhandled othersubrs the following pops adjust */
+ /* the stack pointer as necessary */
+
+ count = cf2_stack_count( opStack );
+ FT_ASSERT( (CF2_UInt)arg_cnt <= count );
+
+ opIdx += count - (CF2_UInt)arg_cnt;
+
+ known_othersubr_result_cnt = 0;
+ result_cnt = 0;
+
+ /* XXX TODO: The checks to `arg_count == <whatever>' */
+ /* might not be correct; an othersubr expects a */
+ /* certain number of operands on the PostScript stack */
+ /* (as opposed to the T1 stack) but it doesn't have to */
+ /* put them there by itself; previous othersubrs might */
+ /* have left the operands there if they were not */
+ /* followed by an appropriate number of pops */
+ /* */
+ /* On the other hand, Adobe Reader 7.0.8 for Linux */
+ /* doesn't accept a font that contains charstrings */
+ /* like */
+ /* */
+ /* 100 200 2 20 callothersubr */
+ /* 300 1 20 callothersubr pop */
+ /* */
+ /* Perhaps this is the reason why BuildCharArray */
+ /* exists. */
+
+ switch ( subr_no )
+ {
+ case 0: /* end flex feature */
+ if ( arg_cnt != 3 )
+ goto Unexpected_OtherSubr;
+
+ if ( initial_map_ready &&
+ ( !decoder->flex_state ||
+ decoder->num_flex_vectors != 7 ) )
+ {
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " unexpected flex end\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* the two `results' are popped */
+ /* by the following setcurrentpoint */
+ cf2_stack_pushFixed( opStack, curX );
+ cf2_stack_pushFixed( opStack, curY );
+ known_othersubr_result_cnt = 2;
+ break;
+
+ case 1: /* start flex feature */
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ if ( !initial_map_ready )
+ break;
+
+ if ( ps_builder_check_points( &decoder->builder, 6 ) )
+ goto exit;
+
+ decoder->flex_state = 1;
+ decoder->num_flex_vectors = 0;
+ break;
+
+ case 2: /* add flex vectors */
+ {
+ FT_Int idx;
+ FT_Int idx2;
+
+
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ if ( !initial_map_ready )
+ break;
+
+ if ( !decoder->flex_state )
+ {
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " missing flex start\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* note that we should not add a point for */
+ /* index 0; this will move our current position */
+ /* to the flex point without adding any point */
+ /* to the outline */
+ idx = decoder->num_flex_vectors++;
+ if ( idx > 0 && idx < 7 )
+ {
+ /* in malformed fonts it is possible to have */
+ /* other opcodes in the middle of a flex (which */
+ /* don't increase `num_flex_vectors'); we thus */
+ /* have to check whether we can add a point */
+
+ if ( ps_builder_check_points( &decoder->builder,
+ 1 ) )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* map: 1->2 2->4 3->6 4->2 5->4 6->6 */
+ idx2 = ( idx > 3 ? idx - 3 : idx ) * 2;
+
+ flexStore[idx2 - 2] = curX;
+ flexStore[idx2 - 1] = curY;
+
+ if ( idx == 3 || idx == 6 )
+ cf2_glyphpath_curveTo( &glyphPath,
+ flexStore[0],
+ flexStore[1],
+ flexStore[2],
+ flexStore[3],
+ flexStore[4],
+ flexStore[5] );
+ }
+ }
+ break;
+
+ case 3: /* change hints */
+ if ( arg_cnt != 1 )
+ goto Unexpected_OtherSubr;
+
+ if ( initial_map_ready )
+ {
+ /* do not clear hints if initial hintmap */
+ /* is not ready - we need to collate all */
+ cf2_arrstack_clear( &vStemHintArray );
+ cf2_arrstack_clear( &hStemHintArray );
+
+ cf2_hintmask_init( &hintMask, error );
+ hintMask.isValid = FALSE;
+ hintMask.isNew = TRUE;
+ }
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+ case 12:
+ case 13:
+ /* counter control hints, clear stack */
+ cf2_stack_clear( opStack );
+ break;
+
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18: /* multiple masters */
+ {
+ PS_Blend blend = decoder->blend;
+ FT_UInt num_points, nn, mm;
+ CF2_UInt delta;
+ CF2_UInt values;
+
+
+ if ( !blend )
+ {
+ FT_ERROR((
+ "cf2_interpT2CharString:"
+ " unexpected multiple masters operator\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ num_points = (FT_UInt)subr_no - 13 +
+ ( subr_no == 18 );
+ if ( arg_cnt != (FT_Int)( num_points *
+ blend->num_designs ) )
+ {
+ FT_ERROR((
+ "cf2_interpT2CharString:"
+ " incorrect number of multiple masters arguments\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* We want to compute */
+ /* */
+ /* a0*w0 + a1*w1 + ... + ak*wk */
+ /* */
+ /* but we only have a0, a1-a0, a2-a0, ..., ak-a0. */
+ /* */
+ /* However, given that w0 + w1 + ... + wk == 1, we */
+ /* can rewrite it easily as */
+ /* */
+ /* a0 + (a1-a0)*w1 + (a2-a0)*w2 + ... + (ak-a0)*wk */
+ /* */
+ /* where k == num_designs-1. */
+ /* */
+ /* I guess that's why it's written in this `compact' */
+ /* form. */
+ /* */
+ delta = opIdx + num_points;
+ values = opIdx;
+ for ( nn = 0; nn < num_points; nn++ )
+ {
+ CF2_Fixed tmp = cf2_stack_getReal( opStack,
+ values );
+
+
+ for ( mm = 1; mm < blend->num_designs; mm++ )
+ tmp = ADD_INT32( tmp,
+ FT_MulFix(
+ cf2_stack_getReal( opStack,
+ delta++ ),
+ blend->weight_vector[mm] ) );
+
+ cf2_stack_setReal( opStack, values++, tmp );
+ }
+ cf2_stack_pop( opStack,
+ (CF2_UInt)arg_cnt - num_points );
+
+ known_othersubr_result_cnt = (FT_Int)num_points;
+ break;
+ }
+
+ case 19:
+ /* <idx> 1 19 callothersubr */
+ /* ==> replace elements starting from index */
+ /* cvi( <idx> ) of BuildCharArray with */
+ /* WeightVector */
+ {
+ FT_Int idx;
+ PS_Blend blend = decoder->blend;
+
+
+ if ( arg_cnt != 1 || !blend )
+ goto Unexpected_OtherSubr;
+
+ idx = cf2_stack_popInt( opStack );
+
+ if ( idx < 0 ||
+ (FT_UInt)idx + blend->num_designs >
+ decoder->len_buildchar )
+ goto Unexpected_OtherSubr;
+
+ ft_memcpy( &decoder->buildchar[idx],
+ blend->weight_vector,
+ blend->num_designs *
+ sizeof ( blend->weight_vector[0] ) );
+ }
+ break;
+
+ case 20:
+ /* <arg1> <arg2> 2 20 callothersubr pop */
+ /* ==> push <arg1> + <arg2> onto T1 stack */
+ {
+ CF2_F16Dot16 summand1;
+ CF2_F16Dot16 summand2;
+
+
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ summand2 = cf2_stack_popFixed( opStack );
+ summand1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ ADD_INT32( summand1,
+ summand2 ) );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ case 21:
+ /* <arg1> <arg2> 2 21 callothersubr pop */
+ /* ==> push <arg1> - <arg2> onto T1 stack */
+ {
+ CF2_F16Dot16 minuend;
+ CF2_F16Dot16 subtrahend;
+
+
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ subtrahend = cf2_stack_popFixed( opStack );
+ minuend = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ SUB_INT32( minuend,
+ subtrahend ) );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ case 22:
+ /* <arg1> <arg2> 2 22 callothersubr pop */
+ /* ==> push <arg1> * <arg2> onto T1 stack */
+ {
+ CF2_F16Dot16 factor1;
+ CF2_F16Dot16 factor2;
+
+
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ factor2 = cf2_stack_popFixed( opStack );
+ factor1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ FT_MulFix( factor1, factor2 ) );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ case 23:
+ /* <arg1> <arg2> 2 23 callothersubr pop */
+ /* ==> push <arg1> / <arg2> onto T1 stack */
+ {
+ CF2_F16Dot16 dividend;
+ CF2_F16Dot16 divisor;
+
+
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ divisor = cf2_stack_popFixed( opStack );
+ dividend = cf2_stack_popFixed( opStack );
+
+ if ( divisor == 0 )
+ goto Unexpected_OtherSubr;
+
+ cf2_stack_pushFixed( opStack,
+ FT_DivFix( dividend,
+ divisor ) );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ case 24:
+ /* <val> <idx> 2 24 callothersubr */
+ /* ==> set BuildCharArray[cvi( <idx> )] = <val> */
+ {
+ CF2_Int idx;
+ PS_Blend blend = decoder->blend;
+
+
+ if ( arg_cnt != 2 || !blend )
+ goto Unexpected_OtherSubr;
+
+ idx = cf2_stack_popInt( opStack );
+
+ if ( idx < 0 ||
+ (FT_UInt)idx >= decoder->len_buildchar )
+ goto Unexpected_OtherSubr;
+
+ decoder->buildchar[idx] =
+ cf2_stack_popFixed( opStack );
+ }
+ break;
+
+ case 25:
+ /* <idx> 1 25 callothersubr pop */
+ /* ==> push BuildCharArray[cvi( idx )] */
+ /* onto T1 stack */
+ {
+ CF2_Int idx;
+ PS_Blend blend = decoder->blend;
+
+
+ if ( arg_cnt != 1 || !blend )
+ goto Unexpected_OtherSubr;
+
+ idx = cf2_stack_popInt( opStack );
+
+ if ( idx < 0 ||
+ (FT_UInt)idx >= decoder->len_buildchar )
+ goto Unexpected_OtherSubr;
+
+ cf2_stack_pushFixed( opStack,
+ decoder->buildchar[idx] );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+#if 0
+ case 26:
+ /* <val> mark <idx> */
+ /* ==> set BuildCharArray[cvi( <idx> )] = <val>, */
+ /* leave mark on T1 stack */
+ /* <val> <idx> */
+ /* ==> set BuildCharArray[cvi( <idx> )] = <val> */
+ XXX which routine has left its mark on the
+ XXX (PostScript) stack?;
+ break;
+#endif
+
+ case 27:
+ /* <res1> <res2> <val1> <val2> 4 27 callothersubr pop */
+ /* ==> push <res1> onto T1 stack if <val1> <= <val2>, */
+ /* otherwise push <res2> */
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+ CF2_F16Dot16 cond1;
+ CF2_F16Dot16 cond2;
+
+
+ if ( arg_cnt != 4 )
+ goto Unexpected_OtherSubr;
+
+ cond2 = cf2_stack_popFixed( opStack );
+ cond1 = cf2_stack_popFixed( opStack );
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ cond1 <= cond2 ? arg1 : arg2 );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ case 28:
+ /* 0 28 callothersubr pop */
+ /* ==> push random value from interval [0, 1) */
+ /* onto stack */
+ {
+ CF2_F16Dot16 r;
+
+
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ /* only use the lower 16 bits of `random' */
+ /* to generate a number in the range (0;1] */
+ r = (CF2_F16Dot16)
+ ( ( decoder->current_subfont->random & 0xFFFF ) + 1 );
+
+ decoder->current_subfont->random =
+ cff_random( decoder->current_subfont->random );
+
+ cf2_stack_pushFixed( opStack, r );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ default:
+ if ( arg_cnt >= 0 && subr_no >= 0 )
+ {
+ FT_Int i;
+
+
+ FT_ERROR((
+ "cf2_interpT2CharString (Type 1 mode):"
+ " unknown othersubr [%d %d], wish me luck\n",
+ arg_cnt, subr_no ));
+
+ /* store the unused args */
+ /* for this unhandled OtherSubr */
+
+ if ( arg_cnt > PS_STORAGE_SIZE )
+ arg_cnt = PS_STORAGE_SIZE;
+ result_cnt = arg_cnt;
+
+ for ( i = 1; i <= arg_cnt; i++ )
+ results[result_cnt - i] =
+ cf2_stack_popFixed( opStack );
+
+ break;
+ }
+ /* fall through */
+
+ Unexpected_OtherSubr:
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " invalid othersubr [%d %d]\n",
+ arg_cnt, subr_no ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escPOP:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ FT_TRACE4(( " pop" ));
+
+ if ( known_othersubr_result_cnt > 0 )
+ {
+ known_othersubr_result_cnt--;
+ /* ignore, we pushed the operands ourselves */
+ continue;
+ }
+
+ if ( result_cnt == 0 )
+ {
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " no more operands for othersubr\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ result_cnt--;
+ cf2_stack_pushFixed( opStack, results[result_cnt] );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escDROP:
+ FT_TRACE4(( " drop\n" ));
+
+ (void)cf2_stack_popFixed( opStack );
+ continue; /* do not clear the stack */
+
+ case cf2_escPUT:
+ {
+ CF2_F16Dot16 val;
+ CF2_Int idx;
+
+
+ FT_TRACE4(( " put\n" ));
+
+ idx = cf2_stack_popInt( opStack );
+ val = cf2_stack_popFixed( opStack );
+
+ if ( idx >= 0 && idx < CF2_STORAGE_SIZE )
+ storage[idx] = val;
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escGET:
+ {
+ CF2_Int idx;
+
+
+ FT_TRACE4(( " get\n" ));
+
+ idx = cf2_stack_popInt( opStack );
+
+ if ( idx >= 0 && idx < CF2_STORAGE_SIZE )
+ cf2_stack_pushFixed( opStack, storage[idx] );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escIFELSE:
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+ CF2_F16Dot16 cond1;
+ CF2_F16Dot16 cond2;
+
+
+ FT_TRACE4(( " ifelse\n" ));
+
+ cond2 = cf2_stack_popFixed( opStack );
+ cond1 = cf2_stack_popFixed( opStack );
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ cond1 <= cond2 ? arg1 : arg2 );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escRANDOM: /* in spec */
+ {
+ CF2_F16Dot16 r;
+
+
+ FT_TRACE4(( " random\n" ));
+
+ /* only use the lower 16 bits of `random' */
+ /* to generate a number in the range (0;1] */
+ r = (CF2_F16Dot16)
+ ( ( decoder->current_subfont->random & 0xFFFF ) + 1 );
+
+ decoder->current_subfont->random =
+ cff_random( decoder->current_subfont->random );
+
+ cf2_stack_pushFixed( opStack, r );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escMUL:
+ {
+ CF2_F16Dot16 factor1;
+ CF2_F16Dot16 factor2;
+
+
+ FT_TRACE4(( " mul\n" ));
+
+ factor2 = cf2_stack_popFixed( opStack );
+ factor1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ FT_MulFix( factor1, factor2 ) );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escSQRT:
+ {
+ CF2_F16Dot16 arg;
+
+
+ FT_TRACE4(( " sqrt\n" ));
+
+ arg = cf2_stack_popFixed( opStack );
+ if ( arg > 0 )
+ {
+ /* use a start value that doesn't make */
+ /* the algorithm's addition overflow */
+ FT_Fixed root = arg < 10 ? arg : arg >> 1;
+ FT_Fixed new_root;
+
+
+ /* Babylonian method */
+ for (;;)
+ {
+ new_root = ( root + FT_DivFix( arg, root ) + 1 ) >> 1;
+ if ( new_root == root )
+ break;
+ root = new_root;
+ }
+ arg = new_root;
+ }
+ else
+ arg = 0;
+
+ cf2_stack_pushFixed( opStack, arg );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escDUP:
+ {
+ CF2_F16Dot16 arg;
+
+
+ FT_TRACE4(( " dup\n" ));
+
+ arg = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack, arg );
+ cf2_stack_pushFixed( opStack, arg );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escEXCH:
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+
+
+ FT_TRACE4(( " exch\n" ));
+
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack, arg2 );
+ cf2_stack_pushFixed( opStack, arg1 );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escINDEX:
+ {
+ CF2_Int idx;
+ CF2_UInt size;
+
+
+ FT_TRACE4(( " index\n" ));
+
+ idx = cf2_stack_popInt( opStack );
+ size = cf2_stack_count( opStack );
+
+ if ( size > 0 )
+ {
+ /* for `cf2_stack_getReal', */
+ /* index 0 is bottom of stack */
+ CF2_UInt gr_idx;
+
+
+ if ( idx < 0 )
+ gr_idx = size - 1;
+ else if ( (CF2_UInt)idx >= size )
+ gr_idx = 0;
+ else
+ gr_idx = size - 1 - (CF2_UInt)idx;
+
+ cf2_stack_pushFixed( opStack,
+ cf2_stack_getReal( opStack,
+ gr_idx ) );
+ }
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escROLL:
+ {
+ CF2_Int idx;
+ CF2_Int count;
+
+
+ FT_TRACE4(( " roll\n" ));
+
+ idx = cf2_stack_popInt( opStack );
+ count = cf2_stack_popInt( opStack );
+
+ cf2_stack_roll( opStack, count, idx );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escSETCURRENTPT:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ FT_TRACE4(( " setcurrentpoint" ));
+
+ if ( !initial_map_ready )
+ break;
+
+ /* From the T1 specification, section 6.4: */
+ /* */
+ /* The setcurrentpoint command is used only in */
+ /* conjunction with results from OtherSubrs */
+ /* procedures. */
+
+ /* known_othersubr_result_cnt != 0 is already handled */
+ /* above. */
+
+ /* Note, however, that both Ghostscript and Adobe */
+ /* Distiller handle this situation by silently */
+ /* ignoring the inappropriate `setcurrentpoint' */
+ /* instruction. So we do the same. */
+#if 0
+
+ if ( decoder->flex_state != 1 )
+ {
+ FT_ERROR(( "cf2_interpT2CharString:"
+ " unexpected `setcurrentpoint'\n" ));
+ goto Syntax_Error;
+ }
+ else
+ ...
+#endif
+
+ curY = cf2_stack_popFixed( opStack );
+ curX = cf2_stack_popFixed( opStack );
+
+ decoder->flex_state = 0;
+ }
+ break;
+
+ } /* end of 2nd switch checking op2 */
+ }
+ }
+ } /* end of 1st switch checking op2 */
+ } /* case cf2_cmdESC */
+
+ break;
+
+ case cf2_cmdHSBW:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (%d)\n", op1 ));
+ else
+ {
+ CF2_Fixed lsb_x;
+ PS_Builder* builder;
+
+
+ FT_TRACE4(( " hsbw" ));
+
+ builder = &decoder->builder;
+
+ builder->advance->x = cf2_stack_popFixed( opStack );
+ builder->advance->y = 0;
+
+ lsb_x = cf2_stack_popFixed( opStack );
+
+ builder->left_bearing->x = ADD_INT32( builder->left_bearing->x,
+ lsb_x );
+
+ haveWidth = TRUE;
+
+ /* the `metrics_only' indicates that we only want to compute */
+ /* the glyph's metrics (lsb + advance width), not load the */
+ /* rest of it; so exit immediately */
+ if ( builder->metrics_only )
+ goto exit;
+
+ if ( initial_map_ready )
+ curX = ADD_INT32( curX, lsb_x );
+ }
+ break;
+
+ case cf2_cmdENDCHAR:
+ FT_TRACE4(( " endchar\n" ));
+
+ if ( font->isT1 && !initial_map_ready )
+ {
+ FT_TRACE5(( "cf2_interpT2CharString (Type 1 mode): "
+ "Build initial hintmap, rewinding...\n" ));
+
+ /* trigger initial hintmap build */
+ cf2_glyphpath_moveTo( &glyphPath, curX, curY );
+
+ initial_map_ready = TRUE;
+
+ /* change hints routine - clear for rewind */
+ cf2_arrstack_clear( &vStemHintArray );
+ cf2_arrstack_clear( &hStemHintArray );
+
+ cf2_hintmask_init( &hintMask, error );
+ hintMask.isValid = FALSE;
+ hintMask.isNew = TRUE;
+
+ /* rewind charstring */
+ /* some charstrings use endchar from a final subroutine call */
+ /* without returning, detect these and exit to the top level */
+ /* charstring */
+ while ( charstringIndex > 0 )
+ {
+ FT_TRACE4(( " return (leaving level %d)\n", charstringIndex ));
+
+ /* restore position in previous charstring */
+ charstring = (CF2_Buffer)
+ cf2_arrstack_getPointer(
+ &subrStack,
+ (CF2_UInt)--charstringIndex );
+ }
+ charstring->ptr = charstring->start;
+
+ break;
+ }
+
+ if ( cf2_stack_count( opStack ) == 1 ||
+ cf2_stack_count( opStack ) == 5 )
+ {
+ if ( !haveWidth )
+ *width = ADD_INT32( cf2_stack_getReal( opStack, 0 ),
+ nominalWidthX );
+ }
+
+ /* width is defined or default after this */
+ haveWidth = TRUE;
+
+ if ( decoder->width_only )
+ goto exit;
+
+ /* close path if still open */
+ cf2_glyphpath_closeOpenPath( &glyphPath );
+
+ /* disable seac for CFF2 and Type1 */
+ /* (charstring ending with args on stack) */
+ if ( !font->isCFF2 && !font->isT1 && cf2_stack_count( opStack ) > 1 )
+ {
+ /* must be either 4 or 5 -- */
+ /* this is a (deprecated) implied `seac' operator */
+
+ CF2_Int achar;
+ CF2_Int bchar;
+ CF2_BufferRec component;
+ CF2_Fixed dummyWidth; /* ignore component width */
+ FT_Error error2;
+
+
+ if ( doingSeac )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* nested seac */
+ }
+
+ achar = cf2_stack_popInt( opStack );
+ bchar = cf2_stack_popInt( opStack );
+
+ curY = cf2_stack_popFixed( opStack );
+ curX = cf2_stack_popFixed( opStack );
+
+ error2 = cf2_getSeacComponent( decoder, achar, &component );
+ if ( error2 )
+ {
+ lastError = error2; /* pass FreeType error through */
+ goto exit;
+ }
+ cf2_interpT2CharString( font,
+ &component,
+ callbacks,
+ translation,
+ TRUE,
+ curX,
+ curY,
+ &dummyWidth );
+ cf2_freeSeacComponent( decoder, &component );
+
+ error2 = cf2_getSeacComponent( decoder, bchar, &component );
+ if ( error2 )
+ {
+ lastError = error2; /* pass FreeType error through */
+ goto exit;
+ }
+ cf2_interpT2CharString( font,
+ &component,
+ callbacks,
+ translation,
+ TRUE,
+ 0,
+ 0,
+ &dummyWidth );
+ cf2_freeSeacComponent( decoder, &component );
+ }
+ goto exit;
+
+ case cf2_cmdCNTRMASK:
+ case cf2_cmdHINTMASK:
+ /* the final \n in the tracing message gets added in */
+ /* `cf2_hintmask_read' (which also traces the mask bytes) */
+ FT_TRACE4(( op1 == cf2_cmdCNTRMASK ? " cntrmask" : " hintmask" ));
+
+ /* never add hints after the mask is computed */
+ if ( cf2_stack_count( opStack ) > 1 &&
+ cf2_hintmask_isValid( &hintMask ) )
+ {
+ FT_TRACE4(( "cf2_interpT2CharString: invalid hint mask\n" ));
+ break;
+ }
+
+ /* if there are arguments on the stack, there this is an */
+ /* implied cf2_cmdVSTEMHM */
+ cf2_doStems( font,
+ opStack,
+ &vStemHintArray,
+ width,
+ &haveWidth,
+ 0 );
+
+ if ( decoder->width_only )
+ goto exit;
+
+ if ( op1 == cf2_cmdHINTMASK )
+ {
+ /* consume the hint mask bytes which follow the operator */
+ cf2_hintmask_read( &hintMask,
+ charstring,
+ cf2_arrstack_size( &hStemHintArray ) +
+ cf2_arrstack_size( &vStemHintArray ) );
+ }
+ else
+ {
+ /*
+ * Consume the counter mask bytes which follow the operator:
+ * Build a temporary hint map, just to place and lock those
+ * stems participating in the counter mask. These are most
+ * likely the dominant hstems, and are grouped together in a
+ * few counter groups, not necessarily in correspondence
+ * with the hint groups. This reduces the chances of
+ * conflicts between hstems that are initially placed in
+ * separate hint groups and then brought together. The
+ * positions are copied back to `hStemHintArray', so we can
+ * discard `counterMask' and `counterHintMap'.
+ *
+ */
+ CF2_HintMapRec counterHintMap;
+ CF2_HintMaskRec counterMask;
+
+
+ cf2_hintmap_init( &counterHintMap,
+ font,
+ &glyphPath.initialHintMap,
+ &glyphPath.hintMoves,
+ scaleY );
+ cf2_hintmask_init( &counterMask, error );
+
+ cf2_hintmask_read( &counterMask,
+ charstring,
+ cf2_arrstack_size( &hStemHintArray ) +
+ cf2_arrstack_size( &vStemHintArray ) );
+ cf2_hintmap_build( &counterHintMap,
+ &hStemHintArray,
+ &vStemHintArray,
+ &counterMask,
+ 0,
+ FALSE );
+ }
+ break;
+
+ case cf2_cmdRMOVETO:
+ FT_TRACE4(( " rmoveto\n" ));
+
+ if ( font->isT1 && !decoder->flex_state && !haveWidth )
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " No width. Use hsbw/sbw as first op\n" ));
+
+ if ( cf2_stack_count( opStack ) > 2 && !haveWidth )
+ *width = ADD_INT32( cf2_stack_getReal( opStack, 0 ),
+ nominalWidthX );
+
+ /* width is defined or default after this */
+ haveWidth = TRUE;
+
+ if ( decoder->width_only )
+ goto exit;
+
+ curY = ADD_INT32( curY, cf2_stack_popFixed( opStack ) );
+ curX = ADD_INT32( curX, cf2_stack_popFixed( opStack ) );
+
+ if ( !decoder->flex_state )
+ cf2_glyphpath_moveTo( &glyphPath, curX, curY );
+
+ break;
+
+ case cf2_cmdHMOVETO:
+ FT_TRACE4(( " hmoveto\n" ));
+
+ if ( font->isT1 && !decoder->flex_state && !haveWidth )
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " No width. Use hsbw/sbw as first op\n" ));
+
+ if ( cf2_stack_count( opStack ) > 1 && !haveWidth )
+ *width = ADD_INT32( cf2_stack_getReal( opStack, 0 ),
+ nominalWidthX );
+
+ /* width is defined or default after this */
+ haveWidth = TRUE;
+
+ if ( decoder->width_only )
+ goto exit;
+
+ curX = ADD_INT32( curX, cf2_stack_popFixed( opStack ) );
+
+ if ( !decoder->flex_state )
+ cf2_glyphpath_moveTo( &glyphPath, curX, curY );
+
+ break;
+
+ case cf2_cmdRLINECURVE:
+ {
+ CF2_UInt count = cf2_stack_count( opStack );
+ CF2_UInt idx = 0;
+
+
+ FT_TRACE4(( " rlinecurve\n" ));
+
+ while ( idx + 6 < count )
+ {
+ curX = ADD_INT32( curX, cf2_stack_getReal( opStack,
+ idx + 0 ) );
+ curY = ADD_INT32( curY, cf2_stack_getReal( opStack,
+ idx + 1 ) );
+
+ cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+ idx += 2;
+ }
+
+ while ( idx < count )
+ {
+ CF2_Fixed x1, y1, x2, y2, x3, y3;
+
+
+ x1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curX );
+ y1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), curY );
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), y1 );
+ x3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 4 ), x2 );
+ y3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 5 ), y2 );
+
+ cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+ curX = x3;
+ curY = y3;
+ idx += 6;
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdVVCURVETO:
+ {
+ CF2_UInt count, count1 = cf2_stack_count( opStack );
+ CF2_UInt idx = 0;
+
+
+ /* if `cf2_stack_count' isn't of the form 4n or 4n+1, */
+ /* we enforce it by clearing the second bit */
+ /* (and sorting the stack indexing to suit) */
+ count = count1 & ~2U;
+ idx += count1 - count;
+
+ FT_TRACE4(( " vvcurveto\n" ));
+
+ while ( idx < count )
+ {
+ CF2_Fixed x1, y1, x2, y2, x3, y3;
+
+
+ if ( ( count - idx ) & 1 )
+ {
+ x1 = ADD_INT32( cf2_stack_getReal( opStack, idx ), curX );
+
+ idx++;
+ }
+ else
+ x1 = curX;
+
+ y1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curY );
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), y1 );
+ x3 = x2;
+ y3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), y2 );
+
+ cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+ curX = x3;
+ curY = y3;
+ idx += 4;
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdHHCURVETO:
+ {
+ CF2_UInt count, count1 = cf2_stack_count( opStack );
+ CF2_UInt idx = 0;
+
+
+ /* if `cf2_stack_count' isn't of the form 4n or 4n+1, */
+ /* we enforce it by clearing the second bit */
+ /* (and sorting the stack indexing to suit) */
+ count = count1 & ~2U;
+ idx += count1 - count;
+
+ FT_TRACE4(( " hhcurveto\n" ));
+
+ while ( idx < count )
+ {
+ CF2_Fixed x1, y1, x2, y2, x3, y3;
+
+
+ if ( ( count - idx ) & 1 )
+ {
+ y1 = ADD_INT32( cf2_stack_getReal( opStack, idx ), curY );
+
+ idx++;
+ }
+ else
+ y1 = curY;
+
+ x1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curX );
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), y1 );
+ x3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), x2 );
+ y3 = y2;
+
+ cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+ curX = x3;
+ curY = y3;
+ idx += 4;
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdVHCURVETO:
+ case cf2_cmdHVCURVETO:
+ {
+ CF2_UInt count, count1 = cf2_stack_count( opStack );
+ CF2_UInt idx = 0;
+
+ FT_Bool alternate = FT_BOOL( op1 == cf2_cmdHVCURVETO );
+
+
+ /* if `cf2_stack_count' isn't of the form 8n, 8n+1, */
+ /* 8n+4, or 8n+5, we enforce it by clearing the */
+ /* second bit */
+ /* (and sorting the stack indexing to suit) */
+ count = count1 & ~2U;
+ idx += count1 - count;
+
+ FT_TRACE4(( alternate ? " hvcurveto\n" : " vhcurveto\n" ));
+
+ while ( idx < count )
+ {
+ CF2_Fixed x1, x2, x3, y1, y2, y3;
+
+
+ if ( alternate )
+ {
+ x1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curX );
+ y1 = curY;
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), y1 );
+ y3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), y2 );
+
+ if ( count - idx == 5 )
+ {
+ x3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 4 ), x2 );
+
+ idx++;
+ }
+ else
+ x3 = x2;
+
+ alternate = FALSE;
+ }
+ else
+ {
+ x1 = curX;
+ y1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curY );
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), y1 );
+ x3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), x2 );
+
+ if ( count - idx == 5 )
+ {
+ y3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 4 ), y2 );
+
+ idx++;
+ }
+ else
+ y3 = y2;
+
+ alternate = TRUE;
+ }
+
+ cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+ curX = x3;
+ curY = y3;
+ idx += 4;
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdEXTENDEDNMBR:
+ {
+ CF2_Int v;
+
+ CF2_Int byte1 = cf2_buf_readByte( charstring );
+ CF2_Int byte2 = cf2_buf_readByte( charstring );
+
+
+ v = (FT_Short)( ( byte1 << 8 ) |
+ byte2 );
+
+ FT_TRACE4(( " %d", v ));
+
+ cf2_stack_pushInt( opStack, v );
+ }
+ continue;
+
+ default:
+ /* numbers */
+ {
+ if ( /* op1 >= 32 && */ op1 <= 246 )
+ {
+ CF2_Int v;
+
+
+ v = op1 - 139;
+
+ FT_TRACE4(( " %d", v ));
+
+ /* -107 .. 107 */
+ cf2_stack_pushInt( opStack, v );
+ }
+
+ else if ( /* op1 >= 247 && */ op1 <= 250 )
+ {
+ CF2_Int v;
+
+
+ v = op1;
+ v -= 247;
+ v *= 256;
+ v += cf2_buf_readByte( charstring );
+ v += 108;
+
+ FT_TRACE4(( " %d", v ));
+
+ /* 108 .. 1131 */
+ cf2_stack_pushInt( opStack, v );
+ }
+
+ else if ( /* op1 >= 251 && */ op1 <= 254 )
+ {
+ CF2_Int v;
+
+
+ v = op1;
+ v -= 251;
+ v *= 256;
+ v += cf2_buf_readByte( charstring );
+ v = -v - 108;
+
+ FT_TRACE4(( " %d", v ));
+
+ /* -1131 .. -108 */
+ cf2_stack_pushInt( opStack, v );
+ }
+
+ else /* op1 == 255 */
+ {
+ CF2_Fixed v;
+
+ FT_UInt32 byte1 = (FT_UInt32)cf2_buf_readByte( charstring );
+ FT_UInt32 byte2 = (FT_UInt32)cf2_buf_readByte( charstring );
+ FT_UInt32 byte3 = (FT_UInt32)cf2_buf_readByte( charstring );
+ FT_UInt32 byte4 = (FT_UInt32)cf2_buf_readByte( charstring );
+
+
+ v = (CF2_Fixed)( ( byte1 << 24 ) |
+ ( byte2 << 16 ) |
+ ( byte3 << 8 ) |
+ byte4 );
+
+ /*
+ * For Type 1:
+ *
+ * According to the specification, values > 32000 or < -32000
+ * must be followed by a `div' operator to make the result be
+ * in the range [-32000;32000]. We expect that the second
+ * argument of `div' is not a large number. Additionally, we
+ * don't handle stuff like `<large1> <large2> <num> div <num>
+ * div' or <large1> <large2> <num> div div'. This is probably
+ * not allowed anyway.
+ *
+ * <large> <num> <num>+ div is not checked but should not be
+ * allowed as the large value remains untouched.
+ *
+ */
+ if ( font->isT1 )
+ {
+ if ( v > 32000 || v < -32000 )
+ {
+ if ( large_int )
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " no `div' after large integer\n" ));
+ else
+ large_int = TRUE;
+ }
+
+ FT_TRACE4(( " %d", v ));
+
+ cf2_stack_pushInt( opStack, (CF2_Int)v );
+ }
+ else
+ {
+ FT_TRACE4(( " %.5fF", v / 65536.0 ));
+
+ cf2_stack_pushFixed( opStack, v );
+ }
+ }
+ }
+ continue; /* don't clear stack */
+
+ } /* end of switch statement checking `op1' */
+
+ cf2_stack_clear( opStack );
+
+ } /* end of main interpreter loop */
+
+ /* we get here if the charstring ends without cf2_cmdENDCHAR */
+ FT_TRACE4(( "cf2_interpT2CharString:"
+ " charstring ends without ENDCHAR\n" ));
+
+ exit:
+ /* check whether last error seen is also the first one */
+ cf2_setError( error, lastError );
+
+ if ( *error )
+ FT_TRACE4(( "charstring error %d\n", *error ));
+
+ /* free resources from objects we've used */
+ cf2_glyphpath_finalize( &glyphPath );
+ cf2_arrstack_finalize( &vStemHintArray );
+ cf2_arrstack_finalize( &hStemHintArray );
+ cf2_arrstack_finalize( &subrStack );
+ cf2_stack_free( opStack );
+
+ FT_TRACE4(( "\n" ));
+
+ return;
+ }
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psintrp.h b/thirdparty/freetype/src/psaux/psintrp.h
new file mode 100644
index 0000000000..4790aaa302
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psintrp.h
@@ -0,0 +1,83 @@
+/***************************************************************************/
+/* */
+/* psintrp.h */
+/* */
+/* Adobe's CFF Interpreter (specification). */
+/* */
+/* Copyright 2007-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef PSINTRP_H_
+#define PSINTRP_H_
+
+
+#include "psft.h"
+#include "pshints.h"
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( void )
+ cf2_hintmask_init( CF2_HintMask hintmask,
+ FT_Error* error );
+ FT_LOCAL( FT_Bool )
+ cf2_hintmask_isValid( const CF2_HintMask hintmask );
+ FT_LOCAL( FT_Bool )
+ cf2_hintmask_isNew( const CF2_HintMask hintmask );
+ FT_LOCAL( void )
+ cf2_hintmask_setNew( CF2_HintMask hintmask,
+ FT_Bool val );
+ FT_LOCAL( FT_Byte* )
+ cf2_hintmask_getMaskPtr( CF2_HintMask hintmask );
+ FT_LOCAL( void )
+ cf2_hintmask_setAll( CF2_HintMask hintmask,
+ size_t bitCount );
+
+ FT_LOCAL( void )
+ cf2_interpT2CharString( CF2_Font font,
+ CF2_Buffer charstring,
+ CF2_OutlineCallbacks callbacks,
+ const FT_Vector* translation,
+ FT_Bool doingSeac,
+ CF2_Fixed curX,
+ CF2_Fixed curY,
+ CF2_Fixed* width );
+
+
+FT_END_HEADER
+
+
+#endif /* PSINTRP_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psobjs.c b/thirdparty/freetype/src/psaux/psobjs.c
index f04edea411..f54bc7e416 100644
--- a/thirdparty/freetype/src/psaux/psobjs.c
+++ b/thirdparty/freetype/src/psaux/psobjs.c
@@ -4,7 +4,7 @@
/* */
/* Auxiliary functions for PostScript fonts (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -20,11 +20,13 @@
#include FT_INTERNAL_POSTSCRIPT_AUX_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_CALC_H
+#include FT_DRIVER_H
#include "psobjs.h"
#include "psconv.h"
#include "psauxerr.h"
+#include "psauxmod.h"
/*************************************************************************/
@@ -1761,11 +1763,748 @@
/*************************************************************************/
/*************************************************************************/
/***** *****/
+ /***** CFF BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cff_builder_init */
+ /* */
+ /* <Description> */
+ /* Initializes a given glyph builder. */
+ /* */
+ /* <InOut> */
+ /* builder :: A pointer to the glyph builder to initialize. */
+ /* */
+ /* <Input> */
+ /* face :: The current face object. */
+ /* */
+ /* size :: The current size object. */
+ /* */
+ /* glyph :: The current glyph object. */
+ /* */
+ /* hinting :: Whether hinting is active. */
+ /* */
+ FT_LOCAL_DEF( void )
+ cff_builder_init( CFF_Builder* builder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot glyph,
+ FT_Bool hinting )
+ {
+ builder->path_begun = 0;
+ builder->load_points = 1;
+
+ builder->face = face;
+ builder->glyph = glyph;
+ builder->memory = face->root.memory;
+
+ if ( glyph )
+ {
+ FT_GlyphLoader loader = glyph->root.internal->loader;
+
+
+ builder->loader = loader;
+ builder->base = &loader->base.outline;
+ builder->current = &loader->current.outline;
+ FT_GlyphLoader_Rewind( loader );
+
+ builder->hints_globals = NULL;
+ builder->hints_funcs = NULL;
+
+ if ( hinting && size )
+ {
+ FT_Size ftsize = FT_SIZE( size );
+ CFF_Internal internal = (CFF_Internal)ftsize->internal->module_data;
+
+ if ( internal )
+ {
+ builder->hints_globals = (void *)internal->topfont;
+ builder->hints_funcs = glyph->root.internal->glyph_hints;
+ }
+ }
+ }
+
+ builder->pos_x = 0;
+ builder->pos_y = 0;
+
+ builder->left_bearing.x = 0;
+ builder->left_bearing.y = 0;
+ builder->advance.x = 0;
+ builder->advance.y = 0;
+
+ builder->funcs = cff_builder_funcs;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cff_builder_done */
+ /* */
+ /* <Description> */
+ /* Finalizes a given glyph builder. Its contents can still be used */
+ /* after the call, but the function saves important information */
+ /* within the corresponding glyph slot. */
+ /* */
+ /* <Input> */
+ /* builder :: A pointer to the glyph builder to finalize. */
+ /* */
+ FT_LOCAL_DEF( void )
+ cff_builder_done( CFF_Builder* builder )
+ {
+ CFF_GlyphSlot glyph = builder->glyph;
+
+
+ if ( glyph )
+ glyph->root.outline = *builder->base;
+ }
+
+
+ /* check that there is enough space for `count' more points */
+ FT_LOCAL_DEF( FT_Error )
+ cff_check_points( CFF_Builder* builder,
+ FT_Int count )
+ {
+ return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
+ }
+
+
+ /* add a new point, do not check space */
+ FT_LOCAL_DEF( void )
+ cff_builder_add_point( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag )
+ {
+ FT_Outline* outline = builder->current;
+
+
+ if ( builder->load_points )
+ {
+ FT_Vector* point = outline->points + outline->n_points;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points;
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( builder->face );
+
+
+ if ( driver->hinting_engine == FT_HINTING_FREETYPE )
+ {
+ point->x = x >> 16;
+ point->y = y >> 16;
+ }
+ else
+#endif
+ {
+ /* cf2_decoder_parse_charstrings uses 16.16 coordinates */
+ point->x = x >> 10;
+ point->y = y >> 10;
+ }
+ *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
+ }
+
+ outline->n_points++;
+ }
+
+
+ /* check space for a new on-curve point, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ cff_builder_add_point1( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error;
+
+
+ error = cff_check_points( builder, 1 );
+ if ( !error )
+ cff_builder_add_point( builder, x, y, 1 );
+
+ return error;
+ }
+
+
+ /* check space for a new contour, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ cff_builder_add_contour( CFF_Builder* builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Error error;
+
+
+ if ( !builder->load_points )
+ {
+ outline->n_contours++;
+ return FT_Err_Ok;
+ }
+
+ error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
+ if ( !error )
+ {
+ if ( outline->n_contours > 0 )
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+
+ outline->n_contours++;
+ }
+
+ return error;
+ }
+
+
+ /* if a path was begun, add its first on-curve point */
+ FT_LOCAL_DEF( FT_Error )
+ cff_builder_start_point( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ /* test whether we are building a new contour */
+ if ( !builder->path_begun )
+ {
+ builder->path_begun = 1;
+ error = cff_builder_add_contour( builder );
+ if ( !error )
+ error = cff_builder_add_point1( builder, x, y );
+ }
+
+ return error;
+ }
+
+
+ /* close the current contour */
+ FT_LOCAL_DEF( void )
+ cff_builder_close_contour( CFF_Builder* builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Int first;
+
+
+ if ( !outline )
+ return;
+
+ first = outline->n_contours <= 1
+ ? 0 : outline->contours[outline->n_contours - 2] + 1;
+
+ /* We must not include the last point in the path if it */
+ /* is located on the first point. */
+ if ( outline->n_points > 1 )
+ {
+ FT_Vector* p1 = outline->points + first;
+ FT_Vector* p2 = outline->points + outline->n_points - 1;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1;
+
+
+ /* `delete' last point only if it coincides with the first */
+ /* point and if it is not a control point (which can happen). */
+ if ( p1->x == p2->x && p1->y == p2->y )
+ if ( *control == FT_CURVE_TAG_ON )
+ outline->n_points--;
+ }
+
+ if ( outline->n_contours > 0 )
+ {
+ /* Don't add contours only consisting of one point, i.e., */
+ /* check whether begin point and last point are the same. */
+ if ( first == outline->n_points - 1 )
+ {
+ outline->n_contours--;
+ outline->n_points--;
+ }
+ else
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PS BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ps_builder_init */
+ /* */
+ /* <Description> */
+ /* Initializes a given glyph builder. */
+ /* */
+ /* <InOut> */
+ /* builder :: A pointer to the glyph builder to initialize. */
+ /* */
+ /* <Input> */
+ /* face :: The current face object. */
+ /* */
+ /* size :: The current size object. */
+ /* */
+ /* glyph :: The current glyph object. */
+ /* */
+ /* hinting :: Whether hinting should be applied. */
+ /* */
+ FT_LOCAL_DEF( void )
+ ps_builder_init( PS_Builder* ps_builder,
+ void* builder,
+ FT_Bool is_t1 )
+ {
+ FT_ZERO( ps_builder );
+
+ if ( is_t1 )
+ {
+ T1_Builder t1builder = (T1_Builder)builder;
+
+
+ ps_builder->memory = t1builder->memory;
+ ps_builder->face = (FT_Face)t1builder->face;
+ ps_builder->glyph = (CFF_GlyphSlot)t1builder->glyph;
+ ps_builder->loader = t1builder->loader;
+ ps_builder->base = t1builder->base;
+ ps_builder->current = t1builder->current;
+
+ ps_builder->pos_x = &t1builder->pos_x;
+ ps_builder->pos_y = &t1builder->pos_y;
+
+ ps_builder->left_bearing = &t1builder->left_bearing;
+ ps_builder->advance = &t1builder->advance;
+
+ ps_builder->bbox = &t1builder->bbox;
+ ps_builder->path_begun = 0;
+ ps_builder->load_points = t1builder->load_points;
+ ps_builder->no_recurse = t1builder->no_recurse;
+
+ ps_builder->metrics_only = t1builder->metrics_only;
+ }
+ else
+ {
+ CFF_Builder* cffbuilder = (CFF_Builder*)builder;
+
+
+ ps_builder->memory = cffbuilder->memory;
+ ps_builder->face = (FT_Face)cffbuilder->face;
+ ps_builder->glyph = cffbuilder->glyph;
+ ps_builder->loader = cffbuilder->loader;
+ ps_builder->base = cffbuilder->base;
+ ps_builder->current = cffbuilder->current;
+
+ ps_builder->pos_x = &cffbuilder->pos_x;
+ ps_builder->pos_y = &cffbuilder->pos_y;
+
+ ps_builder->left_bearing = &cffbuilder->left_bearing;
+ ps_builder->advance = &cffbuilder->advance;
+
+ ps_builder->bbox = &cffbuilder->bbox;
+ ps_builder->path_begun = cffbuilder->path_begun;
+ ps_builder->load_points = cffbuilder->load_points;
+ ps_builder->no_recurse = cffbuilder->no_recurse;
+
+ ps_builder->metrics_only = cffbuilder->metrics_only;
+ }
+
+ ps_builder->is_t1 = is_t1;
+ ps_builder->funcs = ps_builder_funcs;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ps_builder_done */
+ /* */
+ /* <Description> */
+ /* Finalizes a given glyph builder. Its contents can still be used */
+ /* after the call, but the function saves important information */
+ /* within the corresponding glyph slot. */
+ /* */
+ /* <Input> */
+ /* builder :: A pointer to the glyph builder to finalize. */
+ /* */
+ FT_LOCAL_DEF( void )
+ ps_builder_done( PS_Builder* builder )
+ {
+ CFF_GlyphSlot glyph = builder->glyph;
+
+
+ if ( glyph )
+ glyph->root.outline = *builder->base;
+ }
+
+
+ /* check that there is enough space for `count' more points */
+ FT_LOCAL_DEF( FT_Error )
+ ps_builder_check_points( PS_Builder* builder,
+ FT_Int count )
+ {
+ return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
+ }
+
+
+ /* add a new point, do not check space */
+ FT_LOCAL_DEF( void )
+ ps_builder_add_point( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag )
+ {
+ FT_Outline* outline = builder->current;
+
+
+ if ( builder->load_points )
+ {
+ FT_Vector* point = outline->points + outline->n_points;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points;
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( builder->face );
+
+
+ if ( !builder->is_t1 &&
+ driver->hinting_engine == FT_HINTING_FREETYPE )
+ {
+ point->x = x >> 16;
+ point->y = y >> 16;
+ }
+ else
+#endif
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+#ifndef CFF_CONFIG_OPTION_OLD_ENGINE
+ PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( builder->face );
+#endif
+ if ( builder->is_t1 &&
+ driver->hinting_engine == FT_HINTING_FREETYPE )
+ {
+ point->x = FIXED_TO_INT( x );
+ point->y = FIXED_TO_INT( y );
+ }
+ else
+#endif
+ {
+ /* cf2_decoder_parse_charstrings uses 16.16 coordinates */
+ point->x = x >> 10;
+ point->y = y >> 10;
+ }
+ *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
+ }
+ outline->n_points++;
+ }
+
+
+ /* check space for a new on-curve point, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ ps_builder_add_point1( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error;
+
+
+ error = ps_builder_check_points( builder, 1 );
+ if ( !error )
+ ps_builder_add_point( builder, x, y, 1 );
+
+ return error;
+ }
+
+
+ /* check space for a new contour, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ ps_builder_add_contour( PS_Builder* builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Error error;
+
+
+ /* this might happen in invalid fonts */
+ if ( !outline )
+ {
+ FT_ERROR(( "ps_builder_add_contour: no outline to add points to\n" ));
+ return FT_THROW( Invalid_File_Format );
+ }
+
+ if ( !builder->load_points )
+ {
+ outline->n_contours++;
+ return FT_Err_Ok;
+ }
+
+ error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
+ if ( !error )
+ {
+ if ( outline->n_contours > 0 )
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+
+ outline->n_contours++;
+ }
+
+ return error;
+ }
+
+
+ /* if a path was begun, add its first on-curve point */
+ FT_LOCAL_DEF( FT_Error )
+ ps_builder_start_point( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ /* test whether we are building a new contour */
+ if ( !builder->path_begun )
+ {
+ builder->path_begun = 1;
+ error = ps_builder_add_contour( builder );
+ if ( !error )
+ error = ps_builder_add_point1( builder, x, y );
+ }
+
+ return error;
+ }
+
+
+ /* close the current contour */
+ FT_LOCAL_DEF( void )
+ ps_builder_close_contour( PS_Builder* builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Int first;
+
+
+ if ( !outline )
+ return;
+
+ first = outline->n_contours <= 1
+ ? 0 : outline->contours[outline->n_contours - 2] + 1;
+
+ /* in malformed fonts it can happen that a contour was started */
+ /* but no points were added */
+ if ( outline->n_contours && first == outline->n_points )
+ {
+ outline->n_contours--;
+ return;
+ }
+
+ /* We must not include the last point in the path if it */
+ /* is located on the first point. */
+ if ( outline->n_points > 1 )
+ {
+ FT_Vector* p1 = outline->points + first;
+ FT_Vector* p2 = outline->points + outline->n_points - 1;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1;
+
+
+ /* `delete' last point only if it coincides with the first */
+ /* point and it is not a control point (which can happen). */
+ if ( p1->x == p2->x && p1->y == p2->y )
+ if ( *control == FT_CURVE_TAG_ON )
+ outline->n_points--;
+ }
+
+ if ( outline->n_contours > 0 )
+ {
+ /* Don't add contours only consisting of one point, i.e., */
+ /* check whether the first and the last point is the same. */
+ if ( first == outline->n_points - 1 )
+ {
+ outline->n_contours--;
+ outline->n_points--;
+ }
+ else
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
/***** OTHER *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ps_decoder_init */
+ /* */
+ /* <Description> */
+ /* Creates a wrapper decoder for use in the combined */
+ /* Type 1 / CFF interpreter. */
+ /* */
+ /* <InOut> */
+ /* ps_decoder :: A pointer to the decoder to initialize. */
+ /* */
+ /* <Input> */
+ /* decoder :: A pointer to the original decoder. */
+ /* */
+ /* is_t1 :: Flag indicating Type 1 or CFF */
+ /* */
+ FT_LOCAL_DEF( void )
+ ps_decoder_init( PS_Decoder* ps_decoder,
+ void* decoder,
+ FT_Bool is_t1 )
+ {
+ FT_ZERO( ps_decoder );
+
+ if ( is_t1 )
+ {
+ T1_Decoder t1_decoder = (T1_Decoder)decoder;
+
+
+ ps_builder_init( &ps_decoder->builder,
+ &t1_decoder->builder,
+ is_t1 );
+
+ ps_decoder->cf2_instance = &t1_decoder->cf2_instance;
+ ps_decoder->psnames = t1_decoder->psnames;
+
+ ps_decoder->num_glyphs = t1_decoder->num_glyphs;
+ ps_decoder->glyph_names = t1_decoder->glyph_names;
+ ps_decoder->hint_mode = t1_decoder->hint_mode;
+ ps_decoder->blend = t1_decoder->blend;
+
+ ps_decoder->num_locals = (FT_UInt)t1_decoder->num_subrs;
+ ps_decoder->locals = t1_decoder->subrs;
+ ps_decoder->locals_len = t1_decoder->subrs_len;
+ ps_decoder->locals_hash = t1_decoder->subrs_hash;
+
+ ps_decoder->buildchar = t1_decoder->buildchar;
+ ps_decoder->len_buildchar = t1_decoder->len_buildchar;
+
+ ps_decoder->lenIV = t1_decoder->lenIV;
+ }
+ else
+ {
+ CFF_Decoder* cff_decoder = (CFF_Decoder*)decoder;
+
+
+ ps_builder_init( &ps_decoder->builder,
+ &cff_decoder->builder,
+ is_t1 );
+
+ ps_decoder->cff = cff_decoder->cff;
+ ps_decoder->cf2_instance = &cff_decoder->cff->cf2_instance;
+ ps_decoder->current_subfont = cff_decoder->current_subfont;
+
+ ps_decoder->num_globals = cff_decoder->num_globals;
+ ps_decoder->globals = cff_decoder->globals;
+ ps_decoder->globals_bias = cff_decoder->globals_bias;
+ ps_decoder->num_locals = cff_decoder->num_locals;
+ ps_decoder->locals = cff_decoder->locals;
+ ps_decoder->locals_bias = cff_decoder->locals_bias;
+
+ ps_decoder->glyph_width = &cff_decoder->glyph_width;
+ ps_decoder->width_only = cff_decoder->width_only;
+
+ ps_decoder->hint_mode = cff_decoder->hint_mode;
+
+ ps_decoder->get_glyph_callback = cff_decoder->get_glyph_callback;
+ ps_decoder->free_glyph_callback = cff_decoder->free_glyph_callback;
+ }
+ }
+
+
+ /* Synthesize a SubFont object for Type 1 fonts, for use in the */
+ /* new interpreter to access Private dict data. */
+ FT_LOCAL_DEF( void )
+ t1_make_subfont( FT_Face face,
+ PS_Private priv,
+ CFF_SubFont subfont )
+ {
+ CFF_Private cpriv = &subfont->private_dict;
+ FT_UInt n, count;
+
+
+ FT_ZERO( subfont );
+ FT_ZERO( cpriv );
+
+ count = cpriv->num_blue_values = priv->num_blue_values;
+ for ( n = 0; n < count; n++ )
+ cpriv->blue_values[n] = (FT_Pos)priv->blue_values[n];
+
+ count = cpriv->num_other_blues = priv->num_other_blues;
+ for ( n = 0; n < count; n++ )
+ cpriv->other_blues[n] = (FT_Pos)priv->other_blues[n];
+
+ count = cpriv->num_family_blues = priv->num_family_blues;
+ for ( n = 0; n < count; n++ )
+ cpriv->family_blues[n] = (FT_Pos)priv->family_blues[n];
+
+ count = cpriv->num_family_other_blues = priv->num_family_other_blues;
+ for ( n = 0; n < count; n++ )
+ cpriv->family_other_blues[n] = (FT_Pos)priv->family_other_blues[n];
+
+ cpriv->blue_scale = priv->blue_scale;
+ cpriv->blue_shift = (FT_Pos)priv->blue_shift;
+ cpriv->blue_fuzz = (FT_Pos)priv->blue_fuzz;
+
+ cpriv->standard_width = (FT_Pos)priv->standard_width[0];
+ cpriv->standard_height = (FT_Pos)priv->standard_height[0];
+
+ count = cpriv->num_snap_widths = priv->num_snap_widths;
+ for ( n = 0; n < count; n++ )
+ cpriv->snap_widths[n] = (FT_Pos)priv->snap_widths[n];
+
+ count = cpriv->num_snap_heights = priv->num_snap_heights;
+ for ( n = 0; n < count; n++ )
+ cpriv->snap_heights[n] = (FT_Pos)priv->snap_heights[n];
+
+ cpriv->force_bold = priv->force_bold;
+ cpriv->lenIV = priv->lenIV;
+ cpriv->language_group = priv->language_group;
+ cpriv->expansion_factor = priv->expansion_factor;
+
+ cpriv->subfont = subfont;
+
+
+ /* Initialize the random number generator. */
+ if ( face->internal->random_seed != -1 )
+ {
+ /* If we have a face-specific seed, use it. */
+ /* If non-zero, update it to a positive value. */
+ subfont->random = (FT_UInt32)face->internal->random_seed;
+ if ( face->internal->random_seed )
+ {
+ do
+ {
+ face->internal->random_seed = (FT_Int32)cff_random(
+ (FT_UInt32)face->internal->random_seed );
+
+ } while ( face->internal->random_seed < 0 );
+ }
+ }
+ if ( !subfont->random )
+ {
+ FT_UInt32 seed;
+
+
+ /* compute random seed from some memory addresses */
+ seed = (FT_UInt32)( (FT_Offset)(char*)&seed ^
+ (FT_Offset)(char*)&face ^
+ (FT_Offset)(char*)&subfont );
+ seed = seed ^ ( seed >> 10 ) ^ ( seed >> 20 );
+ if ( seed == 0 )
+ seed = 0x7384;
+
+ subfont->random = seed;
+ }
+ }
+
+
FT_LOCAL_DEF( void )
t1_decrypt( FT_Byte* buffer,
FT_Offset length,
@@ -1779,4 +2518,16 @@
}
+ FT_LOCAL_DEF( FT_UInt32 )
+ cff_random( FT_UInt32 r )
+ {
+ /* a 32bit version of the `xorshift' algorithm */
+ r ^= r << 13;
+ r ^= r >> 17;
+ r ^= r << 5;
+
+ return r;
+ }
+
+
/* END */
diff --git a/thirdparty/freetype/src/psaux/psobjs.h b/thirdparty/freetype/src/psaux/psobjs.h
index 202e5b2416..8e0fe5fa4c 100644
--- a/thirdparty/freetype/src/psaux/psobjs.h
+++ b/thirdparty/freetype/src/psaux/psobjs.h
@@ -4,7 +4,7 @@
/* */
/* Auxiliary functions for PostScript fonts (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -22,6 +22,7 @@
#include <ft2build.h>
#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_CFF_OBJECTS_TYPES_H
FT_BEGIN_HEADER
@@ -193,17 +194,117 @@ FT_BEGIN_HEADER
/*************************************************************************/
/*************************************************************************/
/***** *****/
+ /***** CFF BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ cff_builder_init( CFF_Builder* builder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot glyph,
+ FT_Bool hinting );
+
+ FT_LOCAL( void )
+ cff_builder_done( CFF_Builder* builder );
+
+ FT_LOCAL( FT_Error )
+ cff_check_points( CFF_Builder* builder,
+ FT_Int count );
+
+ FT_LOCAL( void )
+ cff_builder_add_point( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag );
+ FT_LOCAL( FT_Error )
+ cff_builder_add_point1( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y );
+ FT_LOCAL( FT_Error )
+ cff_builder_start_point( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y );
+ FT_LOCAL( void )
+ cff_builder_close_contour( CFF_Builder* builder );
+
+ FT_LOCAL( FT_Error )
+ cff_builder_add_contour( CFF_Builder* builder );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PS BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ ps_builder_init( PS_Builder* ps_builder,
+ void* builder,
+ FT_Bool is_t1 );
+
+
+ FT_LOCAL( void )
+ ps_builder_done( PS_Builder* builder );
+
+ FT_LOCAL( FT_Error )
+ ps_builder_check_points( PS_Builder* builder,
+ FT_Int count );
+
+ FT_LOCAL( void )
+ ps_builder_add_point( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag );
+
+ FT_LOCAL( FT_Error )
+ ps_builder_add_point1( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y );
+
+ FT_LOCAL( FT_Error )
+ ps_builder_add_contour( PS_Builder* builder );
+
+ FT_LOCAL( FT_Error )
+ ps_builder_start_point( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y );
+
+ FT_LOCAL( void )
+ ps_builder_close_contour( PS_Builder* builder );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
/***** OTHER *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
FT_LOCAL( void )
+ ps_decoder_init( PS_Decoder* ps_decoder,
+ void* decoder,
+ FT_Bool is_t1 );
+
+ FT_LOCAL( void )
+ t1_make_subfont( FT_Face face,
+ PS_Private priv,
+ CFF_SubFont subfont );
+
+ FT_LOCAL( void )
t1_decrypt( FT_Byte* buffer,
FT_Offset length,
FT_UShort seed );
+ FT_LOCAL( FT_UInt32 )
+ cff_random( FT_UInt32 r );
+
+
FT_END_HEADER
#endif /* PSOBJS_H_ */
diff --git a/thirdparty/freetype/src/psaux/psread.c b/thirdparty/freetype/src/psaux/psread.c
new file mode 100644
index 0000000000..719863ce17
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psread.c
@@ -0,0 +1,112 @@
+/***************************************************************************/
+/* */
+/* psread.c */
+/* */
+/* Adobe's code for stream handling (body). */
+/* */
+/* Copyright 2007-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#include "psft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "psglue.h"
+
+#include "pserror.h"
+
+
+ /* Define CF2_IO_FAIL as 1 to enable random errors and random */
+ /* value errors in I/O. */
+#define CF2_IO_FAIL 0
+
+
+#if CF2_IO_FAIL
+
+ /* set the .00 value to a nonzero probability */
+ static int
+ randomError2( void )
+ {
+ /* for region buffer ReadByte (interp) function */
+ return (double)rand() / RAND_MAX < .00;
+ }
+
+ /* set the .00 value to a nonzero probability */
+ static CF2_Int
+ randomValue()
+ {
+ return (double)rand() / RAND_MAX < .00 ? rand() : 0;
+ }
+
+#endif /* CF2_IO_FAIL */
+
+
+ /* Region Buffer */
+ /* */
+ /* Can be constructed from a copied buffer managed by */
+ /* `FCM_getDatablock'. */
+ /* Reads bytes with check for end of buffer. */
+
+ /* reading past the end of the buffer sets error and returns zero */
+ FT_LOCAL_DEF( CF2_Int )
+ cf2_buf_readByte( CF2_Buffer buf )
+ {
+ if ( buf->ptr < buf->end )
+ {
+#if CF2_IO_FAIL
+ if ( randomError2() )
+ {
+ CF2_SET_ERROR( buf->error, Invalid_Stream_Operation );
+ return 0;
+ }
+
+ return *(buf->ptr)++ + randomValue();
+#else
+ return *(buf->ptr)++;
+#endif
+ }
+ else
+ {
+ CF2_SET_ERROR( buf->error, Invalid_Stream_Operation );
+ return 0;
+ }
+ }
+
+
+ /* note: end condition can occur without error */
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_buf_isEnd( CF2_Buffer buf )
+ {
+ return (FT_Bool)( buf->ptr >= buf->end );
+ }
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psread.h b/thirdparty/freetype/src/psaux/psread.h
new file mode 100644
index 0000000000..464b29ba74
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psread.h
@@ -0,0 +1,68 @@
+/***************************************************************************/
+/* */
+/* psread.h */
+/* */
+/* Adobe's code for stream handling (specification). */
+/* */
+/* Copyright 2007-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef PSREAD_H_
+#define PSREAD_H_
+
+
+FT_BEGIN_HEADER
+
+
+ typedef struct CF2_BufferRec_
+ {
+ FT_Error* error;
+ const FT_Byte* start;
+ const FT_Byte* end;
+ const FT_Byte* ptr;
+
+ } CF2_BufferRec, *CF2_Buffer;
+
+
+ FT_LOCAL( CF2_Int )
+ cf2_buf_readByte( CF2_Buffer buf );
+ FT_LOCAL( FT_Bool )
+ cf2_buf_isEnd( CF2_Buffer buf );
+
+
+FT_END_HEADER
+
+
+#endif /* PSREAD_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psstack.c b/thirdparty/freetype/src/psaux/psstack.c
new file mode 100644
index 0000000000..69d063349a
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psstack.c
@@ -0,0 +1,328 @@
+/***************************************************************************/
+/* */
+/* psstack.c */
+/* */
+/* Adobe's code for emulating a CFF stack (body). */
+/* */
+/* Copyright 2007-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#include "psft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "psglue.h"
+#include "psfont.h"
+#include "psstack.h"
+
+#include "pserror.h"
+
+
+ /* Allocate and initialize an instance of CF2_Stack. */
+ /* Note: This function returns NULL on error (does not set */
+ /* `error'). */
+ FT_LOCAL_DEF( CF2_Stack )
+ cf2_stack_init( FT_Memory memory,
+ FT_Error* e,
+ FT_UInt stackSize )
+ {
+ FT_Error error = FT_Err_Ok; /* for FT_NEW */
+
+ CF2_Stack stack = NULL;
+
+
+ if ( !FT_NEW( stack ) )
+ {
+ /* initialize the structure; FT_NEW zeroes it */
+ stack->memory = memory;
+ stack->error = e;
+ }
+
+ /* allocate the stack buffer */
+ if ( FT_NEW_ARRAY( stack->buffer, stackSize ) )
+ {
+ FT_FREE( stack );
+ return NULL;
+ }
+
+ stack->stackSize = stackSize;
+ stack->top = stack->buffer; /* empty stack */
+
+ return stack;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_stack_free( CF2_Stack stack )
+ {
+ if ( stack )
+ {
+ FT_Memory memory = stack->memory;
+
+ /* free the buffer */
+ FT_FREE( stack->buffer );
+
+ /* free the main structure */
+ FT_FREE( stack );
+ }
+ }
+
+
+ FT_LOCAL_DEF( CF2_UInt )
+ cf2_stack_count( CF2_Stack stack )
+ {
+ return (CF2_UInt)( stack->top - stack->buffer );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_stack_pushInt( CF2_Stack stack,
+ CF2_Int val )
+ {
+ if ( stack->top == stack->buffer + stack->stackSize )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Overflow );
+ return; /* stack overflow */
+ }
+
+ stack->top->u.i = val;
+ stack->top->type = CF2_NumberInt;
+ stack->top++;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_stack_pushFixed( CF2_Stack stack,
+ CF2_Fixed val )
+ {
+ if ( stack->top == stack->buffer + stack->stackSize )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Overflow );
+ return; /* stack overflow */
+ }
+
+ stack->top->u.r = val;
+ stack->top->type = CF2_NumberFixed;
+ stack->top++;
+ }
+
+
+ /* this function is only allowed to pop an integer type */
+ FT_LOCAL_DEF( CF2_Int )
+ cf2_stack_popInt( CF2_Stack stack )
+ {
+ if ( stack->top == stack->buffer )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Underflow );
+ return 0; /* underflow */
+ }
+ if ( stack->top[-1].type != CF2_NumberInt )
+ {
+ CF2_SET_ERROR( stack->error, Syntax_Error );
+ return 0; /* type mismatch */
+ }
+
+ stack->top--;
+
+ return stack->top->u.i;
+ }
+
+
+ /* Note: type mismatch is silently cast */
+ /* TODO: check this */
+ FT_LOCAL_DEF( CF2_Fixed )
+ cf2_stack_popFixed( CF2_Stack stack )
+ {
+ if ( stack->top == stack->buffer )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Underflow );
+ return cf2_intToFixed( 0 ); /* underflow */
+ }
+
+ stack->top--;
+
+ switch ( stack->top->type )
+ {
+ case CF2_NumberInt:
+ return cf2_intToFixed( stack->top->u.i );
+ case CF2_NumberFrac:
+ return cf2_fracToFixed( stack->top->u.f );
+ default:
+ return stack->top->u.r;
+ }
+ }
+
+
+ /* Note: type mismatch is silently cast */
+ /* TODO: check this */
+ FT_LOCAL_DEF( CF2_Fixed )
+ cf2_stack_getReal( CF2_Stack stack,
+ CF2_UInt idx )
+ {
+ FT_ASSERT( cf2_stack_count( stack ) <= stack->stackSize );
+
+ if ( idx >= cf2_stack_count( stack ) )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Overflow );
+ return cf2_intToFixed( 0 ); /* bounds error */
+ }
+
+ switch ( stack->buffer[idx].type )
+ {
+ case CF2_NumberInt:
+ return cf2_intToFixed( stack->buffer[idx].u.i );
+ case CF2_NumberFrac:
+ return cf2_fracToFixed( stack->buffer[idx].u.f );
+ default:
+ return stack->buffer[idx].u.r;
+ }
+ }
+
+
+ /* provide random access to stack */
+ FT_LOCAL_DEF( void )
+ cf2_stack_setReal( CF2_Stack stack,
+ CF2_UInt idx,
+ CF2_Fixed val )
+ {
+ if ( idx > cf2_stack_count( stack ) )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Overflow );
+ return;
+ }
+
+ stack->buffer[idx].u.r = val;
+ stack->buffer[idx].type = CF2_NumberFixed;
+ }
+
+
+ /* discard (pop) num values from stack */
+ FT_LOCAL_DEF( void )
+ cf2_stack_pop( CF2_Stack stack,
+ CF2_UInt num )
+ {
+ if ( num > cf2_stack_count( stack ) )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Underflow );
+ return;
+ }
+ stack->top -= num;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_stack_roll( CF2_Stack stack,
+ CF2_Int count,
+ CF2_Int shift )
+ {
+ /* we initialize this variable to avoid compiler warnings */
+ CF2_StackNumber last = { { 0 }, CF2_NumberInt };
+
+ CF2_Int start_idx, idx, i;
+
+
+ if ( count < 2 )
+ return; /* nothing to do (values 0 and 1), or undefined value */
+
+ if ( (CF2_UInt)count > cf2_stack_count( stack ) )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Overflow );
+ return;
+ }
+
+ if ( shift < 0 )
+ shift = -( ( -shift ) % count );
+ else
+ shift %= count;
+
+ if ( shift == 0 )
+ return; /* nothing to do */
+
+ /* We use the following algorithm to do the rolling, */
+ /* which needs two temporary variables only. */
+ /* */
+ /* Example: */
+ /* */
+ /* count = 8 */
+ /* shift = 2 */
+ /* */
+ /* stack indices before roll: 7 6 5 4 3 2 1 0 */
+ /* stack indices after roll: 1 0 7 6 5 4 3 2 */
+ /* */
+ /* The value of index 0 gets moved to index 2, while */
+ /* the old value of index 2 gets moved to index 4, */
+ /* and so on. We thus have the following copying */
+ /* chains for shift value 2. */
+ /* */
+ /* 0 -> 2 -> 4 -> 6 -> 0 */
+ /* 1 -> 3 -> 5 -> 7 -> 1 */
+ /* */
+ /* If `count' and `shift' are incommensurable, we */
+ /* have a single chain only. Otherwise, increase */
+ /* the start index by 1 after the first chain, then */
+ /* do the next chain until all elements in all */
+ /* chains are handled. */
+
+ start_idx = -1;
+ idx = -1;
+ for ( i = 0; i < count; i++ )
+ {
+ CF2_StackNumber tmp;
+
+
+ if ( start_idx == idx )
+ {
+ start_idx++;
+ idx = start_idx;
+ last = stack->buffer[idx];
+ }
+
+ idx += shift;
+ if ( idx >= count )
+ idx -= count;
+ else if ( idx < 0 )
+ idx += count;
+
+ tmp = stack->buffer[idx];
+ stack->buffer[idx] = last;
+ last = tmp;
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_stack_clear( CF2_Stack stack )
+ {
+ stack->top = stack->buffer;
+ }
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/psstack.h b/thirdparty/freetype/src/psaux/psstack.h
new file mode 100644
index 0000000000..38f7b41c68
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/psstack.h
@@ -0,0 +1,121 @@
+/***************************************************************************/
+/* */
+/* psstack.h */
+/* */
+/* Adobe's code for emulating a CFF stack (specification). */
+/* */
+/* Copyright 2007-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef PSSTACK_H_
+#define PSSTACK_H_
+
+
+FT_BEGIN_HEADER
+
+
+ /* CFF operand stack; specified maximum of 48 or 192 values */
+ typedef struct CF2_StackNumber_
+ {
+ union
+ {
+ CF2_Fixed r; /* 16.16 fixed point */
+ CF2_Frac f; /* 2.30 fixed point (for font matrix) */
+ CF2_Int i;
+ } u;
+
+ CF2_NumberType type;
+
+ } CF2_StackNumber;
+
+
+ typedef struct CF2_StackRec_
+ {
+ FT_Memory memory;
+ FT_Error* error;
+ CF2_StackNumber* buffer;
+ CF2_StackNumber* top;
+ FT_UInt stackSize;
+
+ } CF2_StackRec, *CF2_Stack;
+
+
+ FT_LOCAL( CF2_Stack )
+ cf2_stack_init( FT_Memory memory,
+ FT_Error* error,
+ FT_UInt stackSize );
+ FT_LOCAL( void )
+ cf2_stack_free( CF2_Stack stack );
+
+ FT_LOCAL( CF2_UInt )
+ cf2_stack_count( CF2_Stack stack );
+
+ FT_LOCAL( void )
+ cf2_stack_pushInt( CF2_Stack stack,
+ CF2_Int val );
+ FT_LOCAL( void )
+ cf2_stack_pushFixed( CF2_Stack stack,
+ CF2_Fixed val );
+
+ FT_LOCAL( CF2_Int )
+ cf2_stack_popInt( CF2_Stack stack );
+ FT_LOCAL( CF2_Fixed )
+ cf2_stack_popFixed( CF2_Stack stack );
+
+ FT_LOCAL( CF2_Fixed )
+ cf2_stack_getReal( CF2_Stack stack,
+ CF2_UInt idx );
+ FT_LOCAL( void )
+ cf2_stack_setReal( CF2_Stack stack,
+ CF2_UInt idx,
+ CF2_Fixed val );
+
+ FT_LOCAL( void )
+ cf2_stack_pop( CF2_Stack stack,
+ CF2_UInt num );
+
+ FT_LOCAL( void )
+ cf2_stack_roll( CF2_Stack stack,
+ CF2_Int count,
+ CF2_Int idx );
+
+ FT_LOCAL( void )
+ cf2_stack_clear( CF2_Stack stack );
+
+
+FT_END_HEADER
+
+
+#endif /* PSSTACK_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/pstypes.h b/thirdparty/freetype/src/psaux/pstypes.h
new file mode 100644
index 0000000000..dfbaa3d475
--- /dev/null
+++ b/thirdparty/freetype/src/psaux/pstypes.h
@@ -0,0 +1,78 @@
+/***************************************************************************/
+/* */
+/* pstypes.h */
+/* */
+/* Adobe's code for defining data types (specification only). */
+/* */
+/* Copyright 2011-2013 Adobe Systems Incorporated. */
+/* */
+/* This software, and all works of authorship, whether in source or */
+/* object code form as indicated by the copyright notice(s) included */
+/* herein (collectively, the "Work") is made available, and may only be */
+/* used, modified, and distributed under the FreeType Project License, */
+/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
+/* FreeType Project License, each contributor to the Work hereby grants */
+/* to any individual or legal entity exercising permissions granted by */
+/* the FreeType Project License and this section (hereafter, "You" or */
+/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
+/* royalty-free, irrevocable (except as stated in this section) patent */
+/* license to make, have made, use, offer to sell, sell, import, and */
+/* otherwise transfer the Work, where such license applies only to those */
+/* patent claims licensable by such contributor that are necessarily */
+/* infringed by their contribution(s) alone or by combination of their */
+/* contribution(s) with the Work to which such contribution(s) was */
+/* submitted. If You institute patent litigation against any entity */
+/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
+/* the Work or a contribution incorporated within the Work constitutes */
+/* direct or contributory patent infringement, then any patent licenses */
+/* granted to You under this License for that Work shall terminate as of */
+/* the date such litigation is filed. */
+/* */
+/* By using, modifying, or distributing the Work you indicate that you */
+/* have read and understood the terms and conditions of the */
+/* FreeType Project License as well as those provided in this section, */
+/* and you accept them fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef PSTYPES_H_
+#define PSTYPES_H_
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*
+ * The data models that we expect to support are as follows:
+ *
+ * name char short int long long-long pointer example
+ * -----------------------------------------------------
+ * ILP32 8 16 32 32 64* 32 32-bit MacOS, x86
+ * LLP64 8 16 32 32 64 64 x64
+ * LP64 8 16 32 64 64 64 64-bit MacOS
+ *
+ * *) type may be supported by emulation on a 32-bit architecture
+ *
+ */
+
+
+ /* integers at least 32 bits wide */
+#define CF2_UInt FT_UFast
+#define CF2_Int FT_Fast
+
+
+ /* fixed-float numbers */
+ typedef FT_Int32 CF2_F16Dot16;
+
+
+FT_END_HEADER
+
+
+#endif /* PSTYPES_H_ */
+
+
+/* END */
diff --git a/thirdparty/freetype/src/psaux/rules.mk b/thirdparty/freetype/src/psaux/rules.mk
index 542ae12d2b..a87bfe9687 100644
--- a/thirdparty/freetype/src/psaux/rules.mk
+++ b/thirdparty/freetype/src/psaux/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
@@ -33,12 +33,25 @@ PSAUX_DRV_SRC := $(PSAUX_DIR)/psobjs.c \
$(PSAUX_DIR)/t1cmap.c \
$(PSAUX_DIR)/afmparse.c \
$(PSAUX_DIR)/psconv.c \
- $(PSAUX_DIR)/psauxmod.c
+ $(PSAUX_DIR)/psauxmod.c \
+ $(PSAUX_DIR)/psarrst.c \
+ $(PSAUX_DIR)/psblues.c \
+ $(PSAUX_DIR)/pserror.c \
+ $(PSAUX_DIR)/psfont.c \
+ $(PSAUX_DIR)/psft.c \
+ $(PSAUX_DIR)/pshints.c \
+ $(PSAUX_DIR)/psintrp.c \
+ $(PSAUX_DIR)/psread.c \
+ $(PSAUX_DIR)/psstack.c \
+ $(PSAUX_DIR)/cffdecode.c
# PSAUX driver headers
#
PSAUX_DRV_H := $(PSAUX_DRV_SRC:%c=%h) \
- $(PSAUX_DIR)/psauxerr.h
+ $(PSAUX_DIR)/psauxerr.h \
+ $(PSAUX_DIR)/psfixed.h \
+ $(PSAUX_DIR)/psglue.h \
+ $(PSAUX_DIR)/pstypes.h
# PSAUX driver object(s)
diff --git a/thirdparty/freetype/src/psaux/t1cmap.c b/thirdparty/freetype/src/psaux/t1cmap.c
index 45b713eb7b..112a7892ba 100644
--- a/thirdparty/freetype/src/psaux/t1cmap.c
+++ b/thirdparty/freetype/src/psaux/t1cmap.c
@@ -4,7 +4,7 @@
/* */
/* Type 1 character map support (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psaux/t1cmap.h b/thirdparty/freetype/src/psaux/t1cmap.h
index 7870245a3a..4308e31d2d 100644
--- a/thirdparty/freetype/src/psaux/t1cmap.h
+++ b/thirdparty/freetype/src/psaux/t1cmap.h
@@ -4,7 +4,7 @@
/* */
/* Type 1 character map support (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psaux/t1decode.c b/thirdparty/freetype/src/psaux/t1decode.c
index 1250b53f5d..6ad145661f 100644
--- a/thirdparty/freetype/src/psaux/t1decode.c
+++ b/thirdparty/freetype/src/psaux/t1decode.c
@@ -4,7 +4,7 @@
/* */
/* PostScript Type 1 decoding routines (body). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -112,6 +112,56 @@
/*************************************************************************/
/* */
/* <Function> */
+ /* t1_lookup_glyph_by_stdcharcode_ps */
+ /* */
+ /* <Description> */
+ /* Looks up a given glyph by its StandardEncoding charcode. Used to */
+ /* implement the SEAC Type 1 operator in the Adobe engine */
+ /* */
+ /* <Input> */
+ /* face :: The current face object. */
+ /* */
+ /* charcode :: The character code to look for. */
+ /* */
+ /* <Return> */
+ /* A glyph index in the font face. Returns -1 if the corresponding */
+ /* glyph wasn't found. */
+ /* */
+ FT_LOCAL_DEF( FT_Int )
+ t1_lookup_glyph_by_stdcharcode_ps( PS_Decoder* decoder,
+ FT_Int charcode )
+ {
+ FT_UInt n;
+ const FT_String* glyph_name;
+ FT_Service_PsCMaps psnames = decoder->psnames;
+
+
+ /* check range of standard char code */
+ if ( charcode < 0 || charcode > 255 )
+ return -1;
+
+ glyph_name = psnames->adobe_std_strings(
+ psnames->adobe_std_encoding[charcode]);
+
+ for ( n = 0; n < decoder->num_glyphs; n++ )
+ {
+ FT_String* name = (FT_String*)decoder->glyph_names[n];
+
+
+ if ( name &&
+ name[0] == glyph_name[0] &&
+ ft_strcmp( name, glyph_name ) == 0 )
+ return (FT_Int)n;
+ }
+
+ return -1;
+ }
+
+
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ /*************************************************************************/
+ /* */
+ /* <Function> */
/* t1_lookup_glyph_by_stdcharcode */
/* */
/* <Description> */
@@ -158,6 +208,15 @@
}
+ /* parse a single Type 1 glyph */
+ FT_LOCAL_DEF( FT_Error )
+ t1_decoder_parse_glyph( T1_Decoder decoder,
+ FT_UInt glyph )
+ {
+ return decoder->parse_callback( decoder, glyph );
+ }
+
+
/*************************************************************************/
/* */
/* <Function> */
@@ -1579,14 +1638,286 @@
return FT_THROW( Stack_Underflow );
}
+#else /* T1_CONFIG_OPTION_OLD_ENGINE */
- /* parse a single Type 1 glyph */
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* t1_decoder_parse_metrics */
+ /* */
+ /* <Description> */
+ /* Parses a given Type 1 charstrings program to extract width */
+ /* */
+ /* <Input> */
+ /* decoder :: The current Type 1 decoder. */
+ /* */
+ /* charstring_base :: The base address of the charstring stream. */
+ /* */
+ /* charstring_len :: The length in bytes of the charstring stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
FT_LOCAL_DEF( FT_Error )
- t1_decoder_parse_glyph( T1_Decoder decoder,
- FT_UInt glyph )
+ t1_decoder_parse_metrics( T1_Decoder decoder,
+ FT_Byte* charstring_base,
+ FT_UInt charstring_len )
{
- return decoder->parse_callback( decoder, glyph );
+ T1_Decoder_Zone zone;
+ FT_Byte* ip;
+ FT_Byte* limit;
+ T1_Builder builder = &decoder->builder;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_Bool bol = TRUE;
+#endif
+
+
+ /* First of all, initialize the decoder */
+ decoder->top = decoder->stack;
+ decoder->zone = decoder->zones;
+ zone = decoder->zones;
+
+ builder->parse_state = T1_Parse_Start;
+
+ FT_TRACE4(( "\n"
+ "Start charstring: get width\n" ));
+
+ zone->base = charstring_base;
+ limit = zone->limit = charstring_base + charstring_len;
+ ip = zone->cursor = zone->base;
+
+ /* now, execute loop */
+ while ( ip < limit )
+ {
+ FT_Long* top = decoder->top;
+ T1_Operator op = op_none;
+ FT_Int32 value = 0;
+
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( bol )
+ {
+ FT_TRACE5(( " (%d)", decoder->top - decoder->stack ));
+ bol = FALSE;
+ }
+#endif
+
+ /*********************************************************************/
+ /* */
+ /* Decode operator or operand */
+ /* */
+ /* */
+
+ /* first of all, decompress operator or value */
+ switch ( *ip++ )
+ {
+ case 1:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 14:
+ case 15:
+ case 21:
+ case 22:
+ case 30:
+ case 31:
+ goto No_Width;
+
+ case 13:
+ op = op_hsbw;
+ break;
+
+ case 12:
+ if ( ip >= limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " invalid escape (12+EOF)\n" ));
+ goto Syntax_Error;
+ }
+
+ switch ( *ip++ )
+ {
+ case 7:
+ op = op_sbw;
+ break;
+
+ default:
+ goto No_Width;
+ }
+ break;
+
+ case 255: /* four bytes integer */
+ if ( ip + 4 > limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " unexpected EOF in integer\n" ));
+ goto Syntax_Error;
+ }
+
+ value = (FT_Int32)( ( (FT_UInt32)ip[0] << 24 ) |
+ ( (FT_UInt32)ip[1] << 16 ) |
+ ( (FT_UInt32)ip[2] << 8 ) |
+ (FT_UInt32)ip[3] );
+ ip += 4;
+
+ /* According to the specification, values > 32000 or < -32000 must */
+ /* be followed by a `div' operator to make the result be in the */
+ /* range [-32000;32000]. We expect that the second argument of */
+ /* `div' is not a large number. Additionally, we don't handle */
+ /* stuff like `<large1> <large2> <num> div <num> div' or */
+ /* <large1> <large2> <num> div div'. This is probably not allowed */
+ /* anyway. */
+ if ( value > 32000 || value < -32000 )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " large integer found for width\n" ));
+ goto Syntax_Error;
+ }
+ else
+ {
+ value = (FT_Int32)( (FT_UInt32)value << 16 );
+ }
+
+ break;
+
+ default:
+ if ( ip[-1] >= 32 )
+ {
+ if ( ip[-1] < 247 )
+ value = (FT_Int32)ip[-1] - 139;
+ else
+ {
+ if ( ++ip > limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " unexpected EOF in integer\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( ip[-2] < 251 )
+ value = ( ( ip[-2] - 247 ) * 256 ) + ip[-1] + 108;
+ else
+ value = -( ( ( ip[-2] - 251 ) * 256 ) + ip[-1] + 108 );
+ }
+
+ value = (FT_Int32)( (FT_UInt32)value << 16 );
+ }
+ else
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " invalid byte (%d)\n", ip[-1] ));
+ goto Syntax_Error;
+ }
+ }
+
+ /*********************************************************************/
+ /* */
+ /* Push value on stack, or process operator */
+ /* */
+ /* */
+ if ( op == op_none )
+ {
+ if ( top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics: stack overflow\n" ));
+ goto Syntax_Error;
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_TRACE4(( " %d", value / 65536 ));
+#endif
+
+ *top++ = value;
+ decoder->top = top;
+ }
+ else /* general operator */
+ {
+ FT_Int num_args = t1_args_count[op];
+
+
+ FT_ASSERT( num_args >= 0 );
+
+ if ( top - decoder->stack < num_args )
+ goto Stack_Underflow;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+ if ( top - decoder->stack != num_args )
+ FT_TRACE0(( "t1_decoder_parse_metrics:"
+ " too much operands on the stack"
+ " (seen %d, expected %d)\n",
+ top - decoder->stack, num_args ));
+
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+ top -= num_args;
+
+ switch ( op )
+ {
+ case op_hsbw:
+ FT_TRACE4(( " hsbw" ));
+
+ builder->parse_state = T1_Parse_Have_Width;
+
+ builder->left_bearing.x = ADD_LONG( builder->left_bearing.x,
+ top[0] );
+
+ builder->advance.x = top[1];
+ builder->advance.y = 0;
+
+ /* we only want to compute the glyph's metrics */
+ /* (lsb + advance width), not load the rest of */
+ /* it; so exit immediately */
+ return FT_Err_Ok;
+
+ case op_sbw:
+ FT_TRACE4(( " sbw" ));
+
+ builder->parse_state = T1_Parse_Have_Width;
+
+ builder->left_bearing.x = ADD_LONG( builder->left_bearing.x,
+ top[0] );
+ builder->left_bearing.y = ADD_LONG( builder->left_bearing.y,
+ top[1] );
+
+ builder->advance.x = top[2];
+ builder->advance.y = top[3];
+
+ /* we only want to compute the glyph's metrics */
+ /* (lsb + advance width), not load the rest of */
+ /* it; so exit immediately */
+ return FT_Err_Ok;
+
+ default:
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " unhandled opcode %d\n", op ));
+ goto Syntax_Error;
+ }
+
+ } /* general operator processing */
+
+ } /* while ip < limit */
+
+ FT_TRACE4(( "..end..\n\n" ));
+
+ No_Width:
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " no width, found op %d instead\n",
+ ip[-1] ));
+ Syntax_Error:
+ return FT_THROW( Syntax_Error );
+
+ Stack_Underflow:
+ return FT_THROW( Stack_Underflow );
}
+#endif /* T1_CONFIG_OPTION_OLD_ENGINE */
/* initialize T1 decoder */
@@ -1641,7 +1972,16 @@
FT_LOCAL_DEF( void )
t1_decoder_done( T1_Decoder decoder )
{
+ FT_Memory memory = decoder->builder.memory;
+
+
t1_builder_done( &decoder->builder );
+
+ if ( decoder->cf2_instance.finalizer )
+ {
+ decoder->cf2_instance.finalizer( decoder->cf2_instance.data );
+ FT_FREE( decoder->cf2_instance.data );
+ }
}
diff --git a/thirdparty/freetype/src/psaux/t1decode.h b/thirdparty/freetype/src/psaux/t1decode.h
index 12c27de775..1d9718d678 100644
--- a/thirdparty/freetype/src/psaux/t1decode.h
+++ b/thirdparty/freetype/src/psaux/t1decode.h
@@ -4,7 +4,7 @@
/* */
/* PostScript Type 1 decoding routines (specification). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -31,7 +31,11 @@ FT_BEGIN_HEADER
FT_CALLBACK_TABLE
const T1_Decoder_FuncsRec t1_decoder_funcs;
+ FT_LOCAL( FT_Int )
+ t1_lookup_glyph_by_stdcharcode_ps( PS_Decoder* decoder,
+ FT_Int charcode );
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
FT_LOCAL( FT_Error )
t1_decoder_parse_glyph( T1_Decoder decoder,
FT_UInt glyph_index );
@@ -40,6 +44,12 @@ FT_BEGIN_HEADER
t1_decoder_parse_charstrings( T1_Decoder decoder,
FT_Byte* base,
FT_UInt len );
+#else
+ FT_LOCAL( FT_Error )
+ t1_decoder_parse_metrics( T1_Decoder decoder,
+ FT_Byte* charstring_base,
+ FT_UInt charstring_len );
+#endif
FT_LOCAL( FT_Error )
t1_decoder_init( T1_Decoder decoder,
diff --git a/thirdparty/freetype/src/pshinter/module.mk b/thirdparty/freetype/src/pshinter/module.mk
index 77e35c4c10..06707be3b4 100644
--- a/thirdparty/freetype/src/pshinter/module.mk
+++ b/thirdparty/freetype/src/pshinter/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/pshinter/pshalgo.c b/thirdparty/freetype/src/pshinter/pshalgo.c
index 9ad1a3a02a..b98077c62e 100644
--- a/thirdparty/freetype/src/pshinter/pshalgo.c
+++ b/thirdparty/freetype/src/pshinter/pshalgo.c
@@ -4,7 +4,7 @@
/* */
/* PostScript hinting algorithm (body). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used */
@@ -26,7 +26,7 @@
#undef FT_COMPONENT
-#define FT_COMPONENT trace_pshalgo2
+#define FT_COMPONENT trace_pshalgo
#ifdef DEBUG_HINTER
diff --git a/thirdparty/freetype/src/pshinter/pshalgo.h b/thirdparty/freetype/src/pshinter/pshalgo.h
index 62e97d152b..c50683fbec 100644
--- a/thirdparty/freetype/src/pshinter/pshalgo.h
+++ b/thirdparty/freetype/src/pshinter/pshalgo.h
@@ -4,7 +4,7 @@
/* */
/* PostScript hinting algorithm (specification). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pshinter/pshglob.c b/thirdparty/freetype/src/pshinter/pshglob.c
index c68770c73a..accc04921d 100644
--- a/thirdparty/freetype/src/pshinter/pshglob.c
+++ b/thirdparty/freetype/src/pshinter/pshglob.c
@@ -5,7 +5,7 @@
/* PostScript hinter global hinting management (body). */
/* Inspired by the new auto-hinter module. */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used */
@@ -227,8 +227,8 @@
}
- /* Re-read blue zones from the original fonts and store them into out */
- /* private structure. This function re-orders, sanitizes and */
+ /* Re-read blue zones from the original fonts and store them into our */
+ /* private structure. This function re-orders, sanitizes, and */
/* fuzz-expands the zones as well. */
static void
psh_blues_set_zones( PSH_Blues target,
diff --git a/thirdparty/freetype/src/pshinter/pshglob.h b/thirdparty/freetype/src/pshinter/pshglob.h
index 8801cbada4..cf80bf40e6 100644
--- a/thirdparty/freetype/src/pshinter/pshglob.h
+++ b/thirdparty/freetype/src/pshinter/pshglob.h
@@ -4,7 +4,7 @@
/* */
/* PostScript hinter global hinting management. */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pshinter/pshinter.c b/thirdparty/freetype/src/pshinter/pshinter.c
index 13e07e1485..0eedac452d 100644
--- a/thirdparty/freetype/src/pshinter/pshinter.c
+++ b/thirdparty/freetype/src/pshinter/pshinter.c
@@ -4,7 +4,7 @@
/* */
/* FreeType PostScript Hinting module */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pshinter/pshmod.c b/thirdparty/freetype/src/pshinter/pshmod.c
index 860dc0ae82..0b8f6f99b8 100644
--- a/thirdparty/freetype/src/pshinter/pshmod.c
+++ b/thirdparty/freetype/src/pshinter/pshmod.c
@@ -4,7 +4,7 @@
/* */
/* FreeType PostScript hinter module implementation (body). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pshinter/pshmod.h b/thirdparty/freetype/src/pshinter/pshmod.h
index 1d2b40fa13..556de2fbc0 100644
--- a/thirdparty/freetype/src/pshinter/pshmod.h
+++ b/thirdparty/freetype/src/pshinter/pshmod.h
@@ -4,7 +4,7 @@
/* */
/* PostScript hinter module interface (specification). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pshinter/pshnterr.h b/thirdparty/freetype/src/pshinter/pshnterr.h
index 73d144e34c..b9d02d2956 100644
--- a/thirdparty/freetype/src/pshinter/pshnterr.h
+++ b/thirdparty/freetype/src/pshinter/pshnterr.h
@@ -4,7 +4,7 @@
/* */
/* PS Hinter error codes (specification only). */
/* */
-/* Copyright 2003-2017 by */
+/* Copyright 2003-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pshinter/pshpic.c b/thirdparty/freetype/src/pshinter/pshpic.c
index c0d3a64f29..465ad31885 100644
--- a/thirdparty/freetype/src/pshinter/pshpic.c
+++ b/thirdparty/freetype/src/pshinter/pshpic.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for pshinter module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pshinter/pshpic.h b/thirdparty/freetype/src/pshinter/pshpic.h
index 8d9a01c9c5..4469ba87c8 100644
--- a/thirdparty/freetype/src/pshinter/pshpic.h
+++ b/thirdparty/freetype/src/pshinter/pshpic.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for pshinter module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pshinter/pshrec.c b/thirdparty/freetype/src/pshinter/pshrec.c
index fff6d34250..6648d13d60 100644
--- a/thirdparty/freetype/src/pshinter/pshrec.c
+++ b/thirdparty/freetype/src/pshinter/pshrec.c
@@ -4,7 +4,7 @@
/* */
/* FreeType PostScript hints recorder (body). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pshinter/pshrec.h b/thirdparty/freetype/src/pshinter/pshrec.h
index e10bc2b120..7e3dfe0d53 100644
--- a/thirdparty/freetype/src/pshinter/pshrec.h
+++ b/thirdparty/freetype/src/pshinter/pshrec.h
@@ -4,7 +4,7 @@
/* */
/* Postscript (Type1/Type2) hints recorder (specification). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/pshinter/rules.mk b/thirdparty/freetype/src/pshinter/rules.mk
index 2be6404380..966690efc2 100644
--- a/thirdparty/freetype/src/pshinter/rules.mk
+++ b/thirdparty/freetype/src/pshinter/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2001-2017 by
+# Copyright 2001-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/psnames/module.mk b/thirdparty/freetype/src/psnames/module.mk
index ddd22960c1..410f48a191 100644
--- a/thirdparty/freetype/src/psnames/module.mk
+++ b/thirdparty/freetype/src/psnames/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/psnames/psmodule.c b/thirdparty/freetype/src/psnames/psmodule.c
index 44ba9ec6ab..8929ebe751 100644
--- a/thirdparty/freetype/src/psnames/psmodule.c
+++ b/thirdparty/freetype/src/psnames/psmodule.c
@@ -4,7 +4,7 @@
/* */
/* PSNames module implementation (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psnames/psmodule.h b/thirdparty/freetype/src/psnames/psmodule.h
index 6983b79234..3e94f8b437 100644
--- a/thirdparty/freetype/src/psnames/psmodule.h
+++ b/thirdparty/freetype/src/psnames/psmodule.h
@@ -4,7 +4,7 @@
/* */
/* High-level PSNames module interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psnames/psnamerr.h b/thirdparty/freetype/src/psnames/psnamerr.h
index f90bf5ea43..14eb76c99c 100644
--- a/thirdparty/freetype/src/psnames/psnamerr.h
+++ b/thirdparty/freetype/src/psnames/psnamerr.h
@@ -4,7 +4,7 @@
/* */
/* PS names module error codes (specification only). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psnames/psnames.c b/thirdparty/freetype/src/psnames/psnames.c
index 22466d6230..febb80d594 100644
--- a/thirdparty/freetype/src/psnames/psnames.c
+++ b/thirdparty/freetype/src/psnames/psnames.c
@@ -4,7 +4,7 @@
/* */
/* FreeType PSNames module component (body only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psnames/pspic.c b/thirdparty/freetype/src/psnames/pspic.c
index 8b9003439b..85a06f3603 100644
--- a/thirdparty/freetype/src/psnames/pspic.c
+++ b/thirdparty/freetype/src/psnames/pspic.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for psnames module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psnames/pspic.h b/thirdparty/freetype/src/psnames/pspic.h
index 14497e73fa..889780cc03 100644
--- a/thirdparty/freetype/src/psnames/pspic.h
+++ b/thirdparty/freetype/src/psnames/pspic.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for psnames module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psnames/pstables.h b/thirdparty/freetype/src/psnames/pstables.h
index 2a2b717d8f..79545ee039 100644
--- a/thirdparty/freetype/src/psnames/pstables.h
+++ b/thirdparty/freetype/src/psnames/pstables.h
@@ -4,7 +4,7 @@
/* */
/* PostScript glyph names. */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/psnames/rules.mk b/thirdparty/freetype/src/psnames/rules.mk
index 69fa732200..4d629d841c 100644
--- a/thirdparty/freetype/src/psnames/rules.mk
+++ b/thirdparty/freetype/src/psnames/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/raster/ftmisc.h b/thirdparty/freetype/src/raster/ftmisc.h
index d1e6627ab4..7e40119071 100644
--- a/thirdparty/freetype/src/raster/ftmisc.h
+++ b/thirdparty/freetype/src/raster/ftmisc.h
@@ -5,7 +5,7 @@
/* Miscellaneous macros for stand-alone rasterizer (specification */
/* only). */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used */
diff --git a/thirdparty/freetype/src/raster/ftraster.c b/thirdparty/freetype/src/raster/ftraster.c
index c5643f6334..4354730d54 100644
--- a/thirdparty/freetype/src/raster/ftraster.c
+++ b/thirdparty/freetype/src/raster/ftraster.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType glyph rasterizer (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/raster/ftraster.h b/thirdparty/freetype/src/raster/ftraster.h
index 6b3050cb3d..40b5d6d321 100644
--- a/thirdparty/freetype/src/raster/ftraster.h
+++ b/thirdparty/freetype/src/raster/ftraster.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType glyph rasterizer (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used */
diff --git a/thirdparty/freetype/src/raster/ftrend1.c b/thirdparty/freetype/src/raster/ftrend1.c
index 185a7f6fc2..a7ce9731d7 100644
--- a/thirdparty/freetype/src/raster/ftrend1.c
+++ b/thirdparty/freetype/src/raster/ftrend1.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType glyph rasterizer interface (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -97,12 +97,12 @@
FT_Render_Mode mode,
const FT_Vector* origin )
{
- FT_Error error;
- FT_Outline* outline;
- FT_BBox cbox, cbox0;
- FT_UInt width, height, pitch;
- FT_Bitmap* bitmap;
- FT_Memory memory;
+ FT_Error error = FT_Err_Ok;
+ FT_Outline* outline = &slot->outline;
+ FT_Bitmap* bitmap = &slot->bitmap;
+ FT_Memory memory = render->root.memory;
+ FT_Pos x_shift = 0;
+ FT_Pos y_shift = 0;
FT_Raster_Params params;
@@ -121,60 +121,6 @@
return FT_THROW( Cannot_Render_Glyph );
}
- outline = &slot->outline;
-
- /* translate the outline to the new origin if needed */
- if ( origin )
- FT_Outline_Translate( outline, origin->x, origin->y );
-
- /* compute the control box, and grid fit it */
- FT_Outline_Get_CBox( outline, &cbox0 );
-
- /* undocumented but confirmed: bbox values get rounded */
-#if 1
- cbox.xMin = FT_PIX_ROUND( cbox0.xMin );
- cbox.yMin = FT_PIX_ROUND( cbox0.yMin );
- cbox.xMax = FT_PIX_ROUND( cbox0.xMax );
- cbox.yMax = FT_PIX_ROUND( cbox0.yMax );
-#else
- cbox.xMin = FT_PIX_FLOOR( cbox.xMin );
- cbox.yMin = FT_PIX_FLOOR( cbox.yMin );
- cbox.xMax = FT_PIX_CEIL( cbox.xMax );
- cbox.yMax = FT_PIX_CEIL( cbox.yMax );
-#endif
-
- /* If either `width' or `height' round to 0, try */
- /* explicitly rounding up/down. In the case of */
- /* glyphs containing only one very narrow feature, */
- /* this gives the drop-out compensation in the scan */
- /* conversion code a chance to do its stuff. */
- width = (FT_UInt)( ( cbox.xMax - cbox.xMin ) >> 6 );
- if ( width == 0 )
- {
- cbox.xMin = FT_PIX_FLOOR( cbox0.xMin );
- cbox.xMax = FT_PIX_CEIL( cbox0.xMax );
-
- width = (FT_UInt)( ( cbox.xMax - cbox.xMin ) >> 6 );
- }
-
- height = (FT_UInt)( ( cbox.yMax - cbox.yMin ) >> 6 );
- if ( height == 0 )
- {
- cbox.yMin = FT_PIX_FLOOR( cbox0.yMin );
- cbox.yMax = FT_PIX_CEIL( cbox0.yMax );
-
- height = (FT_UInt)( ( cbox.yMax - cbox.yMin ) >> 6 );
- }
-
- if ( width > FT_USHORT_MAX || height > FT_USHORT_MAX )
- {
- error = FT_THROW( Invalid_Argument );
- goto Exit;
- }
-
- bitmap = &slot->bitmap;
- memory = render->root.memory;
-
/* release old bitmap buffer */
if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
{
@@ -182,39 +128,48 @@
slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
}
- pitch = ( ( width + 15 ) >> 4 ) << 1;
- bitmap->pixel_mode = FT_PIXEL_MODE_MONO;
-
- bitmap->width = width;
- bitmap->rows = height;
- bitmap->pitch = (int)pitch;
+ ft_glyphslot_preset_bitmap( slot, mode, origin );
- if ( FT_ALLOC_MULT( bitmap->buffer, height, pitch ) )
+ /* allocate new one */
+ if ( FT_ALLOC_MULT( bitmap->buffer, bitmap->rows, bitmap->pitch ) )
goto Exit;
slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
+ x_shift = -slot->bitmap_left * 64;
+ y_shift = ( (FT_Int)bitmap->rows - slot->bitmap_top ) * 64;
+
+ if ( origin )
+ {
+ x_shift += origin->x;
+ y_shift += origin->y;
+ }
+
/* translate outline to render it into the bitmap */
- FT_Outline_Translate( outline, -cbox.xMin, -cbox.yMin );
+ if ( x_shift || y_shift )
+ FT_Outline_Translate( outline, x_shift, y_shift );
/* set up parameters */
params.target = bitmap;
params.source = outline;
- params.flags = 0;
+ params.flags = FT_RASTER_FLAG_DEFAULT;
/* render outline into the bitmap */
error = render->raster_render( render->raster, &params );
- FT_Outline_Translate( outline, cbox.xMin, cbox.yMin );
-
- if ( error )
- goto Exit;
+ Exit:
+ if ( !error )
+ /* everything is fine; the glyph is now officially a bitmap */
+ slot->format = FT_GLYPH_FORMAT_BITMAP;
+ else if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
+ {
+ FT_FREE( bitmap->buffer );
+ slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
+ }
- slot->format = FT_GLYPH_FORMAT_BITMAP;
- slot->bitmap_left = (FT_Int)( cbox.xMin >> 6 );
- slot->bitmap_top = (FT_Int)( cbox.yMax >> 6 );
+ if ( x_shift || y_shift )
+ FT_Outline_Translate( outline, -x_shift, -y_shift );
- Exit:
return error;
}
diff --git a/thirdparty/freetype/src/raster/ftrend1.h b/thirdparty/freetype/src/raster/ftrend1.h
index cff702d140..2abdf2d703 100644
--- a/thirdparty/freetype/src/raster/ftrend1.h
+++ b/thirdparty/freetype/src/raster/ftrend1.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType glyph rasterizer interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/raster/module.mk b/thirdparty/freetype/src/raster/module.mk
index aad39cb56a..b115f416b2 100644
--- a/thirdparty/freetype/src/raster/module.mk
+++ b/thirdparty/freetype/src/raster/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/raster/raster.c b/thirdparty/freetype/src/raster/raster.c
index 46a6690b17..76edd21e1e 100644
--- a/thirdparty/freetype/src/raster/raster.c
+++ b/thirdparty/freetype/src/raster/raster.c
@@ -4,7 +4,7 @@
/* */
/* FreeType monochrome rasterer module component (body only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/raster/rasterrs.h b/thirdparty/freetype/src/raster/rasterrs.h
index 0d646908ad..22a3e15340 100644
--- a/thirdparty/freetype/src/raster/rasterrs.h
+++ b/thirdparty/freetype/src/raster/rasterrs.h
@@ -4,7 +4,7 @@
/* */
/* monochrome renderer error codes (specification only). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/raster/rastpic.c b/thirdparty/freetype/src/raster/rastpic.c
index 7085339b7b..1dc8981b8a 100644
--- a/thirdparty/freetype/src/raster/rastpic.c
+++ b/thirdparty/freetype/src/raster/rastpic.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for raster module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/raster/rastpic.h b/thirdparty/freetype/src/raster/rastpic.h
index dcd691310d..6d0877c423 100644
--- a/thirdparty/freetype/src/raster/rastpic.h
+++ b/thirdparty/freetype/src/raster/rastpic.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for raster module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/raster/rules.mk b/thirdparty/freetype/src/raster/rules.mk
index 0462c93177..9aef1f0bab 100644
--- a/thirdparty/freetype/src/raster/rules.mk
+++ b/thirdparty/freetype/src/raster/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/sfnt/module.mk b/thirdparty/freetype/src/sfnt/module.mk
index 81dea17de0..51ca67e784 100644
--- a/thirdparty/freetype/src/sfnt/module.mk
+++ b/thirdparty/freetype/src/sfnt/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/sfnt/pngshim.c b/thirdparty/freetype/src/sfnt/pngshim.c
index 560db4835a..16020266af 100644
--- a/thirdparty/freetype/src/sfnt/pngshim.c
+++ b/thirdparty/freetype/src/sfnt/pngshim.c
@@ -4,7 +4,7 @@
/* */
/* PNG Bitmap glyph support. */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* Google, Inc. */
/* Written by Stuart Gill and Behdad Esfahbod. */
/* */
@@ -82,42 +82,45 @@
typedef unsigned short v82 __attribute__(( vector_size( 16 ) ));
- /* process blocks of 16 bytes in one rush, which gives a nice speed-up */
- limit = row_info->rowbytes - 16 + 1;
- for ( ; i < limit; i += 16 )
+ if ( row_info->rowbytes > 15 )
{
- unsigned char* base = &data[i];
+ /* process blocks of 16 bytes in one rush, which gives a nice speed-up */
+ limit = row_info->rowbytes - 16 + 1;
+ for ( ; i < limit; i += 16 )
+ {
+ unsigned char* base = &data[i];
- v82 s, s0, s1, a;
+ v82 s, s0, s1, a;
- /* clang <= 3.9 can't apply scalar values to vectors */
- /* (or rather, it needs a different syntax) */
- v82 n0x80 = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
- v82 n0xFF = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
- v82 n8 = { 8, 8, 8, 8, 8, 8, 8, 8 };
+ /* clang <= 3.9 can't apply scalar values to vectors */
+ /* (or rather, it needs a different syntax) */
+ v82 n0x80 = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
+ v82 n0xFF = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ v82 n8 = { 8, 8, 8, 8, 8, 8, 8, 8 };
- v82 ma = { 1, 1, 3, 3, 5, 5, 7, 7 };
- v82 o1 = { 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF };
- v82 m0 = { 1, 0, 3, 2, 5, 4, 7, 6 };
+ v82 ma = { 1, 1, 3, 3, 5, 5, 7, 7 };
+ v82 o1 = { 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF };
+ v82 m0 = { 1, 0, 3, 2, 5, 4, 7, 6 };
- memcpy( &s, base, 16 ); /* RGBA RGBA RGBA RGBA */
- s0 = s & n0xFF; /* R B R B R B R B */
- s1 = s >> n8; /* G A G A G A G A */
+ ft_memcpy( &s, base, 16 ); /* RGBA RGBA RGBA RGBA */
+ s0 = s & n0xFF; /* R B R B R B R B */
+ s1 = s >> n8; /* G A G A G A G A */
- a = vector_shuffle( s1, ma ); /* A A A A A A A A */
- s1 |= o1; /* G 1 G 1 G 1 G 1 */
- s0 = vector_shuffle( s0, m0 ); /* B R B R B R B R */
+ a = vector_shuffle( s1, ma ); /* A A A A A A A A */
+ s1 |= o1; /* G 1 G 1 G 1 G 1 */
+ s0 = vector_shuffle( s0, m0 ); /* B R B R B R B R */
- s0 *= a;
- s1 *= a;
- s0 += n0x80;
- s1 += n0x80;
- s0 = ( s0 + ( s0 >> n8 ) ) >> n8;
- s1 = ( s1 + ( s1 >> n8 ) ) >> n8;
+ s0 *= a;
+ s1 *= a;
+ s0 += n0x80;
+ s1 += n0x80;
+ s0 = ( s0 + ( s0 >> n8 ) ) >> n8;
+ s1 = ( s1 + ( s1 >> n8 ) ) >> n8;
- s = s0 | ( s1 << n8 );
- memcpy( base, &s, 16 );
+ s = s0 | ( s1 << n8 );
+ ft_memcpy( base, &s, 16 );
+ }
}
#endif /* use `vector_size' */
@@ -234,7 +237,7 @@
return;
}
- memcpy( data, stream->cursor, length );
+ ft_memcpy( data, stream->cursor, length );
FT_FRAME_EXIT();
}
diff --git a/thirdparty/freetype/src/sfnt/pngshim.h b/thirdparty/freetype/src/sfnt/pngshim.h
index 344eceac12..194238c3a2 100644
--- a/thirdparty/freetype/src/sfnt/pngshim.h
+++ b/thirdparty/freetype/src/sfnt/pngshim.h
@@ -4,7 +4,7 @@
/* */
/* PNG Bitmap glyph support. */
/* */
-/* Copyright 2013-2017 by */
+/* Copyright 2013-2018 by */
/* Google, Inc. */
/* Written by Stuart Gill and Behdad Esfahbod. */
/* */
diff --git a/thirdparty/freetype/src/sfnt/rules.mk b/thirdparty/freetype/src/sfnt/rules.mk
index 230d56c946..83acc66a8f 100644
--- a/thirdparty/freetype/src/sfnt/rules.mk
+++ b/thirdparty/freetype/src/sfnt/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/sfnt/sfdriver.c b/thirdparty/freetype/src/sfnt/sfdriver.c
index 991433ee4c..303e1ca9f1 100644
--- a/thirdparty/freetype/src/sfnt/sfdriver.c
+++ b/thirdparty/freetype/src/sfnt/sfdriver.c
@@ -4,7 +4,7 @@
/* */
/* High-level SFNT driver interface (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -862,7 +862,8 @@
NULL,
&mm_var );
- if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
+ if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) &&
+ !FT_IS_VARIATION( FT_FACE( face ) ) )
{
SFNT_Service sfnt = (SFNT_Service)face->sfnt;
@@ -1029,7 +1030,9 @@
return face->postscript_name;
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
- if ( face->blend )
+ if ( face->blend &&
+ ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ||
+ FT_IS_VARIATION( FT_FACE( face ) ) ) )
{
face->postscript_name = sfnt_get_var_ps_name( face );
return face->postscript_name;
diff --git a/thirdparty/freetype/src/sfnt/sfdriver.h b/thirdparty/freetype/src/sfnt/sfdriver.h
index 38710b60f2..81c22d2887 100644
--- a/thirdparty/freetype/src/sfnt/sfdriver.h
+++ b/thirdparty/freetype/src/sfnt/sfdriver.h
@@ -4,7 +4,7 @@
/* */
/* High-level SFNT driver interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/sferrors.h b/thirdparty/freetype/src/sfnt/sferrors.h
index 3cf73d725d..74003d4b38 100644
--- a/thirdparty/freetype/src/sfnt/sferrors.h
+++ b/thirdparty/freetype/src/sfnt/sferrors.h
@@ -4,7 +4,7 @@
/* */
/* SFNT error codes (specification only). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/sfnt.c b/thirdparty/freetype/src/sfnt/sfnt.c
index 6cf8c9ef30..8b9a6b345d 100644
--- a/thirdparty/freetype/src/sfnt/sfnt.c
+++ b/thirdparty/freetype/src/sfnt/sfnt.c
@@ -4,7 +4,7 @@
/* */
/* Single object library component. */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/sfntpic.c b/thirdparty/freetype/src/sfnt/sfntpic.c
index 8eadd601fd..db2d816ce6 100644
--- a/thirdparty/freetype/src/sfnt/sfntpic.c
+++ b/thirdparty/freetype/src/sfnt/sfntpic.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for sfnt module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/sfntpic.h b/thirdparty/freetype/src/sfnt/sfntpic.h
index 3afb668db0..8f43122d81 100644
--- a/thirdparty/freetype/src/sfnt/sfntpic.h
+++ b/thirdparty/freetype/src/sfnt/sfntpic.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for sfnt module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/sfobjs.c b/thirdparty/freetype/src/sfnt/sfobjs.c
index 69bf0a5c3d..6ba8509f56 100644
--- a/thirdparty/freetype/src/sfnt/sfobjs.c
+++ b/thirdparty/freetype/src/sfnt/sfobjs.c
@@ -4,7 +4,7 @@
/* */
/* SFNT object management (base). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -962,8 +962,6 @@
FT_Byte* instance_values = NULL;
- face->is_default_instance = 1;
-
instance_index = FT_ABS( face_instance_index ) >> 16;
/* test whether current face is a GX font with named instances */
@@ -1146,6 +1144,8 @@
FT_Bool has_outline;
FT_Bool is_apple_sbit;
FT_Bool is_apple_sbix;
+ FT_Bool has_CBLC;
+ FT_Bool has_CBDT;
FT_Bool ignore_typographic_family = FALSE;
FT_Bool ignore_typographic_subfamily = FALSE;
@@ -1226,6 +1226,13 @@
goto Exit;
}
+ has_CBLC = !face->goto_table( face, TTAG_CBLC, stream, 0 );
+ has_CBDT = !face->goto_table( face, TTAG_CBDT, stream, 0 );
+
+ /* Ignore outlines for CBLC/CBDT fonts. */
+ if ( has_CBLC || has_CBDT )
+ has_outline = FALSE;
+
/* OpenType 1.8.2 introduced limits to this value; */
/* however, they make sense for older SFNT fonts also */
if ( face->header.Units_Per_EM < 16 ||
@@ -1686,9 +1693,9 @@
(FT_Short)( face->vertical_info ? face->vertical.advance_Height_Max
: root->height );
- /* See http://www.microsoft.com/OpenType/OTSpec/post.htm -- */
- /* Adjust underline position from top edge to centre of */
- /* stroke to convert TrueType meaning to FreeType meaning. */
+ /* See https://www.microsoft.com/typography/otspec/post.htm -- */
+ /* Adjust underline position from top edge to centre of */
+ /* stroke to convert TrueType meaning to FreeType meaning. */
root->underline_position = face->postscript.underlinePosition -
face->postscript.underlineThickness / 2;
root->underline_thickness = face->postscript.underlineThickness;
diff --git a/thirdparty/freetype/src/sfnt/sfobjs.h b/thirdparty/freetype/src/sfnt/sfobjs.h
index 705381459a..1b8d1be5b1 100644
--- a/thirdparty/freetype/src/sfnt/sfobjs.h
+++ b/thirdparty/freetype/src/sfnt/sfobjs.h
@@ -4,7 +4,7 @@
/* */
/* SFNT object management (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/ttbdf.c b/thirdparty/freetype/src/sfnt/ttbdf.c
index 2196e3791e..534201f229 100644
--- a/thirdparty/freetype/src/sfnt/ttbdf.c
+++ b/thirdparty/freetype/src/sfnt/ttbdf.c
@@ -4,7 +4,7 @@
/* */
/* TrueType and OpenType embedded BDF properties (body). */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/ttbdf.h b/thirdparty/freetype/src/sfnt/ttbdf.h
index 398b620600..809a663001 100644
--- a/thirdparty/freetype/src/sfnt/ttbdf.h
+++ b/thirdparty/freetype/src/sfnt/ttbdf.h
@@ -4,7 +4,7 @@
/* */
/* TrueType and OpenType embedded BDF properties (specification). */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -28,6 +28,8 @@
FT_BEGIN_HEADER
+#ifdef TT_CONFIG_OPTION_BDF
+
FT_LOCAL( void )
tt_face_free_bdf_props( TT_Face face );
@@ -37,6 +39,8 @@ FT_BEGIN_HEADER
const char* property_name,
BDF_PropertyRec *aprop );
+#endif /* TT_CONFIG_OPTION_BDF */
+
FT_END_HEADER
diff --git a/thirdparty/freetype/src/sfnt/ttcmap.c b/thirdparty/freetype/src/sfnt/ttcmap.c
index b995e5c050..996e66485f 100644
--- a/thirdparty/freetype/src/sfnt/ttcmap.c
+++ b/thirdparty/freetype/src/sfnt/ttcmap.c
@@ -4,7 +4,7 @@
/* */
/* TrueType character mapping table (cmap) support (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -222,10 +222,10 @@
/***** The following charmap lookup and iteration functions all *****/
/***** assume that the value `charcode' fulfills the following. *****/
/***** *****/
- /***** - For one byte characters, `charcode' is simply the *****/
+ /***** - For one-byte characters, `charcode' is simply the *****/
/***** character code. *****/
/***** *****/
- /***** - For two byte characters, `charcode' is the 2-byte *****/
+ /***** - For two-byte characters, `charcode' is the 2-byte *****/
/***** character code in big endian format. More precisely: *****/
/***** *****/
/***** (charcode >> 8) is the first byte value *****/
@@ -252,11 +252,11 @@
/* subs 518 SUBHEAD[NSUBS] sub-headers array */
/* glyph_ids 518+NSUB*8 USHORT[] glyph ID array */
/* */
- /* The `keys' table is used to map charcode high-bytes to sub-headers. */
+ /* The `keys' table is used to map charcode high bytes to sub-headers. */
/* The value of `NSUBS' is the number of sub-headers defined in the */
/* table and is computed by finding the maximum of the `keys' table. */
/* */
- /* Note that for any n, `keys[n]' is a byte offset within the `subs' */
+ /* Note that for any `n', `keys[n]' is a byte offset within the `subs' */
/* table, i.e., it is the corresponding sub-header index multiplied */
/* by 8. */
/* */
@@ -269,8 +269,8 @@
/* delta 4 SHORT see below */
/* offset 6 USHORT see below */
/* */
- /* A sub-header defines, for each high-byte, the range of valid */
- /* low-bytes within the charmap. Note that the range defined by `first' */
+ /* A sub-header defines, for each high byte, the range of valid */
+ /* low bytes within the charmap. Note that the range defined by `first' */
/* and `count' must be completely included in the interval [0..255] */
/* according to the specification. */
/* */
@@ -360,7 +360,7 @@
/* check range within 0..255 */
if ( valid->level >= FT_VALIDATE_PARANOID )
{
- if ( first_code >= 256 || first_code + code_count > 256 )
+ if ( first_code >= 256 || code_count > 256 - first_code )
FT_INVALID_DATA;
}
@@ -412,7 +412,7 @@
{
FT_UInt char_lo = (FT_UInt)( char_code & 0xFF );
FT_UInt char_hi = (FT_UInt)( char_code >> 8 );
- FT_Byte* p = table + 6; /* keys table */
+ FT_Byte* p = table + 6; /* keys table */
FT_Byte* subs = table + 518; /* subheaders table */
FT_Byte* sub;
@@ -425,8 +425,8 @@
sub = subs; /* jump to first sub-header */
/* check that the sub-header for this byte is 0, which */
- /* indicates that it is really a valid one-byte value */
- /* Otherwise, return 0 */
+ /* indicates that it is really a valid one-byte value; */
+ /* otherwise, return 0 */
/* */
p += char_lo * 2;
if ( TT_PEEK_USHORT( p ) != 0 )
@@ -445,6 +445,7 @@
if ( sub == subs )
goto Exit;
}
+
result = sub;
}
@@ -517,8 +518,19 @@
FT_UInt pos, idx;
+ if ( char_lo >= start + count && charcode <= 0xFF )
+ {
+ /* this happens only for a malformed cmap */
+ charcode = 0x100;
+ continue;
+ }
+
if ( offset == 0 )
+ {
+ if ( charcode == 0x100 )
+ goto Exit; /* this happens only for a malformed cmap */
goto Next_SubHeader;
+ }
if ( char_lo < start )
{
@@ -545,11 +557,20 @@
}
}
}
+
+ /* if unsuccessful, avoid `charcode' leaving */
+ /* the current 256-character block */
+ if ( count )
+ charcode--;
}
- /* jump to next sub-header, i.e. higher byte value */
+ /* If `charcode' is <= 0xFF, retry with `charcode + 1'. */
+ /* Otherwise jump to the next 256-character block and retry. */
Next_SubHeader:
- charcode = FT_PAD_FLOOR( charcode, 256 ) + 256;
+ if ( charcode <= 0xFF )
+ charcode++;
+ else
+ charcode = FT_PAD_FLOOR( charcode, 0x100 ) + 0x100;
}
Exit:
diff --git a/thirdparty/freetype/src/sfnt/ttcmap.h b/thirdparty/freetype/src/sfnt/ttcmap.h
index f7de0437b0..d264d99d2c 100644
--- a/thirdparty/freetype/src/sfnt/ttcmap.h
+++ b/thirdparty/freetype/src/sfnt/ttcmap.h
@@ -4,7 +4,7 @@
/* */
/* TrueType character mapping table (cmap) support (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/ttcmapc.h b/thirdparty/freetype/src/sfnt/ttcmapc.h
index 9a5e70825e..4980e9dd3d 100644
--- a/thirdparty/freetype/src/sfnt/ttcmapc.h
+++ b/thirdparty/freetype/src/sfnt/ttcmapc.h
@@ -4,7 +4,7 @@
/* */
/* TT CMAP classes definitions (specification only). */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/ttkern.c b/thirdparty/freetype/src/sfnt/ttkern.c
index 53d2436ae5..68f15a2010 100644
--- a/thirdparty/freetype/src/sfnt/ttkern.c
+++ b/thirdparty/freetype/src/sfnt/ttkern.c
@@ -5,7 +5,7 @@
/* Load the basic TrueType kerning table. This doesn't handle */
/* kerning data within the GPOS table at the moment. */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/ttkern.h b/thirdparty/freetype/src/sfnt/ttkern.h
index db1a30bdb0..4e45d0964b 100644
--- a/thirdparty/freetype/src/sfnt/ttkern.h
+++ b/thirdparty/freetype/src/sfnt/ttkern.h
@@ -5,7 +5,7 @@
/* Load the basic TrueType kerning table. This doesn't handle */
/* kerning data within the GPOS table at the moment. */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/ttload.c b/thirdparty/freetype/src/sfnt/ttload.c
index df99baa53e..a86a546c3d 100644
--- a/thirdparty/freetype/src/sfnt/ttload.c
+++ b/thirdparty/freetype/src/sfnt/ttload.c
@@ -5,7 +5,7 @@
/* Load the basic TrueType tables, i.e., tables that can be either in */
/* TTF or OTF fonts (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -338,7 +338,7 @@
SFNT_HeaderRec sfnt;
FT_Error error;
FT_Memory memory = stream->memory;
- FT_UShort nn, valid_entries;
+ FT_UShort nn, valid_entries = 0;
static const FT_Frame_Field offset_table_fields[] =
{
diff --git a/thirdparty/freetype/src/sfnt/ttload.h b/thirdparty/freetype/src/sfnt/ttload.h
index 296da86ed3..f94be8b7bd 100644
--- a/thirdparty/freetype/src/sfnt/ttload.h
+++ b/thirdparty/freetype/src/sfnt/ttload.h
@@ -5,7 +5,7 @@
/* Load the basic TrueType tables, i.e., tables that can be either in */
/* TTF or OTF fonts (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/ttmtx.c b/thirdparty/freetype/src/sfnt/ttmtx.c
index 394c6db85c..6ddda95b56 100644
--- a/thirdparty/freetype/src/sfnt/ttmtx.c
+++ b/thirdparty/freetype/src/sfnt/ttmtx.c
@@ -4,7 +4,7 @@
/* */
/* Load the metrics tables common to TTF and OTF fonts (body). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/ttmtx.h b/thirdparty/freetype/src/sfnt/ttmtx.h
index 2b93ab2f0e..ab00acd795 100644
--- a/thirdparty/freetype/src/sfnt/ttmtx.h
+++ b/thirdparty/freetype/src/sfnt/ttmtx.h
@@ -4,7 +4,7 @@
/* */
/* Load the metrics tables common to TTF and OTF fonts (specification). */
/* */
-/* Copyright 2006-2017 by */
+/* Copyright 2006-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/ttpost.c b/thirdparty/freetype/src/sfnt/ttpost.c
index 69929c8d45..6de99ef977 100644
--- a/thirdparty/freetype/src/sfnt/ttpost.c
+++ b/thirdparty/freetype/src/sfnt/ttpost.c
@@ -5,7 +5,7 @@
/* PostScript name table processing for TrueType and OpenType fonts */
/* (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -473,8 +473,8 @@
/* idx :: The glyph index. */
/* */
/* <InOut> */
- /* PSname :: The address of a string pointer. Will be NULL in case */
- /* of error, otherwise it is a pointer to the glyph name. */
+ /* PSname :: The address of a string pointer. Undefined in case of */
+ /* error, otherwise it is a pointer to the glyph name. */
/* */
/* You must not modify the returned string! */
/* */
diff --git a/thirdparty/freetype/src/sfnt/ttpost.h b/thirdparty/freetype/src/sfnt/ttpost.h
index 722485e32d..3bec07e445 100644
--- a/thirdparty/freetype/src/sfnt/ttpost.h
+++ b/thirdparty/freetype/src/sfnt/ttpost.h
@@ -5,7 +5,7 @@
/* PostScript name table processing for TrueType and OpenType fonts */
/* (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/sfnt/ttsbit.c b/thirdparty/freetype/src/sfnt/ttsbit.c
index f41847b0af..33b8640bc3 100644
--- a/thirdparty/freetype/src/sfnt/ttsbit.c
+++ b/thirdparty/freetype/src/sfnt/ttsbit.c
@@ -4,7 +4,7 @@
/* */
/* TrueType and OpenType embedded bitmap support (body). */
/* */
-/* Copyright 2005-2017 by */
+/* Copyright 2005-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* Copyright 2013 by Google, Inc. */
@@ -1007,8 +1007,9 @@
goto Fail;
}
- FT_TRACE3(( "tt_sbit_decoder_load_compound: loading %d components\n",
- num_components ));
+ FT_TRACE3(( "tt_sbit_decoder_load_compound: loading %d component%s\n",
+ num_components,
+ num_components == 1 ? "" : "s" ));
for ( nn = 0; nn < num_components; nn++ )
{
@@ -1513,7 +1514,7 @@
FT_FRAME_EXIT();
if ( glyph_start == glyph_end )
- return FT_THROW( Invalid_Argument );
+ return FT_THROW( Missing_Bitmap );
if ( glyph_start > glyph_end ||
glyph_end - glyph_start < 8 ||
face->ebdt_size - strike_offset < glyph_end )
diff --git a/thirdparty/freetype/src/sfnt/ttsbit.h b/thirdparty/freetype/src/sfnt/ttsbit.h
index e859ddda45..ce2af3c162 100644
--- a/thirdparty/freetype/src/sfnt/ttsbit.h
+++ b/thirdparty/freetype/src/sfnt/ttsbit.h
@@ -4,7 +4,7 @@
/* */
/* TrueType and OpenType embedded bitmap support (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/smooth/ftgrays.c b/thirdparty/freetype/src/smooth/ftgrays.c
index df645e66c9..803a19e415 100644
--- a/thirdparty/freetype/src/smooth/ftgrays.c
+++ b/thirdparty/freetype/src/smooth/ftgrays.c
@@ -4,7 +4,7 @@
/* */
/* A new `perfect' anti-aliasing renderer (body). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -370,7 +370,7 @@ typedef ptrdiff_t FT_PtrDist;
/* optimize a division and modulo operation on the same parameters */
/* into a single call to `__aeabi_idivmod'. See */
/* */
- /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43721 */
+ /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43721 */
#undef FT_DIV_MOD
#define FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \
FT_BEGIN_STMNT \
@@ -404,7 +404,7 @@ typedef ptrdiff_t FT_PtrDist;
/* need to define them to "float" or "double" when experimenting with */
/* new algorithms */
- typedef long TPos; /* sub-pixel coordinate */
+ typedef long TPos; /* subpixel coordinate */
typedef int TCoord; /* integer scanline/pixel coordinate */
typedef int TArea; /* cell areas, coordinate products */
@@ -582,8 +582,8 @@ typedef ptrdiff_t FT_PtrDist;
if ( ex < ras.min_ex )
ex = ras.min_ex - 1;
- /* record the current one if it is valid */
- if ( !ras.invalid )
+ /* record the current one if it is valid and substantial */
+ if ( !ras.invalid && ( ras.area || ras.cover ) )
gray_record_cell( RAS_VAR );
ras.area = 0;
@@ -1300,8 +1300,6 @@ typedef ptrdiff_t FT_PtrDist;
int y;
- FT_TRACE7(( "gray_sweep: start\n" ));
-
for ( y = ras.min_ey; y < ras.max_ey; y++ )
{
PCell cell = ras.ycells[y - ras.min_ey];
@@ -1327,8 +1325,6 @@ typedef ptrdiff_t FT_PtrDist;
if ( cover != 0 )
gray_hline( RAS_VAR_ x, y, cover, ras.max_ex - x );
}
-
- FT_TRACE7(( "gray_sweep: end\n" ));
}
@@ -1722,8 +1718,11 @@ typedef ptrdiff_t FT_PtrDist;
if ( !ras.invalid )
gray_record_cell( RAS_VAR );
- FT_TRACE7(( "band [%d..%d]: %d cells\n",
- ras.min_ey, ras.max_ey, ras.num_cells ));
+ FT_TRACE7(( "band [%d..%d]: %d cell%s\n",
+ ras.min_ey,
+ ras.max_ey,
+ ras.num_cells,
+ ras.num_cells == 1 ? "" : "s" ));
}
else
{
@@ -1740,35 +1739,43 @@ typedef ptrdiff_t FT_PtrDist;
static int
gray_convert_glyph( RAS_ARG )
{
+ const TCoord yMin = ras.min_ey;
+ const TCoord yMax = ras.max_ey;
+ const TCoord xMin = ras.min_ex;
+ const TCoord xMax = ras.max_ex;
+
TCell buffer[FT_MAX_GRAY_POOL];
- TCoord band_size = FT_MAX_GRAY_POOL / 8;
- TCoord count = ras.max_ey - ras.min_ey;
- int num_bands;
- TCoord min, max, max_y;
+ size_t height = (size_t)( yMax - yMin );
+ size_t n = FT_MAX_GRAY_POOL / 8;
+ TCoord y;
TCoord bands[32]; /* enough to accommodate bisections */
TCoord* band;
/* set up vertical bands */
- if ( count > band_size )
+ if ( height > n )
{
/* two divisions rounded up */
- num_bands = (int)( ( count + band_size - 1) / band_size );
- band_size = ( count + num_bands - 1 ) / num_bands;
+ n = ( height + n - 1 ) / n;
+ height = ( height + n - 1 ) / n;
}
- min = ras.min_ey;
- max_y = ras.max_ey;
+ /* memory management */
+ n = ( height * sizeof ( PCell ) + sizeof ( TCell ) - 1 ) / sizeof ( TCell );
- for ( ; min < max_y; min = max )
+ ras.cells = buffer + n;
+ ras.max_cells = (FT_PtrDist)( FT_MAX_GRAY_POOL - n );
+ ras.ycells = (PCell*)buffer;
+
+ for ( y = yMin; y < yMax; )
{
- max = min + band_size;
- if ( max > max_y )
- max = max_y;
+ ras.min_ey = y;
+ y += height;
+ ras.max_ey = FT_MIN( y, yMax );
band = bands;
- band[1] = min;
- band[0] = max;
+ band[1] = xMin;
+ band[0] = xMax;
do
{
@@ -1776,27 +1783,12 @@ typedef ptrdiff_t FT_PtrDist;
int error;
- /* memory management */
- {
- size_t ycount = (size_t)width;
- size_t cell_start;
-
-
- cell_start = ( ycount * sizeof ( PCell ) + sizeof ( TCell ) - 1 ) /
- sizeof ( TCell );
-
- ras.cells = buffer + cell_start;
- ras.max_cells = (FT_PtrDist)( FT_MAX_GRAY_POOL - cell_start );
- ras.num_cells = 0;
-
- ras.ycells = (PCell*)buffer;
- while ( ycount )
- ras.ycells[--ycount] = NULL;
- }
+ FT_MEM_ZERO( ras.ycells, height * sizeof ( PCell ) );
+ ras.num_cells = 0;
ras.invalid = 1;
- ras.min_ey = band[1];
- ras.max_ey = band[0];
+ ras.min_ex = band[1];
+ ras.max_ex = band[0];
error = gray_convert_glyph_inner( RAS_VAR );
@@ -1812,8 +1804,7 @@ typedef ptrdiff_t FT_PtrDist;
/* render pool overflow; we will reduce the render band by half */
width >>= 1;
- /* This is too complex for a single scanline; there must */
- /* be some problems. */
+ /* this should never happen even with tiny rendering pool */
if ( width == 0 )
{
FT_TRACE7(( "gray_convert_glyph: rotten glyph\n" ));
diff --git a/thirdparty/freetype/src/smooth/ftgrays.h b/thirdparty/freetype/src/smooth/ftgrays.h
index a5447da1a9..9e11ca675e 100644
--- a/thirdparty/freetype/src/smooth/ftgrays.h
+++ b/thirdparty/freetype/src/smooth/ftgrays.h
@@ -4,7 +4,7 @@
/* */
/* FreeType smooth renderer declaration */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/smooth/ftsmerrs.h b/thirdparty/freetype/src/smooth/ftsmerrs.h
index a528c61832..226dc1b001 100644
--- a/thirdparty/freetype/src/smooth/ftsmerrs.h
+++ b/thirdparty/freetype/src/smooth/ftsmerrs.h
@@ -4,7 +4,7 @@
/* */
/* smooth renderer error codes (specification only). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/smooth/ftsmooth.c b/thirdparty/freetype/src/smooth/ftsmooth.c
index 963435de15..ef176bdf1e 100644
--- a/thirdparty/freetype/src/smooth/ftsmooth.c
+++ b/thirdparty/freetype/src/smooth/ftsmooth.c
@@ -4,7 +4,7 @@
/* */
/* Anti-aliasing renderer interface (body). */
/* */
-/* Copyright 2000-2017 by */
+/* Copyright 2000-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -97,74 +97,17 @@
const FT_Vector* origin,
FT_Render_Mode required_mode )
{
- FT_Error error;
+ FT_Error error = FT_Err_Ok;
FT_Outline* outline = &slot->outline;
FT_Bitmap* bitmap = &slot->bitmap;
FT_Memory memory = render->root.memory;
- FT_BBox cbox;
FT_Pos x_shift = 0;
FT_Pos y_shift = 0;
- FT_Pos x_left, y_top;
- FT_Pos width, height, pitch;
FT_Int hmul = ( mode == FT_RENDER_MODE_LCD );
FT_Int vmul = ( mode == FT_RENDER_MODE_LCD_V );
FT_Raster_Params params;
- FT_Bool have_outline_shifted = FALSE;
- FT_Bool have_buffer = FALSE;
-
-#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
-
- FT_LcdFiveTapFilter lcd_weights = { 0 };
- FT_Bool have_custom_weight = FALSE;
- FT_Bitmap_LcdFilterFunc lcd_filter_func = NULL;
-
-
- if ( slot->face )
- {
- FT_Char i;
-
-
- for ( i = 0; i < FT_LCD_FILTER_FIVE_TAPS; i++ )
- if ( slot->face->internal->lcd_weights[i] != 0 )
- {
- have_custom_weight = TRUE;
- break;
- }
- }
-
- /*
- * The LCD filter can be set library-wide and per-face. Face overrides
- * library. If the face filter weights are all zero (the default), it
- * means that the library default should be used.
- */
- if ( have_custom_weight )
- {
- /*
- * A per-font filter is set. It always uses the default 5-tap
- * in-place FIR filter.
- */
- ft_memcpy( lcd_weights,
- slot->face->internal->lcd_weights,
- FT_LCD_FILTER_FIVE_TAPS );
- lcd_filter_func = ft_lcd_filter_fir;
- }
- else
- {
- /*
- * The face's lcd_weights is {0, 0, 0, 0, 0}, meaning `use library
- * default'. If the library is set to use no LCD filtering
- * (lcd_filter_func == NULL), `lcd_filter_func' here is also set to
- * NULL and the tests further below pass over the filtering process.
- */
- ft_memcpy( lcd_weights,
- slot->library->lcd_weights,
- FT_LCD_FILTER_FIVE_TAPS );
- lcd_filter_func = slot->library->lcd_filter_func;
- }
-
-#endif /*FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
/* check glyph image format */
if ( slot->format != render->glyph_format )
@@ -180,100 +123,6 @@
goto Exit;
}
- if ( origin )
- {
- x_shift = origin->x;
- y_shift = origin->y;
- }
-
- /* compute the control box, and grid fit it */
- /* taking into account the origin shift */
- FT_Outline_Get_CBox( outline, &cbox );
-
-#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
-
- /* add minimal padding for LCD rendering */
- if ( hmul )
- {
- cbox.xMax += 21;
- cbox.xMin -= 21;
- }
-
- if ( vmul )
- {
- cbox.yMax += 21;
- cbox.yMin -= 21;
- }
-
-#else /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
-
- /* add minimal padding for LCD filter depending on specific weights */
- if ( lcd_filter_func )
- {
- if ( hmul )
- {
- cbox.xMax += lcd_weights[4] ? 43
- : lcd_weights[3] ? 22 : 0;
- cbox.xMin -= lcd_weights[0] ? 43
- : lcd_weights[1] ? 22 : 0;
- }
-
- if ( vmul )
- {
- cbox.yMax += lcd_weights[4] ? 43
- : lcd_weights[3] ? 22 : 0;
- cbox.yMin -= lcd_weights[0] ? 43
- : lcd_weights[1] ? 22 : 0;
- }
- }
-
-#endif /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
-
- cbox.xMin = FT_PIX_FLOOR( cbox.xMin + x_shift );
- cbox.yMin = FT_PIX_FLOOR( cbox.yMin + y_shift );
- cbox.xMax = FT_PIX_CEIL( cbox.xMax + x_shift );
- cbox.yMax = FT_PIX_CEIL( cbox.yMax + y_shift );
-
- x_shift -= cbox.xMin;
- y_shift -= cbox.yMin;
-
- x_left = cbox.xMin >> 6;
- y_top = cbox.yMax >> 6;
-
- width = (FT_ULong)( cbox.xMax - cbox.xMin ) >> 6;
- height = (FT_ULong)( cbox.yMax - cbox.yMin ) >> 6;
-
- pitch = width;
- if ( hmul )
- {
- width *= 3;
- pitch = FT_PAD_CEIL( width, 4 );
- }
-
- if ( vmul )
- height *= 3;
-
- /*
- * XXX: on 16bit system, we return an error for huge bitmap
- * to prevent an overflow.
- */
- if ( x_left > FT_INT_MAX || y_top > FT_INT_MAX ||
- x_left < FT_INT_MIN || y_top < FT_INT_MIN )
- {
- error = FT_THROW( Invalid_Pixel_Size );
- goto Exit;
- }
-
- /* Required check is (pitch * height < FT_ULONG_MAX), */
- /* but we care realistic cases only. Always pitch <= width. */
- if ( width > 0x7FFF || height > 0x7FFF )
- {
- FT_ERROR(( "ft_smooth_render_generic: glyph too large: %u x %u\n",
- width, height ));
- error = FT_THROW( Raster_Overflow );
- goto Exit;
- }
-
/* release old bitmap buffer */
if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
{
@@ -281,30 +130,30 @@
slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
}
+ ft_glyphslot_preset_bitmap( slot, mode, origin );
+
/* allocate new one */
- if ( FT_ALLOC( bitmap->buffer, (FT_ULong)( pitch * height ) ) )
+ if ( FT_ALLOC_MULT( bitmap->buffer, bitmap->rows, bitmap->pitch ) )
goto Exit;
- else
- have_buffer = TRUE;
slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
- slot->format = FT_GLYPH_FORMAT_BITMAP;
- slot->bitmap_left = (FT_Int)x_left;
- slot->bitmap_top = (FT_Int)y_top;
+ x_shift = 64 * -slot->bitmap_left;
+ y_shift = 64 * -slot->bitmap_top;
+ if ( bitmap->pixel_mode == FT_PIXEL_MODE_LCD_V )
+ y_shift += 64 * (FT_Int)bitmap->rows / 3;
+ else
+ y_shift += 64 * (FT_Int)bitmap->rows;
- bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
- bitmap->num_grays = 256;
- bitmap->width = (unsigned int)width;
- bitmap->rows = (unsigned int)height;
- bitmap->pitch = pitch;
+ if ( origin )
+ {
+ x_shift += origin->x;
+ y_shift += origin->y;
+ }
/* translate outline to render it into the bitmap */
if ( x_shift || y_shift )
- {
FT_Outline_Translate( outline, x_shift, y_shift );
- have_outline_shifted = TRUE;
- }
/* set up parameters */
params.target = bitmap;
@@ -351,45 +200,68 @@
if ( error )
goto Exit;
- if ( lcd_filter_func )
- lcd_filter_func( bitmap, mode, lcd_weights );
+ /* finally apply filtering */
+ if ( hmul || vmul )
+ {
+ FT_Byte* lcd_weights;
+ FT_Bitmap_LcdFilterFunc lcd_filter_func;
+
+
+ /* Per-face LCD filtering takes priority if set up. */
+ if ( slot->face && slot->face->internal->lcd_filter_func )
+ {
+ lcd_weights = slot->face->internal->lcd_weights;
+ lcd_filter_func = slot->face->internal->lcd_filter_func;
+ }
+ else
+ {
+ lcd_weights = slot->library->lcd_weights;
+ lcd_filter_func = slot->library->lcd_filter_func;
+ }
+
+ if ( lcd_filter_func )
+ lcd_filter_func( bitmap, mode, lcd_weights );
+ }
#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
if ( hmul ) /* lcd */
{
FT_Byte* line;
- FT_Byte* temp;
- FT_Int i, j;
+ FT_Byte* temp = NULL;
+ FT_UInt i, j;
+
+ unsigned int height = bitmap->rows;
+ unsigned int width = bitmap->width;
+ int pitch = bitmap->pitch;
/* Render 3 separate monochrome bitmaps, shifting the outline */
/* by 1/3 pixel. */
width /= 3;
- FT_Outline_Translate( outline, 21, 0 );
+ bitmap->buffer += width;
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
FT_Outline_Translate( outline, -21, 0 );
+ x_shift -= 21;
bitmap->buffer += width;
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
- FT_Outline_Translate( outline, -21, 0 );
- bitmap->buffer += width;
+ FT_Outline_Translate( outline, 42, 0 );
+ x_shift += 42;
+ bitmap->buffer -= 2 * width;
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
- FT_Outline_Translate( outline, 21, 0 );
- bitmap->buffer -= 2 * width;
-
/* XXX: Rearrange the bytes according to FT_PIXEL_MODE_LCD. */
/* XXX: It is more efficient to render every third byte above. */
@@ -398,7 +270,7 @@
for ( i = 0; i < height; i++ )
{
- line = bitmap->buffer + i * pitch;
+ line = bitmap->buffer + i * (FT_ULong)pitch;
for ( j = 0; j < width; j++ )
{
temp[3 * j ] = line[j];
@@ -412,60 +284,59 @@
}
else if ( vmul ) /* lcd_v */
{
+ int pitch = bitmap->pitch;
+
+
/* Render 3 separate monochrome bitmaps, shifting the outline */
/* by 1/3 pixel. Triple the pitch to render on each third row. */
bitmap->pitch *= 3;
bitmap->rows /= 3;
- FT_Outline_Translate( outline, 0, 21 );
- bitmap->buffer += 2 * pitch;
+ bitmap->buffer += pitch;
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
- FT_Outline_Translate( outline, 0, -21 );
- bitmap->buffer -= pitch;
+ FT_Outline_Translate( outline, 0, 21 );
+ y_shift += 21;
+ bitmap->buffer += pitch;
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
- FT_Outline_Translate( outline, 0, -21 );
- bitmap->buffer -= pitch;
+ FT_Outline_Translate( outline, 0, -42 );
+ y_shift -= 42;
+ bitmap->buffer -= 2 * pitch;
error = render->raster_render( render->raster, &params );
if ( error )
goto Exit;
- FT_Outline_Translate( outline, 0, 21 );
-
bitmap->pitch /= 3;
bitmap->rows *= 3;
}
else /* grayscale */
- {
error = render->raster_render( render->raster, &params );
- if ( error )
- goto Exit;
- }
#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
- /* everything is fine; don't deallocate buffer */
- have_buffer = FALSE;
-
- error = FT_Err_Ok;
-
Exit:
- if ( have_outline_shifted )
- FT_Outline_Translate( outline, -x_shift, -y_shift );
- if ( have_buffer )
+ if ( !error )
+ {
+ /* everything is fine; the glyph is now officially a bitmap */
+ slot->format = FT_GLYPH_FORMAT_BITMAP;
+ }
+ else if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
{
FT_FREE( bitmap->buffer );
slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
}
+ if ( x_shift || y_shift )
+ FT_Outline_Translate( outline, -x_shift, -y_shift );
+
return error;
}
@@ -492,14 +363,8 @@
FT_Render_Mode mode,
const FT_Vector* origin )
{
- FT_Error error;
-
- error = ft_smooth_render_generic( render, slot, mode, origin,
- FT_RENDER_MODE_LCD );
- if ( !error )
- slot->bitmap.pixel_mode = FT_PIXEL_MODE_LCD;
-
- return error;
+ return ft_smooth_render_generic( render, slot, mode, origin,
+ FT_RENDER_MODE_LCD );
}
@@ -510,14 +375,8 @@
FT_Render_Mode mode,
const FT_Vector* origin )
{
- FT_Error error;
-
- error = ft_smooth_render_generic( render, slot, mode, origin,
- FT_RENDER_MODE_LCD_V );
- if ( !error )
- slot->bitmap.pixel_mode = FT_PIXEL_MODE_LCD_V;
-
- return error;
+ return ft_smooth_render_generic( render, slot, mode, origin,
+ FT_RENDER_MODE_LCD_V );
}
diff --git a/thirdparty/freetype/src/smooth/ftsmooth.h b/thirdparty/freetype/src/smooth/ftsmooth.h
index 6dfd65726c..c76ffc5034 100644
--- a/thirdparty/freetype/src/smooth/ftsmooth.h
+++ b/thirdparty/freetype/src/smooth/ftsmooth.h
@@ -4,7 +4,7 @@
/* */
/* Anti-aliasing renderer interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/smooth/ftspic.c b/thirdparty/freetype/src/smooth/ftspic.c
index fb89be3488..10f04cf4cc 100644
--- a/thirdparty/freetype/src/smooth/ftspic.c
+++ b/thirdparty/freetype/src/smooth/ftspic.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for smooth module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/smooth/ftspic.h b/thirdparty/freetype/src/smooth/ftspic.h
index 9ddd1c7905..80fb64cff4 100644
--- a/thirdparty/freetype/src/smooth/ftspic.h
+++ b/thirdparty/freetype/src/smooth/ftspic.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for smooth module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/smooth/module.mk b/thirdparty/freetype/src/smooth/module.mk
index 804e9b1386..5b8bc3be3b 100644
--- a/thirdparty/freetype/src/smooth/module.mk
+++ b/thirdparty/freetype/src/smooth/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/smooth/rules.mk b/thirdparty/freetype/src/smooth/rules.mk
index dfdc9bc30f..f30824a367 100644
--- a/thirdparty/freetype/src/smooth/rules.mk
+++ b/thirdparty/freetype/src/smooth/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/smooth/smooth.c b/thirdparty/freetype/src/smooth/smooth.c
index e0460d9d46..5249a8931e 100644
--- a/thirdparty/freetype/src/smooth/smooth.c
+++ b/thirdparty/freetype/src/smooth/smooth.c
@@ -4,7 +4,7 @@
/* */
/* FreeType anti-aliasing rasterer module component (body only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/truetype/module.mk b/thirdparty/freetype/src/truetype/module.mk
index 563c584e2b..16bc9c8b20 100644
--- a/thirdparty/freetype/src/truetype/module.mk
+++ b/thirdparty/freetype/src/truetype/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/truetype/rules.mk b/thirdparty/freetype/src/truetype/rules.mk
index ad3d007455..e16113f128 100644
--- a/thirdparty/freetype/src/truetype/rules.mk
+++ b/thirdparty/freetype/src/truetype/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/truetype/truetype.c b/thirdparty/freetype/src/truetype/truetype.c
index 301b82ad1c..484370975c 100644
--- a/thirdparty/freetype/src/truetype/truetype.c
+++ b/thirdparty/freetype/src/truetype/truetype.c
@@ -4,7 +4,7 @@
/* */
/* FreeType TrueType driver component (body only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/truetype/ttdriver.c b/thirdparty/freetype/src/truetype/ttdriver.c
index a1653b241c..820cafbb8d 100644
--- a/thirdparty/freetype/src/truetype/ttdriver.c
+++ b/thirdparty/freetype/src/truetype/ttdriver.c
@@ -4,7 +4,7 @@
/* */
/* TrueType font driver implementation (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -31,7 +31,7 @@
#include FT_SERVICE_TRUETYPE_ENGINE_H
#include FT_SERVICE_TRUETYPE_GLYF_H
#include FT_SERVICE_PROPERTIES_H
-#include FT_TRUETYPE_DRIVER_H
+#include FT_DRIVER_H
#include "ttdriver.h"
#include "ttgload.h"
@@ -233,8 +233,8 @@
{
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
/* no fast retrieval for blended MM fonts without VVAR table */
- if ( !face->is_default_instance &&
- !( face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
+ if ( ( FT_IS_NAMED_INSTANCE( ttface ) || FT_IS_VARIATION( ttface ) ) &&
+ !( face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
return FT_THROW( Unimplemented_Feature );
#endif
@@ -253,8 +253,8 @@
{
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
/* no fast retrieval for blended MM fonts without HVAR table */
- if ( !face->is_default_instance &&
- !( face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
+ if ( ( FT_IS_NAMED_INSTANCE( ttface ) || FT_IS_VARIATION( ttface ) ) &&
+ !( face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
return FT_THROW( Unimplemented_Feature );
#endif
@@ -498,6 +498,7 @@
(FT_Get_MM_Var_Func) TT_Get_MM_Var, /* get_mm_var */
(FT_Set_Var_Design_Func)TT_Set_Var_Design, /* set_var_design */
(FT_Get_Var_Design_Func)TT_Get_Var_Design, /* get_var_design */
+ (FT_Set_Instance_Func) TT_Set_Named_Instance, /* set_instance */
(FT_Get_Var_Blend_Func) tt_get_var_blend, /* get_var_blend */
(FT_Done_Blend_Func) tt_done_blend /* done_blend */
diff --git a/thirdparty/freetype/src/truetype/ttdriver.h b/thirdparty/freetype/src/truetype/ttdriver.h
index 3bcba7f745..707aa68edf 100644
--- a/thirdparty/freetype/src/truetype/ttdriver.h
+++ b/thirdparty/freetype/src/truetype/ttdriver.h
@@ -4,7 +4,7 @@
/* */
/* High-level TrueType driver interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/truetype/tterrors.h b/thirdparty/freetype/src/truetype/tterrors.h
index a49f205156..88bca3a04a 100644
--- a/thirdparty/freetype/src/truetype/tterrors.h
+++ b/thirdparty/freetype/src/truetype/tterrors.h
@@ -4,7 +4,7 @@
/* */
/* TrueType error codes (specification only). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/truetype/ttgload.c b/thirdparty/freetype/src/truetype/ttgload.c
index 5e102c6151..39d9c3f736 100644
--- a/thirdparty/freetype/src/truetype/ttgload.c
+++ b/thirdparty/freetype/src/truetype/ttgload.c
@@ -4,7 +4,7 @@
/* */
/* TrueType Glyph Loader (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -24,7 +24,7 @@
#include FT_INTERNAL_SFNT_H
#include FT_TRUETYPE_TAGS_H
#include FT_OUTLINE_H
-#include FT_TRUETYPE_DRIVER_H
+#include FT_DRIVER_H
#include FT_LIST_H
#include "ttgload.h"
@@ -664,7 +664,52 @@
} while ( subglyph->flags & MORE_COMPONENTS );
gloader->current.num_subglyphs = num_subglyphs;
- FT_TRACE5(( " %d components\n", num_subglyphs ));
+ FT_TRACE5(( " %d component%s\n",
+ num_subglyphs,
+ num_subglyphs > 1 ? "s" : "" ));
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ {
+ FT_UInt i;
+
+
+ subglyph = gloader->current.subglyphs;
+
+ for ( i = 0; i < num_subglyphs; i++ )
+ {
+ if ( num_subglyphs > 1 )
+ FT_TRACE7(( " subglyph %d:\n", i ));
+
+ FT_TRACE7(( " glyph index: %d\n", subglyph->index ));
+
+ if ( subglyph->flags & ARGS_ARE_XY_VALUES )
+ FT_TRACE7(( " offset: x=%d, y=%d\n",
+ subglyph->arg1,
+ subglyph->arg2 ));
+ else
+ FT_TRACE7(( " matching points: base=%d, component=%d\n",
+ subglyph->arg1,
+ subglyph->arg2 ));
+
+ if ( subglyph->flags & WE_HAVE_A_SCALE )
+ FT_TRACE7(( " scaling: %f\n",
+ subglyph->transform.xx / 65536.0 ));
+ else if ( subglyph->flags & WE_HAVE_AN_XY_SCALE )
+ FT_TRACE7(( " scaling: x=%f, y=%f\n",
+ subglyph->transform.xx / 65536.0,
+ subglyph->transform.yy / 65536.0 ));
+ else if ( subglyph->flags & WE_HAVE_A_2X2 )
+ FT_TRACE7(( " scaling: xx=%f, yx=%f\n"
+ " xy=%f, yy=%f\n",
+ subglyph->transform.xx / 65536.0,
+ subglyph->transform.yx / 65536.0,
+ subglyph->transform.xy / 65536.0,
+ subglyph->transform.yy / 65536.0 ));
+
+ subglyph++;
+ }
+ }
+#endif /* FT_DEBUG_LEVEL_TRACE */
#ifdef TT_USE_BYTECODE_INTERPRETER
@@ -756,7 +801,7 @@
{
FT_TRACE1(( "TT_Hint_Glyph: too long instructions" ));
FT_TRACE1(( " (0x%lx byte) is truncated\n",
- loader->glyph->control_len ));
+ loader->glyph->control_len ));
}
n_ins = loader->glyph->control_len;
@@ -889,7 +934,8 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
- if ( !loader->face->is_default_instance )
+ if ( FT_IS_NAMED_INSTANCE( FT_FACE( loader->face ) ) ||
+ FT_IS_VARIATION( FT_FACE( loader->face ) ) )
{
/* Deltas apply to the unscaled data. */
error = TT_Vary_Apply_Glyph_Deltas( loader->face,
@@ -991,9 +1037,24 @@
vec->x = FT_MulFix( vec->x, x_scale );
vec->y = FT_MulFix( vec->y, y_scale );
}
+ }
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ /* if we have a HVAR table, `pp1' and/or `pp2' are already adjusted */
+ if ( !( loader->face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) ||
+ !IS_HINTED( loader->load_flags ) )
+#endif
+ {
loader->pp1 = outline->points[n_points - 4];
loader->pp2 = outline->points[n_points - 3];
+ }
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ /* if we have a VVAR table, `pp3' and/or `pp4' are already adjusted */
+ if ( !( loader->face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) ||
+ !IS_HINTED( loader->load_flags ) )
+#endif
+ {
loader->pp3 = outline->points[n_points - 2];
loader->pp4 = outline->points[n_points - 1];
}
@@ -1145,8 +1206,28 @@
if ( subglyph->flags & ROUND_XY_TO_GRID )
{
- x = FT_PIX_ROUND( x );
- y = FT_PIX_ROUND( y );
+ TT_Face face = loader->face;
+ TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face );
+
+
+ if ( IS_HINTED( loader->load_flags ) )
+ {
+ /*
+ * We round the horizontal offset only if there is hinting along
+ * the x axis; this corresponds to integer advance width values.
+ *
+ * Theoretically, a glyph's bytecode can toggle ClearType's
+ * `backward compatibility' mode, which would allow modification
+ * of the advance width. In reality, however, applications
+ * neither allow nor expect modified advance widths if subpixel
+ * rendering is active.
+ *
+ */
+ if ( driver->interpreter_version == TT_INTERPRETER_VERSION_35 )
+ x = FT_PIX_ROUND( x );
+
+ y = FT_PIX_ROUND( y );
+ }
}
}
}
@@ -1359,6 +1440,7 @@
TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( loader->face );
#endif
+
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 )
{
@@ -1577,7 +1659,8 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
- if ( !loader->face->is_default_instance )
+ if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ||
+ FT_IS_VARIATION( FT_FACE( face ) ) )
{
/* a small outline structure with four elements for */
/* communication with `TT_Vary_Apply_Glyph_Deltas' */
@@ -1711,7 +1794,7 @@
/* clear the nodes filled by sibling chains */
node = ft_list_get_node_at( &loader->composites, recurse_count );
for ( node2 = node; node2; node2 = node2->next )
- node2->data = (void*)ULONG_MAX;
+ node2->data = (void*)FT_ULONG_MAX;
/* check whether we already have a composite glyph with this index */
if ( FT_List_Find( &loader->composites,
@@ -1751,7 +1834,8 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
- if ( !face->is_default_instance )
+ if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ||
+ FT_IS_VARIATION( FT_FACE( face ) ) )
{
short i, limit;
FT_SubGlyph subglyph;
@@ -2609,7 +2693,8 @@
TT_LoaderRec loader;
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
-#define IS_DEFAULT_INSTANCE ( ( (TT_Face)glyph->face )->is_default_instance )
+#define IS_DEFAULT_INSTANCE ( !( FT_IS_NAMED_INSTANCE( glyph->face ) || \
+ FT_IS_VARIATION( glyph->face ) ) )
#else
#define IS_DEFAULT_INSTANCE 1
#endif
@@ -2624,6 +2709,10 @@
( load_flags & FT_LOAD_NO_BITMAP ) == 0 &&
IS_DEFAULT_INSTANCE )
{
+ FT_Fixed x_scale = size->root.metrics.x_scale;
+ FT_Fixed y_scale = size->root.metrics.y_scale;
+
+
error = load_sbit_image( size, glyph, glyph_index, load_flags );
if ( FT_ERR_EQ( error, Missing_Bitmap ) )
{
@@ -2631,9 +2720,13 @@
/* if we have a bitmap-only font, return an empty glyph */
if ( !FT_IS_SCALABLE( glyph->face ) )
{
- TT_Face face = (TT_Face)glyph->face;
- FT_Short left_bearing = 0, top_bearing = 0;
- FT_UShort advance_width = 0, advance_height = 0;
+ TT_Face face = (TT_Face)glyph->face;
+
+ FT_Short left_bearing = 0;
+ FT_Short top_bearing = 0;
+
+ FT_UShort advance_width = 0;
+ FT_UShort advance_height = 0;
/* to return an empty glyph, however, we need metrics data */
@@ -2659,13 +2752,13 @@
glyph->metrics.width = 0;
glyph->metrics.height = 0;
- glyph->metrics.horiBearingX = left_bearing;
+ glyph->metrics.horiBearingX = FT_MulFix( left_bearing, x_scale );
glyph->metrics.horiBearingY = 0;
- glyph->metrics.horiAdvance = advance_width;
+ glyph->metrics.horiAdvance = FT_MulFix( advance_width, x_scale );
glyph->metrics.vertBearingX = 0;
- glyph->metrics.vertBearingY = top_bearing;
- glyph->metrics.vertAdvance = advance_height;
+ glyph->metrics.vertBearingY = FT_MulFix( top_bearing, y_scale );
+ glyph->metrics.vertAdvance = FT_MulFix( advance_height, y_scale );
glyph->format = FT_GLYPH_FORMAT_BITMAP;
glyph->bitmap.pixel_mode = FT_PIXEL_MODE_MONO;
@@ -2696,13 +2789,11 @@
/* sanity checks: if `xxxAdvance' in the sbit metric */
/* structure isn't set, use `linearXXXAdvance' */
if ( !glyph->metrics.horiAdvance && glyph->linearHoriAdvance )
- glyph->metrics.horiAdvance =
- FT_MulFix( glyph->linearHoriAdvance,
- size->metrics->x_scale );
+ glyph->metrics.horiAdvance = FT_MulFix( glyph->linearHoriAdvance,
+ x_scale );
if ( !glyph->metrics.vertAdvance && glyph->linearVertAdvance )
- glyph->metrics.vertAdvance =
- FT_MulFix( glyph->linearVertAdvance,
- size->metrics->y_scale );
+ glyph->metrics.vertAdvance = FT_MulFix( glyph->linearVertAdvance,
+ y_scale );
}
return FT_Err_Ok;
diff --git a/thirdparty/freetype/src/truetype/ttgload.h b/thirdparty/freetype/src/truetype/ttgload.h
index 1dd6c841db..d237cfd284 100644
--- a/thirdparty/freetype/src/truetype/ttgload.h
+++ b/thirdparty/freetype/src/truetype/ttgload.h
@@ -4,7 +4,7 @@
/* */
/* TrueType Glyph Loader (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/truetype/ttgxvar.c b/thirdparty/freetype/src/truetype/ttgxvar.c
index 49aa53a687..29ab2a4efd 100644
--- a/thirdparty/freetype/src/truetype/ttgxvar.c
+++ b/thirdparty/freetype/src/truetype/ttgxvar.c
@@ -4,7 +4,7 @@
/* */
/* TrueType GX Font Variation loader */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -1043,10 +1043,11 @@
outerIndex,
innerIndex );
- FT_TRACE5(( "%s value %d adjusted by %d units (%s)\n",
+ FT_TRACE5(( "%s value %d adjusted by %d unit%s (%s)\n",
vertical ? "vertical height" : "horizontal width",
*avalue,
delta,
+ delta == 1 ? "" : "s",
vertical ? "VVAR" : "HVAR" ));
*avalue += delta;
@@ -1333,13 +1334,15 @@
if ( p )
{
- FT_TRACE5(( "value %c%c%c%c (%d units) adjusted by %d units (MVAR)\n",
+ FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s (MVAR)\n",
(FT_Char)( value->tag >> 24 ),
(FT_Char)( value->tag >> 16 ),
(FT_Char)( value->tag >> 8 ),
(FT_Char)( value->tag ),
value->unmodified,
- delta ));
+ value->unmodified == 1 ? "" : "s",
+ delta,
+ delta == 1 ? "" : "s" ));
/* since we handle both signed and unsigned values as FT_Short, */
/* ensure proper overflow arithmetic */
@@ -1499,8 +1502,10 @@
blend->gv_glyphcnt = gvar_head.glyphCount;
offsetToData = gvar_start + gvar_head.offsetToData;
- FT_TRACE5(( "gvar: there are %d shared coordinates:\n",
- blend->tuplecount ));
+ FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n",
+ blend->tuplecount == 1 ? "is" : "are",
+ blend->tuplecount,
+ blend->tuplecount == 1 ? "" : "s" ));
if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
goto Exit;
@@ -1728,15 +1733,13 @@
/* based on the [min,def,max] values for the axis to be [-1,0,1]. */
/* Then, if there's an `avar' table, we renormalize this range. */
- FT_TRACE5(( "design coordinates:\n" ));
-
a = mmvar->axis;
for ( i = 0; i < num_coords; i++, a++ )
{
FT_Fixed coord = coords[i];
- FT_TRACE5(( " %.5f\n", coord / 65536.0 ));
+ FT_TRACE5(( " %d: %.5f\n", i, coord / 65536.0 ));
if ( coord > a->maximum || coord < a->minimum )
{
FT_TRACE1((
@@ -1746,17 +1749,17 @@
a->minimum / 65536.0,
a->maximum / 65536.0 ));
- if ( coord > a->maximum)
+ if ( coord > a->maximum )
coord = a->maximum;
else
coord = a->minimum;
}
if ( coord < a->def )
- normalized[i] = -FT_DivFix( coords[i] - a->def,
+ normalized[i] = -FT_DivFix( coord - a->def,
a->minimum - a->def );
else if ( coord > a->def )
- normalized[i] = FT_DivFix( coords[i] - a->def,
+ normalized[i] = FT_DivFix( coord - a->def,
a->maximum - a->def );
else
normalized[i] = 0;
@@ -1822,16 +1825,8 @@
nc = blend->num_axis;
}
- if ( face->doblend )
- {
- for ( i = 0; i < nc; i++ )
- design[i] = coords[i];
- }
- else
- {
- for ( i = 0; i < nc; i++ )
- design[i] = 0;
- }
+ for ( i = 0; i < nc; i++ )
+ design[i] = coords[i];
for ( ; i < num_coords; i++ )
design[i] = 0;
@@ -1940,11 +1935,11 @@
TT_Get_MM_Var( TT_Face face,
FT_MM_Var* *master )
{
- FT_Stream stream = face->root.stream;
- FT_Memory memory = face->root.memory;
+ FT_Stream stream = face->root.stream;
+ FT_Memory memory = face->root.memory;
FT_ULong table_len;
- FT_Error error = FT_Err_Ok;
- FT_ULong fvar_start;
+ FT_Error error = FT_Err_Ok;
+ FT_ULong fvar_start = 0;
FT_UInt i, j;
FT_MM_Var* mmvar = NULL;
FT_Fixed* next_coords;
@@ -1954,10 +1949,20 @@
FT_Fixed* c;
FT_Var_Named_Style* ns;
GX_FVar_Head fvar_head;
- FT_Bool usePsName;
+ FT_Bool usePsName = 0;
FT_UInt num_instances;
+ FT_UInt num_axes;
FT_UShort* axis_flags;
+ FT_Offset mmvar_size;
+ FT_Offset axis_flags_size;
+ FT_Offset axis_size;
+ FT_Offset namedstyle_size;
+ FT_Offset next_coords_size;
+ FT_Offset next_name_size;
+
+ FT_Bool need_init;
+
static const FT_Frame_Field fvar_fields[] =
{
@@ -1995,7 +2000,9 @@
/* read the font data and set up the internal representation */
/* if not already done */
- if ( !face->blend )
+ need_init = !face->blend;
+
+ if ( need_init )
{
FT_TRACE2(( "FVAR " ));
@@ -2032,26 +2039,57 @@
FT_TRACE2(( "loaded\n" ));
- FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount ));
+ FT_TRACE5(( "%d variation ax%s\n",
+ fvar_head.axisCount,
+ fvar_head.axisCount == 1 ? "is" : "es" ));
if ( FT_NEW( face->blend ) )
goto Exit;
- /* `num_instances' holds the number of all named instances, */
- /* including the default instance which might be missing */
- /* in fvar's table of named instances */
- num_instances = face->root.style_flags >> 16;
-
- /* prepare storage area for MM data; this cannot overflow */
- /* 32-bit arithmetic because of the size limits used in the */
- /* `fvar' table validity check in `sfnt_init_face' */
- face->blend->mmvar_len =
- sizeof ( FT_MM_Var ) +
- fvar_head.axisCount * sizeof ( FT_UShort ) +
- fvar_head.axisCount * sizeof ( FT_Var_Axis ) +
- num_instances * sizeof ( FT_Var_Named_Style ) +
- num_instances * fvar_head.axisCount * sizeof ( FT_Fixed ) +
- fvar_head.axisCount * 5;
+ num_axes = fvar_head.axisCount;
+ face->blend->num_axis = num_axes;
+ }
+ else
+ num_axes = face->blend->num_axis;
+
+ /* `num_instances' holds the number of all named instances, */
+ /* including the default instance which might be missing */
+ /* in fvar's table of named instances */
+ num_instances = (FT_UInt)face->root.style_flags >> 16;
+
+ /* prepare storage area for MM data; this cannot overflow */
+ /* 32-bit arithmetic because of the size limits used in the */
+ /* `fvar' table validity check in `sfnt_init_face' */
+
+ /* the various `*_size' variables, which we also use as */
+ /* offsets into the `mmlen' array, must be multiples of the */
+ /* pointer size (except the last one); without such an */
+ /* alignment there might be runtime errors due to */
+ /* misaligned addresses */
+#undef ALIGN_SIZE
+#define ALIGN_SIZE( n ) \
+ ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) )
+
+ mmvar_size = ALIGN_SIZE( sizeof ( FT_MM_Var ) );
+ axis_flags_size = ALIGN_SIZE( num_axes *
+ sizeof ( FT_UShort ) );
+ axis_size = ALIGN_SIZE( num_axes *
+ sizeof ( FT_Var_Axis ) );
+ namedstyle_size = ALIGN_SIZE( num_instances *
+ sizeof ( FT_Var_Named_Style ) );
+ next_coords_size = ALIGN_SIZE( num_instances *
+ num_axes *
+ sizeof ( FT_Fixed ) );
+ next_name_size = num_axes * 5;
+
+ if ( need_init )
+ {
+ face->blend->mmvar_len = mmvar_size +
+ axis_flags_size +
+ axis_size +
+ namedstyle_size +
+ next_coords_size +
+ next_name_size;
if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
goto Exit;
@@ -2061,7 +2099,7 @@
/* the data gets filled in later on */
mmvar->num_axis =
- fvar_head.axisCount;
+ num_axes;
mmvar->num_designs =
~0U; /* meaningless in this context; each glyph */
/* may have a different number of designs */
@@ -2071,22 +2109,23 @@
/* alas, no public field in `FT_Var_Axis' for axis flags */
axis_flags =
- (FT_UShort*)&( mmvar[1] );
+ (FT_UShort*)( (char*)mmvar + mmvar_size );
mmvar->axis =
- (FT_Var_Axis*)&( axis_flags[fvar_head.axisCount] );
+ (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
mmvar->namedstyle =
- (FT_Var_Named_Style*)&( mmvar->axis[fvar_head.axisCount] );
+ (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size );
- next_coords =
- (FT_Fixed*)&( mmvar->namedstyle[num_instances] );
+ next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
+ namedstyle_size );
for ( i = 0; i < num_instances; i++ )
{
mmvar->namedstyle[i].coords = next_coords;
- next_coords += fvar_head.axisCount;
+ next_coords += num_axes;
}
- next_name = (FT_String*)next_coords;
- for ( i = 0; i < fvar_head.axisCount; i++ )
+ next_name = (FT_String*)( (char*)mmvar->namedstyle +
+ namedstyle_size + next_coords_size );
+ for ( i = 0; i < num_axes; i++ )
{
mmvar->axis[i].name = next_name;
next_name += 5;
@@ -2098,10 +2137,14 @@
goto Exit;
a = mmvar->axis;
- for ( i = 0; i < fvar_head.axisCount; i++ )
+ for ( i = 0; i < num_axes; i++ )
{
GX_FVar_Axis axis_rec;
+#ifdef FT_DEBUG_LEVEL_TRACE
+ int invalid = 0;
+#endif
+
if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
goto Exit;
@@ -2122,22 +2165,31 @@
if ( a->minimum > a->def ||
a->def > a->maximum )
{
- FT_TRACE2(( "TT_Get_MM_Var:"
- " invalid \"%s\" axis record; disabling\n",
- a->name ));
-
a->minimum = a->def;
a->maximum = a->def;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ invalid = 1;
+#endif
}
- FT_TRACE5(( " \"%s\":"
- " minimum=%.5f, default=%.5f, maximum=%.5f,"
- " flags=0x%04X\n",
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( i == 0 )
+ FT_TRACE5(( " idx tag "
+ /* " XXX `XXXX'" */
+ " minimum default maximum flags\n" ));
+ /* " XXXX.XXXXX XXXX.XXXXX XXXX.XXXXX 0xXXXX" */
+
+ FT_TRACE5(( " %3d `%s'"
+ " %10.5f %10.5f %10.5f 0x%04X%s\n",
+ i,
a->name,
a->minimum / 65536.0,
a->def / 65536.0,
a->maximum / 65536.0,
- *axis_flags ));
+ *axis_flags,
+ invalid ? " (invalid, disabled)" : "" ));
+#endif
a++;
axis_flags++;
@@ -2148,7 +2200,7 @@
/* named instance coordinates are stored as design coordinates; */
/* we have to convert them to normalized coordinates also */
if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords,
- fvar_head.axisCount * num_instances ) )
+ num_axes * num_instances ) )
goto Exit;
if ( fvar_head.instanceCount && !face->blend->avar_loaded )
@@ -2162,20 +2214,24 @@
goto Exit;
}
+ FT_TRACE5(( "%d instance%s\n",
+ fvar_head.instanceCount,
+ fvar_head.instanceCount == 1 ? "" : "s" ));
+
ns = mmvar->namedstyle;
nsc = face->blend->normalized_stylecoords;
for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
{
/* PostScript names add 2 bytes to the instance record size */
if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) +
- 4L * fvar_head.axisCount ) )
+ 4L * num_axes ) )
goto Exit;
ns->strid = FT_GET_USHORT();
(void) /* flags = */ FT_GET_USHORT();
c = ns->coords;
- for ( j = 0; j < fvar_head.axisCount; j++, c++ )
+ for ( j = 0; j < num_axes; j++, c++ )
*c = FT_GET_LONG();
/* valid psid values are 6, [256;32767], and 0xFFFF */
@@ -2184,11 +2240,54 @@
else
ns->psid = 0xFFFF;
- ft_var_to_normalized( face,
- fvar_head.axisCount,
- ns->coords,
- nsc );
- nsc += fvar_head.axisCount;
+#ifdef FT_DEBUG_LEVEL_TRACE
+ {
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+
+ FT_String* strname = NULL;
+ FT_String* psname = NULL;
+
+ FT_ULong pos;
+
+
+ pos = FT_STREAM_POS();
+
+ if ( ns->strid != 0xFFFF )
+ {
+ (void)sfnt->get_name( face,
+ (FT_UShort)ns->strid,
+ &strname );
+ if ( strname && !ft_strcmp( strname, ".notdef" ) )
+ strname = NULL;
+ }
+
+ if ( ns->psid != 0xFFFF )
+ {
+ (void)sfnt->get_name( face,
+ (FT_UShort)ns->psid,
+ &psname );
+ if ( psname && !ft_strcmp( psname, ".notdef" ) )
+ psname = NULL;
+ }
+
+ (void)FT_STREAM_SEEK( pos );
+
+ FT_TRACE5(( " instance %d (%s%s%s, %s%s%s)\n",
+ i,
+ strname ? "name: `" : "",
+ strname ? strname : "unnamed",
+ strname ? "'" : "",
+ psname ? "PS name: `" : "",
+ psname ? psname : "no PS name",
+ psname ? "'" : "" ));
+
+ FT_FREE( strname );
+ FT_FREE( psname );
+ }
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+ ft_var_to_normalized( face, num_axes, ns->coords, nsc );
+ nsc += num_axes;
FT_FRAME_EXIT();
}
@@ -2237,7 +2336,7 @@
a = mmvar->axis;
c = ns->coords;
- for ( j = 0; j < fvar_head.axisCount; j++, a++, c++ )
+ for ( j = 0; j < num_axes; j++, a++, c++ )
*c = a->def;
}
}
@@ -2258,23 +2357,24 @@
FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
axis_flags =
- (FT_UShort*)&( mmvar[1] );
+ (FT_UShort*)( (char*)mmvar + mmvar_size );
mmvar->axis =
- (FT_Var_Axis*)&( axis_flags[mmvar->num_axis] );
+ (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
mmvar->namedstyle =
- (FT_Var_Named_Style*)&( mmvar->axis[mmvar->num_axis] );
+ (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size );
- next_coords =
- (FT_Fixed*)&( mmvar->namedstyle[mmvar->num_namedstyles] );
+ next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
+ namedstyle_size );
for ( n = 0; n < mmvar->num_namedstyles; n++ )
{
mmvar->namedstyle[n].coords = next_coords;
- next_coords += mmvar->num_axis;
+ next_coords += num_axes;
}
a = mmvar->axis;
- next_name = (FT_String*)next_coords;
- for ( n = 0; n < mmvar->num_axis; n++ )
+ next_name = (FT_String*)( (char*)mmvar->namedstyle +
+ namedstyle_size + next_coords_size );
+ for ( n = 0; n < num_axes; n++ )
{
a->name = next_name;
@@ -2309,10 +2409,9 @@
FT_Error error = FT_Err_Ok;
GX_Blend blend;
FT_MM_Var* mmvar;
- FT_UInt i, j;
+ FT_UInt i;
- FT_Bool is_default_instance = TRUE;
- FT_Bool all_design_coords = FALSE;
+ FT_Bool all_design_coords = FALSE;
FT_Memory memory = face->root.memory;
@@ -2344,11 +2443,12 @@
num_coords = mmvar->num_axis;
}
- FT_TRACE5(( "normalized design coordinates:\n" ));
+ FT_TRACE5(( "TT_Set_MM_Blend:\n"
+ " normalized design coordinates:\n" ));
for ( i = 0; i < num_coords; i++ )
{
- FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 ));
+ FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 ));
if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
{
FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n"
@@ -2357,9 +2457,6 @@
error = FT_THROW( Invalid_Argument );
goto Exit;
}
-
- if ( coords[i] != 0 )
- is_default_instance = FALSE;
}
FT_TRACE5(( "\n" ));
@@ -2390,6 +2487,12 @@
}
else
{
+ FT_Bool have_diff = 0;
+ FT_UInt j;
+ FT_Fixed* c;
+ FT_Fixed* n;
+
+
manageCvt = mcvt_retain;
for ( i = 0; i < num_coords; i++ )
@@ -2397,10 +2500,34 @@
if ( blend->normalizedcoords[i] != coords[i] )
{
manageCvt = mcvt_load;
+ have_diff = 1;
break;
}
}
+ if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
+ {
+ FT_UInt idx = (FT_UInt)face->root.face_index >> 16;
+
+
+ c = blend->normalizedcoords + i;
+ n = blend->normalized_stylecoords + idx * mmvar->num_axis + i;
+ for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
+ if ( *c != *n )
+ have_diff = 1;
+ }
+ else
+ {
+ c = blend->normalizedcoords + i;
+ for ( j = i; j < mmvar->num_axis; j++, c++ )
+ if ( *c != 0 )
+ have_diff = 1;
+ }
+
+ /* return value -1 indicates `no change' */
+ if ( !have_diff )
+ return -1;
+
for ( ; i < mmvar->num_axis; i++ )
{
if ( blend->normalizedcoords[i] != 0 )
@@ -2454,32 +2581,6 @@
}
}
- /* check whether the current variation tuple coincides */
- /* with a named instance */
-
- for ( i = 0; i < blend->mmvar->num_namedstyles; i++ )
- {
- FT_Fixed* nsc = blend->normalized_stylecoords + i * blend->num_axis;
- FT_Fixed* ns = blend->normalizedcoords;
-
-
- for ( j = 0; j < blend->num_axis; j++, nsc++, ns++ )
- {
- if ( *nsc != *ns )
- break;
- }
-
- if ( j == blend->num_axis )
- break;
- }
-
- /* adjust named instance index */
- face->root.face_index &= 0xFFFF;
- if ( i < blend->mmvar->num_namedstyles )
- face->root.face_index |= ( i + 1 ) << 16;
-
- face->is_default_instance = is_default_instance;
-
/* enforce recomputation of the PostScript name; */
FT_FREE( face->postscript_name );
face->postscript_name = NULL;
@@ -2519,7 +2620,19 @@
FT_UInt num_coords,
FT_Fixed* coords )
{
- return tt_set_mm_blend( face, num_coords, coords, 1 );
+ FT_Error error;
+
+
+ error = tt_set_mm_blend( face, num_coords, coords, 1 );
+ if ( error )
+ return error;
+
+ if ( num_coords )
+ face->root.face_flags |= FT_FACE_FLAG_VARIATION;
+ else
+ face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
+
+ return FT_Err_Ok;
}
@@ -2635,11 +2748,12 @@
FT_UInt i;
FT_Memory memory = face->root.memory;
- FT_Var_Axis* a;
- FT_Fixed* c;
-
+ FT_Fixed* c;
+ FT_Fixed* n;
FT_Fixed* normalized = NULL;
+ FT_Bool have_diff = 0;
+
if ( !face->blend )
{
@@ -2664,14 +2778,56 @@
goto Exit;
}
- FT_MEM_COPY( blend->coords,
- coords,
- num_coords * sizeof ( FT_Fixed ) );
+ c = blend->coords;
+ n = coords;
+ for ( i = 0; i < num_coords; i++, n++, c++ )
+ {
+ if ( *c != *n )
+ {
+ *c = *n;
+ have_diff = 1;
+ }
+ }
+
+ if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
+ {
+ FT_UInt instance_index;
+ FT_Var_Named_Style* named_style;
+
+
+ instance_index = (FT_UInt)face->root.face_index >> 16;
+ named_style = mmvar->namedstyle + instance_index - 1;
+
+ n = named_style->coords + num_coords;
+ for ( ; i < mmvar->num_axis; i++, n++, c++ )
+ {
+ if ( *c != *n )
+ {
+ *c = *n;
+ have_diff = 1;
+ }
+ }
+ }
+ else
+ {
+ FT_Var_Axis* a;
- a = mmvar->axis + num_coords;
- c = blend->coords + num_coords;
- for ( i = num_coords; i < mmvar->num_axis; i++, a++, c++ )
- *c = a->def;
+
+ a = mmvar->axis + num_coords;
+ for ( ; i < mmvar->num_axis; i++, a++, c++ )
+ {
+ if ( *c != a->def )
+ {
+ *c = a->def;
+ have_diff = 1;
+ }
+ }
+ }
+
+ /* return value -1 indicates `no change'; */
+ /* we can exit early if `normalizedcoords' is already computed */
+ if ( blend->normalizedcoords && !have_diff )
+ return -1;
if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
goto Exit;
@@ -2679,9 +2835,18 @@
if ( !face->blend->avar_loaded )
ft_var_load_avar( face );
+ FT_TRACE5(( "TT_Set_Var_Design:\n"
+ " normalized design coordinates:\n" ));
ft_var_to_normalized( face, num_coords, blend->coords, normalized );
error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 );
+ if ( error )
+ goto Exit;
+
+ if ( num_coords )
+ face->root.face_flags |= FT_FACE_FLAG_VARIATION;
+ else
+ face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
Exit:
FT_FREE( normalized );
@@ -2765,6 +2930,90 @@
/*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Set_Named_Instance */
+ /* */
+ /* <Description> */
+ /* Set the given named instance, also resetting any further */
+ /* variation. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the source face. */
+ /* */
+ /* instance_index :: The instance index, starting with value 1. */
+ /* Value 0 indicates to not use an instance. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Set_Named_Instance( TT_Face face,
+ FT_UInt instance_index )
+ {
+ FT_Error error = FT_ERR( Invalid_Argument );
+ GX_Blend blend;
+ FT_MM_Var* mmvar;
+
+ FT_UInt num_instances;
+
+
+ if ( !face->blend )
+ {
+ if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
+ goto Exit;
+ }
+
+ blend = face->blend;
+ mmvar = blend->mmvar;
+
+ num_instances = (FT_UInt)face->root.style_flags >> 16;
+
+ /* `instance_index' starts with value 1, thus `>' */
+ if ( instance_index > num_instances )
+ goto Exit;
+
+ if ( instance_index > 0 && mmvar->namedstyle )
+ {
+ FT_Memory memory = face->root.memory;
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+
+ FT_Var_Named_Style* named_style;
+ FT_String* style_name;
+
+
+ named_style = mmvar->namedstyle + instance_index - 1;
+
+ error = sfnt->get_name( face,
+ (FT_UShort)named_style->strid,
+ &style_name );
+ if ( error )
+ goto Exit;
+
+ /* set (or replace) style name */
+ FT_FREE( face->root.style_name );
+ face->root.style_name = style_name;
+
+ /* finally, select the named instance */
+ error = TT_Set_Var_Design( face,
+ mmvar->num_axis,
+ named_style->coords );
+ if ( error )
+ goto Exit;
+ }
+ else
+ error = TT_Set_Var_Design( face, 0, NULL );
+
+ face->root.face_index = ( instance_index << 16 ) |
+ ( face->root.face_index & 0xFFFFL );
+ face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GX VAR PARSING ROUTINES *****/
@@ -2810,8 +3059,10 @@
FT_Fixed* im_start_coords = NULL;
FT_Fixed* im_end_coords = NULL;
GX_Blend blend = face->blend;
- FT_UInt point_count;
- FT_UShort* localpoints;
+ FT_UInt point_count, spoint_count = 0;
+ FT_UShort* sharedpoints = NULL;
+ FT_UShort* localpoints = NULL;
+ FT_UShort* points;
FT_Short* deltas;
@@ -2880,11 +3131,24 @@
offsetToData += table_start;
- /* The documentation implies there are flags packed into */
- /* `tupleCount', but John Jenkins says that shared points don't apply */
- /* to `cvar', and no other flags are defined. */
+ if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
+ {
+ here = FT_Stream_FTell( stream );
+
+ FT_Stream_SeekSet( stream, offsetToData );
- FT_TRACE5(( "cvar: there are %d tuples:\n", tupleCount & 0xFFF ));
+ sharedpoints = ft_var_readpackedpoints( stream,
+ table_len,
+ &spoint_count );
+ offsetToData = FT_Stream_FTell( stream );
+
+ FT_Stream_SeekSet( stream, here );
+ }
+
+ FT_TRACE5(( "cvar: there %s %d tuple%s:\n",
+ ( tupleCount & 0xFFF ) == 1 ? "is" : "are",
+ tupleCount & 0xFFF,
+ ( tupleCount & 0xFFF ) == 1 ? "" : "s" ));
for ( i = 0; i < ( tupleCount & 0xFFF ); i++ )
{
@@ -2898,26 +3162,25 @@
tupleDataSize = FT_GET_USHORT();
tupleIndex = FT_GET_USHORT();
- /* There is no provision here for a global tuple coordinate section, */
- /* so John says. There are no tuple indices, just embedded tuples. */
-
if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
{
for ( j = 0; j < blend->num_axis; j++ )
tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */
/* short frac to fixed */
}
- else
+ else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
{
- /* skip this tuple; it makes no sense */
-
- if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
- for ( j = 0; j < 2 * blend->num_axis; j++ )
- (void)FT_GET_SHORT();
+ FT_TRACE2(( "tt_face_vary_cvt:"
+ " invalid tuple index\n" ));
- offsetToData += tupleDataSize;
- continue;
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
}
+ else
+ FT_MEM_COPY(
+ tuple_coords,
+ &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
+ blend->num_axis * sizeof ( FT_Fixed ) );
if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
{
@@ -2932,11 +3195,8 @@
tuple_coords,
im_start_coords,
im_end_coords );
- if ( /* tuple isn't active for our blend */
- apply == 0 ||
- /* global points not allowed, */
- /* if they aren't local, makes no sense */
- !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) )
+
+ if ( apply == 0 ) /* tuple isn't active for our blend */
{
offsetToData += tupleDataSize;
continue;
@@ -2946,14 +3206,27 @@
FT_Stream_SeekSet( stream, offsetToData );
- localpoints = ft_var_readpackedpoints( stream,
- table_len,
- &point_count );
- deltas = ft_var_readpackeddeltas( stream,
- table_len,
- point_count == 0 ? face->cvt_size
- : point_count );
- if ( !localpoints || !deltas )
+ if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
+ {
+ localpoints = ft_var_readpackedpoints( stream,
+ table_len,
+ &point_count );
+ points = localpoints;
+ }
+ else
+ {
+ points = sharedpoints;
+ point_count = spoint_count;
+ }
+
+ deltas = ft_var_readpackeddeltas( stream,
+ table_len,
+ point_count == 0 ? face->cvt_size
+ : point_count );
+
+ if ( !points ||
+ !deltas ||
+ ( localpoints == ALL_POINTS && point_count != face->cvt_size ) )
; /* failure, ignore it */
else if ( localpoints == ALL_POINTS )
@@ -3005,7 +3278,7 @@
FT_Long orig_cvt;
- pindex = localpoints[j];
+ pindex = points[j];
if ( (FT_ULong)pindex >= face->cvt_size )
continue;
@@ -3044,6 +3317,8 @@
FT_FRAME_EXIT();
Exit:
+ if ( sharedpoints != ALL_POINTS )
+ FT_FREE( sharedpoints );
FT_FREE( tuple_coords );
FT_FREE( im_start_coords );
FT_FREE( im_end_coords );
@@ -3330,7 +3605,6 @@
glyph_start = FT_Stream_FTell( stream );
/* each set of glyph variation data is formatted similarly to `cvar' */
- /* (except we get shared points and global tuples) */
if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) ||
FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
@@ -3367,8 +3641,10 @@
FT_Stream_SeekSet( stream, here );
}
- FT_TRACE5(( "gvar: there are %d tuples:\n",
- tupleCount & GX_TC_TUPLE_COUNT_MASK ));
+ FT_TRACE5(( "gvar: there %s %d tuple%s:\n",
+ ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
+ tupleCount & GX_TC_TUPLE_COUNT_MASK,
+ ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
for ( j = 0; j < n_points; j++ )
points_org[j] = outline->points[j];
@@ -3470,7 +3746,7 @@
FT_Pos delta_y = FT_MulFix( deltas_y[j], apply );
- if ( j < n_points - 3 )
+ if ( j < n_points - 4 )
{
outline->points[j].x += delta_x;
outline->points[j].y += delta_y;
@@ -3480,25 +3756,25 @@
/* To avoid double adjustment of advance width or height, */
/* adjust phantom points only if there is no HVAR or VVAR */
/* support, respectively. */
- if ( j == ( n_points - 3 ) &&
- !( face->variation_support &
- TT_FACE_FLAG_VAR_HADVANCE ) )
+ if ( j == ( n_points - 4 ) &&
+ !( face->variation_support &
+ TT_FACE_FLAG_VAR_LSB ) )
+ outline->points[j].x += delta_x;
+
+ else if ( j == ( n_points - 3 ) &&
+ !( face->variation_support &
+ TT_FACE_FLAG_VAR_HADVANCE ) )
outline->points[j].x += delta_x;
else if ( j == ( n_points - 2 ) &&
!( face->variation_support &
- TT_FACE_FLAG_VAR_LSB ) )
- outline->points[j].x += delta_x;
+ TT_FACE_FLAG_VAR_TSB ) )
+ outline->points[j].y += delta_y;
else if ( j == ( n_points - 1 ) &&
!( face->variation_support &
TT_FACE_FLAG_VAR_VADVANCE ) )
outline->points[j].y += delta_y;
-
- else if ( j == ( n_points - 0 ) &&
- !( face->variation_support &
- TT_FACE_FLAG_VAR_TSB ) )
- outline->points[j].y += delta_y;
}
#ifdef FT_DEBUG_LEVEL_TRACE
@@ -3565,8 +3841,36 @@
FT_Pos delta_y = points_out[j].y - points_org[j].y;
- outline->points[j].x += delta_x;
- outline->points[j].y += delta_y;
+ if ( j < n_points - 4 )
+ {
+ outline->points[j].x += delta_x;
+ outline->points[j].y += delta_y;
+ }
+ else
+ {
+ /* To avoid double adjustment of advance width or height, */
+ /* adjust phantom points only if there is no HVAR or VVAR */
+ /* support, respectively. */
+ if ( j == ( n_points - 4 ) &&
+ !( face->variation_support &
+ TT_FACE_FLAG_VAR_LSB ) )
+ outline->points[j].x += delta_x;
+
+ else if ( j == ( n_points - 3 ) &&
+ !( face->variation_support &
+ TT_FACE_FLAG_VAR_HADVANCE ) )
+ outline->points[j].x += delta_x;
+
+ else if ( j == ( n_points - 2 ) &&
+ !( face->variation_support &
+ TT_FACE_FLAG_VAR_TSB ) )
+ outline->points[j].y += delta_y;
+
+ else if ( j == ( n_points - 1 ) &&
+ !( face->variation_support &
+ TT_FACE_FLAG_VAR_VADVANCE ) )
+ outline->points[j].y += delta_y;
+ }
#ifdef FT_DEBUG_LEVEL_TRACE
if ( delta_x || delta_y )
diff --git a/thirdparty/freetype/src/truetype/ttgxvar.h b/thirdparty/freetype/src/truetype/ttgxvar.h
index 7e81719a3e..a37bb90266 100644
--- a/thirdparty/freetype/src/truetype/ttgxvar.h
+++ b/thirdparty/freetype/src/truetype/ttgxvar.h
@@ -4,7 +4,7 @@
/* */
/* TrueType GX Font Variation loader (specification) */
/* */
-/* Copyright 2004-2017 by */
+/* Copyright 2004-2018 by */
/* David Turner, Robert Wilhelm, Werner Lemberg and George Williams. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -27,6 +27,8 @@
FT_BEGIN_HEADER
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
/*************************************************************************/
/* */
/* <Struct> */
@@ -402,6 +404,10 @@ FT_BEGIN_HEADER
FT_Fixed* coords );
FT_LOCAL( FT_Error )
+ TT_Set_Named_Instance( TT_Face face,
+ FT_UInt instance_index );
+
+ FT_LOCAL( FT_Error )
tt_face_vary_cvt( TT_Face face,
FT_Stream stream );
@@ -435,6 +441,8 @@ FT_BEGIN_HEADER
FT_LOCAL( void )
tt_done_blend( TT_Face face );
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
FT_END_HEADER
diff --git a/thirdparty/freetype/src/truetype/ttinterp.c b/thirdparty/freetype/src/truetype/ttinterp.c
index ddcc839bb3..da9b595aba 100644
--- a/thirdparty/freetype/src/truetype/ttinterp.c
+++ b/thirdparty/freetype/src/truetype/ttinterp.c
@@ -4,7 +4,7 @@
/* */
/* TrueType bytecode interpreter (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -25,7 +25,7 @@
#include FT_INTERNAL_CALC_H
#include FT_TRIGONOMETRY_H
#include FT_SYSTEM_H
-#include FT_TRUETYPE_DRIVER_H
+#include FT_DRIVER_H
#include FT_MULTIPLE_MASTERS_H
#include "ttinterp.h"
@@ -2165,7 +2165,7 @@
val = ADD_LONG( distance,
exc->threshold - exc->phase + compensation ) &
-exc->period;
- val += exc->phase;
+ val = ADD_LONG( val, exc->phase );
if ( val < 0 )
val = exc->phase;
}
@@ -2174,7 +2174,7 @@
val = NEG_LONG( SUB_LONG( exc->threshold - exc->phase + compensation,
distance ) &
-exc->period );
- val -= exc->phase;
+ val = SUB_LONG( val, exc->phase );
if ( val > 0 )
val = -exc->phase;
}
@@ -2216,7 +2216,7 @@
val = ( ADD_LONG( distance,
exc->threshold - exc->phase + compensation ) /
exc->period ) * exc->period;
- val += exc->phase;
+ val = ADD_LONG( val, exc->phase );
if ( val < 0 )
val = exc->phase;
}
@@ -2225,7 +2225,7 @@
val = NEG_LONG( ( SUB_LONG( exc->threshold - exc->phase + compensation,
distance ) /
exc->period ) * exc->period );
- val -= exc->phase;
+ val = SUB_LONG( val, exc->phase );
if ( val > 0 )
val = -exc->phase;
}
@@ -2954,7 +2954,7 @@
static void
Ins_CEILING( FT_Long* args )
{
- args[0] = FT_PIX_CEIL( args[0] );
+ args[0] = FT_PIX_CEIL_LONG( args[0] );
}
@@ -3289,7 +3289,10 @@
if ( args[0] < 0 )
exc->error = FT_THROW( Bad_Argument );
else
- exc->GS.loop = args[0];
+ {
+ /* we heuristically limit the number of loops to 16 bits */
+ exc->GS.loop = args[0] > 0xFFFFL ? 0xFFFFL : args[0];
+ }
}
@@ -5690,7 +5693,11 @@
( B1 & 63 ) != 0 &&
( B2 & 63 ) != 0 &&
B1 != B2 )
- Move_Zp2_Point( exc, point, -dx, -dy, TRUE );
+ Move_Zp2_Point( exc,
+ point,
+ NEG_LONG( dx ),
+ NEG_LONG( dy ),
+ TRUE );
}
}
else if ( exc->face->sph_compatibility_mode )
@@ -5722,7 +5729,7 @@
if ( ( B1 & 63 ) == 0 &&
( B2 & 63 ) != 0 &&
B1 != B2 )
- Move_Zp2_Point( exc, point, 0, -dy, TRUE );
+ Move_Zp2_Point( exc, point, 0, NEG_LONG( dy ), TRUE );
}
}
else if ( exc->sph_in_func_flags & SPH_FDEF_TYPEMAN_DIAGENDCTRL )
@@ -5778,6 +5785,7 @@
FT_F26Dot6 distance;
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
FT_F26Dot6 control_value_cutin = 0;
+ FT_F26Dot6 delta;
if ( SUBPIXEL_HINTING_INFINALITY )
@@ -5813,11 +5821,15 @@
distance = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 );
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
+ delta = SUB_LONG( distance, args[1] );
+ if ( delta < 0 )
+ delta = NEG_LONG( delta );
+
/* subpixel hinting - make MSIRP respect CVT cut-in; */
- if ( SUBPIXEL_HINTING_INFINALITY &&
- exc->ignore_x_mode &&
- exc->GS.freeVector.x != 0 &&
- FT_ABS( SUB_LONG( distance, args[1] ) ) >= control_value_cutin )
+ if ( SUBPIXEL_HINTING_INFINALITY &&
+ exc->ignore_x_mode &&
+ exc->GS.freeVector.x != 0 &&
+ delta >= control_value_cutin )
distance = args[1];
#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
@@ -5865,16 +5877,18 @@
if ( SUBPIXEL_HINTING_INFINALITY &&
exc->ignore_x_mode &&
exc->GS.freeVector.x != 0 )
- distance = Round_None(
- exc,
- cur_dist,
- exc->tt_metrics.compensations[0] ) - cur_dist;
+ distance = SUB_LONG(
+ Round_None( exc,
+ cur_dist,
+ exc->tt_metrics.compensations[0] ),
+ cur_dist );
else
#endif
- distance = exc->func_round(
- exc,
- cur_dist,
- exc->tt_metrics.compensations[0] ) - cur_dist;
+ distance = SUB_LONG(
+ exc->func_round( exc,
+ cur_dist,
+ exc->tt_metrics.compensations[0] ),
+ cur_dist );
}
else
distance = 0;
@@ -5974,7 +5988,14 @@
if ( ( exc->opcode & 1 ) != 0 ) /* rounding and control cut-in flag */
{
- if ( FT_ABS( distance - org_dist ) > control_value_cutin )
+ FT_F26Dot6 delta;
+
+
+ delta = SUB_LONG( distance, org_dist );
+ if ( delta < 0 )
+ delta = NEG_LONG( delta );
+
+ if ( delta > control_value_cutin )
distance = org_dist;
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
@@ -5991,7 +6012,7 @@
exc->tt_metrics.compensations[0] );
}
- exc->func_move( exc, &exc->zp0, point, distance - org_dist );
+ exc->func_move( exc, &exc->zp0, point, SUB_LONG( distance, org_dist ) );
Fail:
exc->GS.rp0 = point;
@@ -6074,8 +6095,12 @@
/* single width cut-in test */
- if ( FT_ABS( org_dist - exc->GS.single_width_value ) <
- exc->GS.single_width_cutin )
+ /* |org_dist - single_width_value| < single_width_cutin */
+ if ( exc->GS.single_width_cutin > 0 &&
+ org_dist < exc->GS.single_width_value +
+ exc->GS.single_width_cutin &&
+ org_dist > exc->GS.single_width_value -
+ exc->GS.single_width_cutin )
{
if ( org_dist >= 0 )
org_dist = exc->GS.single_width_value;
@@ -6168,7 +6193,7 @@
minimum_distance = exc->GS.minimum_distance;
control_value_cutin = exc->GS.control_value_cutin;
point = (FT_UShort)args[0];
- cvtEntry = (FT_ULong)( args[1] + 1 );
+ cvtEntry = (FT_ULong)( ADD_LONG( args[1], 1 ) );
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
if ( SUBPIXEL_HINTING_INFINALITY &&
@@ -6251,6 +6276,9 @@
if ( exc->GS.gep0 == exc->GS.gep1 )
{
+ FT_F26Dot6 delta;
+
+
/* XXX: According to Greg Hitchcock, the following wording is */
/* the right one: */
/* */
@@ -6263,7 +6291,11 @@
/* `ttinst2.doc', version 1.66, is thus incorrect since */
/* it implies `>=' instead of `>'. */
- if ( FT_ABS( cvt_dist - org_dist ) > control_value_cutin )
+ delta = SUB_LONG( cvt_dist, org_dist );
+ if ( delta < 0 )
+ delta = NEG_LONG( delta );
+
+ if ( delta > control_value_cutin )
cvt_dist = org_dist;
}
@@ -6281,7 +6313,14 @@
exc->ignore_x_mode &&
exc->GS.gep0 == exc->GS.gep1 )
{
- if ( FT_ABS( cvt_dist - org_dist ) > control_value_cutin )
+ FT_F26Dot6 delta;
+
+
+ delta = SUB_LONG( cvt_dist, org_dist );
+ if ( delta < 0 )
+ delta = NEG_LONG( delta );
+
+ if ( delta > control_value_cutin )
cvt_dist = org_dist;
}
#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
@@ -7153,7 +7192,7 @@
SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES_DELTAP ) &&
( B1 & 63 ) != 0 &&
( B2 & 63 ) != 0 ) ) )
- exc->func_move( exc, &exc->zp0, A, -B );
+ exc->func_move( exc, &exc->zp0, A, NEG_LONG( B ) );
}
}
else
@@ -7524,8 +7563,16 @@
return;
}
- for ( i = 0; i < num_axes; i++ )
- args[i] = coords[i] >> 2; /* convert 16.16 to 2.14 format */
+ if ( coords )
+ {
+ for ( i = 0; i < num_axes; i++ )
+ args[i] = coords[i] >> 2; /* convert 16.16 to 2.14 format */
+ }
+ else
+ {
+ for ( i = 0; i < num_axes; i++ )
+ args[i] = 0;
+ }
}
@@ -8478,7 +8525,9 @@
} while ( !exc->instruction_trap );
LNo_Error_:
- FT_TRACE4(( " %d instructions executed\n", ins_counter ));
+ FT_TRACE4(( " %d instruction%s executed\n",
+ ins_counter,
+ ins_counter == 1 ? "" : "s" ));
return FT_Err_Ok;
LErrorCodeOverflow_:
diff --git a/thirdparty/freetype/src/truetype/ttinterp.h b/thirdparty/freetype/src/truetype/ttinterp.h
index abbecfcee3..2966439ea2 100644
--- a/thirdparty/freetype/src/truetype/ttinterp.h
+++ b/thirdparty/freetype/src/truetype/ttinterp.h
@@ -4,7 +4,7 @@
/* */
/* TrueType bytecode interpreter (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/truetype/ttobjs.c b/thirdparty/freetype/src/truetype/ttobjs.c
index 081fa2f1a5..6685dc8196 100644
--- a/thirdparty/freetype/src/truetype/ttobjs.c
+++ b/thirdparty/freetype/src/truetype/ttobjs.c
@@ -4,7 +4,7 @@
/* */
/* Objects manager (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -21,7 +21,7 @@
#include FT_INTERNAL_STREAM_H
#include FT_TRUETYPE_TAGS_H
#include FT_INTERNAL_SFNT_H
-#include FT_TRUETYPE_DRIVER_H
+#include FT_DRIVER_H
#include "ttgload.h"
#include "ttpload.h"
@@ -147,7 +147,7 @@
{
#define TRICK_NAMES_MAX_CHARACTERS 19
-#define TRICK_NAMES_COUNT 18
+#define TRICK_NAMES_COUNT 26
static const char trick_names[TRICK_NAMES_COUNT]
[TRICK_NAMES_MAX_CHARACTERS + 1] =
@@ -165,9 +165,17 @@
"cpop", /* dftt-p7.ttf; version 1.00, 1992 [DLJGyShoMedium] */
"DFGirl-W6-WIN-BF", /* dftt-h6.ttf; version 1.00, 1993 */
+ "DFGothic-EB", /* DynaLab Inc. 1992-1995 */
+ "DFGyoSho-Lt", /* DynaLab Inc. 1992-1995 */
+ "DFHei-Md-HK-BF", /* maybe DynaLab Inc. */
+ "DFHSGothic-W5", /* DynaLab Inc. 1992-1995 */
+ "DFHSMincho-W3", /* DynaLab Inc. 1992-1995 */
+ "DFHSMincho-W7", /* DynaLab Inc. 1992-1995 */
"DFKaiSho-SB", /* dfkaisb.ttf */
"DFKaiShu",
+ "DFKaiShu-Md-HK-BF", /* maybe DynaLab Inc. */
"DFKai-SB", /* kaiu.ttf; version 3.00, 1998 [DFKaiShu-SB-Estd-BF] */
+ "DFMing-Bd-HK-BF", /* maybe DynaLab Inc. */
"DLC", /* dftt-m7.ttf; version 1.00, 1993 [DLCMingBold] */
/* dftt-f5.ttf; version 1.00, 1993 [DLCFongSung] */
"DLCHayMedium", /* dftt-b5.ttf; version 1.00, 1993 */
@@ -265,7 +273,7 @@
tt_check_trickyness_sfnt_ids( TT_Face face )
{
#define TRICK_SFNT_IDS_PER_FACE 3
-#define TRICK_SFNT_IDS_NUM_FACES 19
+#define TRICK_SFNT_IDS_NUM_FACES 29
static const tt_sfnt_id_rec sfnt_id[TRICK_SFNT_IDS_NUM_FACES]
[TRICK_SFNT_IDS_PER_FACE] = {
@@ -284,6 +292,36 @@
{ 0x28233BF1UL, 0x000087C4UL }, /* fpgm */
{ 0xA344A1EBUL, 0x000001E1UL } /* prep */
},
+ { /* DFGothic-EB */
+ { 0x12C3EBB2UL, 0x00000350UL }, /* cvt */
+ { 0xB680EE64UL, 0x000087A7UL }, /* fpgm */
+ { 0xCE939563UL, 0x00000758UL } /* prep */
+ },
+ { /* DFGyoSho-Lt */
+ { 0x11E5EAD4UL, 0x00000350UL }, /* cvt */
+ { 0xCE5956E9UL, 0x0000BC85UL }, /* fpgm */
+ { 0x8272F416UL, 0x00000045UL } /* prep */
+ },
+ { /* DFHei-Md-HK-BF */
+ { 0x1257EB46UL, 0x00000350UL }, /* cvt */
+ { 0xF699D160UL, 0x0000715FUL }, /* fpgm */
+ { 0xD222F568UL, 0x000003BCUL } /* prep */
+ },
+ { /* DFHSGothic-W5 */
+ { 0x1262EB4EUL, 0x00000350UL }, /* cvt */
+ { 0xE86A5D64UL, 0x00007940UL }, /* fpgm */
+ { 0x7850F729UL, 0x000005FFUL } /* prep */
+ },
+ { /* DFHSMincho-W3 */
+ { 0x122DEB0AUL, 0x00000350UL }, /* cvt */
+ { 0x3D16328AUL, 0x0000859BUL }, /* fpgm */
+ { 0xA93FC33BUL, 0x000002CBUL } /* prep */
+ },
+ { /* DFHSMincho-W7 */
+ { 0x125FEB26UL, 0x00000350UL }, /* cvt */
+ { 0xA5ACC982UL, 0x00007EE1UL }, /* fpgm */
+ { 0x90999196UL, 0x0000041FUL } /* prep */
+ },
{ /* DFKaiShu */
{ 0x11E5EAD4UL, 0x00000350UL }, /* cvt */
{ 0x5A30CA3BUL, 0x00009063UL }, /* fpgm */
@@ -294,6 +332,26 @@
{ 0xA6E78C01UL, 0x00008998UL }, /* fpgm */
{ 0x13A42602UL, 0x0000007EUL } /* prep */
},
+ { /* DFKaiShu-Md-HK-BF */
+ { 0x11E5EAD4UL, 0x00000360UL }, /* cvt */
+ { 0x9DB282B2UL, 0x0000C06EUL }, /* fpgm */
+ { 0x53E6D7CAUL, 0x00000082UL } /* prep */
+ },
+ { /* DFMing-Bd-HK-BF */
+ { 0x1243EB18UL, 0x00000350UL }, /* cvt */
+ { 0xBA0A8C30UL, 0x000074ADUL }, /* fpgm */
+ { 0xF3D83409UL, 0x0000037BUL } /* prep */
+ },
+ { /* DLCLiShu */
+ { 0x07DCF546UL, 0x00000308UL }, /* cvt */
+ { 0x40FE7C90UL, 0x00008E2AUL }, /* fpgm */
+ { 0x608174B5UL, 0x0000007AUL } /* prep */
+ },
+ { /* DLCHayBold */
+ { 0xEB891238UL, 0x00000308UL }, /* cvt */
+ { 0xD2E4DCD4UL, 0x0000676FUL }, /* fpgm */
+ { 0x8EA5F293UL, 0x000003B8UL } /* prep */
+ },
{ /* HuaTianKaiTi */
{ 0xFFFBFFFCUL, 0x00000008UL }, /* cvt */
{ 0x9C9E48B8UL, 0x0000BEA2UL }, /* fpgm */
@@ -657,46 +715,17 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
{
- FT_Int instance_index = face_index >> 16;
+ FT_UInt instance_index = (FT_UInt)face_index >> 16;
if ( FT_HAS_MULTIPLE_MASTERS( ttface ) &&
instance_index > 0 )
{
- error = TT_Get_MM_Var( face, NULL );
+ error = TT_Set_Named_Instance( face, instance_index );
if ( error )
goto Exit;
- if ( face->blend->mmvar->namedstyle )
- {
- FT_Memory memory = ttface->memory;
-
- FT_Var_Named_Style* named_style;
- FT_String* style_name;
-
-
- /* in `face_index', the instance index starts with value 1 */
- named_style = face->blend->mmvar->namedstyle + instance_index - 1;
- error = sfnt->get_name( face,
- (FT_UShort)named_style->strid,
- &style_name );
- if ( error )
- goto Exit;
-
- /* set style name; if already set, replace it */
- if ( face->root.style_name )
- FT_FREE( face->root.style_name );
- face->root.style_name = style_name;
-
- /* finally, select the named instance */
- error = TT_Set_Var_Design( face,
- face->blend->mmvar->num_axis,
- named_style->coords );
- if ( error )
- goto Exit;
-
- tt_apply_mvar( face );
- }
+ tt_apply_mvar( face );
}
}
diff --git a/thirdparty/freetype/src/truetype/ttobjs.h b/thirdparty/freetype/src/truetype/ttobjs.h
index cdacee75e5..38fa30e4e9 100644
--- a/thirdparty/freetype/src/truetype/ttobjs.h
+++ b/thirdparty/freetype/src/truetype/ttobjs.h
@@ -4,7 +4,7 @@
/* */
/* Objects manager (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/truetype/ttpic.c b/thirdparty/freetype/src/truetype/ttpic.c
index 66bd7e1934..cdbb80639e 100644
--- a/thirdparty/freetype/src/truetype/ttpic.c
+++ b/thirdparty/freetype/src/truetype/ttpic.c
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for truetype module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/truetype/ttpic.h b/thirdparty/freetype/src/truetype/ttpic.h
index 1410cd73c3..df878ae6f1 100644
--- a/thirdparty/freetype/src/truetype/ttpic.h
+++ b/thirdparty/freetype/src/truetype/ttpic.h
@@ -4,7 +4,7 @@
/* */
/* The FreeType position independent code services for truetype module. */
/* */
-/* Copyright 2009-2017 by */
+/* Copyright 2009-2018 by */
/* Oran Agra and Mickey Gabel. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/truetype/ttpload.c b/thirdparty/freetype/src/truetype/ttpload.c
index bcf6b34f67..d9526ad082 100644
--- a/thirdparty/freetype/src/truetype/ttpload.c
+++ b/thirdparty/freetype/src/truetype/ttpload.c
@@ -4,7 +4,7 @@
/* */
/* TrueType-specific tables loader (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/truetype/ttpload.h b/thirdparty/freetype/src/truetype/ttpload.h
index 79079f345a..fa12527247 100644
--- a/thirdparty/freetype/src/truetype/ttpload.h
+++ b/thirdparty/freetype/src/truetype/ttpload.h
@@ -4,7 +4,7 @@
/* */
/* TrueType-specific tables loader (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/truetype/ttsubpix.c b/thirdparty/freetype/src/truetype/ttsubpix.c
index 1c8cf01109..d94bcc8b50 100644
--- a/thirdparty/freetype/src/truetype/ttsubpix.c
+++ b/thirdparty/freetype/src/truetype/ttsubpix.c
@@ -4,7 +4,7 @@
/* */
/* TrueType Subpixel Hinting. */
/* */
-/* Copyright 2010-2017 by */
+/* Copyright 2010-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -22,7 +22,7 @@
#include FT_INTERNAL_SFNT_H
#include FT_TRUETYPE_TAGS_H
#include FT_OUTLINE_H
-#include FT_TRUETYPE_DRIVER_H
+#include FT_DRIVER_H
#include "ttsubpix.h"
@@ -753,24 +753,24 @@
/* Does font name match rule family? */
- if ( strcmp( detected_font_name, rule_font_name ) == 0 )
+ if ( ft_strcmp( detected_font_name, rule_font_name ) == 0 )
return TRUE;
/* Is font name a wildcard ""? */
- if ( strcmp( rule_font_name, "" ) == 0 )
+ if ( ft_strcmp( rule_font_name, "" ) == 0 )
return TRUE;
/* Is font name contained in a class list? */
for ( i = 0; i < FAMILY_CLASS_RULES_SIZE; i++ )
{
- if ( strcmp( FAMILY_CLASS_Rules[i].name, rule_font_name ) == 0 )
+ if ( ft_strcmp( FAMILY_CLASS_Rules[i].name, rule_font_name ) == 0 )
{
for ( j = 0; j < SPH_MAX_CLASS_MEMBERS; j++ )
{
- if ( strcmp( FAMILY_CLASS_Rules[i].member[j], "" ) == 0 )
+ if ( ft_strcmp( FAMILY_CLASS_Rules[i].member[j], "" ) == 0 )
continue;
- if ( strcmp( FAMILY_CLASS_Rules[i].member[j],
- detected_font_name ) == 0 )
+ if ( ft_strcmp( FAMILY_CLASS_Rules[i].member[j],
+ detected_font_name ) == 0 )
return TRUE;
}
}
@@ -788,24 +788,24 @@
/* Does font style match rule style? */
- if ( strcmp( detected_font_style, rule_font_style ) == 0 )
+ if ( ft_strcmp( detected_font_style, rule_font_style ) == 0 )
return TRUE;
/* Is font style a wildcard ""? */
- if ( strcmp( rule_font_style, "" ) == 0 )
+ if ( ft_strcmp( rule_font_style, "" ) == 0 )
return TRUE;
/* Is font style contained in a class list? */
for ( i = 0; i < STYLE_CLASS_RULES_SIZE; i++ )
{
- if ( strcmp( STYLE_CLASS_Rules[i].name, rule_font_style ) == 0 )
+ if ( ft_strcmp( STYLE_CLASS_Rules[i].name, rule_font_style ) == 0 )
{
for ( j = 0; j < SPH_MAX_CLASS_MEMBERS; j++ )
{
- if ( strcmp( STYLE_CLASS_Rules[i].member[j], "" ) == 0 )
+ if ( ft_strcmp( STYLE_CLASS_Rules[i].member[j], "" ) == 0 )
continue;
- if ( strcmp( STYLE_CLASS_Rules[i].member[j],
- detected_font_style ) == 0 )
+ if ( ft_strcmp( STYLE_CLASS_Rules[i].member[j],
+ detected_font_style ) == 0 )
return TRUE;
}
}
diff --git a/thirdparty/freetype/src/truetype/ttsubpix.h b/thirdparty/freetype/src/truetype/ttsubpix.h
index c68f97ff07..1070bb016f 100644
--- a/thirdparty/freetype/src/truetype/ttsubpix.h
+++ b/thirdparty/freetype/src/truetype/ttsubpix.h
@@ -4,7 +4,7 @@
/* */
/* TrueType Subpixel Hinting. */
/* */
-/* Copyright 2010-2017 by */
+/* Copyright 2010-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type1/module.mk b/thirdparty/freetype/src/type1/module.mk
index f299d6fe88..3fea5cc16f 100644
--- a/thirdparty/freetype/src/type1/module.mk
+++ b/thirdparty/freetype/src/type1/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/type1/rules.mk b/thirdparty/freetype/src/type1/rules.mk
index 97bef288f0..cb1a142860 100644
--- a/thirdparty/freetype/src/type1/rules.mk
+++ b/thirdparty/freetype/src/type1/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/type1/t1afm.c b/thirdparty/freetype/src/type1/t1afm.c
index 11a2646fc2..61053d9a64 100644
--- a/thirdparty/freetype/src/type1/t1afm.c
+++ b/thirdparty/freetype/src/type1/t1afm.c
@@ -4,7 +4,7 @@
/* */
/* AFM support for Type 1 fonts (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type1/t1afm.h b/thirdparty/freetype/src/type1/t1afm.h
index 9f62cd013d..cb8d302b4d 100644
--- a/thirdparty/freetype/src/type1/t1afm.h
+++ b/thirdparty/freetype/src/type1/t1afm.h
@@ -4,7 +4,7 @@
/* */
/* AFM support for Type 1 fonts (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type1/t1driver.c b/thirdparty/freetype/src/type1/t1driver.c
index c2089947f9..029b410b47 100644
--- a/thirdparty/freetype/src/type1/t1driver.c
+++ b/thirdparty/freetype/src/type1/t1driver.c
@@ -4,7 +4,7 @@
/* */
/* Type 1 driver interface (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -30,6 +30,8 @@
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_HASH_H
+#include FT_INTERNAL_POSTSCRIPT_PROPS_H
+#include FT_DRIVER_H
#include FT_SERVICE_MULTIPLE_MASTERS_H
#include FT_SERVICE_GLYPH_DICT_H
@@ -37,6 +39,7 @@
#include FT_SERVICE_POSTSCRIPT_NAME_H
#include FT_SERVICE_POSTSCRIPT_CMAPS_H
#include FT_SERVICE_POSTSCRIPT_INFO_H
+#include FT_SERVICE_PROPERTIES_H
#include FT_SERVICE_KERNING_H
@@ -126,6 +129,7 @@
(FT_Get_MM_Var_Func) T1_Get_MM_Var, /* get_mm_var */
(FT_Set_Var_Design_Func)T1_Set_Var_Design, /* set_var_design */
(FT_Get_Var_Design_Func)T1_Get_Var_Design, /* get_var_design */
+ (FT_Set_Instance_Func) T1_Reset_MM_Blend, /* set_instance */
(FT_Get_Var_Blend_Func) NULL, /* get_var_blend */
(FT_Done_Blend_Func) T1_Done_Blend /* done_blend */
@@ -614,6 +618,18 @@
/*
+ * PROPERTY SERVICE
+ *
+ */
+
+ FT_DEFINE_SERVICE_PROPERTIESREC(
+ t1_service_properties,
+
+ (FT_Properties_SetFunc)ps_property_set, /* set_property */
+ (FT_Properties_GetFunc)ps_property_get ) /* get_property */
+
+
+ /*
* SERVICE LIST
*
*/
@@ -624,6 +640,7 @@
{ FT_SERVICE_ID_GLYPH_DICT, &t1_service_glyph_dict },
{ FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_TYPE_1 },
{ FT_SERVICE_ID_POSTSCRIPT_INFO, &t1_service_ps_info },
+ { FT_SERVICE_ID_PROPERTIES, &t1_service_properties },
#ifndef T1_CONFIG_OPTION_NO_AFM
{ FT_SERVICE_ID_KERNING, &t1_service_kerning },
@@ -713,7 +730,7 @@
FT_MODULE_DRIVER_SCALABLE |
FT_MODULE_DRIVER_HAS_HINTER,
- sizeof ( FT_DriverRec ),
+ sizeof ( PS_DriverRec ),
"type1",
0x10000L,
diff --git a/thirdparty/freetype/src/type1/t1driver.h b/thirdparty/freetype/src/type1/t1driver.h
index 292786448d..2b1507233d 100644
--- a/thirdparty/freetype/src/type1/t1driver.h
+++ b/thirdparty/freetype/src/type1/t1driver.h
@@ -4,7 +4,7 @@
/* */
/* High-level Type 1 driver interface (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type1/t1errors.h b/thirdparty/freetype/src/type1/t1errors.h
index 492dbb4a42..9e0151b957 100644
--- a/thirdparty/freetype/src/type1/t1errors.h
+++ b/thirdparty/freetype/src/type1/t1errors.h
@@ -4,7 +4,7 @@
/* */
/* Type 1 error codes (specification only). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type1/t1gload.c b/thirdparty/freetype/src/type1/t1gload.c
index aaf19b6dcc..87d40e7566 100644
--- a/thirdparty/freetype/src/type1/t1gload.c
+++ b/thirdparty/freetype/src/type1/t1gload.c
@@ -4,7 +4,7 @@
/* */
/* Type 1 Glyph Loader (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -23,6 +23,8 @@
#include FT_INTERNAL_STREAM_H
#include FT_OUTLINE_H
#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_CFF_TYPES_H
+#include FT_DRIVER_H
#include "t1errors.h"
@@ -37,37 +39,28 @@
#define FT_COMPONENT trace_t1gload
- /*************************************************************************/
- /*************************************************************************/
- /*************************************************************************/
- /********** *********/
- /********** COMPUTE THE MAXIMUM ADVANCE WIDTH *********/
- /********** *********/
- /********** The following code is in charge of computing *********/
- /********** the maximum advance width of the font. It *********/
- /********** quickly processes each glyph charstring to *********/
- /********** extract the value from either a `sbw' or `seac' *********/
- /********** operator. *********/
- /********** *********/
- /*************************************************************************/
- /*************************************************************************/
- /*************************************************************************/
-
-
static FT_Error
T1_Parse_Glyph_And_Get_Char_String( T1_Decoder decoder,
FT_UInt glyph_index,
- FT_Data* char_string )
+ FT_Data* char_string,
+ FT_Bool* force_scaling )
{
T1_Face face = (T1_Face)decoder->builder.face;
T1_Font type1 = &face->type1;
FT_Error error = FT_Err_Ok;
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+ const T1_Decoder_Funcs decoder_funcs = psaux->t1_decoder_funcs;
+ PS_Decoder psdecoder;
+
#ifdef FT_CONFIG_OPTION_INCREMENTAL
FT_Incremental_InterfaceRec *inc =
face->root.internal->incremental_interface;
#endif
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( face );
+#endif
decoder->font_matrix = type1->font_matrix;
decoder->font_offset = type1->font_offset;
@@ -90,9 +83,56 @@
}
if ( !error )
- error = decoder->funcs.parse_charstrings(
- decoder, (FT_Byte*)char_string->pointer,
- (FT_UInt)char_string->length );
+ {
+ /* choose which renderer to use */
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ if ( driver->hinting_engine == FT_HINTING_FREETYPE ||
+ decoder->builder.metrics_only )
+ error = decoder_funcs->parse_charstrings_old(
+ decoder,
+ (FT_Byte*)char_string->pointer,
+ (FT_UInt)char_string->length );
+#else
+ if ( decoder->builder.metrics_only )
+ error = decoder_funcs->parse_metrics(
+ decoder,
+ (FT_Byte*)char_string->pointer,
+ (FT_UInt)char_string->length );
+#endif
+ else
+ {
+ CFF_SubFontRec subfont;
+
+
+ psaux->ps_decoder_init( &psdecoder, decoder, TRUE );
+
+ psaux->t1_make_subfont( FT_FACE( face ),
+ &face->type1.private_dict, &subfont );
+ psdecoder.current_subfont = &subfont;
+
+ error = decoder_funcs->parse_charstrings(
+ &psdecoder,
+ (FT_Byte*)char_string->pointer,
+ (FT_ULong)char_string->length );
+
+ /* Adobe's engine uses 16.16 numbers everywhere; */
+ /* as a consequence, glyphs larger than 2000ppem get rejected */
+ if ( FT_ERR_EQ( error, Glyph_Too_Big ) )
+ {
+ /* this time, we retry unhinted and scale up the glyph later on */
+ /* (the engine uses and sets the hardcoded value 0x10000 / 64 = */
+ /* 0x400 for both `x_scale' and `y_scale' in this case) */
+ ((T1_GlyphSlot)decoder->builder.glyph)->hint = FALSE;
+
+ *force_scaling = TRUE;
+
+ error = decoder_funcs->parse_charstrings(
+ &psdecoder,
+ (FT_Byte*)char_string->pointer,
+ (FT_ULong)char_string->length );
+ }
+ }
+ }
#ifdef FT_CONFIG_OPTION_INCREMENTAL
@@ -126,8 +166,10 @@
FT_UInt glyph_index )
{
FT_Data glyph_data;
- FT_Error error = T1_Parse_Glyph_And_Get_Char_String(
- decoder, glyph_index, &glyph_data );
+ FT_Bool force_scaling = FALSE;
+ FT_Error error = T1_Parse_Glyph_And_Get_Char_String(
+ decoder, glyph_index, &glyph_data,
+ &force_scaling );
#ifdef FT_CONFIG_OPTION_INCREMENTAL
@@ -149,6 +191,23 @@
}
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /********** *********/
+ /********** COMPUTE THE MAXIMUM ADVANCE WIDTH *********/
+ /********** *********/
+ /********** The following code is in charge of computing *********/
+ /********** the maximum advance width of the font. It *********/
+ /********** quickly processes each glyph charstring to *********/
+ /********** extract the value from either a `sbw' or `seac' *********/
+ /********** operator. *********/
+ /********** *********/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
FT_LOCAL_DEF( FT_Error )
T1_Compute_Max_Advance( T1_Face face,
FT_Pos* max_advance )
@@ -278,6 +337,8 @@
T1_DecoderRec decoder;
T1_Face face = (T1_Face)t1glyph->face;
FT_Bool hinting;
+ FT_Bool scaled;
+ FT_Bool force_scaling = FALSE;
T1_Font type1 = &face->type1;
PSAux_Service psaux = (PSAux_Service)face->psaux;
const T1_Decoder_Funcs decoder_funcs = psaux->t1_decoder_funcs;
@@ -325,7 +386,10 @@
hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 &&
( load_flags & FT_LOAD_NO_HINTING ) == 0 );
+ scaled = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 );
+ glyph->hint = hinting;
+ glyph->scaled = scaled;
t1glyph->format = FT_GLYPH_FORMAT_OUTLINE;
error = decoder_funcs->init( &decoder,
@@ -355,13 +419,15 @@
/* now load the unscaled outline */
error = T1_Parse_Glyph_And_Get_Char_String( &decoder, glyph_index,
- &glyph_data );
+ &glyph_data,
+ &force_scaling );
if ( error )
goto Exit;
#ifdef FT_CONFIG_OPTION_INCREMENTAL
glyph_data_loaded = 1;
#endif
+ hinting = glyph->hint;
font_matrix = decoder.font_matrix;
font_offset = decoder.font_offset;
@@ -451,7 +517,7 @@
}
#endif
- if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 )
+ if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 || force_scaling )
{
/* scale the outline and the metrics */
FT_Int n;
diff --git a/thirdparty/freetype/src/type1/t1gload.h b/thirdparty/freetype/src/type1/t1gload.h
index cc4d5e734f..72ef76f6ae 100644
--- a/thirdparty/freetype/src/type1/t1gload.h
+++ b/thirdparty/freetype/src/type1/t1gload.h
@@ -4,7 +4,7 @@
/* */
/* Type 1 Glyph Loader (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type1/t1load.c b/thirdparty/freetype/src/type1/t1load.c
index f569d6bec3..9dfa637a69 100644
--- a/thirdparty/freetype/src/type1/t1load.c
+++ b/thirdparty/freetype/src/type1/t1load.c
@@ -4,7 +4,7 @@
/* */
/* Type 1 font loader (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -366,14 +366,16 @@
}
- FT_LOCAL_DEF( FT_Error )
- T1_Set_MM_Blend( T1_Face face,
+ static FT_Error
+ t1_set_mm_blend( T1_Face face,
FT_UInt num_coords,
FT_Fixed* coords )
{
PS_Blend blend = face->blend;
FT_UInt n, m;
+ FT_Bool have_diff = 0;
+
if ( !blend )
return FT_THROW( Invalid_Argument );
@@ -405,9 +407,36 @@
result = FT_MulFix( result, factor );
}
- blend->weight_vector[n] = result;
+
+ if ( blend->weight_vector[n] != result )
+ {
+ blend->weight_vector[n] = result;
+ have_diff = 1;
+ }
}
+ /* return value -1 indicates `no change' */
+ return have_diff ? FT_Err_Ok : -1;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Set_MM_Blend( T1_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error;
+
+
+ error = t1_set_mm_blend( face, num_coords, coords );
+ if ( error )
+ return error;
+
+ if ( num_coords )
+ face->root.face_flags |= FT_FACE_FLAG_VARIATION;
+ else
+ face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
+
return FT_Err_Ok;
}
@@ -452,6 +481,7 @@
FT_UInt num_coords,
FT_Long* coords )
{
+ FT_Error error;
PS_Blend blend = face->blend;
FT_UInt n, p;
FT_Fixed final_blends[T1_MAX_MM_DESIGNS];
@@ -518,7 +548,28 @@
final_blends[n] = the_blend;
}
- return T1_Set_MM_Blend( face, blend->num_axis, final_blends );
+ error = t1_set_mm_blend( face, blend->num_axis, final_blends );
+ if ( error )
+ return error;
+
+ if ( num_coords )
+ face->root.face_flags |= FT_FACE_FLAG_VARIATION;
+ else
+ face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* MM fonts don't have named instances, so only the design is reset */
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Reset_MM_Blend( T1_Face face,
+ FT_UInt instance_index )
+ {
+ FT_UNUSED( instance_index );
+
+ return T1_Set_MM_Blend( face, 0, NULL );
}
@@ -1266,7 +1317,7 @@
if ( ft_isdigit( *cur ) || *cur == '[' )
{
T1_Encoding encode = &face->type1.encoding;
- FT_Int count, n;
+ FT_Int count, array_size, n;
PS_Table char_table = &loader->encoding_table;
FT_Memory memory = parser->root.memory;
FT_Error error;
@@ -1283,13 +1334,12 @@
else
count = (FT_Int)T1_ToInt( parser );
- /* only composite fonts (which we don't support) */
- /* can have larger values */
+ array_size = count;
if ( count > 256 )
{
- FT_ERROR(( "parse_encoding: invalid encoding array size\n" ));
- parser->root.error = FT_THROW( Invalid_File_Format );
- return;
+ FT_TRACE2(( "parse_encoding:"
+ " only using first 256 encoding array entries\n" ));
+ array_size = 256;
}
T1_Skip_Spaces( parser );
@@ -1305,18 +1355,18 @@
}
/* we use a T1_Table to store our charnames */
- loader->num_chars = encode->num_chars = count;
- if ( FT_NEW_ARRAY( encode->char_index, count ) ||
- FT_NEW_ARRAY( encode->char_name, count ) ||
+ loader->num_chars = encode->num_chars = array_size;
+ if ( FT_NEW_ARRAY( encode->char_index, array_size ) ||
+ FT_NEW_ARRAY( encode->char_name, array_size ) ||
FT_SET_ERROR( psaux->ps_table_funcs->init(
- char_table, count, memory ) ) )
+ char_table, array_size, memory ) ) )
{
parser->root.error = error;
return;
}
/* We need to `zero' out encoding_table.elements */
- for ( n = 0; n < count; n++ )
+ for ( n = 0; n < array_size; n++ )
{
char* notdef = (char *)".notdef";
@@ -1409,11 +1459,14 @@
len = (FT_UInt)( parser->root.cursor - cur );
- parser->root.error = T1_Add_Table( char_table, charcode,
- cur, len + 1 );
- if ( parser->root.error )
- return;
- char_table->elements[charcode][len] = '\0';
+ if ( n < array_size )
+ {
+ parser->root.error = T1_Add_Table( char_table, charcode,
+ cur, len + 1 );
+ if ( parser->root.error )
+ return;
+ char_table->elements[charcode][len] = '\0';
+ }
n++;
}
@@ -2440,6 +2493,24 @@
type1->encoding.num_chars = loader.num_chars;
}
+ /* some sanitizing to avoid overflows later on; */
+ /* the upper limits are ad-hoc values */
+ if ( priv->blue_shift > 1000 || priv->blue_shift < 0 )
+ {
+ FT_TRACE2(( "T1_Open_Face:"
+ " setting unlikely BlueShift value %d to default (7)\n",
+ priv->blue_shift ));
+ priv->blue_shift = 7;
+ }
+
+ if ( priv->blue_fuzz > 1000 || priv->blue_fuzz < 0 )
+ {
+ FT_TRACE2(( "T1_Open_Face:"
+ " setting unlikely BlueFuzz value %d to default (1)\n",
+ priv->blue_fuzz ));
+ priv->blue_fuzz = 1;
+ }
+
Exit:
t1_done_loader( &loader );
return error;
diff --git a/thirdparty/freetype/src/type1/t1load.h b/thirdparty/freetype/src/type1/t1load.h
index 2d86984f0e..03be3f7f93 100644
--- a/thirdparty/freetype/src/type1/t1load.h
+++ b/thirdparty/freetype/src/type1/t1load.h
@@ -4,7 +4,7 @@
/* */
/* Type 1 font loader (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -90,6 +90,10 @@ FT_BEGIN_HEADER
FT_Long* coords );
FT_LOCAL( FT_Error )
+ T1_Reset_MM_Blend( T1_Face face,
+ FT_UInt instance_index );
+
+ FT_LOCAL( FT_Error )
T1_Get_Var_Design( T1_Face face,
FT_UInt num_coords,
FT_Fixed* coords );
diff --git a/thirdparty/freetype/src/type1/t1objs.c b/thirdparty/freetype/src/type1/t1objs.c
index 5ac1292ae0..7333c4c958 100644
--- a/thirdparty/freetype/src/type1/t1objs.c
+++ b/thirdparty/freetype/src/type1/t1objs.c
@@ -4,7 +4,7 @@
/* */
/* Type 1 objects manager (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -21,6 +21,7 @@
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_TRUETYPE_IDS_H
+#include FT_DRIVER_H
#include "t1gload.h"
#include "t1load.h"
@@ -379,7 +380,7 @@
/* simplistic and might get some things wrong. For a full-featured */
/* algorithm you might have a look at the whitepaper given at */
/* */
- /* http://blogs.msdn.com/text/archive/2007/04/23/wpf-font-selection-model.aspx */
+ /* https://blogs.msdn.com/text/archive/2007/04/23/wpf-font-selection-model.aspx */
/* get style name -- be careful, some broken fonts only */
/* have a `/FontName' dictionary entry! */
@@ -578,9 +579,42 @@
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
- T1_Driver_Init( FT_Module driver )
+ T1_Driver_Init( FT_Module module )
{
- FT_UNUSED( driver );
+ PS_Driver driver = (PS_Driver)module;
+
+ FT_UInt32 seed;
+
+
+ /* set default property values, cf. `ftt1drv.h' */
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ driver->hinting_engine = FT_HINTING_FREETYPE;
+#else
+ driver->hinting_engine = FT_HINTING_ADOBE;
+#endif
+
+ driver->no_stem_darkening = TRUE;
+
+ driver->darken_params[0] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1;
+ driver->darken_params[1] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1;
+ driver->darken_params[2] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2;
+ driver->darken_params[3] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2;
+ driver->darken_params[4] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3;
+ driver->darken_params[5] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3;
+ driver->darken_params[6] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4;
+ driver->darken_params[7] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4;
+
+ /* compute random seed from some memory addresses */
+ seed = (FT_UInt32)( (FT_Offset)(char*)&seed ^
+ (FT_Offset)(char*)&module ^
+ (FT_Offset)(char*)module->memory );
+ seed = seed ^ ( seed >> 10 ) ^ ( seed >> 20 );
+
+ driver->random_seed = (FT_Int32)seed;
+ if ( driver->random_seed < 0 )
+ driver->random_seed = -driver->random_seed;
+ else if ( driver->random_seed == 0 )
+ driver->random_seed = 123456789;
return FT_Err_Ok;
}
diff --git a/thirdparty/freetype/src/type1/t1objs.h b/thirdparty/freetype/src/type1/t1objs.h
index 39d26bf8b9..8298e036f4 100644
--- a/thirdparty/freetype/src/type1/t1objs.h
+++ b/thirdparty/freetype/src/type1/t1objs.h
@@ -4,7 +4,7 @@
/* */
/* Type 1 objects manager (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -120,12 +120,12 @@ FT_BEGIN_HEADER
FT_Bool hint;
FT_Bool scaled;
- FT_Int max_points;
- FT_Int max_contours;
-
FT_Fixed x_scale;
FT_Fixed y_scale;
+ FT_Int max_points;
+ FT_Int max_contours;
+
} T1_GlyphSlotRec;
diff --git a/thirdparty/freetype/src/type1/t1parse.c b/thirdparty/freetype/src/type1/t1parse.c
index 18dd26434c..8e201e5ef5 100644
--- a/thirdparty/freetype/src/type1/t1parse.c
+++ b/thirdparty/freetype/src/type1/t1parse.c
@@ -4,7 +4,7 @@
/* */
/* Type 1 parser (body). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type1/t1parse.h b/thirdparty/freetype/src/type1/t1parse.h
index 3396680d1a..4ac82ae913 100644
--- a/thirdparty/freetype/src/type1/t1parse.h
+++ b/thirdparty/freetype/src/type1/t1parse.h
@@ -4,7 +4,7 @@
/* */
/* Type 1 parser (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type1/t1tokens.h b/thirdparty/freetype/src/type1/t1tokens.h
index ca0c55f903..43a65d88ea 100644
--- a/thirdparty/freetype/src/type1/t1tokens.h
+++ b/thirdparty/freetype/src/type1/t1tokens.h
@@ -4,7 +4,7 @@
/* */
/* Type 1 tokenizer (specification). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type1/type1.c b/thirdparty/freetype/src/type1/type1.c
index 81795376ef..72eff59bfe 100644
--- a/thirdparty/freetype/src/type1/type1.c
+++ b/thirdparty/freetype/src/type1/type1.c
@@ -4,7 +4,7 @@
/* */
/* FreeType Type 1 driver component (body only). */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type42/module.mk b/thirdparty/freetype/src/type42/module.mk
index 2f52806808..3d4732bb6f 100644
--- a/thirdparty/freetype/src/type42/module.mk
+++ b/thirdparty/freetype/src/type42/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2002-2017 by
+# Copyright 2002-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/type42/rules.mk b/thirdparty/freetype/src/type42/rules.mk
index d7e8965015..9325d3898f 100644
--- a/thirdparty/freetype/src/type42/rules.mk
+++ b/thirdparty/freetype/src/type42/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 2002-2017 by
+# Copyright 2002-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/type42/t42drivr.c b/thirdparty/freetype/src/type42/t42drivr.c
index 366cfb3a1d..f579b2708c 100644
--- a/thirdparty/freetype/src/type42/t42drivr.c
+++ b/thirdparty/freetype/src/type42/t42drivr.c
@@ -4,7 +4,7 @@
/* */
/* High-level Type 42 driver interface (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* Roberto Alameda. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type42/t42drivr.h b/thirdparty/freetype/src/type42/t42drivr.h
index 1ac4a0a1a1..3667f3e066 100644
--- a/thirdparty/freetype/src/type42/t42drivr.h
+++ b/thirdparty/freetype/src/type42/t42drivr.h
@@ -4,7 +4,7 @@
/* */
/* High-level Type 42 driver interface (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* Roberto Alameda. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type42/t42error.h b/thirdparty/freetype/src/type42/t42error.h
index fda92abf57..e3978a7607 100644
--- a/thirdparty/freetype/src/type42/t42error.h
+++ b/thirdparty/freetype/src/type42/t42error.h
@@ -4,7 +4,7 @@
/* */
/* Type 42 error codes (specification only). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type42/t42objs.c b/thirdparty/freetype/src/type42/t42objs.c
index 1c4ebd768a..66e5c40382 100644
--- a/thirdparty/freetype/src/type42/t42objs.c
+++ b/thirdparty/freetype/src/type42/t42objs.c
@@ -4,7 +4,7 @@
/* */
/* Type 42 objects manager (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* Roberto Alameda. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type42/t42objs.h b/thirdparty/freetype/src/type42/t42objs.h
index eb4c5bf69f..3bad5135e0 100644
--- a/thirdparty/freetype/src/type42/t42objs.h
+++ b/thirdparty/freetype/src/type42/t42objs.h
@@ -4,7 +4,7 @@
/* */
/* Type 42 objects manager (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* Roberto Alameda. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type42/t42parse.c b/thirdparty/freetype/src/type42/t42parse.c
index e7c6770bd2..4813d1f3f9 100644
--- a/thirdparty/freetype/src/type42/t42parse.c
+++ b/thirdparty/freetype/src/type42/t42parse.c
@@ -4,7 +4,7 @@
/* */
/* Type 42 font parser (body). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* Roberto Alameda. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type42/t42parse.h b/thirdparty/freetype/src/type42/t42parse.h
index 7a68606f2e..f35d23de63 100644
--- a/thirdparty/freetype/src/type42/t42parse.h
+++ b/thirdparty/freetype/src/type42/t42parse.h
@@ -4,7 +4,7 @@
/* */
/* Type 42 font parser (specification). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* Roberto Alameda. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type42/t42types.h b/thirdparty/freetype/src/type42/t42types.h
index 2306ab6c77..d0aa2de570 100644
--- a/thirdparty/freetype/src/type42/t42types.h
+++ b/thirdparty/freetype/src/type42/t42types.h
@@ -4,7 +4,7 @@
/* */
/* Type 42 font data types (specification only). */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* Roberto Alameda. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/type42/type42.c b/thirdparty/freetype/src/type42/type42.c
index ae8ac26782..6a89cfbed1 100644
--- a/thirdparty/freetype/src/type42/type42.c
+++ b/thirdparty/freetype/src/type42/type42.c
@@ -4,7 +4,7 @@
/* */
/* FreeType Type 42 driver component. */
/* */
-/* Copyright 2002-2017 by */
+/* Copyright 2002-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/winfonts/fnterrs.h b/thirdparty/freetype/src/winfonts/fnterrs.h
index 4251021a70..3a86af5aac 100644
--- a/thirdparty/freetype/src/winfonts/fnterrs.h
+++ b/thirdparty/freetype/src/winfonts/fnterrs.h
@@ -4,7 +4,7 @@
/* */
/* Win FNT/FON error codes (specification only). */
/* */
-/* Copyright 2001-2017 by */
+/* Copyright 2001-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
diff --git a/thirdparty/freetype/src/winfonts/module.mk b/thirdparty/freetype/src/winfonts/module.mk
index ffc53a19f9..13f9077cfc 100644
--- a/thirdparty/freetype/src/winfonts/module.mk
+++ b/thirdparty/freetype/src/winfonts/module.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/winfonts/rules.mk b/thirdparty/freetype/src/winfonts/rules.mk
index 73e825d9ae..d694d1a771 100644
--- a/thirdparty/freetype/src/winfonts/rules.mk
+++ b/thirdparty/freetype/src/winfonts/rules.mk
@@ -3,7 +3,7 @@
#
-# Copyright 1996-2017 by
+# Copyright 1996-2018 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
diff --git a/thirdparty/freetype/src/winfonts/winfnt.c b/thirdparty/freetype/src/winfonts/winfnt.c
index 4c47962319..36bd3148d5 100644
--- a/thirdparty/freetype/src/winfonts/winfnt.c
+++ b/thirdparty/freetype/src/winfonts/winfnt.c
@@ -4,7 +4,7 @@
/* */
/* FreeType font driver for Windows FNT/FON files */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* Copyright 2003 Huw D M Davies for Codeweavers */
/* Copyright 2007 Dmitry Timoshkov for Codeweavers */
diff --git a/thirdparty/freetype/src/winfonts/winfnt.h b/thirdparty/freetype/src/winfonts/winfnt.h
index 884b645a2d..4885c9d745 100644
--- a/thirdparty/freetype/src/winfonts/winfnt.h
+++ b/thirdparty/freetype/src/winfonts/winfnt.h
@@ -4,7 +4,7 @@
/* */
/* FreeType font driver for Windows FNT/FON files */
/* */
-/* Copyright 1996-2017 by */
+/* Copyright 1996-2018 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* Copyright 2007 Dmitry Timoshkov for Codeweavers */
/* */
diff --git a/thirdparty/libvorbis/COPYING b/thirdparty/libvorbis/COPYING
index 8f1d18cc2b..153b926a15 100644
--- a/thirdparty/libvorbis/COPYING
+++ b/thirdparty/libvorbis/COPYING
@@ -1,4 +1,4 @@
-Copyright (c) 2002-2015 Xiph.org Foundation
+Copyright (c) 2002-2018 Xiph.org Foundation
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
diff --git a/thirdparty/libvorbis/analysis.c b/thirdparty/libvorbis/analysis.c
index 01aa6f30db..0e11a167be 100644
--- a/thirdparty/libvorbis/analysis.c
+++ b/thirdparty/libvorbis/analysis.c
@@ -11,7 +11,6 @@
********************************************************************
function: single-block PCM analysis mode dispatch
- last mod: $Id: analysis.c 16226 2009-07-08 06:43:49Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/backends.h b/thirdparty/libvorbis/backends.h
index ff5bcc95fe..22809d46d5 100644
--- a/thirdparty/libvorbis/backends.h
+++ b/thirdparty/libvorbis/backends.h
@@ -12,7 +12,6 @@
function: libvorbis backend and mapping structures; needed for
static mode headers
- last mod: $Id: backends.h 16962 2010-03-11 07:30:34Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/barkmel.c b/thirdparty/libvorbis/barkmel.c
index 37b6c4c7ba..4b19935f30 100644
--- a/thirdparty/libvorbis/barkmel.c
+++ b/thirdparty/libvorbis/barkmel.c
@@ -11,7 +11,6 @@
********************************************************************
function: bark scale utility
- last mod: $Id: barkmel.c 19454 2015-03-02 22:39:28Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/bitrate.c b/thirdparty/libvorbis/bitrate.c
index 3a71b1dc23..96055140f7 100644
--- a/thirdparty/libvorbis/bitrate.c
+++ b/thirdparty/libvorbis/bitrate.c
@@ -11,7 +11,6 @@
********************************************************************
function: bitrate tracking and management
- last mod: $Id: bitrate.c 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/bitrate.h b/thirdparty/libvorbis/bitrate.h
index db48fcb645..655a68cc09 100644
--- a/thirdparty/libvorbis/bitrate.h
+++ b/thirdparty/libvorbis/bitrate.h
@@ -11,7 +11,6 @@
********************************************************************
function: bitrate tracking and management
- last mod: $Id: bitrate.h 13293 2007-07-24 00:09:47Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/block.c b/thirdparty/libvorbis/block.c
index 345c042769..db245b3e69 100644
--- a/thirdparty/libvorbis/block.c
+++ b/thirdparty/libvorbis/block.c
@@ -11,7 +11,6 @@
********************************************************************
function: PCM data vector blocking, windowing and dis/reassembly
- last mod: $Id: block.c 19457 2015-03-03 00:15:29Z giles $
Handle windowing, overlap-add, etc of the PCM vectors. This is made
more amusing by Vorbis' current two allowed block sizes.
diff --git a/thirdparty/libvorbis/books/coupled/res_books_51.h b/thirdparty/libvorbis/books/coupled/res_books_51.h
index 93910ff481..47df4b221b 100644
--- a/thirdparty/libvorbis/books/coupled/res_books_51.h
+++ b/thirdparty/libvorbis/books/coupled/res_books_51.h
@@ -11,7 +11,6 @@
********************************************************************
*
* function: static codebooks for 5.1 surround
- * last modified: $Id: res_books_51.h 19057 2014-01-22 12:32:31Z xiphmont $
*
********************************************************************/
diff --git a/thirdparty/libvorbis/books/coupled/res_books_stereo.h b/thirdparty/libvorbis/books/coupled/res_books_stereo.h
index 9a9049f6ed..61d934046d 100644
--- a/thirdparty/libvorbis/books/coupled/res_books_stereo.h
+++ b/thirdparty/libvorbis/books/coupled/res_books_stereo.h
@@ -11,7 +11,6 @@
********************************************************************
function: static codebooks autogenerated by huff/huffbuld
- last modified: $Id: res_books_stereo.h 19057 2014-01-22 12:32:31Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/books/floor/floor_books.h b/thirdparty/libvorbis/books/floor/floor_books.h
index e925313f7b..67d5f31a3b 100644
--- a/thirdparty/libvorbis/books/floor/floor_books.h
+++ b/thirdparty/libvorbis/books/floor/floor_books.h
@@ -11,7 +11,6 @@
********************************************************************
function: static codebooks autogenerated by huff/huffbuld
- last modified: $Id: floor_books.h 19057 2014-01-22 12:32:31Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/books/uncoupled/res_books_uncoupled.h b/thirdparty/libvorbis/books/uncoupled/res_books_uncoupled.h
index 736353b675..3d658ec470 100644
--- a/thirdparty/libvorbis/books/uncoupled/res_books_uncoupled.h
+++ b/thirdparty/libvorbis/books/uncoupled/res_books_uncoupled.h
@@ -11,7 +11,6 @@
********************************************************************
function: static codebooks autogenerated by huff/huffbuld
- last modified: $Id: res_books_uncoupled.h 19057 2014-01-22 12:32:31Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/codebook.c b/thirdparty/libvorbis/codebook.c
index 72f8a17a35..78672e222d 100644
--- a/thirdparty/libvorbis/codebook.c
+++ b/thirdparty/libvorbis/codebook.c
@@ -11,7 +11,6 @@
********************************************************************
function: basic codebook pack/unpack/code/decode operations
- last mod: $Id: codebook.c 19457 2015-03-03 00:15:29Z giles $
********************************************************************/
@@ -387,7 +386,7 @@ long vorbis_book_decodevs_add(codebook *book,float *a,oggpack_buffer *b,int n){
t[i] = book->valuelist+entry[i]*book->dim;
}
for(i=0,o=0;i<book->dim;i++,o+=step)
- for (j=0;j<step;j++)
+ for (j=0;o+j<n && j<step;j++)
a[o+j]+=t[j][i];
}
return(0);
@@ -399,41 +398,12 @@ long vorbis_book_decodev_add(codebook *book,float *a,oggpack_buffer *b,int n){
int i,j,entry;
float *t;
- if(book->dim>8){
- for(i=0;i<n;){
- entry = decode_packed_entry_number(book,b);
- if(entry==-1)return(-1);
- t = book->valuelist+entry*book->dim;
- for (j=0;j<book->dim;)
- a[i++]+=t[j++];
- }
- }else{
- for(i=0;i<n;){
- entry = decode_packed_entry_number(book,b);
- if(entry==-1)return(-1);
- t = book->valuelist+entry*book->dim;
- j=0;
- switch((int)book->dim){
- case 8:
- a[i++]+=t[j++];
- case 7:
- a[i++]+=t[j++];
- case 6:
- a[i++]+=t[j++];
- case 5:
- a[i++]+=t[j++];
- case 4:
- a[i++]+=t[j++];
- case 3:
- a[i++]+=t[j++];
- case 2:
- a[i++]+=t[j++];
- case 1:
- a[i++]+=t[j++];
- case 0:
- break;
- }
- }
+ for(i=0;i<n;){
+ entry = decode_packed_entry_number(book,b);
+ if(entry==-1)return(-1);
+ t = book->valuelist+entry*book->dim;
+ for(j=0;i<n && j<book->dim;)
+ a[i++]+=t[j++];
}
}
return(0);
@@ -471,12 +441,13 @@ long vorbis_book_decodevv_add(codebook *book,float **a,long offset,int ch,
long i,j,entry;
int chptr=0;
if(book->used_entries>0){
- for(i=offset/ch;i<(offset+n)/ch;){
+ int m=(offset+n)/ch;
+ for(i=offset/ch;i<m;){
entry = decode_packed_entry_number(book,b);
if(entry==-1)return(-1);
{
const float *t = book->valuelist+entry*book->dim;
- for (j=0;j<book->dim;j++){
+ for (j=0;i<m && j<book->dim;j++){
a[chptr++][i]+=t[j];
if(chptr==ch){
chptr=0;
diff --git a/thirdparty/libvorbis/codebook.h b/thirdparty/libvorbis/codebook.h
index 537d6c12d3..08440c6962 100644
--- a/thirdparty/libvorbis/codebook.h
+++ b/thirdparty/libvorbis/codebook.h
@@ -11,7 +11,6 @@
********************************************************************
function: basic shared codebook operations
- last mod: $Id: codebook.h 19457 2015-03-03 00:15:29Z giles $
********************************************************************/
diff --git a/thirdparty/libvorbis/codec_internal.h b/thirdparty/libvorbis/codec_internal.h
index de1bccaedf..e522be18da 100644
--- a/thirdparty/libvorbis/codec_internal.h
+++ b/thirdparty/libvorbis/codec_internal.h
@@ -11,7 +11,6 @@
********************************************************************
function: libvorbis codec headers
- last mod: $Id: codec_internal.h 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/envelope.c b/thirdparty/libvorbis/envelope.c
index 010c66e2d6..da75237542 100644
--- a/thirdparty/libvorbis/envelope.c
+++ b/thirdparty/libvorbis/envelope.c
@@ -11,7 +11,6 @@
********************************************************************
function: PCM data envelope analysis
- last mod: $Id: envelope.c 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/envelope.h b/thirdparty/libvorbis/envelope.h
index fd15fb32a7..f466efde8a 100644
--- a/thirdparty/libvorbis/envelope.h
+++ b/thirdparty/libvorbis/envelope.h
@@ -11,7 +11,6 @@
********************************************************************
function: PCM data envelope analysis and manipulation
- last mod: $Id: envelope.h 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/floor0.c b/thirdparty/libvorbis/floor0.c
index 213cce4ec8..443c0e5a96 100644
--- a/thirdparty/libvorbis/floor0.c
+++ b/thirdparty/libvorbis/floor0.c
@@ -11,7 +11,6 @@
********************************************************************
function: floor backend 0 implementation
- last mod: $Id: floor0.c 19457 2015-03-03 00:15:29Z giles $
********************************************************************/
diff --git a/thirdparty/libvorbis/floor1.c b/thirdparty/libvorbis/floor1.c
index d8bd4645c1..673e954c53 100644
--- a/thirdparty/libvorbis/floor1.c
+++ b/thirdparty/libvorbis/floor1.c
@@ -11,7 +11,6 @@
********************************************************************
function: floor backend 1 implementation
- last mod: $Id: floor1.c 19457 2015-03-03 00:15:29Z giles $
********************************************************************/
diff --git a/thirdparty/libvorbis/highlevel.h b/thirdparty/libvorbis/highlevel.h
index e38f370fd6..337b75bfa4 100644
--- a/thirdparty/libvorbis/highlevel.h
+++ b/thirdparty/libvorbis/highlevel.h
@@ -11,7 +11,6 @@
********************************************************************
function: highlevel encoder setup struct separated out for vorbisenc clarity
- last mod: $Id: highlevel.h 17195 2010-05-05 21:49:51Z giles $
********************************************************************/
diff --git a/thirdparty/libvorbis/info.c b/thirdparty/libvorbis/info.c
index 8a2a001f99..3fbb7c757a 100644
--- a/thirdparty/libvorbis/info.c
+++ b/thirdparty/libvorbis/info.c
@@ -11,7 +11,6 @@
********************************************************************
function: maintain the info structure, info <-> header packets
- last mod: $Id: info.c 19441 2015-01-21 01:17:41Z xiphmont $
********************************************************************/
@@ -31,8 +30,8 @@
#include "misc.h"
#include "os.h"
-#define GENERAL_VENDOR_STRING "Xiph.Org libVorbis 1.3.5"
-#define ENCODE_VENDOR_STRING "Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)"
+#define GENERAL_VENDOR_STRING "Xiph.Org libVorbis 1.3.6"
+#define ENCODE_VENDOR_STRING "Xiph.Org libVorbis I 20180316 (Now 100% fewer shells)"
/* helpers */
static void _v_writestring(oggpack_buffer *o,const char *s, int bytes){
@@ -65,11 +64,13 @@ void vorbis_comment_add(vorbis_comment *vc,const char *comment){
}
void vorbis_comment_add_tag(vorbis_comment *vc, const char *tag, const char *contents){
- char *comment=alloca(strlen(tag)+strlen(contents)+2); /* +2 for = and \0 */
+ /* Length for key and value +2 for = and \0 */
+ char *comment=_ogg_malloc(strlen(tag)+strlen(contents)+2);
strcpy(comment, tag);
strcat(comment, "=");
strcat(comment, contents);
vorbis_comment_add(vc, comment);
+ _ogg_free(comment);
}
/* This is more or less the same as strncasecmp - but that doesn't exist
@@ -88,27 +89,30 @@ char *vorbis_comment_query(vorbis_comment *vc, const char *tag, int count){
long i;
int found = 0;
int taglen = strlen(tag)+1; /* +1 for the = we append */
- char *fulltag = alloca(taglen+ 1);
+ char *fulltag = _ogg_malloc(taglen+1);
strcpy(fulltag, tag);
strcat(fulltag, "=");
for(i=0;i<vc->comments;i++){
if(!tagcompare(vc->user_comments[i], fulltag, taglen)){
- if(count == found)
+ if(count == found) {
/* We return a pointer to the data, not a copy */
- return vc->user_comments[i] + taglen;
- else
+ _ogg_free(fulltag);
+ return vc->user_comments[i] + taglen;
+ } else {
found++;
+ }
}
}
+ _ogg_free(fulltag);
return NULL; /* didn't find anything */
}
int vorbis_comment_query_count(vorbis_comment *vc, const char *tag){
int i,count=0;
int taglen = strlen(tag)+1; /* +1 for the = we append */
- char *fulltag = alloca(taglen+1);
+ char *fulltag = _ogg_malloc(taglen+1);
strcpy(fulltag,tag);
strcat(fulltag, "=");
@@ -117,6 +121,7 @@ int vorbis_comment_query_count(vorbis_comment *vc, const char *tag){
count++;
}
+ _ogg_free(fulltag);
return count;
}
@@ -206,9 +211,9 @@ static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){
vi->channels=oggpack_read(opb,8);
vi->rate=oggpack_read(opb,32);
- vi->bitrate_upper=oggpack_read(opb,32);
- vi->bitrate_nominal=oggpack_read(opb,32);
- vi->bitrate_lower=oggpack_read(opb,32);
+ vi->bitrate_upper=(ogg_int32_t)oggpack_read(opb,32);
+ vi->bitrate_nominal=(ogg_int32_t)oggpack_read(opb,32);
+ vi->bitrate_lower=(ogg_int32_t)oggpack_read(opb,32);
ci->blocksizes[0]=1<<oggpack_read(opb,4);
ci->blocksizes[1]=1<<oggpack_read(opb,4);
@@ -583,7 +588,8 @@ int vorbis_analysis_headerout(vorbis_dsp_state *v,
oggpack_buffer opb;
private_state *b=v->backend_state;
- if(!b||vi->channels<=0){
+ if(!b||vi->channels<=0||vi->channels>256){
+ b = NULL;
ret=OV_EFAULT;
goto err_out;
}
@@ -642,7 +648,7 @@ int vorbis_analysis_headerout(vorbis_dsp_state *v,
memset(op_code,0,sizeof(*op_code));
if(b){
- oggpack_writeclear(&opb);
+ if(vi->channels>0)oggpack_writeclear(&opb);
if(b->header)_ogg_free(b->header);
if(b->header1)_ogg_free(b->header1);
if(b->header2)_ogg_free(b->header2);
diff --git a/thirdparty/libvorbis/lookup.c b/thirdparty/libvorbis/lookup.c
index 3321ed3dbc..1cc1f88ee9 100644
--- a/thirdparty/libvorbis/lookup.c
+++ b/thirdparty/libvorbis/lookup.c
@@ -11,7 +11,6 @@
********************************************************************
function: lookup based functions
- last mod: $Id: lookup.c 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/lookup.h b/thirdparty/libvorbis/lookup.h
index f8b5b82730..4bc0f3a206 100644
--- a/thirdparty/libvorbis/lookup.h
+++ b/thirdparty/libvorbis/lookup.h
@@ -11,7 +11,6 @@
********************************************************************
function: lookup based functions
- last mod: $Id: lookup.h 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/lookup_data.h b/thirdparty/libvorbis/lookup_data.h
index 2424a1b386..5de3cfdc7e 100644
--- a/thirdparty/libvorbis/lookup_data.h
+++ b/thirdparty/libvorbis/lookup_data.h
@@ -11,7 +11,6 @@
********************************************************************
function: lookup data; generated by lookups.pl; edit there
- last mod: $Id: lookup_data.h 16037 2009-05-26 21:10:58Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/lpc.c b/thirdparty/libvorbis/lpc.c
index f5199ec235..798f4cf076 100644
--- a/thirdparty/libvorbis/lpc.c
+++ b/thirdparty/libvorbis/lpc.c
@@ -11,7 +11,6 @@
********************************************************************
function: LPC low level routines
- last mod: $Id: lpc.c 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/lpc.h b/thirdparty/libvorbis/lpc.h
index 39d237601b..9cc79451b6 100644
--- a/thirdparty/libvorbis/lpc.h
+++ b/thirdparty/libvorbis/lpc.h
@@ -11,7 +11,6 @@
********************************************************************
function: LPC low level routines
- last mod: $Id: lpc.h 16037 2009-05-26 21:10:58Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/lsp.c b/thirdparty/libvorbis/lsp.c
index 6a619f7b0c..8588054515 100644
--- a/thirdparty/libvorbis/lsp.c
+++ b/thirdparty/libvorbis/lsp.c
@@ -11,7 +11,6 @@
********************************************************************
function: LSP (also called LSF) conversion routines
- last mod: $Id: lsp.c 19453 2015-03-02 22:35:34Z xiphmont $
The LSP generation code is taken (with minimal modification and a
few bugfixes) from "On the Computation of the LSP Frequencies" by
diff --git a/thirdparty/libvorbis/lsp.h b/thirdparty/libvorbis/lsp.h
index bacfb0971f..8a8d10e978 100644
--- a/thirdparty/libvorbis/lsp.h
+++ b/thirdparty/libvorbis/lsp.h
@@ -11,7 +11,6 @@
********************************************************************
function: LSP (also called LSF) conversion routines
- last mod: $Id: lsp.h 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/mapping0.c b/thirdparty/libvorbis/mapping0.c
index 85c7d22d83..ccb4493d4c 100644
--- a/thirdparty/libvorbis/mapping0.c
+++ b/thirdparty/libvorbis/mapping0.c
@@ -11,7 +11,6 @@
********************************************************************
function: channel mapping 0 implementation
- last mod: $Id: mapping0.c 19441 2015-01-21 01:17:41Z xiphmont $
********************************************************************/
@@ -93,7 +92,6 @@ static vorbis_info_mapping *mapping0_unpack(vorbis_info *vi,oggpack_buffer *opb)
int i,b;
vorbis_info_mapping0 *info=_ogg_calloc(1,sizeof(*info));
codec_setup_info *ci=vi->codec_setup;
- memset(info,0,sizeof(*info));
if(vi->channels<=0)goto err_out;
b=oggpack_read(opb,1);
diff --git a/thirdparty/libvorbis/masking.h b/thirdparty/libvorbis/masking.h
index 3576ab7885..955e18c719 100644
--- a/thirdparty/libvorbis/masking.h
+++ b/thirdparty/libvorbis/masking.h
@@ -11,7 +11,6 @@
********************************************************************
function: masking curve data for psychoacoustics
- last mod: $Id: masking.h 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/mdct.c b/thirdparty/libvorbis/mdct.c
index 0816331805..f3f1ed805b 100644
--- a/thirdparty/libvorbis/mdct.c
+++ b/thirdparty/libvorbis/mdct.c
@@ -12,7 +12,6 @@
function: normalized modified discrete cosine transform
power of two length transform only [64 <= n ]
- last mod: $Id: mdct.c 16227 2009-07-08 06:58:46Z xiphmont $
Original algorithm adapted long ago from _The use of multirate filter
banks for coding of high quality digital audio_, by T. Sporer,
diff --git a/thirdparty/libvorbis/mdct.h b/thirdparty/libvorbis/mdct.h
index 3ed94333c5..3b8c9ba4a2 100644
--- a/thirdparty/libvorbis/mdct.h
+++ b/thirdparty/libvorbis/mdct.h
@@ -11,7 +11,6 @@
********************************************************************
function: modified discrete cosine transform prototypes
- last mod: $Id: mdct.h 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/misc.h b/thirdparty/libvorbis/misc.h
index 73b4519898..13788445a3 100644
--- a/thirdparty/libvorbis/misc.h
+++ b/thirdparty/libvorbis/misc.h
@@ -11,7 +11,6 @@
********************************************************************
function: miscellaneous prototypes
- last mod: $Id: misc.h 19457 2015-03-03 00:15:29Z giles $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/floor_all.h b/thirdparty/libvorbis/modes/floor_all.h
index 4292be326e..20928aac87 100644
--- a/thirdparty/libvorbis/modes/floor_all.h
+++ b/thirdparty/libvorbis/modes/floor_all.h
@@ -11,7 +11,6 @@
********************************************************************
function: key floor settings
- last mod: $Id: floor_all.h 17050 2010-03-26 01:34:42Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/psych_11.h b/thirdparty/libvorbis/modes/psych_11.h
index 844a8ed3cd..cc5eea2402 100644
--- a/thirdparty/libvorbis/modes/psych_11.h
+++ b/thirdparty/libvorbis/modes/psych_11.h
@@ -11,7 +11,6 @@
********************************************************************
function: 11kHz settings
- last mod: $Id: psych_11.h 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/psych_16.h b/thirdparty/libvorbis/modes/psych_16.h
index 1c10b3954e..477cb4d90f 100644
--- a/thirdparty/libvorbis/modes/psych_16.h
+++ b/thirdparty/libvorbis/modes/psych_16.h
@@ -11,7 +11,6 @@
********************************************************************
function: 16kHz settings
- last mod: $Id: psych_16.h 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/psych_44.h b/thirdparty/libvorbis/modes/psych_44.h
index f05c032653..6c9eaa4e5f 100644
--- a/thirdparty/libvorbis/modes/psych_44.h
+++ b/thirdparty/libvorbis/modes/psych_44.h
@@ -11,7 +11,6 @@
********************************************************************
function: key psychoacoustic settings for 44.1/48kHz
- last mod: $Id: psych_44.h 16962 2010-03-11 07:30:34Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/psych_8.h b/thirdparty/libvorbis/modes/psych_8.h
index 0e2dd57371..277db8436c 100644
--- a/thirdparty/libvorbis/modes/psych_8.h
+++ b/thirdparty/libvorbis/modes/psych_8.h
@@ -11,7 +11,6 @@
********************************************************************
function: 8kHz psychoacoustic settings
- last mod: $Id: psych_8.h 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/residue_16.h b/thirdparty/libvorbis/modes/residue_16.h
index dcaca5451e..3e05471cec 100644
--- a/thirdparty/libvorbis/modes/residue_16.h
+++ b/thirdparty/libvorbis/modes/residue_16.h
@@ -11,7 +11,6 @@
********************************************************************
function: toplevel residue templates 16/22kHz
- last mod: $Id: residue_16.h 16962 2010-03-11 07:30:34Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/residue_44.h b/thirdparty/libvorbis/modes/residue_44.h
index 236c18341b..e89bc0e486 100644
--- a/thirdparty/libvorbis/modes/residue_44.h
+++ b/thirdparty/libvorbis/modes/residue_44.h
@@ -11,7 +11,6 @@
********************************************************************
function: toplevel residue templates for 32/44.1/48kHz
- last mod: $Id: residue_44.h 16962 2010-03-11 07:30:34Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/residue_44p51.h b/thirdparty/libvorbis/modes/residue_44p51.h
index a52cc5245e..7f33e250e2 100644
--- a/thirdparty/libvorbis/modes/residue_44p51.h
+++ b/thirdparty/libvorbis/modes/residue_44p51.h
@@ -11,7 +11,6 @@
********************************************************************
function: toplevel residue templates for 32/44.1/48kHz uncoupled
- last mod: $Id: residue_44p51.h 19013 2013-11-12 04:04:50Z giles $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/residue_44u.h b/thirdparty/libvorbis/modes/residue_44u.h
index 92c4a09ce3..e55ac12548 100644
--- a/thirdparty/libvorbis/modes/residue_44u.h
+++ b/thirdparty/libvorbis/modes/residue_44u.h
@@ -11,7 +11,6 @@
********************************************************************
function: toplevel residue templates for 32/44.1/48kHz uncoupled
- last mod: $Id: residue_44u.h 16962 2010-03-11 07:30:34Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/residue_8.h b/thirdparty/libvorbis/modes/residue_8.h
index 94c6d84c44..ae123a276a 100644
--- a/thirdparty/libvorbis/modes/residue_8.h
+++ b/thirdparty/libvorbis/modes/residue_8.h
@@ -11,7 +11,6 @@
********************************************************************
function: toplevel residue templates 8/11kHz
- last mod: $Id: residue_8.h 16962 2010-03-11 07:30:34Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/setup_11.h b/thirdparty/libvorbis/modes/setup_11.h
index 4c2d619ca2..0cbcaafcb2 100644
--- a/thirdparty/libvorbis/modes/setup_11.h
+++ b/thirdparty/libvorbis/modes/setup_11.h
@@ -11,7 +11,6 @@
********************************************************************
function: 11kHz settings
- last mod: $Id: setup_11.h 16894 2010-02-12 20:32:12Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/setup_16.h b/thirdparty/libvorbis/modes/setup_16.h
index 336007f98e..d59ad70d2e 100644
--- a/thirdparty/libvorbis/modes/setup_16.h
+++ b/thirdparty/libvorbis/modes/setup_16.h
@@ -11,7 +11,6 @@
********************************************************************
function: 16kHz settings
- last mod: $Id: setup_16.h 16894 2010-02-12 20:32:12Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/setup_22.h b/thirdparty/libvorbis/modes/setup_22.h
index 4fd5e57111..bc38af9630 100644
--- a/thirdparty/libvorbis/modes/setup_22.h
+++ b/thirdparty/libvorbis/modes/setup_22.h
@@ -11,7 +11,6 @@
********************************************************************
function: 22kHz settings
- last mod: $Id: setup_22.h 17026 2010-03-25 05:00:27Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/setup_32.h b/thirdparty/libvorbis/modes/setup_32.h
index 2275ac9615..f66a0bcd00 100644
--- a/thirdparty/libvorbis/modes/setup_32.h
+++ b/thirdparty/libvorbis/modes/setup_32.h
@@ -11,7 +11,6 @@
********************************************************************
function: toplevel settings for 32kHz
- last mod: $Id: setup_32.h 16894 2010-02-12 20:32:12Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/setup_44.h b/thirdparty/libvorbis/modes/setup_44.h
index 3b88a89ac5..a189b5fb95 100644
--- a/thirdparty/libvorbis/modes/setup_44.h
+++ b/thirdparty/libvorbis/modes/setup_44.h
@@ -11,7 +11,6 @@
********************************************************************
function: toplevel settings for 44.1/48kHz
- last mod: $Id: setup_44.h 16962 2010-03-11 07:30:34Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/setup_44p51.h b/thirdparty/libvorbis/modes/setup_44p51.h
index 67d9979608..3bde7b340c 100644
--- a/thirdparty/libvorbis/modes/setup_44p51.h
+++ b/thirdparty/libvorbis/modes/setup_44p51.h
@@ -11,7 +11,6 @@
********************************************************************
function: toplevel settings for 44.1/48kHz 5.1 surround modes
- last mod: $Id: setup_44p51.h 19013 2013-11-12 04:04:50Z giles $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/setup_44u.h b/thirdparty/libvorbis/modes/setup_44u.h
index 568b5f8959..7ae3af6b2a 100644
--- a/thirdparty/libvorbis/modes/setup_44u.h
+++ b/thirdparty/libvorbis/modes/setup_44u.h
@@ -11,7 +11,6 @@
********************************************************************
function: toplevel settings for 44.1/48kHz uncoupled modes
- last mod: $Id: setup_44u.h 16962 2010-03-11 07:30:34Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/setup_8.h b/thirdparty/libvorbis/modes/setup_8.h
index 14c48374fa..7502556879 100644
--- a/thirdparty/libvorbis/modes/setup_8.h
+++ b/thirdparty/libvorbis/modes/setup_8.h
@@ -11,7 +11,6 @@
********************************************************************
function: 8kHz settings
- last mod: $Id: setup_8.h 16894 2010-02-12 20:32:12Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/modes/setup_X.h b/thirdparty/libvorbis/modes/setup_X.h
index a69f5d40a2..2229a5ef2f 100644
--- a/thirdparty/libvorbis/modes/setup_X.h
+++ b/thirdparty/libvorbis/modes/setup_X.h
@@ -11,7 +11,6 @@
********************************************************************
function: catch-all toplevel settings for q modes only
- last mod: $Id: setup_X.h 16894 2010-02-12 20:32:12Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/os.h b/thirdparty/libvorbis/os.h
index 8bc3e5fe9c..416a401dd1 100644
--- a/thirdparty/libvorbis/os.h
+++ b/thirdparty/libvorbis/os.h
@@ -13,7 +13,6 @@
********************************************************************
function: #ifdef jail to whip a few platforms into the UNIX ideal.
- last mod: $Id: os.h 19457 2015-03-03 00:15:29Z giles $
********************************************************************/
@@ -31,7 +30,7 @@
# ifdef __GNUC__
# define STIN static __inline__
-# elif _WIN32
+# elif defined(_WIN32)
# define STIN static __inline
# else
# define STIN static
diff --git a/thirdparty/libvorbis/psy.c b/thirdparty/libvorbis/psy.c
index f7a44c6d00..422c6f1e41 100644
--- a/thirdparty/libvorbis/psy.c
+++ b/thirdparty/libvorbis/psy.c
@@ -11,7 +11,6 @@
********************************************************************
function: psychoacoustics not including preecho
- last mod: $Id: psy.c 18077 2011-09-02 02:49:00Z giles $
********************************************************************/
diff --git a/thirdparty/libvorbis/psy.h b/thirdparty/libvorbis/psy.h
index c1ea824401..ab2534db3a 100644
--- a/thirdparty/libvorbis/psy.h
+++ b/thirdparty/libvorbis/psy.h
@@ -11,7 +11,6 @@
********************************************************************
function: random psychoacoustics (not including preecho)
- last mod: $Id: psy.h 16946 2010-03-03 16:12:40Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/psytune.c b/thirdparty/libvorbis/psytune.c
index 64c13171f7..6952136c6b 100644
--- a/thirdparty/libvorbis/psytune.c
+++ b/thirdparty/libvorbis/psytune.c
@@ -12,7 +12,6 @@
function: simple utility that runs audio through the psychoacoustics
without encoding
- last mod: $Id: psytune.c 16037 2009-05-26 21:10:58Z xiphmont $
********************************************************************/
@@ -41,11 +40,11 @@
static vorbis_info_psy_global _psy_set0G={
0, /* decaydBpms */
8, /* lines per eighth octave */
-
+
/* thresh sample period, preecho clamp trigger threshhold, range, minenergy */
256, {26.f,26.f,26.f,30.f}, {-90.f,-90.f,-90.f,-90.f}, -90.f,
- -6.f,
-
+ -6.f,
+
0,
0.,
@@ -68,7 +67,7 @@ static vp_couple _vp_couple0[]={
static vorbis_info_psy _psy_set0={
ATH_Bark_dB_lineaggressive,
-
+
-100.f,
-140.f,
6.f, /* floor master att */
@@ -148,7 +147,7 @@ static vorbis_info_psy _psy_set0={
.900f, 0.f, /*11500*/
.900f, 1.f, /*16000*/
},
-
+
95.f, /* even decade + 5 is important; saves an rint() later in a
tight loop) */
-44.,
@@ -159,7 +158,7 @@ static vorbis_info_psy _psy_set0={
static vorbis_info_floor1 _floor_set0={1,
{0},
-
+
{32},
{0},
{0},
@@ -171,12 +170,12 @@ static vorbis_info_floor1 _floor_set0={1,
88,31,243,
14,54,143,460,
-
- 6,3,10, 22,18,26, 41,36,47,
- 69,61,78, 112,99,126, 185,162,211,
+
+ 6,3,10, 22,18,26, 41,36,47,
+ 69,61,78, 112,99,126, 185,162,211,
329,282,387, 672,553,825
},
-
+
60,30,400,
20,8,1,18.,
20,600,
@@ -184,8 +183,8 @@ static vorbis_info_floor1 _floor_set0={1,
static vorbis_info_mapping0 mapping_info={1,{0,1},{0},{0},{0},0, 1, {0},{1}};
-static codec_setup_info codec_setup0={ {0,0},
- 1,1,1,1,1,0,1,
+static codec_setup_info codec_setup0={ {0,0},
+ 1,1,1,1,1,0,1,
{NULL},
{0},{&mapping_info},
{0},{NULL},
@@ -194,7 +193,7 @@ static codec_setup_info codec_setup0={ {0,0},
{NULL},
{&_psy_set0},
&_psy_set0G};
-
+
static int noisy=0;
void analysis(char *base,int i,float *v,int n,int bark,int dB){
if(noisy){
@@ -212,7 +211,7 @@ void analysis(char *base,int i,float *v,int n,int bark,int dB){
fprintf(of,"%g ",toBARK(22050.f*j/n));
else
fprintf(of,"%g ",(float)j);
-
+
if(dB){
fprintf(of,"%g\n",todB(v+j));
}else{
@@ -269,7 +268,7 @@ int main(int argc,char *argv[]){
framesize=atoi(argv[0]);
argv++;
}
-
+
vi.channels=2;
vi.codec_setup=&codec_setup0;
@@ -292,7 +291,7 @@ int main(int argc,char *argv[]){
/* we cheat on the WAV header; we just bypass 44 bytes and never
verify that it matches 16bit/stereo/44.1kHz. */
-
+
fread(buffer,1,44,stdin);
fwrite(buffer,1,44,stdout);
memset(buffer,0,framesize*2);
@@ -302,10 +301,10 @@ int main(int argc,char *argv[]){
fprintf(stderr,"Processing for frame size %d...\n",framesize);
while(!eos){
- long bytes=fread(buffer2,1,framesize*2,stdin);
+ long bytes=fread(buffer2,1,framesize*2,stdin);
if(bytes<framesize*2)
memset(buffer2+bytes,0,framesize*2-bytes);
-
+
if(bytes!=0){
int nonzero[2];
@@ -316,10 +315,10 @@ int main(int argc,char *argv[]){
pcm[1][i]=((buffer[i*4+3]<<8)|
(0x00ff&(int)buffer[i*4+2]))/32768.f;
}
-
+
{
float secs=framesize/44100.;
-
+
ampmax+=secs*ampmax_att_per_sec;
if(ampmax<-9999)ampmax=-9999;
}
@@ -331,11 +330,11 @@ int main(int argc,char *argv[]){
float *logmdct=mdct+framesize/2;
analysis("pre",frameno+i,pcm[i],framesize,0,0);
-
+
/* fft and mdct transforms */
for(j=0;j<framesize;j++)
fft[j]=pcm[i][j]*=window[j];
-
+
drft_forward(&f_look,fft);
local_ampmax[i]=-9999.f;
@@ -347,7 +346,7 @@ int main(int argc,char *argv[]){
if(temp>local_ampmax[i])local_ampmax[i]=temp;
}
if(local_ampmax[i]>ampmax)ampmax=local_ampmax[i];
-
+
mdct_forward(&m_look,pcm[i],mdct);
for(j=0;j<framesize/2;j++)
logmdct[j]=todB(mdct+j);
@@ -391,7 +390,7 @@ int main(int argc,char *argv[]){
logmdct,
mask,
logmax,
-
+
flr[i]);
}
@@ -406,7 +405,7 @@ int main(int argc,char *argv[]){
for(j=0;j<framesize/2;j++)
if(fabs(pcm[i][j])>1500)
fprintf(stderr,"%ld ",frameno+i);
-
+
analysis("res",frameno+i,pcm[i],framesize/2,1,0);
analysis("codedflr",frameno+i,flr[i],framesize/2,1,1);
}
@@ -416,7 +415,7 @@ int main(int argc,char *argv[]){
&vi,
pcm,
nonzero);
-
+
for(i=0;i<2;i++)
analysis("quant",frameno+i,pcm[i],framesize/2,1,0);
@@ -426,7 +425,7 @@ int main(int argc,char *argv[]){
&mapping_info,
pcm,
nonzero);
-
+
for(i=0;i<2;i++)
analysis("coupled",frameno+i,pcm[i],framesize/2,1,0);
@@ -434,11 +433,11 @@ int main(int argc,char *argv[]){
for(i=mapping_info.coupling_steps-1;i>=0;i--){
float *pcmM=pcm[mapping_info.coupling_mag[i]];
float *pcmA=pcm[mapping_info.coupling_ang[i]];
-
+
for(j=0;j<framesize/2;j++){
float mag=pcmM[j];
float ang=pcmA[j];
-
+
if(mag>0)
if(ang>0){
pcmM[j]=mag;
@@ -457,7 +456,7 @@ int main(int argc,char *argv[]){
}
}
}
-
+
for(i=0;i<2;i++)
analysis("decoupled",frameno+i,pcm[i],framesize/2,1,0);
@@ -479,7 +478,7 @@ int main(int argc,char *argv[]){
}
-
+
/* write data. Use the part of buffer we're about to shift out */
for(i=0;i<2;i++){
char *ptr=buffer+i*2;
@@ -503,7 +502,7 @@ int main(int argc,char *argv[]){
ptr+=4;
}
}
-
+
fprintf(stderr,"*");
fwrite(buffer,1,framesize*2,stdout);
memmove(buffer,buffer2,framesize*2);
diff --git a/thirdparty/libvorbis/registry.c b/thirdparty/libvorbis/registry.c
index 3961ed1403..74f7ef0396 100644
--- a/thirdparty/libvorbis/registry.c
+++ b/thirdparty/libvorbis/registry.c
@@ -11,7 +11,6 @@
********************************************************************
function: registry for time, floor, res backends and channel mappings
- last mod: $Id: registry.c 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/registry.h b/thirdparty/libvorbis/registry.h
index 3ae04776d8..599d959942 100644
--- a/thirdparty/libvorbis/registry.h
+++ b/thirdparty/libvorbis/registry.h
@@ -11,7 +11,6 @@
********************************************************************
function: registry for time, floor, res backends and channel mappings
- last mod: $Id: registry.h 15531 2008-11-24 23:50:06Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/res0.c b/thirdparty/libvorbis/res0.c
index ec11488c2f..6d623d730f 100644
--- a/thirdparty/libvorbis/res0.c
+++ b/thirdparty/libvorbis/res0.c
@@ -11,7 +11,6 @@
********************************************************************
function: residue backend 0, 1 and 2 implementation
- last mod: $Id: res0.c 19441 2015-01-21 01:17:41Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/scales.h b/thirdparty/libvorbis/scales.h
index 613f796e77..18bc4e7518 100644
--- a/thirdparty/libvorbis/scales.h
+++ b/thirdparty/libvorbis/scales.h
@@ -11,7 +11,6 @@
********************************************************************
function: linear scale -> dB, Bark and Mel scales
- last mod: $Id: scales.h 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/sharedbook.c b/thirdparty/libvorbis/sharedbook.c
index 6bfdf7311e..4545d4f459 100644
--- a/thirdparty/libvorbis/sharedbook.c
+++ b/thirdparty/libvorbis/sharedbook.c
@@ -11,11 +11,11 @@
********************************************************************
function: basic shared codebook operations
- last mod: $Id: sharedbook.c 19457 2015-03-03 00:15:29Z giles $
********************************************************************/
#include <stdlib.h>
+#include <limits.h>
#include <math.h>
#include <string.h>
#include <ogg/ogg.h>
@@ -158,25 +158,34 @@ ogg_uint32_t *_make_words(char *l,long n,long sparsecount){
that's portable and totally safe against roundoff, but I haven't
thought of it. Therefore, we opt on the side of caution */
long _book_maptype1_quantvals(const static_codebook *b){
- long vals=floor(pow((float)b->entries,1.f/b->dim));
+ long vals;
+ if(b->entries<1){
+ return(0);
+ }
+ vals=floor(pow((float)b->entries,1.f/b->dim));
/* the above *should* be reliable, but we'll not assume that FP is
ever reliable when bitstream sync is at stake; verify via integer
means that vals really is the greatest value of dim for which
vals^b->bim <= b->entries */
/* treat the above as an initial guess */
+ if(vals<1){
+ vals=1;
+ }
while(1){
long acc=1;
long acc1=1;
int i;
for(i=0;i<b->dim;i++){
+ if(b->entries/vals<acc)break;
acc*=vals;
- acc1*=vals+1;
+ if(LONG_MAX/(vals+1)<acc1)acc1=LONG_MAX;
+ else acc1*=vals+1;
}
- if(acc<=b->entries && acc1>b->entries){
+ if(i>=b->dim && acc<=b->entries && acc1>b->entries){
return(vals);
}else{
- if(acc>b->entries){
+ if(i<b->dim || acc>b->entries){
vals--;
}else{
vals++;
diff --git a/thirdparty/libvorbis/smallft.c b/thirdparty/libvorbis/smallft.c
index ae2bc41b6b..6d528af423 100644
--- a/thirdparty/libvorbis/smallft.c
+++ b/thirdparty/libvorbis/smallft.c
@@ -11,7 +11,6 @@
********************************************************************
function: *unnormalized* fft transform
- last mod: $Id: smallft.c 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/smallft.h b/thirdparty/libvorbis/smallft.h
index 456497326c..9e867c67d2 100644
--- a/thirdparty/libvorbis/smallft.h
+++ b/thirdparty/libvorbis/smallft.h
@@ -11,7 +11,6 @@
********************************************************************
function: fft transform
- last mod: $Id: smallft.h 13293 2007-07-24 00:09:47Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/synthesis.c b/thirdparty/libvorbis/synthesis.c
index 932d271a63..5f6092c3d3 100644
--- a/thirdparty/libvorbis/synthesis.c
+++ b/thirdparty/libvorbis/synthesis.c
@@ -11,7 +11,6 @@
********************************************************************
function: single-block PCM synthesis
- last mod: $Id: synthesis.c 19441 2015-01-21 01:17:41Z xiphmont $
********************************************************************/
@@ -117,7 +116,7 @@ int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op){
if(!ci->mode_param[mode]){
return(OV_EBADPACKET);
}
-
+
vb->W=ci->mode_param[mode]->blockflag;
if(vb->W){
vb->lW=oggpack_read(opb,1);
diff --git a/thirdparty/libvorbis/tone.c b/thirdparty/libvorbis/tone.c
index 73afc67d4c..5b8b020604 100644
--- a/thirdparty/libvorbis/tone.c
+++ b/thirdparty/libvorbis/tone.c
@@ -12,7 +12,7 @@ int main (int argc,char *argv[]){
int i,j;
double *f;
double *amp;
-
+
if(argc<2)usage();
f=alloca(sizeof(*f)*(argc-1));
@@ -21,7 +21,7 @@ int main (int argc,char *argv[]){
i=0;
while(argv[i+1]){
char *pos=strchr(argv[i+1],',');
-
+
f[i]=atof(argv[i+1]);
if(pos)
amp[i]=atof(pos+1)*32767.f;
diff --git a/thirdparty/libvorbis/vorbis/codec.h b/thirdparty/libvorbis/vorbis/codec.h
index 999aa33510..42aa29138e 100644
--- a/thirdparty/libvorbis/vorbis/codec.h
+++ b/thirdparty/libvorbis/vorbis/codec.h
@@ -11,7 +11,6 @@
********************************************************************
function: libvorbis codec headers
- last mod: $Id: codec.h 17021 2010-03-24 09:29:41Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/vorbis/vorbisenc.h b/thirdparty/libvorbis/vorbis/vorbisenc.h
index 02332b50ca..55f3b4a667 100644
--- a/thirdparty/libvorbis/vorbis/vorbisenc.h
+++ b/thirdparty/libvorbis/vorbis/vorbisenc.h
@@ -11,7 +11,6 @@
********************************************************************
function: vorbis encode-engine setup
- last mod: $Id: vorbisenc.h 17021 2010-03-24 09:29:41Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/vorbis/vorbisfile.h b/thirdparty/libvorbis/vorbis/vorbisfile.h
index 9271331e72..56626119bb 100644
--- a/thirdparty/libvorbis/vorbis/vorbisfile.h
+++ b/thirdparty/libvorbis/vorbis/vorbisfile.h
@@ -11,7 +11,6 @@
********************************************************************
function: stdio-based convenience library for opening/seeking/decoding
- last mod: $Id: vorbisfile.h 17182 2010-04-29 03:48:32Z xiphmont $
********************************************************************/
diff --git a/thirdparty/libvorbis/vorbisenc.c b/thirdparty/libvorbis/vorbisenc.c
index b5d621e900..4a4607cb41 100644
--- a/thirdparty/libvorbis/vorbisenc.c
+++ b/thirdparty/libvorbis/vorbisenc.c
@@ -11,7 +11,6 @@
********************************************************************
function: simple programmatic interface for encoder mode setup
- last mod: $Id: vorbisenc.c 19457 2015-03-03 00:15:29Z giles $
********************************************************************/
diff --git a/thirdparty/libvorbis/vorbisfile.c b/thirdparty/libvorbis/vorbisfile.c
index fc0c86ff11..b570c3c5f6 100644
--- a/thirdparty/libvorbis/vorbisfile.c
+++ b/thirdparty/libvorbis/vorbisfile.c
@@ -11,7 +11,6 @@
********************************************************************
function: stdio-based convenience library for opening/seeking/decoding
- last mod: $Id: vorbisfile.c 19457 2015-03-03 00:15:29Z giles $
********************************************************************/
diff --git a/thirdparty/libvorbis/window.c b/thirdparty/libvorbis/window.c
index 0305b79297..b3b7ce0163 100644
--- a/thirdparty/libvorbis/window.c
+++ b/thirdparty/libvorbis/window.c
@@ -11,7 +11,6 @@
********************************************************************
function: window functions
- last mod: $Id: window.c 19028 2013-12-02 23:23:39Z tterribe $
********************************************************************/
diff --git a/thirdparty/libvorbis/window.h b/thirdparty/libvorbis/window.h
index 51f97599f5..6ac260749e 100644
--- a/thirdparty/libvorbis/window.h
+++ b/thirdparty/libvorbis/window.h
@@ -11,7 +11,6 @@
********************************************************************
function: window functions
- last mod: $Id: window.h 19028 2013-12-02 23:23:39Z tterribe $
********************************************************************/
diff --git a/thirdparty/libwebp/AUTHORS b/thirdparty/libwebp/AUTHORS
index 70423cb4dd..83c7b9c5eb 100644
--- a/thirdparty/libwebp/AUTHORS
+++ b/thirdparty/libwebp/AUTHORS
@@ -2,25 +2,38 @@ Contributors:
- Charles Munger (clm at google dot com)
- Christian Duvivier (cduvivier at google dot com)
- Djordje Pesut (djordje dot pesut at imgtec dot com)
+- Hui Su (huisu at google dot com)
- James Zern (jzern at google dot com)
- Jan Engelhardt (jengelh at medozas dot de)
+- Jehan (jehan at girinstud dot io)
- Johann (johann dot koenig at duck dot com)
- Jovan Zelincevic (jovan dot zelincevic at imgtec dot com)
- Jyrki Alakuijala (jyrki at google dot com)
-- levytamar82 (tamar dot levy at intel dot com)
+- Lode Vandevenne (lode at google dot com)
- Lou Quillio (louquillio at google dot com)
- Mans Rullgard (mans at mansr dot com)
+- Marcin Kowalczyk (qrczak at google dot com)
- Martin Olsson (mnemo at minimum dot se)
- Mikołaj Zalewski (mikolajz at google dot com)
+- Mislav Bradac (mislavm at google dot com)
+- Nico Weber (thakis at chromium dot org)
- Noel Chromium (noel at chromium dot org)
+- Owen Rodley (orodley at google dot com)
+- Parag Salasakar (img dot mips1 at gmail dot com)
- Pascal Massimino (pascal dot massimino at gmail dot com)
- Paweł Hajdan, Jr (phajdan dot jr at chromium dot org)
- Pierre Joye (pierre dot php at gmail dot com)
- Sam Clegg (sbc at chromium dot org)
+- Scott Hancher (seh at google dot com)
- Scott LaVarnway (slavarnway at google dot com)
- Scott Talbot (s at chikachow dot org)
- Slobodan Prijic (slobodan dot prijic at imgtec dot com)
- Somnath Banerjee (somnath dot banerjee at gmail dot com)
+- Sriraman Tallam (tmsriram at google dot com)
+- Tamar Levy (tamar dot levy at intel dot com)
- Timothy Gu (timothygu99 at gmail dot com)
- Urvang Joshi (urvang at google dot com)
- Vikas Arora (vikasa at google dot com)
+- Vincent Rabaud (vrabaud at google dot com)
+- Vlad Tsyrklevich (vtsyrklevich at chromium dot org)
+- Yang Zhang (yang dot zhang at arm dot com)
diff --git a/thirdparty/libwebp/src/dec/frame_dec.c b/thirdparty/libwebp/src/dec/frame_dec.c
index 517d0f5850..a9d5430d00 100644
--- a/thirdparty/libwebp/src/dec/frame_dec.c
+++ b/thirdparty/libwebp/src/dec/frame_dec.c
@@ -400,7 +400,9 @@ static void DitherRow(VP8Decoder* const dec) {
#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
// Finalize and transmit a complete row. Return false in case of user-abort.
-static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
+static int FinishRow(void* arg1, void* arg2) {
+ VP8Decoder* const dec = (VP8Decoder*)arg1;
+ VP8Io* const io = (VP8Io*)arg2;
int ok = 1;
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
const int cache_id = ctx->id_;
@@ -448,10 +450,9 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
if (y_end > io->crop_bottom) {
y_end = io->crop_bottom; // make sure we don't overflow on last row.
}
+ // If dec->alpha_data_ is not NULL, we have some alpha plane present.
io->a = NULL;
if (dec->alpha_data_ != NULL && y_start < y_end) {
- // TODO(skal): testing presence of alpha with dec->alpha_data_ is not a
- // good idea.
io->a = VP8DecompressAlphaRows(dec, io, y_start, y_end - y_start);
if (io->a == NULL) {
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
@@ -558,7 +559,6 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
if (io->bypass_filtering) {
dec->filter_type_ = 0;
}
- // TODO(skal): filter type / strength / sharpness forcing
// Define the area where we can skip in-loop filtering, in case of cropping.
//
@@ -569,8 +569,6 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
// Means: there's a dependency chain that goes all the way up to the
// top-left corner of the picture (MB #0). We must filter all the previous
// macroblocks.
- // TODO(skal): add an 'approximate_decoding' option, that won't produce
- // a 1:1 bit-exactness for complex filtering?
{
const int extra_pixels = kFilterExtraRows[dec->filter_type_];
if (dec->filter_type_ == 2) {
@@ -651,7 +649,7 @@ static int InitThreadContext(VP8Decoder* const dec) {
}
worker->data1 = dec;
worker->data2 = (void*)&dec->thread_ctx_.io_;
- worker->hook = (WebPWorkerHook)FinishRow;
+ worker->hook = FinishRow;
dec->num_caches_ =
(dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
} else {
diff --git a/thirdparty/libwebp/src/dec/vp8_dec.c b/thirdparty/libwebp/src/dec/vp8_dec.c
index 6212efd179..c904b529f6 100644
--- a/thirdparty/libwebp/src/dec/vp8_dec.c
+++ b/thirdparty/libwebp/src/dec/vp8_dec.c
@@ -491,7 +491,7 @@ static int GetCoeffsAlt(VP8BitReader* const br,
return 16;
}
-WEBP_TSAN_IGNORE_FUNCTION static void InitGetCoeffs(void) {
+static WEBP_TSAN_IGNORE_FUNCTION void InitGetCoeffs(void) {
if (GetCoeffs == NULL) {
if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) {
GetCoeffs = GetCoeffsAlt;
diff --git a/thirdparty/libwebp/src/dec/vp8i_dec.h b/thirdparty/libwebp/src/dec/vp8i_dec.h
index 28244d9d7a..c929933e1c 100644
--- a/thirdparty/libwebp/src/dec/vp8i_dec.h
+++ b/thirdparty/libwebp/src/dec/vp8i_dec.h
@@ -30,9 +30,9 @@ extern "C" {
// Various defines and enums
// version numbers
-#define DEC_MAJ_VERSION 0
-#define DEC_MIN_VERSION 6
-#define DEC_REV_VERSION 1
+#define DEC_MAJ_VERSION 1
+#define DEC_MIN_VERSION 0
+#define DEC_REV_VERSION 0
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
// Constraints are: We need to store one 16x16 block of luma samples (y),
diff --git a/thirdparty/libwebp/src/dec/vp8l_dec.c b/thirdparty/libwebp/src/dec/vp8l_dec.c
index 42ea3b5e4c..0570f53a77 100644
--- a/thirdparty/libwebp/src/dec/vp8l_dec.c
+++ b/thirdparty/libwebp/src/dec/vp8l_dec.c
@@ -1643,17 +1643,17 @@ int VP8LDecodeImage(VP8LDecoder* const dec) {
#if !defined(WEBP_REDUCE_SIZE)
if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err;
-
- if (io->use_scaling || WebPIsPremultipliedMode(dec->output_->colorspace)) {
- // need the alpha-multiply functions for premultiplied output or rescaling
- WebPInitAlphaProcessing();
- }
#else
if (io->use_scaling) {
dec->status_ = VP8_STATUS_INVALID_PARAM;
goto Err;
}
#endif
+ if (io->use_scaling || WebPIsPremultipliedMode(dec->output_->colorspace)) {
+ // need the alpha-multiply functions for premultiplied output or rescaling
+ WebPInitAlphaProcessing();
+ }
+
if (!WebPIsRGBMode(dec->output_->colorspace)) {
WebPInitConvertARGBToYUV();
if (dec->output_->u.YUVA.a != NULL) WebPInitAlphaProcessing();
diff --git a/thirdparty/libwebp/src/demux/demux.c b/thirdparty/libwebp/src/demux/demux.c
index 79c24a5a7f..684215e3de 100644
--- a/thirdparty/libwebp/src/demux/demux.c
+++ b/thirdparty/libwebp/src/demux/demux.c
@@ -23,9 +23,9 @@
#include "src/webp/demux.h"
#include "src/webp/format_constants.h"
-#define DMUX_MAJ_VERSION 0
-#define DMUX_MIN_VERSION 3
-#define DMUX_REV_VERSION 3
+#define DMUX_MAJ_VERSION 1
+#define DMUX_MIN_VERSION 0
+#define DMUX_REV_VERSION 0
typedef struct {
size_t start_; // start location of the data
diff --git a/thirdparty/libwebp/src/dsp/alpha_processing.c b/thirdparty/libwebp/src/dsp/alpha_processing.c
index 590e3bc312..819d1391f2 100644
--- a/thirdparty/libwebp/src/dsp/alpha_processing.c
+++ b/thirdparty/libwebp/src/dsp/alpha_processing.c
@@ -366,6 +366,16 @@ static WEBP_INLINE uint32_t MakeARGB32(int a, int r, int g, int b) {
return (((uint32_t)a << 24) | (r << 16) | (g << 8) | b);
}
+#ifdef WORDS_BIGENDIAN
+static void PackARGB_C(const uint8_t* a, const uint8_t* r, const uint8_t* g,
+ const uint8_t* b, int len, uint32_t* out) {
+ int i;
+ for (i = 0; i < len; ++i) {
+ out[i] = MakeARGB32(a[4 * i], r[4 * i], g[4 * i], b[4 * i]);
+ }
+}
+#endif
+
static void PackRGB_C(const uint8_t* r, const uint8_t* g, const uint8_t* b,
int len, int step, uint32_t* out) {
int i, offset = 0;
@@ -381,6 +391,10 @@ int (*WebPDispatchAlpha)(const uint8_t*, int, int, int, uint8_t*, int);
void (*WebPDispatchAlphaToGreen)(const uint8_t*, int, int, int, uint32_t*, int);
int (*WebPExtractAlpha)(const uint8_t*, int, int, int, uint8_t*, int);
void (*WebPExtractGreen)(const uint32_t* argb, uint8_t* alpha, int size);
+#ifdef WORDS_BIGENDIAN
+void (*WebPPackARGB)(const uint8_t* a, const uint8_t* r, const uint8_t* g,
+ const uint8_t* b, int, uint32_t*);
+#endif
void (*WebPPackRGB)(const uint8_t* r, const uint8_t* g, const uint8_t* b,
int len, int step, uint32_t* out);
@@ -395,16 +409,14 @@ extern void WebPInitAlphaProcessingSSE2(void);
extern void WebPInitAlphaProcessingSSE41(void);
extern void WebPInitAlphaProcessingNEON(void);
-static volatile VP8CPUInfo alpha_processing_last_cpuinfo_used =
- (VP8CPUInfo)&alpha_processing_last_cpuinfo_used;
-
-WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessing(void) {
- if (alpha_processing_last_cpuinfo_used == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(WebPInitAlphaProcessing) {
WebPMultARGBRow = WebPMultARGBRow_C;
WebPMultRow = WebPMultRow_C;
WebPApplyAlphaMultiply4444 = ApplyAlphaMultiply_16b_C;
+#ifdef WORDS_BIGENDIAN
+ WebPPackARGB = PackARGB_C;
+#endif
WebPPackRGB = PackRGB_C;
#if !WEBP_NEON_OMIT_C_CODE
WebPApplyAlphaMultiply = ApplyAlphaMultiply_C;
@@ -451,9 +463,10 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessing(void) {
assert(WebPDispatchAlphaToGreen != NULL);
assert(WebPExtractAlpha != NULL);
assert(WebPExtractGreen != NULL);
+#ifdef WORDS_BIGENDIAN
+ assert(WebPPackARGB != NULL);
+#endif
assert(WebPPackRGB != NULL);
assert(WebPHasAlpha8b != NULL);
assert(WebPHasAlpha32b != NULL);
-
- alpha_processing_last_cpuinfo_used = VP8GetCPUInfo;
}
diff --git a/thirdparty/libwebp/src/dsp/alpha_processing_mips_dsp_r2.c b/thirdparty/libwebp/src/dsp/alpha_processing_mips_dsp_r2.c
index e0dc91bab9..0090e87cd1 100644
--- a/thirdparty/libwebp/src/dsp/alpha_processing_mips_dsp_r2.c
+++ b/thirdparty/libwebp/src/dsp/alpha_processing_mips_dsp_r2.c
@@ -125,6 +125,49 @@ static void MultARGBRow_MIPSdspR2(uint32_t* const ptr, int width,
}
}
+#ifdef WORDS_BIGENDIAN
+static void PackARGB_MIPSdspR2(const uint8_t* a, const uint8_t* r,
+ const uint8_t* g, const uint8_t* b, int len,
+ uint32_t* out) {
+ int temp0, temp1, temp2, temp3, offset;
+ const int rest = len & 1;
+ const uint32_t* const loop_end = out + len - rest;
+ const int step = 4;
+ __asm__ volatile (
+ "xor %[offset], %[offset], %[offset] \n\t"
+ "beq %[loop_end], %[out], 0f \n\t"
+ "2: \n\t"
+ "lbux %[temp0], %[offset](%[a]) \n\t"
+ "lbux %[temp1], %[offset](%[r]) \n\t"
+ "lbux %[temp2], %[offset](%[g]) \n\t"
+ "lbux %[temp3], %[offset](%[b]) \n\t"
+ "ins %[temp1], %[temp0], 16, 16 \n\t"
+ "ins %[temp3], %[temp2], 16, 16 \n\t"
+ "addiu %[out], %[out], 4 \n\t"
+ "precr.qb.ph %[temp0], %[temp1], %[temp3] \n\t"
+ "sw %[temp0], -4(%[out]) \n\t"
+ "addu %[offset], %[offset], %[step] \n\t"
+ "bne %[loop_end], %[out], 2b \n\t"
+ "0: \n\t"
+ "beq %[rest], $zero, 1f \n\t"
+ "lbux %[temp0], %[offset](%[a]) \n\t"
+ "lbux %[temp1], %[offset](%[r]) \n\t"
+ "lbux %[temp2], %[offset](%[g]) \n\t"
+ "lbux %[temp3], %[offset](%[b]) \n\t"
+ "ins %[temp1], %[temp0], 16, 16 \n\t"
+ "ins %[temp3], %[temp2], 16, 16 \n\t"
+ "precr.qb.ph %[temp0], %[temp1], %[temp3] \n\t"
+ "sw %[temp0], 0(%[out]) \n\t"
+ "1: \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [offset]"=&r"(offset), [out]"+&r"(out)
+ : [a]"r"(a), [r]"r"(r), [g]"r"(g), [b]"r"(b), [step]"r"(step),
+ [loop_end]"r"(loop_end), [rest]"r"(rest)
+ : "memory"
+ );
+}
+#endif // WORDS_BIGENDIAN
+
static void PackRGB_MIPSdspR2(const uint8_t* r, const uint8_t* g,
const uint8_t* b, int len, int step,
uint32_t* out) {
@@ -172,6 +215,9 @@ extern void WebPInitAlphaProcessingMIPSdspR2(void);
WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingMIPSdspR2(void) {
WebPDispatchAlpha = DispatchAlpha_MIPSdspR2;
WebPMultARGBRow = MultARGBRow_MIPSdspR2;
+#ifdef WORDS_BIGENDIAN
+ WebPPackARGB = PackARGB_MIPSdspR2;
+#endif
WebPPackRGB = PackRGB_MIPSdspR2;
}
diff --git a/thirdparty/libwebp/src/dsp/common_sse2.h b/thirdparty/libwebp/src/dsp/common_sse2.h
index 995d7cf4ea..e9f1ebff44 100644
--- a/thirdparty/libwebp/src/dsp/common_sse2.h
+++ b/thirdparty/libwebp/src/dsp/common_sse2.h
@@ -128,9 +128,9 @@ static WEBP_INLINE void VP8Transpose_2_4x4_16b(
// Pack the planar buffers
// rrrr... rrrr... gggg... gggg... bbbb... bbbb....
// triplet by triplet in the output buffer rgb as rgbrgbrgbrgb ...
-static WEBP_INLINE void VP8PlanarTo24b(__m128i* const in0, __m128i* const in1,
- __m128i* const in2, __m128i* const in3,
- __m128i* const in4, __m128i* const in5) {
+static WEBP_INLINE void VP8PlanarTo24b_SSE2(
+ __m128i* const in0, __m128i* const in1, __m128i* const in2,
+ __m128i* const in3, __m128i* const in4, __m128i* const in5) {
// The input is 6 registers of sixteen 8b but for the sake of explanation,
// let's take 6 registers of four 8b values.
// To pack, we will keep taking one every two 8b integer and move it
@@ -159,10 +159,10 @@ static WEBP_INLINE void VP8PlanarTo24b(__m128i* const in0, __m128i* const in1,
// Convert four packed four-channel buffers like argbargbargbargb... into the
// split channels aaaaa ... rrrr ... gggg .... bbbbb ......
-static WEBP_INLINE void VP8L32bToPlanar(__m128i* const in0,
- __m128i* const in1,
- __m128i* const in2,
- __m128i* const in3) {
+static WEBP_INLINE void VP8L32bToPlanar_SSE2(__m128i* const in0,
+ __m128i* const in1,
+ __m128i* const in2,
+ __m128i* const in3) {
// Column-wise transpose.
const __m128i A0 = _mm_unpacklo_epi8(*in0, *in1);
const __m128i A1 = _mm_unpackhi_epi8(*in0, *in1);
diff --git a/thirdparty/libwebp/src/dsp/common_sse41.h b/thirdparty/libwebp/src/dsp/common_sse41.h
new file mode 100644
index 0000000000..2f173c024a
--- /dev/null
+++ b/thirdparty/libwebp/src/dsp/common_sse41.h
@@ -0,0 +1,132 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE4 code common to several files.
+//
+// Author: Vincent Rabaud (vrabaud@google.com)
+
+#ifndef WEBP_DSP_COMMON_SSE41_H_
+#define WEBP_DSP_COMMON_SSE41_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(WEBP_USE_SSE41)
+#include <smmintrin.h>
+
+//------------------------------------------------------------------------------
+// Channel mixing.
+// Shuffles the input buffer as A0 0 0 A1 0 0 A2 ...
+#define WEBP_SSE41_SHUFF(OUT, IN0, IN1) \
+ OUT##0 = _mm_shuffle_epi8(*IN0, shuff0); \
+ OUT##1 = _mm_shuffle_epi8(*IN0, shuff1); \
+ OUT##2 = _mm_shuffle_epi8(*IN0, shuff2); \
+ OUT##3 = _mm_shuffle_epi8(*IN1, shuff0); \
+ OUT##4 = _mm_shuffle_epi8(*IN1, shuff1); \
+ OUT##5 = _mm_shuffle_epi8(*IN1, shuff2);
+
+// Pack the planar buffers
+// rrrr... rrrr... gggg... gggg... bbbb... bbbb....
+// triplet by triplet in the output buffer rgb as rgbrgbrgbrgb ...
+static WEBP_INLINE void VP8PlanarTo24b_SSE41(
+ __m128i* const in0, __m128i* const in1, __m128i* const in2,
+ __m128i* const in3, __m128i* const in4, __m128i* const in5) {
+ __m128i R0, R1, R2, R3, R4, R5;
+ __m128i G0, G1, G2, G3, G4, G5;
+ __m128i B0, B1, B2, B3, B4, B5;
+
+ // Process R.
+ {
+ const __m128i shuff0 = _mm_set_epi8(
+ 5, -1, -1, 4, -1, -1, 3, -1, -1, 2, -1, -1, 1, -1, -1, 0);
+ const __m128i shuff1 = _mm_set_epi8(
+ -1, 10, -1, -1, 9, -1, -1, 8, -1, -1, 7, -1, -1, 6, -1, -1);
+ const __m128i shuff2 = _mm_set_epi8(
+ -1, -1, 15, -1, -1, 14, -1, -1, 13, -1, -1, 12, -1, -1, 11, -1);
+ WEBP_SSE41_SHUFF(R, in0, in1)
+ }
+
+ // Process G.
+ {
+ // Same as before, just shifted to the left by one and including the right
+ // padding.
+ const __m128i shuff0 = _mm_set_epi8(
+ -1, -1, 4, -1, -1, 3, -1, -1, 2, -1, -1, 1, -1, -1, 0, -1);
+ const __m128i shuff1 = _mm_set_epi8(
+ 10, -1, -1, 9, -1, -1, 8, -1, -1, 7, -1, -1, 6, -1, -1, 5);
+ const __m128i shuff2 = _mm_set_epi8(
+ -1, 15, -1, -1, 14, -1, -1, 13, -1, -1, 12, -1, -1, 11, -1, -1);
+ WEBP_SSE41_SHUFF(G, in2, in3)
+ }
+
+ // Process B.
+ {
+ const __m128i shuff0 = _mm_set_epi8(
+ -1, 4, -1, -1, 3, -1, -1, 2, -1, -1, 1, -1, -1, 0, -1, -1);
+ const __m128i shuff1 = _mm_set_epi8(
+ -1, -1, 9, -1, -1, 8, -1, -1, 7, -1, -1, 6, -1, -1, 5, -1);
+ const __m128i shuff2 = _mm_set_epi8(
+ 15, -1, -1, 14, -1, -1, 13, -1, -1, 12, -1, -1, 11, -1, -1, 10);
+ WEBP_SSE41_SHUFF(B, in4, in5)
+ }
+
+ // OR the different channels.
+ {
+ const __m128i RG0 = _mm_or_si128(R0, G0);
+ const __m128i RG1 = _mm_or_si128(R1, G1);
+ const __m128i RG2 = _mm_or_si128(R2, G2);
+ const __m128i RG3 = _mm_or_si128(R3, G3);
+ const __m128i RG4 = _mm_or_si128(R4, G4);
+ const __m128i RG5 = _mm_or_si128(R5, G5);
+ *in0 = _mm_or_si128(RG0, B0);
+ *in1 = _mm_or_si128(RG1, B1);
+ *in2 = _mm_or_si128(RG2, B2);
+ *in3 = _mm_or_si128(RG3, B3);
+ *in4 = _mm_or_si128(RG4, B4);
+ *in5 = _mm_or_si128(RG5, B5);
+ }
+}
+
+#undef WEBP_SSE41_SHUFF
+
+// Convert four packed four-channel buffers like argbargbargbargb... into the
+// split channels aaaaa ... rrrr ... gggg .... bbbbb ......
+static WEBP_INLINE void VP8L32bToPlanar_SSE41(__m128i* const in0,
+ __m128i* const in1,
+ __m128i* const in2,
+ __m128i* const in3) {
+ // aaaarrrrggggbbbb
+ const __m128i shuff0 =
+ _mm_set_epi8(15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0);
+ const __m128i A0 = _mm_shuffle_epi8(*in0, shuff0);
+ const __m128i A1 = _mm_shuffle_epi8(*in1, shuff0);
+ const __m128i A2 = _mm_shuffle_epi8(*in2, shuff0);
+ const __m128i A3 = _mm_shuffle_epi8(*in3, shuff0);
+ // A0A1R0R1
+ // G0G1B0B1
+ // A2A3R2R3
+ // G0G1B0B1
+ const __m128i B0 = _mm_unpacklo_epi32(A0, A1);
+ const __m128i B1 = _mm_unpackhi_epi32(A0, A1);
+ const __m128i B2 = _mm_unpacklo_epi32(A2, A3);
+ const __m128i B3 = _mm_unpackhi_epi32(A2, A3);
+ *in3 = _mm_unpacklo_epi64(B0, B2);
+ *in2 = _mm_unpackhi_epi64(B0, B2);
+ *in1 = _mm_unpacklo_epi64(B1, B3);
+ *in0 = _mm_unpackhi_epi64(B1, B3);
+}
+
+#endif // WEBP_USE_SSE41
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_DSP_COMMON_SSE41_H_
diff --git a/thirdparty/libwebp/src/dsp/cost.c b/thirdparty/libwebp/src/dsp/cost.c
index a732389d58..634ccc2085 100644
--- a/thirdparty/libwebp/src/dsp/cost.c
+++ b/thirdparty/libwebp/src/dsp/cost.c
@@ -378,12 +378,7 @@ extern void VP8EncDspCostInitMIPS32(void);
extern void VP8EncDspCostInitMIPSdspR2(void);
extern void VP8EncDspCostInitSSE2(void);
-static volatile VP8CPUInfo cost_last_cpuinfo_used =
- (VP8CPUInfo)&cost_last_cpuinfo_used;
-
-WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspCostInit(void) {
- if (cost_last_cpuinfo_used == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(VP8EncDspCostInit) {
VP8GetResidualCost = GetResidualCost_C;
VP8SetResidualCoeffs = SetResidualCoeffs_C;
@@ -405,8 +400,6 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspCostInit(void) {
}
#endif
}
-
- cost_last_cpuinfo_used = VP8GetCPUInfo;
}
//------------------------------------------------------------------------------
diff --git a/thirdparty/libwebp/src/dsp/dec.c b/thirdparty/libwebp/src/dsp/dec.c
index 7e82407567..1119842dd3 100644
--- a/thirdparty/libwebp/src/dsp/dec.c
+++ b/thirdparty/libwebp/src/dsp/dec.c
@@ -741,12 +741,7 @@ extern void VP8DspInitMIPS32(void);
extern void VP8DspInitMIPSdspR2(void);
extern void VP8DspInitMSA(void);
-static volatile VP8CPUInfo dec_last_cpuinfo_used =
- (VP8CPUInfo)&dec_last_cpuinfo_used;
-
-WEBP_TSAN_IGNORE_FUNCTION void VP8DspInit(void) {
- if (dec_last_cpuinfo_used == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(VP8DspInit) {
VP8InitClipTables();
#if !WEBP_NEON_OMIT_C_CODE
@@ -889,6 +884,4 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8DspInit(void) {
assert(VP8PredChroma8[5] != NULL);
assert(VP8PredChroma8[6] != NULL);
assert(VP8DitherCombine8x8 != NULL);
-
- dec_last_cpuinfo_used = VP8GetCPUInfo;
}
diff --git a/thirdparty/libwebp/src/dsp/dsp.h b/thirdparty/libwebp/src/dsp/dsp.h
index 99eefe092f..4ab77a5130 100644
--- a/thirdparty/libwebp/src/dsp/dsp.h
+++ b/thirdparty/libwebp/src/dsp/dsp.h
@@ -141,6 +141,42 @@ extern "C" {
#endif
#endif
+#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
+#include <pthread.h> // NOLINT
+
+#define WEBP_DSP_INIT(func) do { \
+ static volatile VP8CPUInfo func ## _last_cpuinfo_used = \
+ (VP8CPUInfo)&func ## _last_cpuinfo_used; \
+ static pthread_mutex_t func ## _lock = PTHREAD_MUTEX_INITIALIZER; \
+ if (pthread_mutex_lock(&func ## _lock)) break; \
+ if (func ## _last_cpuinfo_used != VP8GetCPUInfo) func(); \
+ func ## _last_cpuinfo_used = VP8GetCPUInfo; \
+ (void)pthread_mutex_unlock(&func ## _lock); \
+} while (0)
+#else // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))
+#define WEBP_DSP_INIT(func) do { \
+ static volatile VP8CPUInfo func ## _last_cpuinfo_used = \
+ (VP8CPUInfo)&func ## _last_cpuinfo_used; \
+ if (func ## _last_cpuinfo_used == VP8GetCPUInfo) break; \
+ func(); \
+ func ## _last_cpuinfo_used = VP8GetCPUInfo; \
+} while (0)
+#endif // defined(WEBP_USE_THREAD) && !defined(_WIN32)
+
+// Defines an Init + helper function that control multiple initialization of
+// function pointers / tables.
+/* Usage:
+ WEBP_DSP_INIT_FUNC(InitFunc) {
+ ...function body
+ }
+*/
+#define WEBP_DSP_INIT_FUNC(name) \
+ static WEBP_TSAN_IGNORE_FUNCTION void name ## _body(void); \
+ WEBP_TSAN_IGNORE_FUNCTION void name(void) { \
+ WEBP_DSP_INIT(name ## _body); \
+ } \
+ static WEBP_TSAN_IGNORE_FUNCTION void name ## _body(void)
+
#define WEBP_UBSAN_IGNORE_UNDEF
#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
#if defined(__clang__) && defined(__has_attribute)
@@ -166,6 +202,13 @@ extern "C" {
#define WEBP_SWAP_16BIT_CSP 0
#endif
+// some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__)
+#if !defined(WORDS_BIGENDIAN) && \
+ (defined(__BIG_ENDIAN__) || defined(_M_PPC) || \
+ (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
+#define WORDS_BIGENDIAN
+#endif
+
typedef enum {
kSSE2,
kSSE3,
@@ -189,7 +232,7 @@ WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
// avoiding a compiler warning.
#define WEBP_DSP_INIT_STUB(func) \
extern void func(void); \
- WEBP_TSAN_IGNORE_FUNCTION void func(void) {}
+ void func(void) {}
//------------------------------------------------------------------------------
// Encoding
@@ -578,6 +621,13 @@ void WebPMultRow_C(uint8_t* const ptr, const uint8_t* const alpha,
int width, int inverse);
void WebPMultARGBRow_C(uint32_t* const ptr, int width, int inverse);
+#ifdef WORDS_BIGENDIAN
+// ARGB packing function: a/r/g/b input is rgba or bgra order.
+extern void (*WebPPackARGB)(const uint8_t* a, const uint8_t* r,
+ const uint8_t* g, const uint8_t* b, int len,
+ uint32_t* out);
+#endif
+
// RGB packing function. 'step' can be 3 or 4. r/g/b input is rgb or bgr order.
extern void (*WebPPackRGB)(const uint8_t* r, const uint8_t* g, const uint8_t* b,
int len, int step, uint32_t* out);
diff --git a/thirdparty/libwebp/src/dsp/enc.c b/thirdparty/libwebp/src/dsp/enc.c
index 1c807f1df7..fa23b40a30 100644
--- a/thirdparty/libwebp/src/dsp/enc.c
+++ b/thirdparty/libwebp/src/dsp/enc.c
@@ -740,12 +740,7 @@ extern void VP8EncDspInitMIPS32(void);
extern void VP8EncDspInitMIPSdspR2(void);
extern void VP8EncDspInitMSA(void);
-static volatile VP8CPUInfo enc_last_cpuinfo_used =
- (VP8CPUInfo)&enc_last_cpuinfo_used;
-
-WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInit(void) {
- if (enc_last_cpuinfo_used == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(VP8EncDspInit) {
VP8DspInit(); // common inverse transforms
InitTables();
@@ -838,6 +833,4 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInit(void) {
assert(VP8EncQuantizeBlockWHT != NULL);
assert(VP8Copy4x4 != NULL);
assert(VP8Copy16x8 != NULL);
-
- enc_last_cpuinfo_used = VP8GetCPUInfo;
}
diff --git a/thirdparty/libwebp/src/dsp/filters.c b/thirdparty/libwebp/src/dsp/filters.c
index ca5f877da7..069a22eaef 100644
--- a/thirdparty/libwebp/src/dsp/filters.c
+++ b/thirdparty/libwebp/src/dsp/filters.c
@@ -238,12 +238,7 @@ extern void VP8FiltersInitMSA(void);
extern void VP8FiltersInitNEON(void);
extern void VP8FiltersInitSSE2(void);
-static volatile VP8CPUInfo filters_last_cpuinfo_used =
- (VP8CPUInfo)&filters_last_cpuinfo_used;
-
-WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInit(void) {
- if (filters_last_cpuinfo_used == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(VP8FiltersInit) {
WebPUnfilters[WEBP_FILTER_NONE] = NULL;
#if !WEBP_NEON_OMIT_C_CODE
WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_C;
@@ -289,6 +284,4 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInit(void) {
assert(WebPFilters[WEBP_FILTER_HORIZONTAL] != NULL);
assert(WebPFilters[WEBP_FILTER_VERTICAL] != NULL);
assert(WebPFilters[WEBP_FILTER_GRADIENT] != NULL);
-
- filters_last_cpuinfo_used = VP8GetCPUInfo;
}
diff --git a/thirdparty/libwebp/src/dsp/lossless.c b/thirdparty/libwebp/src/dsp/lossless.c
index 83f553d9ad..f9b3c182d3 100644
--- a/thirdparty/libwebp/src/dsp/lossless.c
+++ b/thirdparty/libwebp/src/dsp/lossless.c
@@ -577,9 +577,6 @@ extern void VP8LDspInitNEON(void);
extern void VP8LDspInitMIPSdspR2(void);
extern void VP8LDspInitMSA(void);
-static volatile VP8CPUInfo lossless_last_cpuinfo_used =
- (VP8CPUInfo)&lossless_last_cpuinfo_used;
-
#define COPY_PREDICTOR_ARRAY(IN, OUT) do { \
(OUT)[0] = IN##0_C; \
(OUT)[1] = IN##1_C; \
@@ -599,9 +596,7 @@ static volatile VP8CPUInfo lossless_last_cpuinfo_used =
(OUT)[15] = IN##0_C; \
} while (0);
-WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInit(void) {
- if (lossless_last_cpuinfo_used == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(VP8LDspInit) {
COPY_PREDICTOR_ARRAY(Predictor, VP8LPredictors)
COPY_PREDICTOR_ARRAY(Predictor, VP8LPredictors_C)
COPY_PREDICTOR_ARRAY(PredictorAdd, VP8LPredictorsAdd)
@@ -658,8 +653,6 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInit(void) {
assert(VP8LConvertBGRAToRGB565 != NULL);
assert(VP8LMapColor32b != NULL);
assert(VP8LMapColor8b != NULL);
-
- lossless_last_cpuinfo_used = VP8GetCPUInfo;
}
#undef COPY_PREDICTOR_ARRAY
diff --git a/thirdparty/libwebp/src/dsp/lossless.h b/thirdparty/libwebp/src/dsp/lossless.h
index a99dbda686..b2bbdfc93c 100644
--- a/thirdparty/libwebp/src/dsp/lossless.h
+++ b/thirdparty/libwebp/src/dsp/lossless.h
@@ -25,10 +25,6 @@
extern "C" {
#endif
-#ifdef WEBP_EXPERIMENTAL_FEATURES
-#include "src/enc/delta_palettization_enc.h"
-#endif // WEBP_EXPERIMENTAL_FEATURES
-
//------------------------------------------------------------------------------
// Decoding
diff --git a/thirdparty/libwebp/src/dsp/lossless_enc.c b/thirdparty/libwebp/src/dsp/lossless_enc.c
index 92ca3c0542..d608326fef 100644
--- a/thirdparty/libwebp/src/dsp/lossless_enc.c
+++ b/thirdparty/libwebp/src/dsp/lossless_enc.c
@@ -863,12 +863,7 @@ extern void VP8LEncDspInitMIPS32(void);
extern void VP8LEncDspInitMIPSdspR2(void);
extern void VP8LEncDspInitMSA(void);
-static volatile VP8CPUInfo lossless_enc_last_cpuinfo_used =
- (VP8CPUInfo)&lossless_enc_last_cpuinfo_used;
-
-WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInit(void) {
- if (lossless_enc_last_cpuinfo_used == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(VP8LEncDspInit) {
VP8LDspInit();
#if !WEBP_NEON_OMIT_C_CODE
@@ -1011,8 +1006,6 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInit(void) {
assert(VP8LPredictorsSub_C[13] != NULL);
assert(VP8LPredictorsSub_C[14] != NULL);
assert(VP8LPredictorsSub_C[15] != NULL);
-
- lossless_enc_last_cpuinfo_used = VP8GetCPUInfo;
}
//------------------------------------------------------------------------------
diff --git a/thirdparty/libwebp/src/dsp/lossless_enc_sse2.c b/thirdparty/libwebp/src/dsp/lossless_enc_sse2.c
index 1eaf35ca8e..f84a9909e1 100644
--- a/thirdparty/libwebp/src/dsp/lossless_enc_sse2.c
+++ b/thirdparty/libwebp/src/dsp/lossless_enc_sse2.c
@@ -46,16 +46,14 @@ static void SubtractGreenFromBlueAndRed_SSE2(uint32_t* argb_data,
//------------------------------------------------------------------------------
// Color Transform
+#define MK_CST_16(HI, LO) \
+ _mm_set1_epi32((int)(((uint32_t)(HI) << 16) | ((LO) & 0xffff)))
+
static void TransformColor_SSE2(const VP8LMultipliers* const m,
uint32_t* argb_data, int num_pixels) {
- const __m128i mults_rb = _mm_set_epi16(
- CST_5b(m->green_to_red_), CST_5b(m->green_to_blue_),
- CST_5b(m->green_to_red_), CST_5b(m->green_to_blue_),
- CST_5b(m->green_to_red_), CST_5b(m->green_to_blue_),
- CST_5b(m->green_to_red_), CST_5b(m->green_to_blue_));
- const __m128i mults_b2 = _mm_set_epi16(
- CST_5b(m->red_to_blue_), 0, CST_5b(m->red_to_blue_), 0,
- CST_5b(m->red_to_blue_), 0, CST_5b(m->red_to_blue_), 0);
+ const __m128i mults_rb = MK_CST_16(CST_5b(m->green_to_red_),
+ CST_5b(m->green_to_blue_));
+ const __m128i mults_b2 = MK_CST_16(CST_5b(m->red_to_blue_), 0);
const __m128i mask_ag = _mm_set1_epi32(0xff00ff00); // alpha-green masks
const __m128i mask_rb = _mm_set1_epi32(0x00ff00ff); // red-blue masks
int i;
@@ -85,12 +83,8 @@ static void CollectColorBlueTransforms_SSE2(const uint32_t* argb, int stride,
int tile_width, int tile_height,
int green_to_blue, int red_to_blue,
int histo[]) {
- const __m128i mults_r = _mm_set_epi16(
- CST_5b(red_to_blue), 0, CST_5b(red_to_blue), 0,
- CST_5b(red_to_blue), 0, CST_5b(red_to_blue), 0);
- const __m128i mults_g = _mm_set_epi16(
- 0, CST_5b(green_to_blue), 0, CST_5b(green_to_blue),
- 0, CST_5b(green_to_blue), 0, CST_5b(green_to_blue));
+ const __m128i mults_r = MK_CST_16(CST_5b(red_to_blue), 0);
+ const __m128i mults_g = MK_CST_16(0, CST_5b(green_to_blue));
const __m128i mask_g = _mm_set1_epi32(0x00ff00); // green mask
const __m128i mask_b = _mm_set1_epi32(0x0000ff); // blue mask
int y;
@@ -135,9 +129,7 @@ static void CollectColorBlueTransforms_SSE2(const uint32_t* argb, int stride,
static void CollectColorRedTransforms_SSE2(const uint32_t* argb, int stride,
int tile_width, int tile_height,
int green_to_red, int histo[]) {
- const __m128i mults_g = _mm_set_epi16(
- 0, CST_5b(green_to_red), 0, CST_5b(green_to_red),
- 0, CST_5b(green_to_red), 0, CST_5b(green_to_red));
+ const __m128i mults_g = MK_CST_16(0, CST_5b(green_to_red));
const __m128i mask_g = _mm_set1_epi32(0x00ff00); // green mask
const __m128i mask = _mm_set1_epi32(0xff);
@@ -174,6 +166,7 @@ static void CollectColorRedTransforms_SSE2(const uint32_t* argb, int stride,
}
}
#undef SPAN
+#undef MK_CST_16
//------------------------------------------------------------------------------
diff --git a/thirdparty/libwebp/src/dsp/lossless_enc_sse41.c b/thirdparty/libwebp/src/dsp/lossless_enc_sse41.c
index 3526a342d3..2e12a712eb 100644
--- a/thirdparty/libwebp/src/dsp/lossless_enc_sse41.c
+++ b/thirdparty/libwebp/src/dsp/lossless_enc_sse41.c
@@ -18,6 +18,9 @@
#include <smmintrin.h>
#include "src/dsp/lossless.h"
+// For sign-extended multiplying constants, pre-shifted by 5:
+#define CST_5b(X) (((int16_t)((uint16_t)(X) << 8)) >> 5)
+
//------------------------------------------------------------------------------
// Subtract-Green Transform
@@ -39,12 +42,103 @@ static void SubtractGreenFromBlueAndRed_SSE41(uint32_t* argb_data,
}
//------------------------------------------------------------------------------
+// Color Transform
+
+#define SPAN 8
+static void CollectColorBlueTransforms_SSE41(const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_blue, int red_to_blue,
+ int histo[]) {
+ const __m128i mults_r = _mm_set1_epi16(CST_5b(red_to_blue));
+ const __m128i mults_g = _mm_set1_epi16(CST_5b(green_to_blue));
+ const __m128i mask_g = _mm_set1_epi16(0xff00); // green mask
+ const __m128i mask_gb = _mm_set1_epi32(0xffff); // green/blue mask
+ const __m128i mask_b = _mm_set1_epi16(0x00ff); // blue mask
+ const __m128i shuffler_lo = _mm_setr_epi8(-1, 2, -1, 6, -1, 10, -1, 14, -1,
+ -1, -1, -1, -1, -1, -1, -1);
+ const __m128i shuffler_hi = _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, -1, 6, -1, 10, -1, 14);
+ int y;
+ for (y = 0; y < tile_height; ++y) {
+ const uint32_t* const src = argb + y * stride;
+ int i, x;
+ for (x = 0; x + SPAN <= tile_width; x += SPAN) {
+ uint16_t values[SPAN];
+ const __m128i in0 = _mm_loadu_si128((__m128i*)&src[x + 0]);
+ const __m128i in1 = _mm_loadu_si128((__m128i*)&src[x + SPAN / 2]);
+ const __m128i r0 = _mm_shuffle_epi8(in0, shuffler_lo);
+ const __m128i r1 = _mm_shuffle_epi8(in1, shuffler_hi);
+ const __m128i r = _mm_or_si128(r0, r1); // r 0
+ const __m128i gb0 = _mm_and_si128(in0, mask_gb);
+ const __m128i gb1 = _mm_and_si128(in1, mask_gb);
+ const __m128i gb = _mm_packus_epi32(gb0, gb1); // g b
+ const __m128i g = _mm_and_si128(gb, mask_g); // g 0
+ const __m128i A = _mm_mulhi_epi16(r, mults_r); // x dbr
+ const __m128i B = _mm_mulhi_epi16(g, mults_g); // x dbg
+ const __m128i C = _mm_sub_epi8(gb, B); // x b'
+ const __m128i D = _mm_sub_epi8(C, A); // x b''
+ const __m128i E = _mm_and_si128(D, mask_b); // 0 b''
+ _mm_storeu_si128((__m128i*)values, E);
+ for (i = 0; i < SPAN; ++i) ++histo[values[i]];
+ }
+ }
+ {
+ const int left_over = tile_width & (SPAN - 1);
+ if (left_over > 0) {
+ VP8LCollectColorBlueTransforms_C(argb + tile_width - left_over, stride,
+ left_over, tile_height,
+ green_to_blue, red_to_blue, histo);
+ }
+ }
+}
+
+static void CollectColorRedTransforms_SSE41(const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_red, int histo[]) {
+ const __m128i mults_g = _mm_set1_epi16(CST_5b(green_to_red));
+ const __m128i mask_g = _mm_set1_epi32(0x00ff00); // green mask
+ const __m128i mask = _mm_set1_epi16(0xff);
+
+ int y;
+ for (y = 0; y < tile_height; ++y) {
+ const uint32_t* const src = argb + y * stride;
+ int i, x;
+ for (x = 0; x + SPAN <= tile_width; x += SPAN) {
+ uint16_t values[SPAN];
+ const __m128i in0 = _mm_loadu_si128((__m128i*)&src[x + 0]);
+ const __m128i in1 = _mm_loadu_si128((__m128i*)&src[x + SPAN / 2]);
+ const __m128i g0 = _mm_and_si128(in0, mask_g); // 0 0 | g 0
+ const __m128i g1 = _mm_and_si128(in1, mask_g);
+ const __m128i g = _mm_packus_epi32(g0, g1); // g 0
+ const __m128i A0 = _mm_srli_epi32(in0, 16); // 0 0 | x r
+ const __m128i A1 = _mm_srli_epi32(in1, 16);
+ const __m128i A = _mm_packus_epi32(A0, A1); // x r
+ const __m128i B = _mm_mulhi_epi16(g, mults_g); // x dr
+ const __m128i C = _mm_sub_epi8(A, B); // x r'
+ const __m128i D = _mm_and_si128(C, mask); // 0 r'
+ _mm_storeu_si128((__m128i*)values, D);
+ for (i = 0; i < SPAN; ++i) ++histo[values[i]];
+ }
+ }
+ {
+ const int left_over = tile_width & (SPAN - 1);
+ if (left_over > 0) {
+ VP8LCollectColorRedTransforms_C(argb + tile_width - left_over, stride,
+ left_over, tile_height, green_to_red,
+ histo);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
// Entry point
extern void VP8LEncDspInitSSE41(void);
WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE41(void) {
VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_SSE41;
+ VP8LCollectColorBlueTransforms = CollectColorBlueTransforms_SSE41;
+ VP8LCollectColorRedTransforms = CollectColorRedTransforms_SSE41;
}
#else // !WEBP_USE_SSE41
diff --git a/thirdparty/libwebp/src/dsp/lossless_sse2.c b/thirdparty/libwebp/src/dsp/lossless_sse2.c
index 653b466cd6..17d7576419 100644
--- a/thirdparty/libwebp/src/dsp/lossless_sse2.c
+++ b/thirdparty/libwebp/src/dsp/lossless_sse2.c
@@ -453,14 +453,11 @@ static void TransformColorInverse_SSE2(const VP8LMultipliers* const m,
int num_pixels, uint32_t* dst) {
// sign-extended multiplying constants, pre-shifted by 5.
#define CST(X) (((int16_t)(m->X << 8)) >> 5) // sign-extend
- const __m128i mults_rb = _mm_set_epi16(
- CST(green_to_red_), CST(green_to_blue_),
- CST(green_to_red_), CST(green_to_blue_),
- CST(green_to_red_), CST(green_to_blue_),
- CST(green_to_red_), CST(green_to_blue_));
- const __m128i mults_b2 = _mm_set_epi16(
- CST(red_to_blue_), 0, CST(red_to_blue_), 0,
- CST(red_to_blue_), 0, CST(red_to_blue_), 0);
+#define MK_CST_16(HI, LO) \
+ _mm_set1_epi32((int)(((uint32_t)(HI) << 16) | ((LO) & 0xffff)))
+ const __m128i mults_rb = MK_CST_16(CST(green_to_red_), CST(green_to_blue_));
+ const __m128i mults_b2 = MK_CST_16(CST(red_to_blue_), 0);
+#undef MK_CST_16
#undef CST
const __m128i mask_ag = _mm_set1_epi32(0xff00ff00); // alpha-green masks
int i;
@@ -503,11 +500,11 @@ static void ConvertBGRAToRGB_SSE2(const uint32_t* src, int num_pixels,
__m128i in5 = _mm_loadu_si128(in + 5);
__m128i in6 = _mm_loadu_si128(in + 6);
__m128i in7 = _mm_loadu_si128(in + 7);
- VP8L32bToPlanar(&in0, &in1, &in2, &in3);
- VP8L32bToPlanar(&in4, &in5, &in6, &in7);
+ VP8L32bToPlanar_SSE2(&in0, &in1, &in2, &in3);
+ VP8L32bToPlanar_SSE2(&in4, &in5, &in6, &in7);
// At this points, in1/in5 contains red only, in2/in6 green only ...
// Pack the colors in 24b RGB.
- VP8PlanarTo24b(&in1, &in5, &in2, &in6, &in3, &in7);
+ VP8PlanarTo24b_SSE2(&in1, &in5, &in2, &in6, &in3, &in7);
_mm_storeu_si128(out + 0, in1);
_mm_storeu_si128(out + 1, in5);
_mm_storeu_si128(out + 2, in2);
diff --git a/thirdparty/libwebp/src/dsp/rescaler.c b/thirdparty/libwebp/src/dsp/rescaler.c
index 4b6b7834e5..f307d35056 100644
--- a/thirdparty/libwebp/src/dsp/rescaler.c
+++ b/thirdparty/libwebp/src/dsp/rescaler.c
@@ -204,11 +204,7 @@ extern void WebPRescalerDspInitMIPSdspR2(void);
extern void WebPRescalerDspInitMSA(void);
extern void WebPRescalerDspInitNEON(void);
-static volatile VP8CPUInfo rescaler_last_cpuinfo_used =
- (VP8CPUInfo)&rescaler_last_cpuinfo_used;
-
-WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInit(void) {
- if (rescaler_last_cpuinfo_used == VP8GetCPUInfo) return;
+WEBP_DSP_INIT_FUNC(WebPRescalerDspInit) {
#if !defined(WEBP_REDUCE_SIZE)
#if !WEBP_NEON_OMIT_C_CODE
WebPRescalerExportRowExpand = WebPRescalerExportRowExpand_C;
@@ -253,5 +249,4 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInit(void) {
assert(WebPRescalerImportRowExpand != NULL);
assert(WebPRescalerImportRowShrink != NULL);
#endif // WEBP_REDUCE_SIZE
- rescaler_last_cpuinfo_used = VP8GetCPUInfo;
}
diff --git a/thirdparty/libwebp/src/dsp/rescaler_sse2.c b/thirdparty/libwebp/src/dsp/rescaler_sse2.c
index f93b204fe1..64c50deab5 100644
--- a/thirdparty/libwebp/src/dsp/rescaler_sse2.c
+++ b/thirdparty/libwebp/src/dsp/rescaler_sse2.c
@@ -36,7 +36,7 @@ static void LoadTwoPixels_SSE2(const uint8_t* const src, __m128i* out) {
}
// input: 8 bytes ABCDEFGH -> output: A0B0C0D0E0F0G0H0
-static void LoadHeightPixels_SSE2(const uint8_t* const src, __m128i* out) {
+static void LoadEightPixels_SSE2(const uint8_t* const src, __m128i* out) {
const __m128i zero = _mm_setzero_si128();
const __m128i A = _mm_loadl_epi64((const __m128i*)(src)); // ABCDEFGH
*out = _mm_unpacklo_epi8(A, zero);
@@ -50,13 +50,15 @@ static void RescalerImportRowExpand_SSE2(WebPRescaler* const wrk,
int accum = x_add;
__m128i cur_pixels;
+ // SSE2 implementation only works with 16b signed arithmetic at max.
+ if (wrk->src_width < 8 || accum >= (1 << 15)) {
+ WebPRescalerImportRowExpand_C(wrk, src);
+ return;
+ }
+
assert(!WebPRescalerInputDone(wrk));
assert(wrk->x_expand);
if (wrk->num_channels == 4) {
- if (wrk->src_width < 2) {
- WebPRescalerImportRowExpand_C(wrk, src);
- return;
- }
LoadTwoPixels_SSE2(src, &cur_pixels);
src += 4;
while (1) {
@@ -75,11 +77,7 @@ static void RescalerImportRowExpand_SSE2(WebPRescaler* const wrk,
} else {
int left;
const uint8_t* const src_limit = src + wrk->src_width - 8;
- if (wrk->src_width < 8) {
- WebPRescalerImportRowExpand_C(wrk, src);
- return;
- }
- LoadHeightPixels_SSE2(src, &cur_pixels);
+ LoadEightPixels_SSE2(src, &cur_pixels);
src += 7;
left = 7;
while (1) {
@@ -94,7 +92,7 @@ static void RescalerImportRowExpand_SSE2(WebPRescaler* const wrk,
if (--left) {
cur_pixels = _mm_srli_si128(cur_pixels, 2);
} else if (src <= src_limit) {
- LoadHeightPixels_SSE2(src, &cur_pixels);
+ LoadEightPixels_SSE2(src, &cur_pixels);
src += 7;
left = 7;
} else { // tail
diff --git a/thirdparty/libwebp/src/dsp/ssim.c b/thirdparty/libwebp/src/dsp/ssim.c
index dc1b518a33..989ce8254c 100644
--- a/thirdparty/libwebp/src/dsp/ssim.c
+++ b/thirdparty/libwebp/src/dsp/ssim.c
@@ -139,12 +139,7 @@ VP8AccumulateSSEFunc VP8AccumulateSSE;
extern void VP8SSIMDspInitSSE2(void);
-static volatile VP8CPUInfo ssim_last_cpuinfo_used =
- (VP8CPUInfo)&ssim_last_cpuinfo_used;
-
-WEBP_TSAN_IGNORE_FUNCTION void VP8SSIMDspInit(void) {
- if (ssim_last_cpuinfo_used == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(VP8SSIMDspInit) {
#if !defined(WEBP_REDUCE_SIZE)
VP8SSIMGetClipped = SSIMGetClipped_C;
VP8SSIMGet = SSIMGet_C;
@@ -161,6 +156,4 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8SSIMDspInit(void) {
}
#endif
}
-
- ssim_last_cpuinfo_used = VP8GetCPUInfo;
}
diff --git a/thirdparty/libwebp/src/dsp/upsampling.c b/thirdparty/libwebp/src/dsp/upsampling.c
index e72626a82a..9b60da5bbb 100644
--- a/thirdparty/libwebp/src/dsp/upsampling.c
+++ b/thirdparty/libwebp/src/dsp/upsampling.c
@@ -217,13 +217,9 @@ WebPYUV444Converter WebPYUV444Converters[MODE_LAST];
extern void WebPInitYUV444ConvertersMIPSdspR2(void);
extern void WebPInitYUV444ConvertersSSE2(void);
+extern void WebPInitYUV444ConvertersSSE41(void);
-static volatile VP8CPUInfo upsampling_last_cpuinfo_used1 =
- (VP8CPUInfo)&upsampling_last_cpuinfo_used1;
-
-WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444Converters(void) {
- if (upsampling_last_cpuinfo_used1 == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(WebPInitYUV444Converters) {
WebPYUV444Converters[MODE_RGBA] = WebPYuv444ToRgba_C;
WebPYUV444Converters[MODE_BGRA] = WebPYuv444ToBgra_C;
WebPYUV444Converters[MODE_RGB] = WebPYuv444ToRgb_C;
@@ -242,29 +238,29 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444Converters(void) {
WebPInitYUV444ConvertersSSE2();
}
#endif
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ WebPInitYUV444ConvertersSSE41();
+ }
+#endif
#if defined(WEBP_USE_MIPS_DSP_R2)
if (VP8GetCPUInfo(kMIPSdspR2)) {
WebPInitYUV444ConvertersMIPSdspR2();
}
#endif
}
- upsampling_last_cpuinfo_used1 = VP8GetCPUInfo;
}
//------------------------------------------------------------------------------
// Main calls
extern void WebPInitUpsamplersSSE2(void);
+extern void WebPInitUpsamplersSSE41(void);
extern void WebPInitUpsamplersNEON(void);
extern void WebPInitUpsamplersMIPSdspR2(void);
extern void WebPInitUpsamplersMSA(void);
-static volatile VP8CPUInfo upsampling_last_cpuinfo_used2 =
- (VP8CPUInfo)&upsampling_last_cpuinfo_used2;
-
-WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplers(void) {
- if (upsampling_last_cpuinfo_used2 == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(WebPInitUpsamplers) {
#ifdef FANCY_UPSAMPLING
#if !WEBP_NEON_OMIT_C_CODE
WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair_C;
@@ -287,6 +283,11 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplers(void) {
WebPInitUpsamplersSSE2();
}
#endif
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ WebPInitUpsamplersSSE41();
+ }
+#endif
#if defined(WEBP_USE_MIPS_DSP_R2)
if (VP8GetCPUInfo(kMIPSdspR2)) {
WebPInitUpsamplersMIPSdspR2();
@@ -310,6 +311,7 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplers(void) {
assert(WebPUpsamplers[MODE_BGRA] != NULL);
assert(WebPUpsamplers[MODE_rgbA] != NULL);
assert(WebPUpsamplers[MODE_bgrA] != NULL);
+#if !defined(WEBP_REDUCE_CSP) || !WEBP_NEON_OMIT_C_CODE
assert(WebPUpsamplers[MODE_RGB] != NULL);
assert(WebPUpsamplers[MODE_BGR] != NULL);
assert(WebPUpsamplers[MODE_ARGB] != NULL);
@@ -317,9 +319,9 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplers(void) {
assert(WebPUpsamplers[MODE_RGB_565] != NULL);
assert(WebPUpsamplers[MODE_Argb] != NULL);
assert(WebPUpsamplers[MODE_rgbA_4444] != NULL);
+#endif
#endif // FANCY_UPSAMPLING
- upsampling_last_cpuinfo_used2 = VP8GetCPUInfo;
}
//------------------------------------------------------------------------------
diff --git a/thirdparty/libwebp/src/dsp/upsampling_msa.c b/thirdparty/libwebp/src/dsp/upsampling_msa.c
index 535ffb772c..99eea70e7d 100644
--- a/thirdparty/libwebp/src/dsp/upsampling_msa.c
+++ b/thirdparty/libwebp/src/dsp/upsampling_msa.c
@@ -264,6 +264,7 @@ static void YuvToBgr(int y, int u, int v, uint8_t* const bgr) {
bgr[2] = Clip8(r1 >> 6);
}
+#if !defined(WEBP_REDUCE_CSP)
static void YuvToRgb565(int y, int u, int v, uint8_t* const rgb) {
const int y1 = MultHi(y, 19077);
const int r1 = y1 + MultHi(v, 26149) - 14234;
@@ -306,6 +307,7 @@ static void YuvToArgb(uint8_t y, uint8_t u, uint8_t v, uint8_t* const argb) {
argb[0] = 0xff;
YuvToRgb(y, u, v, argb + 1);
}
+#endif // WEBP_REDUCE_CSP
static void YuvToBgra(uint8_t y, uint8_t u, uint8_t v, uint8_t* const bgra) {
YuvToBgr(y, u, v, bgra);
@@ -317,6 +319,7 @@ static void YuvToRgba(uint8_t y, uint8_t u, uint8_t v, uint8_t* const rgba) {
rgba[3] = 0xff;
}
+#if !defined(WEBP_REDUCE_CSP)
static void YuvToRgbLine(const uint8_t* y, const uint8_t* u,
const uint8_t* v, uint8_t* dst, int length) {
v16u8 R, G, B;
@@ -370,6 +373,7 @@ static void YuvToBgrLine(const uint8_t* y, const uint8_t* u,
memcpy(dst, temp, length * 3 * sizeof(*dst));
}
}
+#endif // WEBP_REDUCE_CSP
static void YuvToRgbaLine(const uint8_t* y, const uint8_t* u,
const uint8_t* v, uint8_t* dst, int length) {
@@ -427,6 +431,7 @@ static void YuvToBgraLine(const uint8_t* y, const uint8_t* u,
}
}
+#if !defined(WEBP_REDUCE_CSP)
static void YuvToArgbLine(const uint8_t* y, const uint8_t* u,
const uint8_t* v, uint8_t* dst, int length) {
v16u8 R, G, B;
@@ -526,6 +531,7 @@ static void YuvToRgb565Line(const uint8_t* y, const uint8_t* u,
memcpy(dst, temp, length * 2 * sizeof(*dst));
}
}
+#endif // WEBP_REDUCE_CSP
#define UPSAMPLE_32PIXELS(a, b, c, d) do { \
v16u8 s = __msa_aver_u_b(a, d); \
diff --git a/thirdparty/libwebp/src/dsp/upsampling_sse2.c b/thirdparty/libwebp/src/dsp/upsampling_sse2.c
index fd5d303982..340f1e2ac2 100644
--- a/thirdparty/libwebp/src/dsp/upsampling_sse2.c
+++ b/thirdparty/libwebp/src/dsp/upsampling_sse2.c
@@ -104,21 +104,6 @@ static void Upsample32Pixels_SSE2(const uint8_t r1[], const uint8_t r2[],
Upsample32Pixels_SSE2(r1, r2, out); \
}
-#define CONVERT2RGB(FUNC, XSTEP, top_y, bottom_y, \
- top_dst, bottom_dst, cur_x, num_pixels) { \
- int n; \
- for (n = 0; n < (num_pixels); ++n) { \
- FUNC((top_y)[(cur_x) + n], r_u[n], r_v[n], \
- (top_dst) + ((cur_x) + n) * (XSTEP)); \
- } \
- if ((bottom_y) != NULL) { \
- for (n = 0; n < (num_pixels); ++n) { \
- FUNC((bottom_y)[(cur_x) + n], r_u[64 + n], r_v[64 + n], \
- (bottom_dst) + ((cur_x) + n) * (XSTEP)); \
- } \
- } \
-}
-
#define CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, \
top_dst, bottom_dst, cur_x) do { \
FUNC##32_SSE2((top_y) + (cur_x), r_u, r_v, (top_dst) + (cur_x) * (XSTEP)); \
@@ -135,7 +120,7 @@ static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
int uv_pos, pos; \
/* 16byte-aligned array to cache reconstructed u and v */ \
- uint8_t uv_buf[4 * 32 + 15]; \
+ uint8_t uv_buf[14 * 32 + 15] = { 0 }; \
uint8_t* const r_u = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \
uint8_t* const r_v = r_u + 32; \
\
@@ -160,11 +145,22 @@ static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
} \
if (len > 1) { \
const int left_over = ((len + 1) >> 1) - (pos >> 1); \
+ uint8_t* const tmp_top_dst = r_u + 4 * 32; \
+ uint8_t* const tmp_bottom_dst = tmp_top_dst + 4 * 32; \
+ uint8_t* const tmp_top = tmp_bottom_dst + 4 * 32; \
+ uint8_t* const tmp_bottom = (bottom_y == NULL) ? NULL : tmp_top + 32; \
assert(left_over > 0); \
UPSAMPLE_LAST_BLOCK(top_u + uv_pos, cur_u + uv_pos, left_over, r_u); \
UPSAMPLE_LAST_BLOCK(top_v + uv_pos, cur_v + uv_pos, left_over, r_v); \
- CONVERT2RGB(FUNC, XSTEP, top_y, bottom_y, top_dst, bottom_dst, \
- pos, len - pos); \
+ memcpy(tmp_top, top_y + pos, len - pos); \
+ if (bottom_y != NULL) memcpy(tmp_bottom, bottom_y + pos, len - pos); \
+ CONVERT2RGB_32(FUNC, XSTEP, tmp_top, tmp_bottom, tmp_top_dst, \
+ tmp_bottom_dst, 0); \
+ memcpy(top_dst + pos * (XSTEP), tmp_top_dst, (len - pos) * (XSTEP)); \
+ if (bottom_y != NULL) { \
+ memcpy(bottom_dst + pos * (XSTEP), tmp_bottom_dst, \
+ (len - pos) * (XSTEP)); \
+ } \
} \
}
diff --git a/thirdparty/libwebp/src/dsp/upsampling_sse41.c b/thirdparty/libwebp/src/dsp/upsampling_sse41.c
new file mode 100644
index 0000000000..648d456027
--- /dev/null
+++ b/thirdparty/libwebp/src/dsp/upsampling_sse41.c
@@ -0,0 +1,239 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE41 version of YUV to RGB upsampling functions.
+//
+// Author: somnath@google.com (Somnath Banerjee)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE41)
+
+#include <assert.h>
+#include <smmintrin.h>
+#include <string.h>
+#include "src/dsp/yuv.h"
+
+#ifdef FANCY_UPSAMPLING
+
+#if !defined(WEBP_REDUCE_CSP)
+
+// We compute (9*a + 3*b + 3*c + d + 8) / 16 as follows
+// u = (9*a + 3*b + 3*c + d + 8) / 16
+// = (a + (a + 3*b + 3*c + d) / 8 + 1) / 2
+// = (a + m + 1) / 2
+// where m = (a + 3*b + 3*c + d) / 8
+// = ((a + b + c + d) / 2 + b + c) / 4
+//
+// Let's say k = (a + b + c + d) / 4.
+// We can compute k as
+// k = (s + t + 1) / 2 - ((a^d) | (b^c) | (s^t)) & 1
+// where s = (a + d + 1) / 2 and t = (b + c + 1) / 2
+//
+// Then m can be written as
+// m = (k + t + 1) / 2 - (((b^c) & (s^t)) | (k^t)) & 1
+
+// Computes out = (k + in + 1) / 2 - ((ij & (s^t)) | (k^in)) & 1
+#define GET_M(ij, in, out) do { \
+ const __m128i tmp0 = _mm_avg_epu8(k, (in)); /* (k + in + 1) / 2 */ \
+ const __m128i tmp1 = _mm_and_si128((ij), st); /* (ij) & (s^t) */ \
+ const __m128i tmp2 = _mm_xor_si128(k, (in)); /* (k^in) */ \
+ const __m128i tmp3 = _mm_or_si128(tmp1, tmp2); /* ((ij) & (s^t)) | (k^in) */\
+ const __m128i tmp4 = _mm_and_si128(tmp3, one); /* & 1 -> lsb_correction */ \
+ (out) = _mm_sub_epi8(tmp0, tmp4); /* (k + in + 1) / 2 - lsb_correction */ \
+} while (0)
+
+// pack and store two alternating pixel rows
+#define PACK_AND_STORE(a, b, da, db, out) do { \
+ const __m128i t_a = _mm_avg_epu8(a, da); /* (9a + 3b + 3c + d + 8) / 16 */ \
+ const __m128i t_b = _mm_avg_epu8(b, db); /* (3a + 9b + c + 3d + 8) / 16 */ \
+ const __m128i t_1 = _mm_unpacklo_epi8(t_a, t_b); \
+ const __m128i t_2 = _mm_unpackhi_epi8(t_a, t_b); \
+ _mm_store_si128(((__m128i*)(out)) + 0, t_1); \
+ _mm_store_si128(((__m128i*)(out)) + 1, t_2); \
+} while (0)
+
+// Loads 17 pixels each from rows r1 and r2 and generates 32 pixels.
+#define UPSAMPLE_32PIXELS(r1, r2, out) { \
+ const __m128i one = _mm_set1_epi8(1); \
+ const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \
+ const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \
+ const __m128i c = _mm_loadu_si128((const __m128i*)&(r2)[0]); \
+ const __m128i d = _mm_loadu_si128((const __m128i*)&(r2)[1]); \
+ \
+ const __m128i s = _mm_avg_epu8(a, d); /* s = (a + d + 1) / 2 */ \
+ const __m128i t = _mm_avg_epu8(b, c); /* t = (b + c + 1) / 2 */ \
+ const __m128i st = _mm_xor_si128(s, t); /* st = s^t */ \
+ \
+ const __m128i ad = _mm_xor_si128(a, d); /* ad = a^d */ \
+ const __m128i bc = _mm_xor_si128(b, c); /* bc = b^c */ \
+ \
+ const __m128i t1 = _mm_or_si128(ad, bc); /* (a^d) | (b^c) */ \
+ const __m128i t2 = _mm_or_si128(t1, st); /* (a^d) | (b^c) | (s^t) */ \
+ const __m128i t3 = _mm_and_si128(t2, one); /* (a^d) | (b^c) | (s^t) & 1 */ \
+ const __m128i t4 = _mm_avg_epu8(s, t); \
+ const __m128i k = _mm_sub_epi8(t4, t3); /* k = (a + b + c + d) / 4 */ \
+ __m128i diag1, diag2; \
+ \
+ GET_M(bc, t, diag1); /* diag1 = (a + 3b + 3c + d) / 8 */ \
+ GET_M(ad, s, diag2); /* diag2 = (3a + b + c + 3d) / 8 */ \
+ \
+ /* pack the alternate pixels */ \
+ PACK_AND_STORE(a, b, diag1, diag2, (out) + 0); /* store top */ \
+ PACK_AND_STORE(c, d, diag2, diag1, (out) + 2 * 32); /* store bottom */ \
+}
+
+// Turn the macro into a function for reducing code-size when non-critical
+static void Upsample32Pixels_SSE41(const uint8_t r1[], const uint8_t r2[],
+ uint8_t* const out) {
+ UPSAMPLE_32PIXELS(r1, r2, out);
+}
+
+#define UPSAMPLE_LAST_BLOCK(tb, bb, num_pixels, out) { \
+ uint8_t r1[17], r2[17]; \
+ memcpy(r1, (tb), (num_pixels)); \
+ memcpy(r2, (bb), (num_pixels)); \
+ /* replicate last byte */ \
+ memset(r1 + (num_pixels), r1[(num_pixels) - 1], 17 - (num_pixels)); \
+ memset(r2 + (num_pixels), r2[(num_pixels) - 1], 17 - (num_pixels)); \
+ /* using the shared function instead of the macro saves ~3k code size */ \
+ Upsample32Pixels_SSE41(r1, r2, out); \
+}
+
+#define CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, \
+ top_dst, bottom_dst, cur_x) do { \
+ FUNC##32_SSE41((top_y) + (cur_x), r_u, r_v, (top_dst) + (cur_x) * (XSTEP)); \
+ if ((bottom_y) != NULL) { \
+ FUNC##32_SSE41((bottom_y) + (cur_x), r_u + 64, r_v + 64, \
+ (bottom_dst) + (cur_x) * (XSTEP)); \
+ } \
+} while (0)
+
+#define SSE4_UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
+static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
+ const uint8_t* top_u, const uint8_t* top_v, \
+ const uint8_t* cur_u, const uint8_t* cur_v, \
+ uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
+ int uv_pos, pos; \
+ /* 16byte-aligned array to cache reconstructed u and v */ \
+ uint8_t uv_buf[14 * 32 + 15] = { 0 }; \
+ uint8_t* const r_u = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \
+ uint8_t* const r_v = r_u + 32; \
+ \
+ assert(top_y != NULL); \
+ { /* Treat the first pixel in regular way */ \
+ const int u_diag = ((top_u[0] + cur_u[0]) >> 1) + 1; \
+ const int v_diag = ((top_v[0] + cur_v[0]) >> 1) + 1; \
+ const int u0_t = (top_u[0] + u_diag) >> 1; \
+ const int v0_t = (top_v[0] + v_diag) >> 1; \
+ FUNC(top_y[0], u0_t, v0_t, top_dst); \
+ if (bottom_y != NULL) { \
+ const int u0_b = (cur_u[0] + u_diag) >> 1; \
+ const int v0_b = (cur_v[0] + v_diag) >> 1; \
+ FUNC(bottom_y[0], u0_b, v0_b, bottom_dst); \
+ } \
+ } \
+ /* For UPSAMPLE_32PIXELS, 17 u/v values must be read-able for each block */ \
+ for (pos = 1, uv_pos = 0; pos + 32 + 1 <= len; pos += 32, uv_pos += 16) { \
+ UPSAMPLE_32PIXELS(top_u + uv_pos, cur_u + uv_pos, r_u); \
+ UPSAMPLE_32PIXELS(top_v + uv_pos, cur_v + uv_pos, r_v); \
+ CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, top_dst, bottom_dst, pos); \
+ } \
+ if (len > 1) { \
+ const int left_over = ((len + 1) >> 1) - (pos >> 1); \
+ uint8_t* const tmp_top_dst = r_u + 4 * 32; \
+ uint8_t* const tmp_bottom_dst = tmp_top_dst + 4 * 32; \
+ uint8_t* const tmp_top = tmp_bottom_dst + 4 * 32; \
+ uint8_t* const tmp_bottom = (bottom_y == NULL) ? NULL : tmp_top + 32; \
+ assert(left_over > 0); \
+ UPSAMPLE_LAST_BLOCK(top_u + uv_pos, cur_u + uv_pos, left_over, r_u); \
+ UPSAMPLE_LAST_BLOCK(top_v + uv_pos, cur_v + uv_pos, left_over, r_v); \
+ memcpy(tmp_top, top_y + pos, len - pos); \
+ if (bottom_y != NULL) memcpy(tmp_bottom, bottom_y + pos, len - pos); \
+ CONVERT2RGB_32(FUNC, XSTEP, tmp_top, tmp_bottom, tmp_top_dst, \
+ tmp_bottom_dst, 0); \
+ memcpy(top_dst + pos * (XSTEP), tmp_top_dst, (len - pos) * (XSTEP)); \
+ if (bottom_y != NULL) { \
+ memcpy(bottom_dst + pos * (XSTEP), tmp_bottom_dst, \
+ (len - pos) * (XSTEP)); \
+ } \
+ } \
+}
+
+// SSE4 variants of the fancy upsampler.
+SSE4_UPSAMPLE_FUNC(UpsampleRgbLinePair_SSE41, VP8YuvToRgb, 3)
+SSE4_UPSAMPLE_FUNC(UpsampleBgrLinePair_SSE41, VP8YuvToBgr, 3)
+
+#undef GET_M
+#undef PACK_AND_STORE
+#undef UPSAMPLE_32PIXELS
+#undef UPSAMPLE_LAST_BLOCK
+#undef CONVERT2RGB
+#undef CONVERT2RGB_32
+#undef SSE4_UPSAMPLE_FUNC
+
+#endif // WEBP_REDUCE_CSP
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
+
+extern void WebPInitUpsamplersSSE41(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersSSE41(void) {
+#if !defined(WEBP_REDUCE_CSP)
+ WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair_SSE41;
+ WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair_SSE41;
+#endif // WEBP_REDUCE_CSP
+}
+
+#endif // FANCY_UPSAMPLING
+
+//------------------------------------------------------------------------------
+
+extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */];
+extern void WebPInitYUV444ConvertersSSE41(void);
+
+#define YUV444_FUNC(FUNC_NAME, CALL, CALL_C, XSTEP) \
+extern void CALL_C(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len); \
+static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len) { \
+ int i; \
+ const int max_len = len & ~31; \
+ for (i = 0; i < max_len; i += 32) { \
+ CALL(y + i, u + i, v + i, dst + i * (XSTEP)); \
+ } \
+ if (i < len) { /* C-fallback */ \
+ CALL_C(y + i, u + i, v + i, dst + i * (XSTEP), len - i); \
+ } \
+}
+
+#if !defined(WEBP_REDUCE_CSP)
+YUV444_FUNC(Yuv444ToRgb_SSE41, VP8YuvToRgb32_SSE41, WebPYuv444ToRgb_C, 3);
+YUV444_FUNC(Yuv444ToBgr_SSE41, VP8YuvToBgr32_SSE41, WebPYuv444ToBgr_C, 3);
+#endif // WEBP_REDUCE_CSP
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444ConvertersSSE41(void) {
+#if !defined(WEBP_REDUCE_CSP)
+ WebPYUV444Converters[MODE_RGB] = Yuv444ToRgb_SSE41;
+ WebPYUV444Converters[MODE_BGR] = Yuv444ToBgr_SSE41;
+#endif // WEBP_REDUCE_CSP
+}
+
+#else
+
+WEBP_DSP_INIT_STUB(WebPInitYUV444ConvertersSSE41)
+
+#endif // WEBP_USE_SSE41
+
+#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_SSE41))
+WEBP_DSP_INIT_STUB(WebPInitUpsamplersSSE41)
+#endif
diff --git a/thirdparty/libwebp/src/dsp/yuv.c b/thirdparty/libwebp/src/dsp/yuv.c
index bddf81fe09..14e67fc28e 100644
--- a/thirdparty/libwebp/src/dsp/yuv.c
+++ b/thirdparty/libwebp/src/dsp/yuv.c
@@ -71,15 +71,11 @@ void WebPSamplerProcessPlane(const uint8_t* y, int y_stride,
WebPSamplerRowFunc WebPSamplers[MODE_LAST];
extern void WebPInitSamplersSSE2(void);
+extern void WebPInitSamplersSSE41(void);
extern void WebPInitSamplersMIPS32(void);
extern void WebPInitSamplersMIPSdspR2(void);
-static volatile VP8CPUInfo yuv_last_cpuinfo_used =
- (VP8CPUInfo)&yuv_last_cpuinfo_used;
-
-WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplers(void) {
- if (yuv_last_cpuinfo_used == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(WebPInitSamplers) {
WebPSamplers[MODE_RGB] = YuvToRgbRow;
WebPSamplers[MODE_RGBA] = YuvToRgbaRow;
WebPSamplers[MODE_BGR] = YuvToBgrRow;
@@ -99,6 +95,11 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplers(void) {
WebPInitSamplersSSE2();
}
#endif // WEBP_USE_SSE2
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ WebPInitSamplersSSE41();
+ }
+#endif // WEBP_USE_SSE41
#if defined(WEBP_USE_MIPS32)
if (VP8GetCPUInfo(kMIPS32)) {
WebPInitSamplersMIPS32();
@@ -110,7 +111,6 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplers(void) {
}
#endif // WEBP_USE_MIPS_DSP_R2
}
- yuv_last_cpuinfo_used = VP8GetCPUInfo;
}
//-----------------------------------------------------------------------------
@@ -254,17 +254,13 @@ void (*WebPSharpYUVUpdateRGB)(const int16_t* ref, const int16_t* src,
void (*WebPSharpYUVFilterRow)(const int16_t* A, const int16_t* B, int len,
const uint16_t* best_y, uint16_t* out);
-static volatile VP8CPUInfo rgba_to_yuv_last_cpuinfo_used =
- (VP8CPUInfo)&rgba_to_yuv_last_cpuinfo_used;
-
extern void WebPInitConvertARGBToYUVSSE2(void);
+extern void WebPInitConvertARGBToYUVSSE41(void);
extern void WebPInitConvertARGBToYUVNEON(void);
extern void WebPInitSharpYUVSSE2(void);
extern void WebPInitSharpYUVNEON(void);
-WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUV(void) {
- if (rgba_to_yuv_last_cpuinfo_used == VP8GetCPUInfo) return;
-
+WEBP_DSP_INIT_FUNC(WebPInitConvertARGBToYUV) {
WebPConvertARGBToY = ConvertARGBToY_C;
WebPConvertARGBToUV = WebPConvertARGBToUV_C;
@@ -286,6 +282,11 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUV(void) {
WebPInitSharpYUVSSE2();
}
#endif // WEBP_USE_SSE2
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ WebPInitConvertARGBToYUVSSE41();
+ }
+#endif // WEBP_USE_SSE41
}
#if defined(WEBP_USE_NEON)
@@ -304,6 +305,4 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUV(void) {
assert(WebPSharpYUVUpdateY != NULL);
assert(WebPSharpYUVUpdateRGB != NULL);
assert(WebPSharpYUVFilterRow != NULL);
-
- rgba_to_yuv_last_cpuinfo_used = VP8GetCPUInfo;
}
diff --git a/thirdparty/libwebp/src/dsp/yuv.h b/thirdparty/libwebp/src/dsp/yuv.h
index c8a55832d4..eb787270d2 100644
--- a/thirdparty/libwebp/src/dsp/yuv.h
+++ b/thirdparty/libwebp/src/dsp/yuv.h
@@ -166,6 +166,19 @@ void VP8YuvToRgb56532_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
#endif // WEBP_USE_SSE2
+//-----------------------------------------------------------------------------
+// SSE41 extra functions (mostly for upsampling_sse41.c)
+
+#if defined(WEBP_USE_SSE41)
+
+// Process 32 pixels and store the result (16b, 24b or 32b per pixel) in *dst.
+void VP8YuvToRgb32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst);
+void VP8YuvToBgr32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst);
+
+#endif // WEBP_USE_SSE41
+
//------------------------------------------------------------------------------
// RGB -> YUV conversion
diff --git a/thirdparty/libwebp/src/dsp/yuv_sse2.c b/thirdparty/libwebp/src/dsp/yuv_sse2.c
index 6810bf8d15..baa48d5371 100644
--- a/thirdparty/libwebp/src/dsp/yuv_sse2.c
+++ b/thirdparty/libwebp/src/dsp/yuv_sse2.c
@@ -180,7 +180,7 @@ static WEBP_INLINE void PlanarTo24b_SSE2(__m128i* const in0, __m128i* const in1,
// Repeat the same permutations twice more:
// r0r4g0g4 | b0b4r1r5 | g1g5b1b5 | r2r6g2g6 | b2b6r3r7 | g3g7b3b7
// r0g0b0r1 | g1b1r2g2 | b2r3g3b3 | r4g4b4r5 | g5b5r6g6 | b6r7g7b7
- VP8PlanarTo24b(in0, in1, in2, in3, in4, in5);
+ VP8PlanarTo24b_SSE2(in0, in1, in2, in3, in4, in5);
_mm_storeu_si128((__m128i*)(rgb + 0), *in0);
_mm_storeu_si128((__m128i*)(rgb + 16), *in1);
@@ -492,7 +492,7 @@ static WEBP_INLINE void RGB32PackedToPlanar_SSE2(const uint32_t* const argb,
__m128i a1 = LOAD_16(argb + 4);
__m128i a2 = LOAD_16(argb + 8);
__m128i a3 = LOAD_16(argb + 12);
- VP8L32bToPlanar(&a0, &a1, &a2, &a3);
+ VP8L32bToPlanar_SSE2(&a0, &a1, &a2, &a3);
rgb[0] = _mm_unpacklo_epi8(a1, zero);
rgb[1] = _mm_unpackhi_epi8(a1, zero);
rgb[2] = _mm_unpacklo_epi8(a2, zero);
diff --git a/thirdparty/libwebp/src/dsp/yuv_sse41.c b/thirdparty/libwebp/src/dsp/yuv_sse41.c
new file mode 100644
index 0000000000..579d1f7402
--- /dev/null
+++ b/thirdparty/libwebp/src/dsp/yuv_sse41.c
@@ -0,0 +1,613 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// YUV->RGB conversion functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/yuv.h"
+
+#if defined(WEBP_USE_SSE41)
+
+#include "src/dsp/common_sse41.h"
+#include <stdlib.h>
+#include <smmintrin.h>
+
+//-----------------------------------------------------------------------------
+// Convert spans of 32 pixels to various RGB formats for the fancy upsampler.
+
+// These constants are 14b fixed-point version of ITU-R BT.601 constants.
+// R = (19077 * y + 26149 * v - 14234) >> 6
+// G = (19077 * y - 6419 * u - 13320 * v + 8708) >> 6
+// B = (19077 * y + 33050 * u - 17685) >> 6
+static void ConvertYUV444ToRGB_SSE41(const __m128i* const Y0,
+ const __m128i* const U0,
+ const __m128i* const V0,
+ __m128i* const R,
+ __m128i* const G,
+ __m128i* const B) {
+ const __m128i k19077 = _mm_set1_epi16(19077);
+ const __m128i k26149 = _mm_set1_epi16(26149);
+ const __m128i k14234 = _mm_set1_epi16(14234);
+ // 33050 doesn't fit in a signed short: only use this with unsigned arithmetic
+ const __m128i k33050 = _mm_set1_epi16((short)33050);
+ const __m128i k17685 = _mm_set1_epi16(17685);
+ const __m128i k6419 = _mm_set1_epi16(6419);
+ const __m128i k13320 = _mm_set1_epi16(13320);
+ const __m128i k8708 = _mm_set1_epi16(8708);
+
+ const __m128i Y1 = _mm_mulhi_epu16(*Y0, k19077);
+
+ const __m128i R0 = _mm_mulhi_epu16(*V0, k26149);
+ const __m128i R1 = _mm_sub_epi16(Y1, k14234);
+ const __m128i R2 = _mm_add_epi16(R1, R0);
+
+ const __m128i G0 = _mm_mulhi_epu16(*U0, k6419);
+ const __m128i G1 = _mm_mulhi_epu16(*V0, k13320);
+ const __m128i G2 = _mm_add_epi16(Y1, k8708);
+ const __m128i G3 = _mm_add_epi16(G0, G1);
+ const __m128i G4 = _mm_sub_epi16(G2, G3);
+
+ // be careful with the saturated *unsigned* arithmetic here!
+ const __m128i B0 = _mm_mulhi_epu16(*U0, k33050);
+ const __m128i B1 = _mm_adds_epu16(B0, Y1);
+ const __m128i B2 = _mm_subs_epu16(B1, k17685);
+
+ // use logical shift for B2, which can be larger than 32767
+ *R = _mm_srai_epi16(R2, 6); // range: [-14234, 30815]
+ *G = _mm_srai_epi16(G4, 6); // range: [-10953, 27710]
+ *B = _mm_srli_epi16(B2, 6); // range: [0, 34238]
+}
+
+// Load the bytes into the *upper* part of 16b words. That's "<< 8", basically.
+static WEBP_INLINE __m128i Load_HI_16_SSE41(const uint8_t* src) {
+ const __m128i zero = _mm_setzero_si128();
+ return _mm_unpacklo_epi8(zero, _mm_loadl_epi64((const __m128i*)src));
+}
+
+// Load and replicate the U/V samples
+static WEBP_INLINE __m128i Load_UV_HI_8_SSE41(const uint8_t* src) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i tmp0 = _mm_cvtsi32_si128(*(const uint32_t*)src);
+ const __m128i tmp1 = _mm_unpacklo_epi8(zero, tmp0);
+ return _mm_unpacklo_epi16(tmp1, tmp1); // replicate samples
+}
+
+// Convert 32 samples of YUV444 to R/G/B
+static void YUV444ToRGB_SSE41(const uint8_t* const y,
+ const uint8_t* const u,
+ const uint8_t* const v,
+ __m128i* const R, __m128i* const G,
+ __m128i* const B) {
+ const __m128i Y0 = Load_HI_16_SSE41(y), U0 = Load_HI_16_SSE41(u),
+ V0 = Load_HI_16_SSE41(v);
+ ConvertYUV444ToRGB_SSE41(&Y0, &U0, &V0, R, G, B);
+}
+
+// Convert 32 samples of YUV420 to R/G/B
+static void YUV420ToRGB_SSE41(const uint8_t* const y,
+ const uint8_t* const u,
+ const uint8_t* const v,
+ __m128i* const R, __m128i* const G,
+ __m128i* const B) {
+ const __m128i Y0 = Load_HI_16_SSE41(y), U0 = Load_UV_HI_8_SSE41(u),
+ V0 = Load_UV_HI_8_SSE41(v);
+ ConvertYUV444ToRGB_SSE41(&Y0, &U0, &V0, R, G, B);
+}
+
+// Pack the planar buffers
+// rrrr... rrrr... gggg... gggg... bbbb... bbbb....
+// triplet by triplet in the output buffer rgb as rgbrgbrgbrgb ...
+static WEBP_INLINE void PlanarTo24b_SSE41(
+ __m128i* const in0, __m128i* const in1, __m128i* const in2,
+ __m128i* const in3, __m128i* const in4, __m128i* const in5,
+ uint8_t* const rgb) {
+ // The input is 6 registers of sixteen 8b but for the sake of explanation,
+ // let's take 6 registers of four 8b values.
+ // To pack, we will keep taking one every two 8b integer and move it
+ // around as follows:
+ // Input:
+ // r0r1r2r3 | r4r5r6r7 | g0g1g2g3 | g4g5g6g7 | b0b1b2b3 | b4b5b6b7
+ // Split the 6 registers in two sets of 3 registers: the first set as the even
+ // 8b bytes, the second the odd ones:
+ // r0r2r4r6 | g0g2g4g6 | b0b2b4b6 | r1r3r5r7 | g1g3g5g7 | b1b3b5b7
+ // Repeat the same permutations twice more:
+ // r0r4g0g4 | b0b4r1r5 | g1g5b1b5 | r2r6g2g6 | b2b6r3r7 | g3g7b3b7
+ // r0g0b0r1 | g1b1r2g2 | b2r3g3b3 | r4g4b4r5 | g5b5r6g6 | b6r7g7b7
+ VP8PlanarTo24b_SSE41(in0, in1, in2, in3, in4, in5);
+
+ _mm_storeu_si128((__m128i*)(rgb + 0), *in0);
+ _mm_storeu_si128((__m128i*)(rgb + 16), *in1);
+ _mm_storeu_si128((__m128i*)(rgb + 32), *in2);
+ _mm_storeu_si128((__m128i*)(rgb + 48), *in3);
+ _mm_storeu_si128((__m128i*)(rgb + 64), *in4);
+ _mm_storeu_si128((__m128i*)(rgb + 80), *in5);
+}
+
+void VP8YuvToRgb32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i rgb0, rgb1, rgb2, rgb3, rgb4, rgb5;
+
+ YUV444ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV444ToRGB_SSE41(y + 8, u + 8, v + 8, &R1, &G1, &B1);
+ YUV444ToRGB_SSE41(y + 16, u + 16, v + 16, &R2, &G2, &B2);
+ YUV444ToRGB_SSE41(y + 24, u + 24, v + 24, &R3, &G3, &B3);
+
+ // Cast to 8b and store as RRRRGGGGBBBB.
+ rgb0 = _mm_packus_epi16(R0, R1);
+ rgb1 = _mm_packus_epi16(R2, R3);
+ rgb2 = _mm_packus_epi16(G0, G1);
+ rgb3 = _mm_packus_epi16(G2, G3);
+ rgb4 = _mm_packus_epi16(B0, B1);
+ rgb5 = _mm_packus_epi16(B2, B3);
+
+ // Pack as RGBRGBRGBRGB.
+ PlanarTo24b_SSE41(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst);
+}
+
+void VP8YuvToBgr32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i bgr0, bgr1, bgr2, bgr3, bgr4, bgr5;
+
+ YUV444ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV444ToRGB_SSE41(y + 8, u + 8, v + 8, &R1, &G1, &B1);
+ YUV444ToRGB_SSE41(y + 16, u + 16, v + 16, &R2, &G2, &B2);
+ YUV444ToRGB_SSE41(y + 24, u + 24, v + 24, &R3, &G3, &B3);
+
+ // Cast to 8b and store as BBBBGGGGRRRR.
+ bgr0 = _mm_packus_epi16(B0, B1);
+ bgr1 = _mm_packus_epi16(B2, B3);
+ bgr2 = _mm_packus_epi16(G0, G1);
+ bgr3 = _mm_packus_epi16(G2, G3);
+ bgr4 = _mm_packus_epi16(R0, R1);
+ bgr5= _mm_packus_epi16(R2, R3);
+
+ // Pack as BGRBGRBGRBGR.
+ PlanarTo24b_SSE41(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst);
+}
+
+//-----------------------------------------------------------------------------
+// Arbitrary-length row conversion functions
+
+static void YuvToRgbRow_SSE41(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len) {
+ int n;
+ for (n = 0; n + 32 <= len; n += 32, dst += 32 * 3) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i rgb0, rgb1, rgb2, rgb3, rgb4, rgb5;
+
+ YUV420ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV420ToRGB_SSE41(y + 8, u + 4, v + 4, &R1, &G1, &B1);
+ YUV420ToRGB_SSE41(y + 16, u + 8, v + 8, &R2, &G2, &B2);
+ YUV420ToRGB_SSE41(y + 24, u + 12, v + 12, &R3, &G3, &B3);
+
+ // Cast to 8b and store as RRRRGGGGBBBB.
+ rgb0 = _mm_packus_epi16(R0, R1);
+ rgb1 = _mm_packus_epi16(R2, R3);
+ rgb2 = _mm_packus_epi16(G0, G1);
+ rgb3 = _mm_packus_epi16(G2, G3);
+ rgb4 = _mm_packus_epi16(B0, B1);
+ rgb5 = _mm_packus_epi16(B2, B3);
+
+ // Pack as RGBRGBRGBRGB.
+ PlanarTo24b_SSE41(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst);
+
+ y += 32;
+ u += 16;
+ v += 16;
+ }
+ for (; n < len; ++n) { // Finish off
+ VP8YuvToRgb(y[0], u[0], v[0], dst);
+ dst += 3;
+ y += 1;
+ u += (n & 1);
+ v += (n & 1);
+ }
+}
+
+static void YuvToBgrRow_SSE41(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len) {
+ int n;
+ for (n = 0; n + 32 <= len; n += 32, dst += 32 * 3) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i bgr0, bgr1, bgr2, bgr3, bgr4, bgr5;
+
+ YUV420ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV420ToRGB_SSE41(y + 8, u + 4, v + 4, &R1, &G1, &B1);
+ YUV420ToRGB_SSE41(y + 16, u + 8, v + 8, &R2, &G2, &B2);
+ YUV420ToRGB_SSE41(y + 24, u + 12, v + 12, &R3, &G3, &B3);
+
+ // Cast to 8b and store as BBBBGGGGRRRR.
+ bgr0 = _mm_packus_epi16(B0, B1);
+ bgr1 = _mm_packus_epi16(B2, B3);
+ bgr2 = _mm_packus_epi16(G0, G1);
+ bgr3 = _mm_packus_epi16(G2, G3);
+ bgr4 = _mm_packus_epi16(R0, R1);
+ bgr5 = _mm_packus_epi16(R2, R3);
+
+ // Pack as BGRBGRBGRBGR.
+ PlanarTo24b_SSE41(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst);
+
+ y += 32;
+ u += 16;
+ v += 16;
+ }
+ for (; n < len; ++n) { // Finish off
+ VP8YuvToBgr(y[0], u[0], v[0], dst);
+ dst += 3;
+ y += 1;
+ u += (n & 1);
+ v += (n & 1);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPInitSamplersSSE41(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplersSSE41(void) {
+ WebPSamplers[MODE_RGB] = YuvToRgbRow_SSE41;
+ WebPSamplers[MODE_BGR] = YuvToBgrRow_SSE41;
+}
+
+//------------------------------------------------------------------------------
+// RGB24/32 -> YUV converters
+
+// Load eight 16b-words from *src.
+#define LOAD_16(src) _mm_loadu_si128((const __m128i*)(src))
+// Store either 16b-words into *dst
+#define STORE_16(V, dst) _mm_storeu_si128((__m128i*)(dst), (V))
+
+#define WEBP_SSE41_SHUFF(OUT) do { \
+ const __m128i tmp0 = _mm_shuffle_epi8(A0, shuff0); \
+ const __m128i tmp1 = _mm_shuffle_epi8(A1, shuff1); \
+ const __m128i tmp2 = _mm_shuffle_epi8(A2, shuff2); \
+ const __m128i tmp3 = _mm_shuffle_epi8(A3, shuff0); \
+ const __m128i tmp4 = _mm_shuffle_epi8(A4, shuff1); \
+ const __m128i tmp5 = _mm_shuffle_epi8(A5, shuff2); \
+ \
+ /* OR everything to get one channel */ \
+ const __m128i tmp6 = _mm_or_si128(tmp0, tmp1); \
+ const __m128i tmp7 = _mm_or_si128(tmp3, tmp4); \
+ out[OUT + 0] = _mm_or_si128(tmp6, tmp2); \
+ out[OUT + 1] = _mm_or_si128(tmp7, tmp5); \
+} while (0);
+
+// Unpack the 8b input rgbrgbrgbrgb ... as contiguous registers:
+// rrrr... rrrr... gggg... gggg... bbbb... bbbb....
+// Similar to PlanarTo24bHelper(), but in reverse order.
+static WEBP_INLINE void RGB24PackedToPlanar_SSE41(
+ const uint8_t* const rgb, __m128i* const out /*out[6]*/) {
+ const __m128i A0 = _mm_loadu_si128((const __m128i*)(rgb + 0));
+ const __m128i A1 = _mm_loadu_si128((const __m128i*)(rgb + 16));
+ const __m128i A2 = _mm_loadu_si128((const __m128i*)(rgb + 32));
+ const __m128i A3 = _mm_loadu_si128((const __m128i*)(rgb + 48));
+ const __m128i A4 = _mm_loadu_si128((const __m128i*)(rgb + 64));
+ const __m128i A5 = _mm_loadu_si128((const __m128i*)(rgb + 80));
+
+ // Compute RR.
+ {
+ const __m128i shuff0 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, 12, 9, 6, 3, 0);
+ const __m128i shuff1 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, 14, 11, 8, 5, 2, -1, -1, -1, -1, -1, -1);
+ const __m128i shuff2 = _mm_set_epi8(
+ 13, 10, 7, 4, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ WEBP_SSE41_SHUFF(0)
+ }
+ // Compute GG.
+ {
+ const __m128i shuff0 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13, 10, 7, 4, 1);
+ const __m128i shuff1 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, 15, 12, 9, 6, 3, 0, -1, -1, -1, -1, -1);
+ const __m128i shuff2 = _mm_set_epi8(
+ 14, 11, 8, 5, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ WEBP_SSE41_SHUFF(2)
+ }
+ // Compute BB.
+ {
+ const __m128i shuff0 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 14, 11, 8, 5, 2);
+ const __m128i shuff1 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, -1, 13, 10, 7, 4, 1, -1, -1, -1, -1, -1);
+ const __m128i shuff2 = _mm_set_epi8(
+ 15, 12, 9, 6, 3, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ WEBP_SSE41_SHUFF(4)
+ }
+}
+
+#undef WEBP_SSE41_SHUFF
+
+// Convert 8 packed ARGB to r[], g[], b[]
+static WEBP_INLINE void RGB32PackedToPlanar_SSE41(
+ const uint32_t* const argb, __m128i* const rgb /*in[6]*/) {
+ const __m128i zero = _mm_setzero_si128();
+ __m128i a0 = LOAD_16(argb + 0);
+ __m128i a1 = LOAD_16(argb + 4);
+ __m128i a2 = LOAD_16(argb + 8);
+ __m128i a3 = LOAD_16(argb + 12);
+ VP8L32bToPlanar_SSE41(&a0, &a1, &a2, &a3);
+ rgb[0] = _mm_unpacklo_epi8(a1, zero);
+ rgb[1] = _mm_unpackhi_epi8(a1, zero);
+ rgb[2] = _mm_unpacklo_epi8(a2, zero);
+ rgb[3] = _mm_unpackhi_epi8(a2, zero);
+ rgb[4] = _mm_unpacklo_epi8(a3, zero);
+ rgb[5] = _mm_unpackhi_epi8(a3, zero);
+}
+
+// This macro computes (RG * MULT_RG + GB * MULT_GB + ROUNDER) >> DESCALE_FIX
+// It's a macro and not a function because we need to use immediate values with
+// srai_epi32, e.g.
+#define TRANSFORM(RG_LO, RG_HI, GB_LO, GB_HI, MULT_RG, MULT_GB, \
+ ROUNDER, DESCALE_FIX, OUT) do { \
+ const __m128i V0_lo = _mm_madd_epi16(RG_LO, MULT_RG); \
+ const __m128i V0_hi = _mm_madd_epi16(RG_HI, MULT_RG); \
+ const __m128i V1_lo = _mm_madd_epi16(GB_LO, MULT_GB); \
+ const __m128i V1_hi = _mm_madd_epi16(GB_HI, MULT_GB); \
+ const __m128i V2_lo = _mm_add_epi32(V0_lo, V1_lo); \
+ const __m128i V2_hi = _mm_add_epi32(V0_hi, V1_hi); \
+ const __m128i V3_lo = _mm_add_epi32(V2_lo, ROUNDER); \
+ const __m128i V3_hi = _mm_add_epi32(V2_hi, ROUNDER); \
+ const __m128i V5_lo = _mm_srai_epi32(V3_lo, DESCALE_FIX); \
+ const __m128i V5_hi = _mm_srai_epi32(V3_hi, DESCALE_FIX); \
+ (OUT) = _mm_packs_epi32(V5_lo, V5_hi); \
+} while (0)
+
+#define MK_CST_16(A, B) _mm_set_epi16((B), (A), (B), (A), (B), (A), (B), (A))
+static WEBP_INLINE void ConvertRGBToY_SSE41(const __m128i* const R,
+ const __m128i* const G,
+ const __m128i* const B,
+ __m128i* const Y) {
+ const __m128i kRG_y = MK_CST_16(16839, 33059 - 16384);
+ const __m128i kGB_y = MK_CST_16(16384, 6420);
+ const __m128i kHALF_Y = _mm_set1_epi32((16 << YUV_FIX) + YUV_HALF);
+
+ const __m128i RG_lo = _mm_unpacklo_epi16(*R, *G);
+ const __m128i RG_hi = _mm_unpackhi_epi16(*R, *G);
+ const __m128i GB_lo = _mm_unpacklo_epi16(*G, *B);
+ const __m128i GB_hi = _mm_unpackhi_epi16(*G, *B);
+ TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_y, kGB_y, kHALF_Y, YUV_FIX, *Y);
+}
+
+static WEBP_INLINE void ConvertRGBToUV_SSE41(const __m128i* const R,
+ const __m128i* const G,
+ const __m128i* const B,
+ __m128i* const U,
+ __m128i* const V) {
+ const __m128i kRG_u = MK_CST_16(-9719, -19081);
+ const __m128i kGB_u = MK_CST_16(0, 28800);
+ const __m128i kRG_v = MK_CST_16(28800, 0);
+ const __m128i kGB_v = MK_CST_16(-24116, -4684);
+ const __m128i kHALF_UV = _mm_set1_epi32(((128 << YUV_FIX) + YUV_HALF) << 2);
+
+ const __m128i RG_lo = _mm_unpacklo_epi16(*R, *G);
+ const __m128i RG_hi = _mm_unpackhi_epi16(*R, *G);
+ const __m128i GB_lo = _mm_unpacklo_epi16(*G, *B);
+ const __m128i GB_hi = _mm_unpackhi_epi16(*G, *B);
+ TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_u, kGB_u,
+ kHALF_UV, YUV_FIX + 2, *U);
+ TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_v, kGB_v,
+ kHALF_UV, YUV_FIX + 2, *V);
+}
+
+#undef MK_CST_16
+#undef TRANSFORM
+
+static void ConvertRGB24ToY_SSE41(const uint8_t* rgb, uint8_t* y, int width) {
+ const int max_width = width & ~31;
+ int i;
+ for (i = 0; i < max_width; rgb += 3 * 16 * 2) {
+ __m128i rgb_plane[6];
+ int j;
+
+ RGB24PackedToPlanar_SSE41(rgb, rgb_plane);
+
+ for (j = 0; j < 2; ++j, i += 16) {
+ const __m128i zero = _mm_setzero_si128();
+ __m128i r, g, b, Y0, Y1;
+
+ // Convert to 16-bit Y.
+ r = _mm_unpacklo_epi8(rgb_plane[0 + j], zero);
+ g = _mm_unpacklo_epi8(rgb_plane[2 + j], zero);
+ b = _mm_unpacklo_epi8(rgb_plane[4 + j], zero);
+ ConvertRGBToY_SSE41(&r, &g, &b, &Y0);
+
+ // Convert to 16-bit Y.
+ r = _mm_unpackhi_epi8(rgb_plane[0 + j], zero);
+ g = _mm_unpackhi_epi8(rgb_plane[2 + j], zero);
+ b = _mm_unpackhi_epi8(rgb_plane[4 + j], zero);
+ ConvertRGBToY_SSE41(&r, &g, &b, &Y1);
+
+ // Cast to 8-bit and store.
+ STORE_16(_mm_packus_epi16(Y0, Y1), y + i);
+ }
+ }
+ for (; i < width; ++i, rgb += 3) { // left-over
+ y[i] = VP8RGBToY(rgb[0], rgb[1], rgb[2], YUV_HALF);
+ }
+}
+
+static void ConvertBGR24ToY_SSE41(const uint8_t* bgr, uint8_t* y, int width) {
+ const int max_width = width & ~31;
+ int i;
+ for (i = 0; i < max_width; bgr += 3 * 16 * 2) {
+ __m128i bgr_plane[6];
+ int j;
+
+ RGB24PackedToPlanar_SSE41(bgr, bgr_plane);
+
+ for (j = 0; j < 2; ++j, i += 16) {
+ const __m128i zero = _mm_setzero_si128();
+ __m128i r, g, b, Y0, Y1;
+
+ // Convert to 16-bit Y.
+ b = _mm_unpacklo_epi8(bgr_plane[0 + j], zero);
+ g = _mm_unpacklo_epi8(bgr_plane[2 + j], zero);
+ r = _mm_unpacklo_epi8(bgr_plane[4 + j], zero);
+ ConvertRGBToY_SSE41(&r, &g, &b, &Y0);
+
+ // Convert to 16-bit Y.
+ b = _mm_unpackhi_epi8(bgr_plane[0 + j], zero);
+ g = _mm_unpackhi_epi8(bgr_plane[2 + j], zero);
+ r = _mm_unpackhi_epi8(bgr_plane[4 + j], zero);
+ ConvertRGBToY_SSE41(&r, &g, &b, &Y1);
+
+ // Cast to 8-bit and store.
+ STORE_16(_mm_packus_epi16(Y0, Y1), y + i);
+ }
+ }
+ for (; i < width; ++i, bgr += 3) { // left-over
+ y[i] = VP8RGBToY(bgr[2], bgr[1], bgr[0], YUV_HALF);
+ }
+}
+
+static void ConvertARGBToY_SSE41(const uint32_t* argb, uint8_t* y, int width) {
+ const int max_width = width & ~15;
+ int i;
+ for (i = 0; i < max_width; i += 16) {
+ __m128i Y0, Y1, rgb[6];
+ RGB32PackedToPlanar_SSE41(&argb[i], rgb);
+ ConvertRGBToY_SSE41(&rgb[0], &rgb[2], &rgb[4], &Y0);
+ ConvertRGBToY_SSE41(&rgb[1], &rgb[3], &rgb[5], &Y1);
+ STORE_16(_mm_packus_epi16(Y0, Y1), y + i);
+ }
+ for (; i < width; ++i) { // left-over
+ const uint32_t p = argb[i];
+ y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff,
+ YUV_HALF);
+ }
+}
+
+// Horizontal add (doubled) of two 16b values, result is 16b.
+// in: A | B | C | D | ... -> out: 2*(A+B) | 2*(C+D) | ...
+static void HorizontalAddPack_SSE41(const __m128i* const A,
+ const __m128i* const B,
+ __m128i* const out) {
+ const __m128i k2 = _mm_set1_epi16(2);
+ const __m128i C = _mm_madd_epi16(*A, k2);
+ const __m128i D = _mm_madd_epi16(*B, k2);
+ *out = _mm_packs_epi32(C, D);
+}
+
+static void ConvertARGBToUV_SSE41(const uint32_t* argb,
+ uint8_t* u, uint8_t* v,
+ int src_width, int do_store) {
+ const int max_width = src_width & ~31;
+ int i;
+ for (i = 0; i < max_width; i += 32, u += 16, v += 16) {
+ __m128i rgb[6], U0, V0, U1, V1;
+ RGB32PackedToPlanar_SSE41(&argb[i], rgb);
+ HorizontalAddPack_SSE41(&rgb[0], &rgb[1], &rgb[0]);
+ HorizontalAddPack_SSE41(&rgb[2], &rgb[3], &rgb[2]);
+ HorizontalAddPack_SSE41(&rgb[4], &rgb[5], &rgb[4]);
+ ConvertRGBToUV_SSE41(&rgb[0], &rgb[2], &rgb[4], &U0, &V0);
+
+ RGB32PackedToPlanar_SSE41(&argb[i + 16], rgb);
+ HorizontalAddPack_SSE41(&rgb[0], &rgb[1], &rgb[0]);
+ HorizontalAddPack_SSE41(&rgb[2], &rgb[3], &rgb[2]);
+ HorizontalAddPack_SSE41(&rgb[4], &rgb[5], &rgb[4]);
+ ConvertRGBToUV_SSE41(&rgb[0], &rgb[2], &rgb[4], &U1, &V1);
+
+ U0 = _mm_packus_epi16(U0, U1);
+ V0 = _mm_packus_epi16(V0, V1);
+ if (!do_store) {
+ const __m128i prev_u = LOAD_16(u);
+ const __m128i prev_v = LOAD_16(v);
+ U0 = _mm_avg_epu8(U0, prev_u);
+ V0 = _mm_avg_epu8(V0, prev_v);
+ }
+ STORE_16(U0, u);
+ STORE_16(V0, v);
+ }
+ if (i < src_width) { // left-over
+ WebPConvertARGBToUV_C(argb + i, u, v, src_width - i, do_store);
+ }
+}
+
+// Convert 16 packed ARGB 16b-values to r[], g[], b[]
+static WEBP_INLINE void RGBA32PackedToPlanar_16b_SSE41(
+ const uint16_t* const rgbx,
+ __m128i* const r, __m128i* const g, __m128i* const b) {
+ const __m128i in0 = LOAD_16(rgbx + 0); // r0 | g0 | b0 |x| r1 | g1 | b1 |x
+ const __m128i in1 = LOAD_16(rgbx + 8); // r2 | g2 | b2 |x| r3 | g3 | b3 |x
+ const __m128i in2 = LOAD_16(rgbx + 16); // r4 | ...
+ const __m128i in3 = LOAD_16(rgbx + 24); // r6 | ...
+ // aarrggbb as 16-bit.
+ const __m128i shuff0 =
+ _mm_set_epi8(-1, -1, -1, -1, 13, 12, 5, 4, 11, 10, 3, 2, 9, 8, 1, 0);
+ const __m128i shuff1 =
+ _mm_set_epi8(13, 12, 5, 4, -1, -1, -1, -1, 11, 10, 3, 2, 9, 8, 1, 0);
+ const __m128i A0 = _mm_shuffle_epi8(in0, shuff0);
+ const __m128i A1 = _mm_shuffle_epi8(in1, shuff1);
+ const __m128i A2 = _mm_shuffle_epi8(in2, shuff0);
+ const __m128i A3 = _mm_shuffle_epi8(in3, shuff1);
+ // R0R1G0G1
+ // B0B1****
+ // R2R3G2G3
+ // B2B3****
+ // (OR is used to free port 5 for the unpack)
+ const __m128i B0 = _mm_unpacklo_epi32(A0, A1);
+ const __m128i B1 = _mm_or_si128(A0, A1);
+ const __m128i B2 = _mm_unpacklo_epi32(A2, A3);
+ const __m128i B3 = _mm_or_si128(A2, A3);
+ // Gather the channels.
+ *r = _mm_unpacklo_epi64(B0, B2);
+ *g = _mm_unpackhi_epi64(B0, B2);
+ *b = _mm_unpackhi_epi64(B1, B3);
+}
+
+static void ConvertRGBA32ToUV_SSE41(const uint16_t* rgb,
+ uint8_t* u, uint8_t* v, int width) {
+ const int max_width = width & ~15;
+ const uint16_t* const last_rgb = rgb + 4 * max_width;
+ while (rgb < last_rgb) {
+ __m128i r, g, b, U0, V0, U1, V1;
+ RGBA32PackedToPlanar_16b_SSE41(rgb + 0, &r, &g, &b);
+ ConvertRGBToUV_SSE41(&r, &g, &b, &U0, &V0);
+ RGBA32PackedToPlanar_16b_SSE41(rgb + 32, &r, &g, &b);
+ ConvertRGBToUV_SSE41(&r, &g, &b, &U1, &V1);
+ STORE_16(_mm_packus_epi16(U0, U1), u);
+ STORE_16(_mm_packus_epi16(V0, V1), v);
+ u += 16;
+ v += 16;
+ rgb += 2 * 32;
+ }
+ if (max_width < width) { // left-over
+ WebPConvertRGBA32ToUV_C(rgb, u, v, width - max_width);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+extern void WebPInitConvertARGBToYUVSSE41(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUVSSE41(void) {
+ WebPConvertARGBToY = ConvertARGBToY_SSE41;
+ WebPConvertARGBToUV = ConvertARGBToUV_SSE41;
+
+ WebPConvertRGB24ToY = ConvertRGB24ToY_SSE41;
+ WebPConvertBGR24ToY = ConvertBGR24ToY_SSE41;
+
+ WebPConvertRGBA32ToUV = ConvertRGBA32ToUV_SSE41;
+}
+
+//------------------------------------------------------------------------------
+
+#else // !WEBP_USE_SSE41
+
+WEBP_DSP_INIT_STUB(WebPInitSamplersSSE41)
+WEBP_DSP_INIT_STUB(WebPInitConvertARGBToYUVSSE41)
+
+#endif // WEBP_USE_SSE41
diff --git a/thirdparty/libwebp/src/enc/alpha_enc.c b/thirdparty/libwebp/src/enc/alpha_enc.c
index 7e8d87f22e..dce9ca957d 100644
--- a/thirdparty/libwebp/src/enc/alpha_enc.c
+++ b/thirdparty/libwebp/src/enc/alpha_enc.c
@@ -361,7 +361,8 @@ static int EncodeAlpha(VP8Encoder* const enc,
//------------------------------------------------------------------------------
// Main calls
-static int CompressAlphaJob(VP8Encoder* const enc, void* dummy) {
+static int CompressAlphaJob(void* arg1, void* dummy) {
+ VP8Encoder* const enc = (VP8Encoder*)arg1;
const WebPConfig* config = enc->config_;
uint8_t* alpha_data = NULL;
size_t alpha_size = 0;
@@ -394,7 +395,7 @@ void VP8EncInitAlpha(VP8Encoder* const enc) {
WebPGetWorkerInterface()->Init(worker);
worker->data1 = enc;
worker->data2 = NULL;
- worker->hook = (WebPWorkerHook)CompressAlphaJob;
+ worker->hook = CompressAlphaJob;
}
}
diff --git a/thirdparty/libwebp/src/enc/analysis_enc.c b/thirdparty/libwebp/src/enc/analysis_enc.c
index 08f471f5f8..a47ff7d4e8 100644
--- a/thirdparty/libwebp/src/enc/analysis_enc.c
+++ b/thirdparty/libwebp/src/enc/analysis_enc.c
@@ -434,7 +434,9 @@ typedef struct {
} SegmentJob;
// main work call
-static int DoSegmentsJob(SegmentJob* const job, VP8EncIterator* const it) {
+static int DoSegmentsJob(void* arg1, void* arg2) {
+ SegmentJob* const job = (SegmentJob*)arg1;
+ VP8EncIterator* const it = (VP8EncIterator*)arg2;
int ok = 1;
if (!VP8IteratorIsDone(it)) {
uint8_t tmp[32 + WEBP_ALIGN_CST];
@@ -462,7 +464,7 @@ static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job,
WebPGetWorkerInterface()->Init(&job->worker);
job->worker.data1 = job;
job->worker.data2 = &job->it;
- job->worker.hook = (WebPWorkerHook)DoSegmentsJob;
+ job->worker.hook = DoSegmentsJob;
VP8IteratorInit(enc, &job->it);
VP8IteratorSetRow(&job->it, start_row);
VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w_);
diff --git a/thirdparty/libwebp/src/enc/frame_enc.c b/thirdparty/libwebp/src/enc/frame_enc.c
index 2b0dc66410..1aec376e44 100644
--- a/thirdparty/libwebp/src/enc/frame_enc.c
+++ b/thirdparty/libwebp/src/enc/frame_enc.c
@@ -198,7 +198,7 @@ static void SetSegmentProbas(VP8Encoder* const enc) {
for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
const VP8MBInfo* const mb = &enc->mb_info_[n];
- p[mb->segment_]++;
+ ++p[mb->segment_];
}
#if !defined(WEBP_DISABLE_STATS)
if (enc->pic_->stats != NULL) {
@@ -520,6 +520,14 @@ static void StoreSideInfo(const VP8EncIterator* const it) {
#endif
}
+static void ResetSideInfo(const VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ WebPPicture* const pic = enc->pic_;
+ if (pic->stats != NULL) {
+ memset(enc->block_count_, 0, sizeof(enc->block_count_));
+ }
+ ResetSSE(enc);
+}
#else // defined(WEBP_DISABLE_STATS)
static void ResetSSE(VP8Encoder* const enc) {
(void)enc;
@@ -528,10 +536,16 @@ static void StoreSideInfo(const VP8EncIterator* const it) {
VP8Encoder* const enc = it->enc_;
WebPPicture* const pic = enc->pic_;
if (pic->extra_info != NULL) {
- memset(pic->extra_info, 0,
- enc->mb_w_ * enc->mb_h_ * sizeof(*pic->extra_info));
+ if (it->x_ == 0 && it->y_ == 0) { // only do it once, at start
+ memset(pic->extra_info, 0,
+ enc->mb_w_ * enc->mb_h_ * sizeof(*pic->extra_info));
+ }
}
}
+
+static void ResetSideInfo(const VP8EncIterator* const it) {
+ (void)it;
+}
#endif // !defined(WEBP_DISABLE_STATS)
static double GetPSNR(uint64_t mse, uint64_t size) {
@@ -570,7 +584,7 @@ static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt,
VP8IteratorImport(&it, NULL);
if (VP8Decimate(&it, &info, rd_opt)) {
// Just record the number of skips and act like skip_proba is not used.
- enc->proba_.nb_skip_++;
+ ++enc->proba_.nb_skip_;
}
RecordResiduals(&it, &info);
size += info.R + info.H;
@@ -841,6 +855,9 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {
++num_pass_left;
enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation...
+ if (is_last_pass) {
+ ResetSideInfo(&it);
+ }
continue; // ...and start over
}
if (is_last_pass) {
@@ -871,4 +888,3 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
#endif // DISABLE_TOKEN_BUFFER
//------------------------------------------------------------------------------
-
diff --git a/thirdparty/libwebp/src/enc/histogram_enc.c b/thirdparty/libwebp/src/enc/histogram_enc.c
index 056a972dda..9fdbc627a1 100644
--- a/thirdparty/libwebp/src/enc/histogram_enc.c
+++ b/thirdparty/libwebp/src/enc/histogram_enc.c
@@ -200,14 +200,9 @@ static WEBP_INLINE double BitsEntropyRefine(const VP8LBitEntropy* entropy) {
}
}
-double VP8LBitsEntropy(const uint32_t* const array, int n,
- uint32_t* const trivial_symbol) {
+double VP8LBitsEntropy(const uint32_t* const array, int n) {
VP8LBitEntropy entropy;
VP8LBitsEntropyUnrefined(array, n, &entropy);
- if (trivial_symbol != NULL) {
- *trivial_symbol =
- (entropy.nonzeros == 1) ? entropy.nonzero_code : VP8L_NON_TRIVIAL_SYM;
- }
return BitsEntropyRefine(&entropy);
}
@@ -605,7 +600,7 @@ static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
}
// Implement a Lehmer random number generator with a multiplicative constant of
-// 48271 and a modulo constant of 2^31 − 1.
+// 48271 and a modulo constant of 2^31 - 1.
static uint32_t MyRand(uint32_t* const seed) {
*seed = (uint32_t)(((uint64_t)(*seed) * 48271u) % 2147483647u);
assert(*seed > 0);
@@ -1031,7 +1026,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
}
}
- // TODO(vikasa): Optimize HistogramRemap for low-effort compression mode also.
+ // TODO(vrabaud): Optimize HistogramRemap for low-effort compression mode.
// Find the optimal map from original histograms to the final ones.
HistogramRemap(orig_histo, image_histo, histogram_symbols);
diff --git a/thirdparty/libwebp/src/enc/histogram_enc.h b/thirdparty/libwebp/src/enc/histogram_enc.h
index 15b1fbda34..e8c4c83f6f 100644
--- a/thirdparty/libwebp/src/enc/histogram_enc.h
+++ b/thirdparty/libwebp/src/enc/histogram_enc.h
@@ -109,10 +109,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
uint16_t* const histogram_symbols);
// Returns the entropy for the symbols in the input array.
-// Also sets trivial_symbol to the code value, if the array has only one code
-// value. Otherwise, set it to VP8L_NON_TRIVIAL_SYM.
-double VP8LBitsEntropy(const uint32_t* const array, int n,
- uint32_t* const trivial_symbol);
+double VP8LBitsEntropy(const uint32_t* const array, int n);
// Estimate how many bits the combined entropy of literals and distance
// approximately maps to.
diff --git a/thirdparty/libwebp/src/enc/iterator_enc.c b/thirdparty/libwebp/src/enc/iterator_enc.c
index cfacfd2401..7c47d51272 100644
--- a/thirdparty/libwebp/src/enc/iterator_enc.c
+++ b/thirdparty/libwebp/src/enc/iterator_enc.c
@@ -26,6 +26,9 @@ static void InitLeft(VP8EncIterator* const it) {
memset(it->u_left_, 129, 8);
memset(it->v_left_, 129, 8);
it->left_nz_[8] = 0;
+ if (it->top_derr_ != NULL) {
+ memset(&it->left_derr_, 0, sizeof(it->left_derr_));
+ }
}
static void InitTop(VP8EncIterator* const it) {
@@ -33,6 +36,9 @@ static void InitTop(VP8EncIterator* const it) {
const size_t top_size = enc->mb_w_ * 16;
memset(enc->y_top_, 127, 2 * top_size);
memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_));
+ if (enc->top_derr_ != NULL) {
+ memset(enc->top_derr_, 0, enc->mb_w_ * sizeof(*enc->top_derr_));
+ }
}
void VP8IteratorSetRow(VP8EncIterator* const it, int y) {
@@ -76,6 +82,7 @@ void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) {
it->y_left_ = (uint8_t*)WEBP_ALIGN(it->yuv_left_mem_ + 1);
it->u_left_ = it->y_left_ + 16 + 16;
it->v_left_ = it->u_left_ + 16;
+ it->top_derr_ = enc->top_derr_;
VP8IteratorReset(it);
}
@@ -450,4 +457,3 @@ int VP8IteratorRotateI4(VP8EncIterator* const it,
}
//------------------------------------------------------------------------------
-
diff --git a/thirdparty/libwebp/src/enc/near_lossless_enc.c b/thirdparty/libwebp/src/enc/near_lossless_enc.c
index cadd14c664..5517a7e271 100644
--- a/thirdparty/libwebp/src/enc/near_lossless_enc.c
+++ b/thirdparty/libwebp/src/enc/near_lossless_enc.c
@@ -146,6 +146,6 @@ int VP8ApplyNearLossless(const WebPPicture* const picture, int quality,
// Define a stub to suppress compiler warnings.
extern void VP8LNearLosslessStub(void);
-WEBP_TSAN_IGNORE_FUNCTION void VP8LNearLosslessStub(void) {}
+void VP8LNearLosslessStub(void) {}
#endif // (WEBP_NEAR_LOSSLESS == 1)
diff --git a/thirdparty/libwebp/src/enc/picture_csp_enc.c b/thirdparty/libwebp/src/enc/picture_csp_enc.c
index d531dd0282..02d9df76d5 100644
--- a/thirdparty/libwebp/src/enc/picture_csp_enc.c
+++ b/thirdparty/libwebp/src/enc/picture_csp_enc.c
@@ -28,11 +28,11 @@
// If defined, use table to compute x / alpha.
#define USE_INVERSE_ALPHA_TABLE
-static const union {
- uint32_t argb;
- uint8_t bytes[4];
-} test_endian = { 0xff000000u };
-#define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff)
+#ifdef WORDS_BIGENDIAN
+#define ALPHA_OFFSET 0 // uint32_t 0xff000000 is 0xff,00,00,00 in memory
+#else
+#define ALPHA_OFFSET 3 // uint32_t 0xff000000 is 0x00,00,00,ff in memory
+#endif
//------------------------------------------------------------------------------
// Detection of non-trivial transparency
@@ -61,7 +61,7 @@ int WebPPictureHasTransparency(const WebPPicture* picture) {
return CheckNonOpaque(picture->a, picture->width, picture->height,
1, picture->a_stride);
} else {
- const int alpha_offset = ALPHA_IS_LAST ? 3 : 0;
+ const int alpha_offset = ALPHA_OFFSET;
return CheckNonOpaque((const uint8_t*)picture->argb + alpha_offset,
picture->width, picture->height,
4, picture->argb_stride * sizeof(*picture->argb));
@@ -126,7 +126,7 @@ static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
#else
-static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTables(void) {}
+static void InitGammaTables(void) {}
static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; }
static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
return (int)(base_value << shift);
@@ -170,29 +170,33 @@ typedef uint16_t fixed_y_t; // unsigned type with extra SFIX precision for W
#if defined(USE_GAMMA_COMPRESSION)
-// float variant of gamma-correction
// We use tables of different size and precision for the Rec709 / BT2020
// transfer function.
#define kGammaF (1./0.45)
-static float kGammaToLinearTabF[MAX_Y_T + 1]; // size scales with Y_FIX
-static float kLinearToGammaTabF[kGammaTabSize + 2];
-static volatile int kGammaTablesFOk = 0;
-
-static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) {
- if (!kGammaTablesFOk) {
+static uint32_t kLinearToGammaTabS[kGammaTabSize + 2];
+#define GAMMA_TO_LINEAR_BITS 14
+static uint32_t kGammaToLinearTabS[MAX_Y_T + 1]; // size scales with Y_FIX
+static volatile int kGammaTablesSOk = 0;
+
+static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesS(void) {
+ assert(2 * GAMMA_TO_LINEAR_BITS < 32); // we use uint32_t intermediate values
+ if (!kGammaTablesSOk) {
int v;
const double norm = 1. / MAX_Y_T;
const double scale = 1. / kGammaTabSize;
const double a = 0.09929682680944;
const double thresh = 0.018053968510807;
+ const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;
for (v = 0; v <= MAX_Y_T; ++v) {
const double g = norm * v;
+ double value;
if (g <= thresh * 4.5) {
- kGammaToLinearTabF[v] = (float)(g / 4.5);
+ value = g / 4.5;
} else {
const double a_rec = 1. / (1. + a);
- kGammaToLinearTabF[v] = (float)pow(a_rec * (g + a), kGammaF);
+ value = pow(a_rec * (g + a), kGammaF);
}
+ kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);
}
for (v = 0; v <= kGammaTabSize; ++v) {
const double g = scale * v;
@@ -202,37 +206,44 @@ static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) {
} else {
value = (1. + a) * pow(g, 1. / kGammaF) - a;
}
- kLinearToGammaTabF[v] = (float)(MAX_Y_T * value);
+ // we already incorporate the 1/2 rounding constant here
+ kLinearToGammaTabS[v] =
+ (uint32_t)(MAX_Y_T * value) + (1 << GAMMA_TO_LINEAR_BITS >> 1);
}
// to prevent small rounding errors to cause read-overflow:
- kLinearToGammaTabF[kGammaTabSize + 1] = kLinearToGammaTabF[kGammaTabSize];
- kGammaTablesFOk = 1;
+ kLinearToGammaTabS[kGammaTabSize + 1] = kLinearToGammaTabS[kGammaTabSize];
+ kGammaTablesSOk = 1;
}
}
-static WEBP_INLINE float GammaToLinearF(int v) {
- return kGammaToLinearTabF[v];
+// return value has a fixed-point precision of GAMMA_TO_LINEAR_BITS
+static WEBP_INLINE uint32_t GammaToLinearS(int v) {
+ return kGammaToLinearTabS[v];
}
-static WEBP_INLINE int LinearToGammaF(float value) {
- const float v = value * kGammaTabSize;
- const int tab_pos = (int)v;
- const float x = v - (float)tab_pos; // fractional part
- const float v0 = kLinearToGammaTabF[tab_pos + 0];
- const float v1 = kLinearToGammaTabF[tab_pos + 1];
- const float y = v1 * x + v0 * (1.f - x); // interpolate
- return (int)(y + .5);
+static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) {
+ // 'value' is in GAMMA_TO_LINEAR_BITS fractional precision
+ const uint32_t v = value * kGammaTabSize;
+ const uint32_t tab_pos = v >> GAMMA_TO_LINEAR_BITS;
+ // fractional part, in GAMMA_TO_LINEAR_BITS fixed-point precision
+ const uint32_t x = v - (tab_pos << GAMMA_TO_LINEAR_BITS); // fractional part
+ // v0 / v1 are in GAMMA_TO_LINEAR_BITS fixed-point precision (range [0..1])
+ const uint32_t v0 = kLinearToGammaTabS[tab_pos + 0];
+ const uint32_t v1 = kLinearToGammaTabS[tab_pos + 1];
+ // Final interpolation. Note that rounding is already included.
+ const uint32_t v2 = (v1 - v0) * x; // note: v1 >= v0.
+ const uint32_t result = v0 + (v2 >> GAMMA_TO_LINEAR_BITS);
+ return result;
}
#else
-static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) {}
-static WEBP_INLINE float GammaToLinearF(int v) {
- const float norm = 1.f / MAX_Y_T;
- return norm * v;
+static void InitGammaTablesS(void) {}
+static WEBP_INLINE uint32_t GammaToLinearS(int v) {
+ return (v << GAMMA_TO_LINEAR_BITS) / MAX_Y_T;
}
-static WEBP_INLINE int LinearToGammaF(float value) {
- return (int)(MAX_Y_T * value + .5);
+static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) {
+ return (MAX_Y_T * value) >> GAMMA_TO_LINEAR_BITS;
}
#endif // USE_GAMMA_COMPRESSION
@@ -254,26 +265,22 @@ static int RGBToGray(int r, int g, int b) {
return (luma >> YUV_FIX);
}
-static float RGBToGrayF(float r, float g, float b) {
- return (float)(0.2126 * r + 0.7152 * g + 0.0722 * b);
-}
-
-static int ScaleDown(int a, int b, int c, int d) {
- const float A = GammaToLinearF(a);
- const float B = GammaToLinearF(b);
- const float C = GammaToLinearF(c);
- const float D = GammaToLinearF(d);
- return LinearToGammaF(0.25f * (A + B + C + D));
+static uint32_t ScaleDown(int a, int b, int c, int d) {
+ const uint32_t A = GammaToLinearS(a);
+ const uint32_t B = GammaToLinearS(b);
+ const uint32_t C = GammaToLinearS(c);
+ const uint32_t D = GammaToLinearS(d);
+ return LinearToGammaS((A + B + C + D + 2) >> 2);
}
static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w) {
int i;
for (i = 0; i < w; ++i) {
- const float R = GammaToLinearF(src[0 * w + i]);
- const float G = GammaToLinearF(src[1 * w + i]);
- const float B = GammaToLinearF(src[2 * w + i]);
- const float Y = RGBToGrayF(R, G, B);
- dst[i] = (fixed_y_t)LinearToGammaF(Y);
+ const uint32_t R = GammaToLinearS(src[0 * w + i]);
+ const uint32_t G = GammaToLinearS(src[1 * w + i]);
+ const uint32_t B = GammaToLinearS(src[2 * w + i]);
+ const uint32_t Y = RGBToGray(R, G, B);
+ dst[i] = (fixed_y_t)LinearToGammaS(Y);
}
}
@@ -863,7 +870,7 @@ static int ImportYUVAFromRGBA(const uint8_t* r_ptr,
}
if (use_iterative_conversion) {
- InitGammaTablesF();
+ InitGammaTablesS();
if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) {
return 0;
}
@@ -990,10 +997,10 @@ static int PictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace,
return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
} else {
const uint8_t* const argb = (const uint8_t*)picture->argb;
- const uint8_t* const r = ALPHA_IS_LAST ? argb + 2 : argb + 1;
- const uint8_t* const g = ALPHA_IS_LAST ? argb + 1 : argb + 2;
- const uint8_t* const b = ALPHA_IS_LAST ? argb + 0 : argb + 3;
- const uint8_t* const a = ALPHA_IS_LAST ? argb + 3 : argb + 0;
+ const uint8_t* const a = argb + (0 ^ ALPHA_OFFSET);
+ const uint8_t* const r = argb + (1 ^ ALPHA_OFFSET);
+ const uint8_t* const g = argb + (2 ^ ALPHA_OFFSET);
+ const uint8_t* const b = argb + (3 ^ ALPHA_OFFSET);
picture->colorspace = WEBP_YUV420;
return ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride,
@@ -1044,7 +1051,8 @@ int WebPPictureYUVAToARGB(WebPPicture* picture) {
const int argb_stride = 4 * picture->argb_stride;
uint8_t* dst = (uint8_t*)picture->argb;
const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y;
- WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST);
+ WebPUpsampleLinePairFunc upsample =
+ WebPGetLinePairConverter(ALPHA_OFFSET > 0);
// First row, with replicated top samples.
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
@@ -1087,6 +1095,7 @@ static int Import(WebPPicture* const picture,
const uint8_t* rgb, int rgb_stride,
int step, int swap_rb, int import_alpha) {
int y;
+ // swap_rb -> b,g,r,a , !swap_rb -> r,g,b,a
const uint8_t* r_ptr = rgb + (swap_rb ? 2 : 0);
const uint8_t* g_ptr = rgb + 1;
const uint8_t* b_ptr = rgb + (swap_rb ? 0 : 2);
@@ -1104,19 +1113,32 @@ static int Import(WebPPicture* const picture,
WebPInitAlphaProcessing();
if (import_alpha) {
+ // dst[] byte order is {a,r,g,b} for big-endian, {b,g,r,a} for little endian
uint32_t* dst = picture->argb;
- const int do_copy =
- (!swap_rb && !ALPHA_IS_LAST) || (swap_rb && ALPHA_IS_LAST);
+ const int do_copy = (ALPHA_OFFSET == 3) && swap_rb;
assert(step == 4);
- for (y = 0; y < height; ++y) {
- if (do_copy) {
+ if (do_copy) {
+ for (y = 0; y < height; ++y) {
memcpy(dst, rgb, width * 4);
- } else {
+ rgb += rgb_stride;
+ dst += picture->argb_stride;
+ }
+ } else {
+ for (y = 0; y < height; ++y) {
+#ifdef WORDS_BIGENDIAN
+ // BGRA or RGBA input order.
+ const uint8_t* a_ptr = rgb + 3;
+ WebPPackARGB(a_ptr, r_ptr, g_ptr, b_ptr, width, dst);
+ r_ptr += rgb_stride;
+ g_ptr += rgb_stride;
+ b_ptr += rgb_stride;
+#else
// RGBA input order. Need to swap R and B.
VP8LConvertBGRAToRGBA((const uint32_t*)rgb, width, (uint8_t*)dst);
+#endif
+ rgb += rgb_stride;
+ dst += picture->argb_stride;
}
- rgb += rgb_stride;
- dst += picture->argb_stride;
}
} else {
uint32_t* dst = picture->argb;
diff --git a/thirdparty/libwebp/src/enc/picture_psnr_enc.c b/thirdparty/libwebp/src/enc/picture_psnr_enc.c
index 362a7c79be..1a2f0bef3e 100644
--- a/thirdparty/libwebp/src/enc/picture_psnr_enc.c
+++ b/thirdparty/libwebp/src/enc/picture_psnr_enc.c
@@ -18,6 +18,7 @@
#include <math.h>
#include <stdlib.h>
+#include "src/dsp/dsp.h"
#include "src/enc/vp8i_enc.h"
#include "src/utils/utils.h"
@@ -169,6 +170,12 @@ int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,
return 1;
}
+#ifdef WORDS_BIGENDIAN
+#define BLUE_OFFSET 3 // uint32_t 0x000000ff is 0x00,00,00,ff in memory
+#else
+#define BLUE_OFFSET 0 // uint32_t 0x000000ff is 0xff,00,00,00 in memory
+#endif
+
int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
int type, float results[5]) {
int w, h, c;
@@ -195,8 +202,10 @@ int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
float distortion;
const size_t stride0 = 4 * (size_t)p0.argb_stride;
const size_t stride1 = 4 * (size_t)p1.argb_stride;
- if (!WebPPlaneDistortion((const uint8_t*)p0.argb + c, stride0,
- (const uint8_t*)p1.argb + c, stride1,
+ // results are reported as BGRA
+ const int offset = c ^ BLUE_OFFSET;
+ if (!WebPPlaneDistortion((const uint8_t*)p0.argb + offset, stride0,
+ (const uint8_t*)p1.argb + offset, stride1,
w, h, 4, type, &distortion, results + c)) {
goto Error;
}
@@ -214,6 +223,8 @@ int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
return ok;
}
+#undef BLUE_OFFSET
+
#else // defined(WEBP_DISABLE_STATS)
int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,
const uint8_t* ref, size_t ref_stride,
diff --git a/thirdparty/libwebp/src/enc/quant_enc.c b/thirdparty/libwebp/src/enc/quant_enc.c
index 3b1a3129b5..35bfaf21ef 100644
--- a/thirdparty/libwebp/src/enc/quant_enc.c
+++ b/thirdparty/libwebp/src/enc/quant_enc.c
@@ -826,6 +826,85 @@ static int ReconstructIntra4(VP8EncIterator* const it,
return nz;
}
+//------------------------------------------------------------------------------
+// DC-error diffusion
+
+// Diffusion weights. We under-correct a bit (15/16th of the error is actually
+// diffused) to avoid 'rainbow' chessboard pattern of blocks at q~=0.
+#define C1 7 // fraction of error sent to the 4x4 block below
+#define C2 8 // fraction of error sent to the 4x4 block on the right
+#define DSHIFT 4
+#define DSCALE 1 // storage descaling, needed to make the error fit int8_t
+
+// Quantize as usual, but also compute and return the quantization error.
+// Error is already divided by DSHIFT.
+static int QuantizeSingle(int16_t* const v, const VP8Matrix* const mtx) {
+ int V = *v;
+ const int sign = (V < 0);
+ if (sign) V = -V;
+ if (V > (int)mtx->zthresh_[0]) {
+ const int qV = QUANTDIV(V, mtx->iq_[0], mtx->bias_[0]) * mtx->q_[0];
+ const int err = (V - qV);
+ *v = sign ? -qV : qV;
+ return (sign ? -err : err) >> DSCALE;
+ }
+ *v = 0;
+ return (sign ? -V : V) >> DSCALE;
+}
+
+static void CorrectDCValues(const VP8EncIterator* const it,
+ const VP8Matrix* const mtx,
+ int16_t tmp[][16], VP8ModeScore* const rd) {
+ // | top[0] | top[1]
+ // --------+--------+---------
+ // left[0] | tmp[0] tmp[1] <-> err0 err1
+ // left[1] | tmp[2] tmp[3] err2 err3
+ //
+ // Final errors {err1,err2,err3} are preserved and later restored
+ // as top[]/left[] on the next block.
+ int ch;
+ for (ch = 0; ch <= 1; ++ch) {
+ const int8_t* const top = it->top_derr_[it->x_][ch];
+ const int8_t* const left = it->left_derr_[ch];
+ int16_t (* const c)[16] = &tmp[ch * 4];
+ int err0, err1, err2, err3;
+ c[0][0] += (C1 * top[0] + C2 * left[0]) >> (DSHIFT - DSCALE);
+ err0 = QuantizeSingle(&c[0][0], mtx);
+ c[1][0] += (C1 * top[1] + C2 * err0) >> (DSHIFT - DSCALE);
+ err1 = QuantizeSingle(&c[1][0], mtx);
+ c[2][0] += (C1 * err0 + C2 * left[1]) >> (DSHIFT - DSCALE);
+ err2 = QuantizeSingle(&c[2][0], mtx);
+ c[3][0] += (C1 * err1 + C2 * err2) >> (DSHIFT - DSCALE);
+ err3 = QuantizeSingle(&c[3][0], mtx);
+ // error 'err' is bounded by mtx->q_[0] which is 132 at max. Hence
+ // err >> DSCALE will fit in an int8_t type if DSCALE>=1.
+ assert(abs(err1) <= 127 && abs(err2) <= 127 && abs(err3) <= 127);
+ rd->derr[ch][0] = (int8_t)err1;
+ rd->derr[ch][1] = (int8_t)err2;
+ rd->derr[ch][2] = (int8_t)err3;
+ }
+}
+
+static void StoreDiffusionErrors(VP8EncIterator* const it,
+ const VP8ModeScore* const rd) {
+ int ch;
+ for (ch = 0; ch <= 1; ++ch) {
+ int8_t* const top = it->top_derr_[it->x_][ch];
+ int8_t* const left = it->left_derr_[ch];
+ left[0] = rd->derr[ch][0]; // restore err1
+ left[1] = 3 * rd->derr[ch][2] >> 2; // ... 3/4th of err3
+ top[0] = rd->derr[ch][1]; // ... err2
+ top[1] = rd->derr[ch][2] - left[1]; // ... 1/4th of err3.
+ }
+}
+
+#undef C1
+#undef C2
+#undef DSHIFT
+#undef DSCALE
+
+//------------------------------------------------------------------------------
+
static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
uint8_t* const yuv_out, int mode) {
const VP8Encoder* const enc = it->enc_;
@@ -839,6 +918,8 @@ static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
for (n = 0; n < 8; n += 2) {
VP8FTransform2(src + VP8ScanUV[n], ref + VP8ScanUV[n], tmp[n]);
}
+ if (it->top_derr_ != NULL) CorrectDCValues(it, &dqm->uv_, tmp, rd);
+
if (DO_TRELLIS_UV && it->do_trellis_) {
int ch, x, y;
for (ch = 0, n = 0; ch <= 2; ch += 2) {
@@ -1101,6 +1182,9 @@ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
CopyScore(&rd_best, &rd_uv);
rd->mode_uv = mode;
memcpy(rd->uv_levels, rd_uv.uv_levels, sizeof(rd->uv_levels));
+ if (it->top_derr_ != NULL) {
+ memcpy(rd->derr, rd_uv.derr, sizeof(rd_uv.derr));
+ }
SwapPtr(&dst, &tmp_dst);
}
}
@@ -1109,6 +1193,9 @@ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
if (dst != dst0) { // copy 16x8 block if needed
VP8Copy16x8(dst, dst0);
}
+ if (it->top_derr_ != NULL) { // store diffusion errors for next block
+ StoreDiffusionErrors(it, rd);
+ }
}
//------------------------------------------------------------------------------
diff --git a/thirdparty/libwebp/src/enc/vp8i_enc.h b/thirdparty/libwebp/src/enc/vp8i_enc.h
index 3463491e9d..624e8f8e66 100644
--- a/thirdparty/libwebp/src/enc/vp8i_enc.h
+++ b/thirdparty/libwebp/src/enc/vp8i_enc.h
@@ -30,9 +30,9 @@ extern "C" {
// Various defines and enums
// version numbers
-#define ENC_MAJ_VERSION 0
-#define ENC_MIN_VERSION 6
-#define ENC_REV_VERSION 1
+#define ENC_MAJ_VERSION 1
+#define ENC_MIN_VERSION 0
+#define ENC_REV_VERSION 0
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
@@ -120,6 +120,9 @@ static WEBP_INLINE int QUANTDIV(uint32_t n, uint32_t iQ, uint32_t B) {
// Uncomment the following to remove token-buffer code:
// #define DISABLE_TOKEN_BUFFER
+// quality below which error-diffusion is enabled
+#define ERROR_DIFFUSION_QUALITY 98
+
//------------------------------------------------------------------------------
// Headers
@@ -201,6 +204,8 @@ typedef struct {
score_t i4_penalty_; // penalty for using Intra4
} VP8SegmentInfo;
+typedef int8_t DError[2 /* u/v */][2 /* top or left */];
+
// Handy transient struct to accumulate score and info during RD-optimization
// and mode evaluation.
typedef struct {
@@ -213,6 +218,7 @@ typedef struct {
uint8_t modes_i4[16]; // mode numbers for intra4 predictions
int mode_uv; // mode number of chroma prediction
uint32_t nz; // non-zero blocks
+ int8_t derr[2][3]; // DC diffusion errors for U/V for blocks #1/2/3
} VP8ModeScore;
// Iterator structure to iterate through macroblocks, pointing to the
@@ -242,6 +248,9 @@ typedef struct {
int count_down0_; // starting counter value (for progress)
int percent0_; // saved initial progress percent
+ DError left_derr_; // left error diffusion (u/v)
+ DError *top_derr_; // top diffusion error - NULL if disabled
+
uint8_t* y_left_; // left luma samples (addressable from index -1 to 15).
uint8_t* u_left_; // left u samples (addressable from index -1 to 7)
uint8_t* v_left_; // left v samples (addressable from index -1 to 7)
@@ -401,6 +410,7 @@ struct VP8Encoder {
uint8_t* uv_top_; // top u/v samples.
// U and V are packed into 16 bytes (8 U + 8 V)
LFStats* lf_stats_; // autofilter stats (if NULL, autofilter is off)
+ DError* top_derr_; // diffusion error (NULL if disabled)
};
//------------------------------------------------------------------------------
diff --git a/thirdparty/libwebp/src/enc/vp8l_enc.c b/thirdparty/libwebp/src/enc/vp8l_enc.c
index 312e521906..a89184eb08 100644
--- a/thirdparty/libwebp/src/enc/vp8l_enc.c
+++ b/thirdparty/libwebp/src/enc/vp8l_enc.c
@@ -26,8 +26,6 @@
#include "src/utils/utils.h"
#include "src/webp/format_constants.h"
-#include "src/enc/delta_palettization_enc.h"
-
// Maximum number of histogram images (sub-blocks).
#define MAX_HUFF_IMAGE_SIZE 2600
@@ -259,7 +257,7 @@ static int AnalyzeEntropy(const uint32_t* argb,
++histo[kHistoAlphaPred * 256];
for (j = 0; j < kHistoTotal; ++j) {
- entropy_comp[j] = VP8LBitsEntropy(&histo[j * 256], 256, NULL);
+ entropy_comp[j] = VP8LBitsEntropy(&histo[j * 256], 256);
}
entropy[kDirect] = entropy_comp[kHistoAlpha] +
entropy_comp[kHistoRed] +
@@ -384,8 +382,7 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
AnalyzeAndCreatePalette(pic, low_effort,
enc->palette_, &enc->palette_size_);
- // TODO(jyrki): replace the decision to be based on an actual estimate
- // of entropy, or even spatial variance of entropy.
+ // Empirical bit sizes.
enc->histo_bits_ = GetHistoBits(method, use_palette,
pic->width, pic->height);
enc->transform_bits_ = GetTransformBits(method, enc->histo_bits_);
@@ -756,7 +753,6 @@ static WebPEncodingError StoreImageToBitMask(
// Don't write the distance with the extra bits code since
// the distance can be up to 18 bits of extra bits, and the prefix
// 15 bits, totaling to 33, and our PutBits only supports up to 32 bits.
- // TODO(jyrki): optimize this further.
VP8LPrefixEncode(distance, &code, &n_bits, &bits);
WriteHuffmanCode(bw, codes + 4, code);
VP8LPutBits(bw, bits, n_bits);
@@ -1464,49 +1460,6 @@ static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, int low_effort,
20 /* quality */, low_effort);
}
-#ifdef WEBP_EXPERIMENTAL_FEATURES
-
-static WebPEncodingError EncodeDeltaPalettePredictorImage(
- VP8LBitWriter* const bw, VP8LEncoder* const enc, int quality,
- int low_effort) {
- const WebPPicture* const pic = enc->pic_;
- const int width = pic->width;
- const int height = pic->height;
-
- const int pred_bits = 5;
- const int transform_width = VP8LSubSampleSize(width, pred_bits);
- const int transform_height = VP8LSubSampleSize(height, pred_bits);
- const int pred = 7; // default is Predictor7 (Top/Left Average)
- const int tiles_per_row = VP8LSubSampleSize(width, pred_bits);
- const int tiles_per_col = VP8LSubSampleSize(height, pred_bits);
- uint32_t* predictors;
- int tile_x, tile_y;
- WebPEncodingError err = VP8_ENC_OK;
-
- predictors = (uint32_t*)WebPSafeMalloc(tiles_per_col * tiles_per_row,
- sizeof(*predictors));
- if (predictors == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
-
- for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) {
- for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) {
- predictors[tile_y * tiles_per_row + tile_x] = 0xff000000u | (pred << 8);
- }
- }
-
- VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
- VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2);
- VP8LPutBits(bw, pred_bits - 2, 3);
- err = EncodeImageNoHuffman(
- bw, predictors, &enc->hash_chain_,
- (VP8LBackwardRefs*)&enc->refs_[0], // cast const away
- (VP8LBackwardRefs*)&enc->refs_[1],
- transform_width, transform_height, quality, low_effort);
- WebPSafeFree(predictors);
- return err;
-}
-
-#endif // WEBP_EXPERIMENTAL_FEATURES
-
// -----------------------------------------------------------------------------
// VP8LEncoder
@@ -1568,7 +1521,7 @@ static int EncodeStreamHook(void* input, void* data2) {
WebPEncodingError err = VP8_ENC_OK;
const int quality = (int)config->quality;
const int low_effort = (config->method == 0);
-#if (WEBP_NEAR_LOSSLESS == 1) || defined(WEBP_EXPERIMENTAL_FEATURES)
+#if (WEBP_NEAR_LOSSLESS == 1)
const int width = picture->width;
#endif
const int height = picture->height;
@@ -1627,29 +1580,6 @@ static int EncodeStreamHook(void* input, void* data2) {
enc->argb_content_ = kEncoderNone;
#endif
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- if (config->use_delta_palette) {
- enc->use_predict_ = 1;
- enc->use_cross_color_ = 0;
- enc->use_subtract_green_ = 0;
- enc->use_palette_ = 1;
- if (enc->argb_content_ != kEncoderNearLossless &&
- enc->argb_content_ != kEncoderPalette) {
- err = MakeInputImageCopy(enc);
- if (err != VP8_ENC_OK) goto Error;
- }
- err = WebPSearchOptimalDeltaPalette(enc);
- if (err != VP8_ENC_OK) goto Error;
- if (enc->use_palette_) {
- err = AllocateTransformBuffer(enc, width, height);
- if (err != VP8_ENC_OK) goto Error;
- err = EncodeDeltaPalettePredictorImage(bw, enc, quality, low_effort);
- if (err != VP8_ENC_OK) goto Error;
- use_delta_palette = 1;
- }
- }
-#endif // WEBP_EXPERIMENTAL_FEATURES
-
// Encode palette
if (enc->use_palette_) {
err = EncodePalette(bw, low_effort, enc);
@@ -1822,7 +1752,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
worker_interface->Init(worker);
worker->data1 = param;
worker->data2 = NULL;
- worker->hook = (WebPWorkerHook)EncodeStreamHook;
+ worker->hook = EncodeStreamHook;
}
}
@@ -1944,7 +1874,6 @@ int VP8LEncodeImage(const WebPConfig* const config,
err = VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/);
if (err != VP8_ENC_OK) goto Error;
- // TODO(skal): have a fine-grained progress report in VP8LEncodeStream().
if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort;
// Finish the RIFF chunk.
diff --git a/thirdparty/libwebp/src/enc/webp_enc.c b/thirdparty/libwebp/src/enc/webp_enc.c
index 283cda8e7b..9f4b10c26c 100644
--- a/thirdparty/libwebp/src/enc/webp_enc.c
+++ b/thirdparty/libwebp/src/enc/webp_enc.c
@@ -159,12 +159,16 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
+ WEBP_ALIGN_CST; // align all
const size_t lf_stats_size =
config->autofilter ? sizeof(*enc->lf_stats_) + WEBP_ALIGN_CST : 0;
+ const size_t top_derr_size =
+ (config->quality <= ERROR_DIFFUSION_QUALITY || config->pass > 1) ?
+ mb_w * sizeof(*enc->top_derr_) : 0;
uint8_t* mem;
const uint64_t size = (uint64_t)sizeof(*enc) // main struct
+ WEBP_ALIGN_CST // cache alignment
+ info_size // modes info
+ preds_size // prediction modes
+ samples_size // top/left samples
+ + top_derr_size // top diffusion error
+ nz_size // coeff context bits
+ lf_stats_size; // autofilter stats
@@ -175,11 +179,12 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
" info: %ld\n"
" preds: %ld\n"
" top samples: %ld\n"
+ " top diffusion: %ld\n"
" non-zero: %ld\n"
" lf-stats: %ld\n"
" total: %ld\n",
sizeof(*enc) + WEBP_ALIGN_CST, info_size,
- preds_size, samples_size, nz_size, lf_stats_size, size);
+ preds_size, samples_size, top_derr_size, nz_size, lf_stats_size, size);
printf("Transient object sizes:\n"
" VP8EncIterator: %ld\n"
" VP8ModeScore: %ld\n"
@@ -219,6 +224,8 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
enc->y_top_ = mem;
enc->uv_top_ = enc->y_top_ + top_stride;
mem += 2 * top_stride;
+ enc->top_derr_ = top_derr_size ? (DError*)mem : NULL;
+ mem += top_derr_size;
assert(mem <= (uint8_t*)enc + size);
enc->config_ = config;
diff --git a/thirdparty/libwebp/src/mux/muxi.h b/thirdparty/libwebp/src/mux/muxi.h
index b73e3fbd7a..6b57eea30f 100644
--- a/thirdparty/libwebp/src/mux/muxi.h
+++ b/thirdparty/libwebp/src/mux/muxi.h
@@ -26,9 +26,9 @@ extern "C" {
//------------------------------------------------------------------------------
// Defines and constants.
-#define MUX_MAJ_VERSION 0
-#define MUX_MIN_VERSION 4
-#define MUX_REV_VERSION 1
+#define MUX_MAJ_VERSION 1
+#define MUX_MIN_VERSION 0
+#define MUX_REV_VERSION 0
// Chunk object.
typedef struct WebPChunk WebPChunk;
diff --git a/thirdparty/libwebp/src/utils/endian_inl_utils.h b/thirdparty/libwebp/src/utils/endian_inl_utils.h
index 4b2f91dfb8..3630a293bf 100644
--- a/thirdparty/libwebp/src/utils/endian_inl_utils.h
+++ b/thirdparty/libwebp/src/utils/endian_inl_utils.h
@@ -19,13 +19,6 @@
#include "src/dsp/dsp.h"
#include "src/webp/types.h"
-// some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__)
-#if !defined(WORDS_BIGENDIAN) && \
- (defined(__BIG_ENDIAN__) || defined(_M_PPC) || \
- (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
-#define WORDS_BIGENDIAN
-#endif
-
#if defined(WORDS_BIGENDIAN)
#define HToLE32 BSwap32
#define HToLE16 BSwap16
diff --git a/thirdparty/lws/LICENSE.txt b/thirdparty/libwebsockets/LICENSE
index 34a42d5687..6c7cd90cdc 100644
--- a/thirdparty/lws/LICENSE.txt
+++ b/thirdparty/libwebsockets/LICENSE
@@ -33,19 +33,21 @@ to get original sources with the liberal terms.
Original liberal license retained
- - lib/sha-1.c - 3-clause BSD license retained, link to original
+ - lib/misc/sha-1.c - 3-clause BSD license retained, link to original
- win32port/zlib - ZLIB license (see zlib.h)
+ - lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)
Relicensed to libwebsocket license
- - lib/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
- - lib/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE,
- link to original Public Domain version
+ - lib/misc/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
+ - lib/misc/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE,
+ link to original Public Domain version
Public Domain (CC-zero) to simplify reuse
- - test-server/*.c
- - test-server/*.h
+ - test-apps/*.c
+ - test-apps/*.h
+ - minimal-examples/*
- lwsws/*
------ end of exceptions
@@ -107,7 +109,7 @@ modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
-
+
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
@@ -163,7 +165,7 @@ modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
-
+
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@@ -210,7 +212,7 @@ Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
-
+
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
@@ -268,7 +270,7 @@ instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
-
+
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
@@ -319,7 +321,7 @@ Library will still fall under Section 6.)
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
-
+
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
@@ -381,7 +383,7 @@ restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
-
+
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
@@ -422,7 +424,7 @@ subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
-
+
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
@@ -474,7 +476,7 @@ conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
-
+
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
@@ -508,7 +510,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
-
+
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
@@ -552,4 +554,3 @@ necessary. Here is a sample; alter the names:
Ty Coon, President of Vice
That's all there is to it!
-
diff --git a/thirdparty/lws/alloc.c b/thirdparty/libwebsockets/core/alloc.c
index 898db12464..f169fc3767 100644
--- a/thirdparty/lws/alloc.c
+++ b/thirdparty/libwebsockets/core/alloc.c
@@ -1,4 +1,4 @@
-#include "private-libwebsockets.h"
+#include "core/private.h"
#if defined(LWS_PLAT_OPTEE)
@@ -51,10 +51,12 @@ void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason))
static void *_realloc(void *ptr, size_t size, const char *reason)
{
if (size) {
-#if defined(LWS_PLAT_ESP32)
- lwsl_notice("%s: size %lu: %s\n", __func__, (unsigned long)size, reason);
+#if defined(LWS_WITH_ESP32)
+ lwsl_notice("%s: size %lu: %s (free heap %d)\n", __func__,
+ (unsigned long)size, reason, (unsigned int)esp_get_free_heap_size() - (int)size);
#else
- lwsl_debug("%s: size %lu: %s\n", __func__, (unsigned long)size, reason);
+ lwsl_debug("%s: size %lu: %s\n", __func__,
+ (unsigned long)size, reason);
#endif
#if defined(LWS_PLAT_OPTEE)
return (void *)TEE_Realloc(ptr, size);
diff --git a/thirdparty/lws/context.c b/thirdparty/libwebsockets/core/context.c
index 9f221f50f1..db9151b95f 100644
--- a/thirdparty/lws/context.c
+++ b/thirdparty/libwebsockets/core/context.c
@@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -19,12 +19,41 @@
* MA 02110-1301 USA
*/
-#include "private-libwebsockets.h"
+#include "core/private.h"
#ifndef LWS_BUILD_HASH
#define LWS_BUILD_HASH "unknown-build-hash"
#endif
+const struct lws_role_ops *available_roles[] = {
+#if defined(LWS_ROLE_H2)
+ &role_ops_h2,
+#endif
+#if defined(LWS_ROLE_H1)
+ &role_ops_h1,
+#endif
+#if defined(LWS_ROLE_WS)
+ &role_ops_ws,
+#endif
+ NULL
+};
+
+const struct lws_event_loop_ops *available_event_libs[] = {
+#if defined(LWS_WITH_POLL)
+ &event_loop_ops_poll,
+#endif
+#if defined(LWS_WITH_LIBUV)
+ &event_loop_ops_uv,
+#endif
+#if defined(LWS_WITH_LIBEVENT)
+ &event_loop_ops_event,
+#endif
+#if defined(LWS_WITH_LIBEV)
+ &event_loop_ops_ev,
+#endif
+ NULL
+};
+
static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH;
/**
@@ -40,6 +69,23 @@ lws_get_library_version(void)
return library_version;
}
+int
+lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn)
+{
+#if defined(LWS_WITH_TLS)
+ if (!alpn)
+ return 0;
+
+ lwsl_info("%s: '%s'\n", __func__, alpn);
+
+ LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
+ if (ar->alpn && !strcmp(ar->alpn, alpn) && ar->alpn_negotiated)
+ return ar->alpn_negotiated(wsi, alpn);
+ LWS_FOR_EVERY_AVAILABLE_ROLE_END;
+#endif
+ return 0;
+}
+
static const char * const mount_protocols[] = {
"http://",
"https://",
@@ -50,66 +96,6 @@ static const char * const mount_protocols[] = {
"callback://"
};
-#if defined(LWS_WITH_HTTP2)
-/*
- * These are the standardized defaults.
- * Override what actually goes in the vhost settings in platform or user code.
- * Leave these alone because they are used to determine "what is different
- * from the protocol defaults".
- */
-const struct http2_settings lws_h2_defaults = { {
- 1,
- /* H2SET_HEADER_TABLE_SIZE */ 4096,
- /* *** This controls how many entries in the dynamic table ***
- * Allows the sender to inform the remote endpoint of the maximum
- * size of the header compression table used to decode header
- * blocks, in octets. The encoder can select any size equal to or
- * less than this value by using signaling specific to the header
- * compression format inside a header block (see [COMPRESSION]).
- * The initial value is 4,096 octets.
- */
- /* H2SET_ENABLE_PUSH */ 1,
- /* H2SET_MAX_CONCURRENT_STREAMS */ 0x7fffffff,
- /* H2SET_INITIAL_WINDOW_SIZE */ 65535,
- /* H2SET_MAX_FRAME_SIZE */ 16384,
- /* H2SET_MAX_HEADER_LIST_SIZE */ 0x7fffffff,
- /*< This advisory setting informs a peer of the maximum size of
- * header list that the sender is prepared to accept, in octets.
- * The value is based on the uncompressed size of header fields,
- * including the length of the name and value in octets plus an
- * overhead of 32 octets for each header field.
- */
-
-}};
-
-const struct http2_settings lws_h2_stock_settings = { {
- 1,
- /* H2SET_HEADER_TABLE_SIZE */ 4096,
- /* *** This controls how many entries in the dynamic table ***
- * Allows the sender to inform the remote endpoint of the maximum
- * size of the header compression table used to decode header
- * blocks, in octets. The encoder can select any size equal to or
- * less than this value by using signaling specific to the header
- * compression format inside a header block (see [COMPRESSION]).
- * The initial value is 4,096 octets.
- *
- * Can't pass h2spec with less than 4096 here...
- */
- /* H2SET_ENABLE_PUSH */ 1,
- /* H2SET_MAX_CONCURRENT_STREAMS */ 24,
- /* H2SET_INITIAL_WINDOW_SIZE */ 65535,
- /* H2SET_MAX_FRAME_SIZE */ 16384,
- /* H2SET_MAX_HEADER_LIST_SIZE */ 4096,
- /*< This advisory setting informs a peer of the maximum size of
- * header list that the sender is prepared to accept, in octets.
- * The value is based on the uncompressed size of header fields,
- * including the length of the name and value in octets plus an
- * overhead of 32 octets for each header field.
- */
-
-}};
-#endif
-
LWS_VISIBLE void *
lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost,
const struct lws_protocols *prot, int size)
@@ -119,7 +105,8 @@ lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost,
/* allocate the vh priv array only on demand */
if (!vhost->protocol_vh_privs) {
vhost->protocol_vh_privs = (void **)lws_zalloc(
- vhost->count_protocols * sizeof(void *), "protocol_vh_privs");
+ vhost->count_protocols * sizeof(void *),
+ "protocol_vh_privs");
if (!vhost->protocol_vh_privs)
return NULL;
}
@@ -195,7 +182,7 @@ lws_protocol_init(struct lws_context *context)
struct lws_vhost *vh = context->vhost_list;
const struct lws_protocol_vhost_options *pvo, *pvo1;
struct lws wsi;
- int n;
+ int n, any = 0;
if (context->doing_protocol_init)
return 0;
@@ -211,7 +198,8 @@ lws_protocol_init(struct lws_context *context)
wsi.vhost = vh;
/* only do the protocol init once for a given vhost */
- if (vh->created_vhost_protocols)
+ if (vh->created_vhost_protocols ||
+ (vh->options & LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT))
goto next;
/* initialize supported protocols on this vhost */
@@ -231,21 +219,23 @@ lws_protocol_init(struct lws_context *context)
pvo = pvo1->options;
while (pvo) {
- lwsl_notice(
- " vhost \"%s\", protocol \"%s\", option \"%s\"\n",
+ lwsl_debug(
+ " vhost \"%s\", "
+ "protocol \"%s\", "
+ "option \"%s\"\n",
vh->name,
vh->protocols[n].name,
pvo->name);
if (!strcmp(pvo->name, "default")) {
- lwsl_notice("Setting default "
+ lwsl_info("Setting default "
"protocol for vh %s to %s\n",
vh->name,
vh->protocols[n].name);
vh->default_protocol_index = n;
}
if (!strcmp(pvo->name, "raw")) {
- lwsl_notice("Setting raw "
+ lwsl_info("Setting raw "
"protocol for vh %s to %s\n",
vh->name,
vh->protocols[n].name);
@@ -257,6 +247,10 @@ lws_protocol_init(struct lws_context *context)
pvo = pvo1->options;
}
+#if defined(LWS_WITH_TLS)
+ any |= !!vh->tls.ssl_ctx;
+#endif
+
/*
* inform all the protocols that they are doing their
* one-time initialization if they want to.
@@ -267,10 +261,10 @@ lws_protocol_init(struct lws_context *context)
if (vh->protocols[n].callback(&wsi,
LWS_CALLBACK_PROTOCOL_INIT, NULL,
(void *)pvo, 0)) {
- lwsl_err("%s: vhost %s failed init\n", __func__,
+ lws_free(vh->protocol_vh_privs[n]);
+ vh->protocol_vh_privs[n] = NULL;
+ lwsl_err("%s: protocol %s failed init\n", __func__,
vh->protocols[n].name);
- context->doing_protocol_init = 0;
- return 1;
}
}
@@ -286,6 +280,9 @@ next:
context->protocol_init_done = 1;
+ if (any)
+ lws_tls_check_all_cert_lifetimes(context);
+
return 0;
}
@@ -303,6 +300,7 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
#endif
switch (reason) {
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
case LWS_CALLBACK_HTTP:
#ifndef LWS_NO_SERVER
if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
@@ -325,14 +323,16 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
LWS_CB_REASON_AUX_BF__CGI)) {
n = lws_cgi_write_split_stdout_headers(wsi);
if (n < 0) {
- lwsl_debug("LWS_CB_REASON_AUX_BF__CGI forcing close\n");
+ lwsl_debug("AUX_BF__CGI forcing close\n");
return -1;
}
if (!n)
- lws_rx_flow_control(wsi->cgi->stdwsi[LWS_STDOUT], 1);
+ lws_rx_flow_control(
+ wsi->http.cgi->stdwsi[LWS_STDOUT], 1);
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS)
- wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI_HEADERS;
+ wsi->reason_bf &=
+ ~LWS_CB_REASON_AUX_BF__CGI_HEADERS;
else
wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI;
break;
@@ -341,12 +341,13 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) {
if (!wsi->http2_substream) {
memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5);
- lwsl_debug("writing chunk terminator and exiting\n");
- n = lws_write(wsi, (unsigned char *)buf + LWS_PRE,
- 5, LWS_WRITE_HTTP);
+ lwsl_debug("writing chunk term and exiting\n");
+ n = lws_write(wsi, (unsigned char *)buf +
+ LWS_PRE, 5, LWS_WRITE_HTTP);
} else
- n = lws_write(wsi, (unsigned char *)buf + LWS_PRE,
- 0, LWS_WRITE_HTTP_FINAL);
+ n = lws_write(wsi, (unsigned char *)buf +
+ LWS_PRE, 0,
+ LWS_WRITE_HTTP_FINAL);
/* always close after sending it */
return -1;
@@ -366,7 +367,8 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY;
if (!lws_get_child(wsi))
break;
- if (lws_http_client_read(lws_get_child(wsi), &px, &lenx) < 0)
+ if (lws_http_client_read(lws_get_child(wsi), &px,
+ &lenx) < 0)
return -1;
break;
}
@@ -467,10 +469,10 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
case LWS_CALLBACK_CGI_TERMINATED:
lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n",
- wsi->cgi->explicitly_chunked,
- (uint64_t)wsi->cgi->content_length);
- if (!wsi->cgi->explicitly_chunked &&
- !wsi->cgi->content_length) {
+ wsi->http.cgi->explicitly_chunked,
+ (uint64_t)wsi->http.cgi->content_length);
+ if (!wsi->http.cgi->explicitly_chunked &&
+ !wsi->http.cgi->content_length) {
/* send terminating chunk */
lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n");
wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END;
@@ -492,7 +494,7 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
"sent %d only %d went", n, args->len);
return n;
#endif
-
+#endif
case LWS_CALLBACK_SSL_INFO:
si = in;
@@ -514,11 +516,13 @@ static const struct lws_protocols protocols_dummy[] = {
/* first protocol must always be HTTP handler */
{
- "http-only", /* name */
- lws_callback_http_dummy, /* callback */
- 0, /* per_session_data_size */
- 0, /* max frame size / rx buffer */
- 0, NULL, 0
+ "http-only", /* name */
+ lws_callback_http_dummy, /* callback */
+ 0, /* per_session_data_size */
+ 0, /* rx_buffer_size */
+ 0, /* id */
+ NULL, /* user */
+ 0 /* tx_packet_size */
},
/*
* the other protocols are provided by lws plugins
@@ -530,20 +534,25 @@ static const struct lws_protocols protocols_dummy[] = {
#undef LWS_HAVE_GETENV
#endif
+static void
+lws_vhost_destroy2(struct lws_vhost *vh);
+
LWS_VISIBLE struct lws_vhost *
lws_create_vhost(struct lws_context *context,
- struct lws_context_creation_info *info)
+ const struct lws_context_creation_info *info)
{
struct lws_vhost *vh = lws_zalloc(sizeof(*vh), "create vhost"),
**vh1 = &context->vhost_list;
const struct lws_http_mount *mounts;
+ const struct lws_protocols *pcols = info->protocols;
const struct lws_protocol_vhost_options *pvo;
#ifdef LWS_WITH_PLUGINS
struct lws_plugin *plugin = context->plugin_list;
#endif
struct lws_protocols *lwsp;
int m, f = !info->pvo;
-#ifdef LWS_HAVE_GETENV
+ char buf[20];
+#if !defined(LWS_WITHOUT_CLIENT) && defined(LWS_HAVE_GETENV)
char *p;
#endif
int n;
@@ -551,8 +560,12 @@ lws_create_vhost(struct lws_context *context,
if (!vh)
return NULL;
- if (!info->protocols)
- info->protocols = &protocols_dummy[0];
+#if LWS_MAX_SMP > 1
+ pthread_mutex_init(&vh->lock, NULL);
+#endif
+
+ if (!pcols)
+ pcols = &protocols_dummy[0];
vh->context = context;
if (!info->vhost_name)
@@ -560,23 +573,21 @@ lws_create_vhost(struct lws_context *context,
else
vh->name = info->vhost_name;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ vh->http.error_document_404 = info->error_document_404;
+#endif
+
if (info->options & LWS_SERVER_OPTION_ONLY_RAW)
lwsl_info("%s set to only support RAW\n", vh->name);
-#if defined(LWS_WITH_HTTP2)
- vh->set = context->set;
- if (info->http2_settings[0])
- for (n = 1; n < LWS_H2_SETTINGS_LEN; n++)
- vh->set.s[n] = info->http2_settings[n];
-#endif
-
vh->iface = info->iface;
-#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32)
+#if !defined(LWS_WITH_ESP32) && \
+ !defined(OPTEE_TA) && !defined(WIN32)
vh->bind_iface = info->bind_iface;
#endif
for (vh->count_protocols = 0;
- info->protocols[vh->count_protocols].callback;
+ pcols[vh->count_protocols].callback;
vh->count_protocols++)
;
@@ -584,7 +595,14 @@ lws_create_vhost(struct lws_context *context,
vh->pvo = info->pvo;
vh->headers = info->headers;
vh->user = info->user;
- vh->ssl_info_event_mask = info->ssl_info_event_mask;
+
+ LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
+ if (ar->init_vhost)
+ if (ar->init_vhost(vh, info))
+ return NULL;
+ LWS_FOR_EVERY_AVAILABLE_ROLE_END;
+
+
if (info->keepalive_timeout)
vh->keepalive_timeout = info->keepalive_timeout;
else
@@ -595,20 +613,49 @@ lws_create_vhost(struct lws_context *context,
else
vh->timeout_secs_ah_idle = 10;
+#if defined(LWS_WITH_TLS)
+
+ vh->tls.alpn = info->alpn;
+ vh->tls.ssl_info_event_mask = info->ssl_info_event_mask;
+
+ if (info->ecdh_curve)
+ lws_strncpy(vh->tls.ecdh_curve, info->ecdh_curve,
+ sizeof(vh->tls.ecdh_curve));
+
+ /* carefully allocate and take a copy of cert + key paths if present */
+ n = 0;
+ if (info->ssl_cert_filepath)
+ n += (int)strlen(info->ssl_cert_filepath) + 1;
+ if (info->ssl_private_key_filepath)
+ n += (int)strlen(info->ssl_private_key_filepath) + 1;
+
+ if (n) {
+ vh->tls.key_path = vh->tls.alloc_cert_path = lws_malloc(n, "vh paths");
+ if (info->ssl_cert_filepath) {
+ n = (int)strlen(info->ssl_cert_filepath) + 1;
+ memcpy(vh->tls.alloc_cert_path, info->ssl_cert_filepath, n);
+ vh->tls.key_path += n;
+ }
+ if (info->ssl_private_key_filepath)
+ memcpy(vh->tls.key_path, info->ssl_private_key_filepath,
+ strlen(info->ssl_private_key_filepath) + 1);
+ }
+#endif
+
/*
* give the vhost a unified list of protocols including the
* ones that came from plugins
*/
- lwsp = lws_zalloc(sizeof(struct lws_protocols) *
- (vh->count_protocols +
- context->plugin_protocol_count + 1), "vhost-specific plugin table");
+ lwsp = lws_zalloc(sizeof(struct lws_protocols) * (vh->count_protocols +
+ context->plugin_protocol_count + 1),
+ "vhost-specific plugin table");
if (!lwsp) {
lwsl_err("OOM\n");
return NULL;
}
m = vh->count_protocols;
- memcpy(lwsp, info->protocols, sizeof(struct lws_protocols) * m);
+ memcpy(lwsp, pcols, sizeof(struct lws_protocols) * m);
/* for compatibility, all protocols enabled on vhost if only
* the default vhost exists. Otherwise only vhosts who ask
@@ -648,43 +695,58 @@ lws_create_vhost(struct lws_context *context,
context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS)
vh->protocols = lwsp;
else {
- vh->protocols = info->protocols;
+ vh->protocols = pcols;
lws_free(lwsp);
}
vh->same_vh_protocol_list = (struct lws **)
- lws_zalloc(sizeof(struct lws *) * vh->count_protocols, "same vh list");
-
- vh->mount_list = info->mounts;
+ lws_zalloc(sizeof(struct lws *) * vh->count_protocols,
+ "same vh list");
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ vh->http.mount_list = info->mounts;
+#endif
#ifdef LWS_WITH_UNIX_SOCK
if (LWS_UNIX_SOCK_ENABLED(context)) {
lwsl_notice("Creating Vhost '%s' path \"%s\", %d protocols\n",
- vh->name, info->iface, vh->count_protocols);
+ vh->name, vh->iface, vh->count_protocols);
} else
#endif
- lwsl_notice("Creating Vhost '%s' port %d, %d protocols, IPv6 %s\n",
- vh->name, info->port, vh->count_protocols,
- LWS_IPV6_ENABLED(vh) ? "on" : "off");
-
+ {
+ switch(info->port) {
+ case CONTEXT_PORT_NO_LISTEN:
+ strcpy(buf, "(serving disabled)");
+ break;
+ case CONTEXT_PORT_NO_LISTEN_SERVER:
+ strcpy(buf, "(no listener)");
+ break;
+ default:
+ lws_snprintf(buf, sizeof(buf), "port %u", info->port);
+ break;
+ }
+ lwsl_notice("Creating Vhost '%s' %s, %d protocols, IPv6 %s\n",
+ vh->name, buf, vh->count_protocols,
+ LWS_IPV6_ENABLED(vh) ? "on" : "off");
+ }
mounts = info->mounts;
while (mounts) {
(void)mount_protocols[0];
- lwsl_notice(" mounting %s%s to %s\n",
- mount_protocols[mounts->origin_protocol],
- mounts->origin, mounts->mountpoint);
+ lwsl_info(" mounting %s%s to %s\n",
+ mount_protocols[mounts->origin_protocol],
+ mounts->origin, mounts->mountpoint);
/* convert interpreter protocol names to pointers */
pvo = mounts->interpret;
while (pvo) {
- for (n = 0; n < vh->count_protocols; n++)
- if (!strcmp(pvo->value, vh->protocols[n].name)) {
- ((struct lws_protocol_vhost_options *)pvo)->value =
- (const char *)(lws_intptr_t)n;
- break;
- }
+ for (n = 0; n < vh->count_protocols; n++) {
+ if (strcmp(pvo->value, vh->protocols[n].name))
+ continue;
+ ((struct lws_protocol_vhost_options *)pvo)->
+ value = (const char *)(lws_intptr_t)n;
+ break;
+ }
if (n == vh->count_protocols)
- lwsl_err("ignoring unknown interpret protocol %s\n",
+ lwsl_err("ignoring unknown interp pr %s\n",
pvo->value);
pvo = pvo->next;
}
@@ -692,64 +754,35 @@ lws_create_vhost(struct lws_context *context,
mounts = mounts->mount_next;
}
-#ifndef LWS_NO_EXTENSIONS
-#ifdef LWS_WITH_PLUGINS
- if (context->plugin_extension_count) {
-
- m = 0;
- while (info->extensions && info->extensions[m].callback)
- m++;
-
- /*
- * give the vhost a unified list of extensions including the
- * ones that came from plugins
- */
- vh->extensions = lws_zalloc(sizeof(struct lws_extension) *
- (m +
- context->plugin_extension_count + 1), "extensions");
- if (!vh->extensions)
- return NULL;
-
- memcpy((struct lws_extension *)vh->extensions, info->extensions,
- sizeof(struct lws_extension) * m);
- plugin = context->plugin_list;
- while (plugin) {
- memcpy((struct lws_extension *)&vh->extensions[m],
- plugin->caps.extensions,
- sizeof(struct lws_extension) *
- plugin->caps.count_extensions);
- m += plugin->caps.count_extensions;
- plugin = plugin->list;
- }
- } else
-#endif
- vh->extensions = info->extensions;
-#endif
-
vh->listen_port = info->port;
-#if !defined(LWS_WITH_ESP8266)
- vh->http_proxy_port = 0;
- vh->http_proxy_address[0] = '\0';
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ vh->http.http_proxy_port = 0;
+ vh->http.http_proxy_address[0] = '\0';
+#endif
#if defined(LWS_WITH_SOCKS5)
vh->socks_proxy_port = 0;
vh->socks_proxy_address[0] = '\0';
#endif
+#if !defined(LWS_WITHOUT_CLIENT)
/* either use proxy from info, or try get it from env var */
-
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
/* http proxy */
if (info->http_proxy_address) {
/* override for backwards compatibility */
if (info->http_proxy_port)
- vh->http_proxy_port = info->http_proxy_port;
+ vh->http.http_proxy_port = info->http_proxy_port;
lws_set_proxy(vh, info->http_proxy_address);
- } else {
+ } else
+#endif
+ {
#ifdef LWS_HAVE_GETENV
p = getenv("http_proxy");
if (p)
lws_set_proxy(vh, p);
#endif
}
+#endif
#if defined(LWS_WITH_SOCKS5)
/* socks proxy */
if (info->socks_proxy_address) {
@@ -765,7 +798,6 @@ lws_create_vhost(struct lws_context *context,
#endif
}
#endif
-#endif
vh->ka_time = info->ka_time;
vh->ka_interval = info->ka_interval;
@@ -793,15 +825,23 @@ lws_create_vhost(struct lws_context *context,
} else
vh->log_fd = (int)LWS_INVALID_FILE;
#endif
- if (lws_context_init_server_ssl(info, vh))
- goto bail;
- if (lws_context_init_client_ssl(info, vh))
- goto bail;
- if (lws_context_init_server(info, vh)) {
+ if (lws_context_init_server_ssl(info, vh)) {
+ lwsl_err("%s: lws_context_init_server_ssl failed\n", __func__);
+ goto bail1;
+ }
+ if (lws_context_init_client_ssl(info, vh)) {
+ lwsl_err("%s: lws_context_init_client_ssl failed\n", __func__);
+ goto bail1;
+ }
+ lws_context_lock(context);
+ n = _lws_vhost_init_server(info, vh);
+ lws_context_unlock(context);
+ if (n < 0) {
lwsl_err("init server failed\n");
- goto bail;
+ goto bail1;
}
+
while (1) {
if (!(*vh1)) {
*vh1 = vh;
@@ -809,15 +849,27 @@ lws_create_vhost(struct lws_context *context,
}
vh1 = &(*vh1)->vhost_next;
};
+
/* for the case we are adding a vhost much later, after server init */
if (context->protocol_init_done)
- lws_protocol_init(context);
+ if (lws_protocol_init(context)) {
+ lwsl_err("%s: lws_protocol_init failed\n", __func__);
+ goto bail1;
+ }
return vh;
+bail1:
+ lws_vhost_destroy(vh);
+ lws_vhost_destroy2(vh);
+
+ return NULL;
+
+#ifdef LWS_WITH_ACCESS_LOG
bail:
lws_free(vh);
+#endif
return NULL;
}
@@ -834,8 +886,100 @@ lws_init_vhost_client_ssl(const struct lws_context_creation_info *info,
return lws_context_init_client_ssl(&i, vhost);
}
+LWS_VISIBLE void
+lws_cancel_service_pt(struct lws *wsi)
+{
+ lws_plat_pipe_signal(wsi);
+}
+
+LWS_VISIBLE void
+lws_cancel_service(struct lws_context *context)
+{
+ struct lws_context_per_thread *pt = &context->pt[0];
+ short m = context->count_threads;
+
+ if (context->being_destroyed1)
+ return;
+
+ lwsl_info("%s\n", __func__);
+
+ while (m--) {
+ if (pt->pipe_wsi)
+ lws_plat_pipe_signal(pt->pipe_wsi);
+ pt++;
+ }
+}
+
+int
+lws_create_event_pipes(struct lws_context *context)
+{
+ struct lws *wsi;
+ int n;
+
+ /*
+ * Create the pt event pipes... these are unique in that they are
+ * not bound to a vhost or protocol (both are NULL)
+ */
+
+ for (n = 0; n < context->count_threads; n++) {
+ if (context->pt[n].pipe_wsi)
+ continue;
+
+ wsi = lws_zalloc(sizeof(*wsi), "event pipe wsi");
+ if (!wsi) {
+ lwsl_err("Out of mem\n");
+ return 1;
+ }
+ wsi->context = context;
+ lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_pipe);
+ wsi->protocol = NULL;
+ wsi->tsi = n;
+ wsi->vhost = NULL;
+ wsi->event_pipe = 1;
+
+ if (lws_plat_pipe_create(wsi)) {
+ lws_free(wsi);
+ continue;
+ }
+ wsi->desc.sockfd = context->pt[n].dummy_pipe_fds[0];
+ lwsl_debug("event pipe fd %d\n", wsi->desc.sockfd);
+
+ context->pt[n].pipe_wsi = wsi;
+
+ if (context->event_loop_ops->accept)
+ context->event_loop_ops->accept(wsi);
+
+ if (__insert_wsi_socket_into_fds(context, wsi))
+ return 1;
+
+ //lws_change_pollfd(context->pt[n].pipe_wsi, 0, LWS_POLLIN);
+ context->count_wsi_allocated++;
+ }
+
+ return 0;
+}
+
+void
+lws_destroy_event_pipe(struct lws *wsi)
+{
+ lwsl_info("%s\n", __func__);
+ __remove_wsi_socket_from_fds(wsi);
+
+ if (wsi->context->event_loop_ops->wsi_logical_close) {
+ wsi->context->event_loop_ops->wsi_logical_close(wsi);
+ lws_plat_pipe_close(wsi);
+ return;
+ }
+
+ if (wsi->context->event_loop_ops->destroy_wsi)
+ wsi->context->event_loop_ops->destroy_wsi(wsi);
+ lws_plat_pipe_close(wsi);
+ wsi->context->count_wsi_allocated--;
+ lws_free(wsi);
+}
+
LWS_VISIBLE struct lws_context *
-lws_create_context(struct lws_context_creation_info *info)
+lws_create_context(const struct lws_context_creation_info *info)
{
struct lws_context *context = NULL;
struct lws_plat_file_ops *prev;
@@ -847,12 +991,14 @@ lws_create_context(struct lws_context_creation_info *info)
struct rlimit rt;
#endif
+
+
lwsl_info("Initial logging level %d\n", log_level);
lwsl_info("Libwebsockets version: %s\n", library_version);
#if defined(GCC_VER)
lwsl_info("Compiled with %s\n", GCC_VER);
#endif
-#if LWS_POSIX
+
#ifdef LWS_WITH_IPV6
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_IPV6))
lwsl_info("IPV6 compiled in and enabled\n");
@@ -861,11 +1007,7 @@ lws_create_context(struct lws_context_creation_info *info)
#else
lwsl_info("IPV6 not compiled in\n");
#endif
-#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_PLAT_ESP32)
- lws_feature_status_libev(info);
- lws_feature_status_libuv(info);
-#endif
-#endif
+
lwsl_info(" LWS_DEF_HEADER_LEN : %u\n", LWS_DEF_HEADER_LEN);
lwsl_info(" LWS_MAX_PROTOCOLS : %u\n", LWS_MAX_PROTOCOLS);
lwsl_info(" LWS_MAX_SMP : %u\n", LWS_MAX_SMP);
@@ -873,13 +1015,11 @@ lws_create_context(struct lws_context_creation_info *info)
#if defined(LWS_WITH_STATS)
lwsl_info(" LWS_WITH_STATS : on\n");
#endif
-#if LWS_POSIX
lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH);
-#endif
#if defined(LWS_WITH_HTTP2)
lwsl_info(" HTTP2 support : available\n");
#else
- lwsl_info(" HTTP2 support : not configured");
+ lwsl_info(" HTTP2 support : not configured\n");
#endif
if (lws_plat_context_early_init())
return NULL;
@@ -889,13 +1029,22 @@ lws_create_context(struct lws_context_creation_info *info)
lwsl_err("No memory for websocket context\n");
return NULL;
}
+
+#if defined(LWS_WITH_TLS)
+#if defined(LWS_WITH_MBEDTLS)
+ context->tls_ops = &tls_ops_mbedtls;
+#else
+ context->tls_ops = &tls_ops_openssl;
+#endif
+#endif
+
if (info->pt_serv_buf_size)
context->pt_serv_buf_size = info->pt_serv_buf_size;
else
context->pt_serv_buf_size = 4096;
-#if defined(LWS_WITH_HTTP2)
- context->set = lws_h2_stock_settings;
+#if defined(LWS_ROLE_H2)
+ role_ops_h2.init_context(context, info);
#endif
#if LWS_MAX_SMP > 1
@@ -943,8 +1092,10 @@ lws_create_context(struct lws_context_creation_info *info)
info->external_baggage_free_on_destroy;
context->time_up = time(NULL);
+ context->pcontext_finalize = info->pcontext;
- context->simultaneous_ssl_restriction = info->simultaneous_ssl_restriction;
+ context->simultaneous_ssl_restriction =
+ info->simultaneous_ssl_restriction;
#ifndef LWS_NO_DAEMONIZE
if (pid_daemon) {
@@ -975,6 +1126,67 @@ lws_create_context(struct lws_context_creation_info *info)
context->options = info->options;
+ /*
+ * set the context event loops ops struct
+ *
+ * after this, all event_loop actions use the generic ops
+ */
+
+#if defined(LWS_WITH_POLL)
+ context->event_loop_ops = &event_loop_ops_poll;
+#endif
+
+ if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV))
+#if defined(LWS_WITH_LIBUV)
+ context->event_loop_ops = &event_loop_ops_uv;
+#else
+ goto fail_event_libs;
+#endif
+
+ if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV))
+#if defined(LWS_WITH_LIBEV)
+ context->event_loop_ops = &event_loop_ops_ev;
+#else
+ goto fail_event_libs;
+#endif
+
+ if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT))
+#if defined(LWS_WITH_LIBEVENT)
+ context->event_loop_ops = &event_loop_ops_event;
+#else
+ goto fail_event_libs;
+#endif
+
+ if (!context->event_loop_ops)
+ goto fail_event_libs;
+
+ lwsl_info("Using event loop: %s\n", context->event_loop_ops->name);
+
+#if defined(LWS_WITH_TLS)
+ time(&context->tls.last_cert_check_s);
+ if (info->alpn)
+ context->tls.alpn_default = info->alpn;
+ else {
+ char *p = context->tls.alpn_discovered, first = 1;
+
+ LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) {
+ if (ar->alpn) {
+ if (!first)
+ *p++ = ',';
+ p += lws_snprintf(p,
+ context->tls.alpn_discovered +
+ sizeof(context->tls.alpn_discovered) -
+ 2 - p, "%s", ar->alpn);
+ first = 0;
+ }
+ } LWS_FOR_EVERY_AVAILABLE_ROLE_END;
+
+ context->tls.alpn_default = context->tls.alpn_discovered;
+ }
+
+ lwsl_info("Default ALPN advertisment: %s\n", context->tls.alpn_default);
+#endif
+
if (info->timeout_secs)
context->timeout_secs = info->timeout_secs;
else
@@ -992,10 +1204,17 @@ lws_create_context(struct lws_context_creation_info *info)
info->max_http_header_data2;
else
context->max_http_header_data = LWS_DEF_HEADER_LEN;
+
if (info->max_http_header_pool)
context->max_http_header_pool = info->max_http_header_pool;
else
- context->max_http_header_pool = LWS_DEF_HEADER_POOL;
+ context->max_http_header_pool = context->max_fds;
+
+ if (info->fd_limit_per_thread)
+ context->fd_limit_per_thread = info->fd_limit_per_thread;
+ else
+ context->fd_limit_per_thread = context->max_fds /
+ context->count_threads;
/*
* Allocate the per-thread storage for scratchpad buffers,
@@ -1009,22 +1228,16 @@ lws_create_context(struct lws_context_creation_info *info)
return NULL;
}
-#ifdef LWS_WITH_LIBUV
context->pt[n].context = context;
-#endif
context->pt[n].tid = n;
- context->pt[n].ah_list = NULL;
- context->pt[n].ah_pool_length = 0;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ context->pt[n].http.ah_list = NULL;
+ context->pt[n].http.ah_pool_length = 0;
+#endif
lws_pt_mutex_init(&context->pt[n]);
}
- if (info->fd_limit_per_thread)
- context->fd_limit_per_thread = info->fd_limit_per_thread;
- else
- context->fd_limit_per_thread = context->max_fds /
- context->count_threads;
-
lwsl_info(" Threads: %d each %d fds\n", context->count_threads,
context->fd_limit_per_thread);
@@ -1033,36 +1246,6 @@ lws_create_context(struct lws_context_creation_info *info)
return NULL;
}
-#ifdef LWS_WITH_LIBEV
- /* (Issue #264) In order to *avoid breaking backwards compatibility*, we
- * enable libev mediated SIGINT handling with a default handler of
- * lws_sigint_cb. The handler can be overridden or disabled
- * by invoking lws_sigint_cfg after creating the context, but
- * before invoking lws_initloop:
- */
- context->use_ev_sigint = 1;
- context->lws_ev_sigint_cb = &lws_ev_sigint_cb;
-#endif /* LWS_WITH_LIBEV */
-#ifdef LWS_WITH_LIBUV
- /* (Issue #264) In order to *avoid breaking backwards compatibility*, we
- * enable libev mediated SIGINT handling with a default handler of
- * lws_sigint_cb. The handler can be overridden or disabled
- * by invoking lws_sigint_cfg after creating the context, but
- * before invoking lws_initloop:
- */
- context->use_ev_sigint = 1;
- context->lws_uv_sigint_cb = &lws_uv_sigint_cb;
-#endif
-#ifdef LWS_WITH_LIBEVENT
- /* (Issue #264) In order to *avoid breaking backwards compatibility*, we
- * enable libev mediated SIGINT handling with a default handler of
- * lws_sigint_cb. The handler can be overridden or disabled
- * by invoking lws_sigint_cfg after creating the context, but
- * before invoking lws_initloop:
- */
- context->use_ev_sigint = 1;
- context->lws_event_sigint_cb = &lws_event_sigint_cb;
-#endif /* LWS_WITH_LIBEVENT */
#if defined(LWS_WITH_PEER_LIMITS)
/* scale the peer hash table according to the max fds for the process,
@@ -1077,14 +1260,14 @@ lws_create_context(struct lws_context_creation_info *info)
context->ip_limit_wsi = info->ip_limit_wsi;
#endif
- lwsl_info(" mem: context: %5lu bytes (%ld ctx + (%ld thr x %d))\n",
+ lwsl_info(" mem: context: %5lu B (%ld ctx + (%ld thr x %d))\n",
(long)sizeof(struct lws_context) +
(context->count_threads * context->pt_serv_buf_size),
(long)sizeof(struct lws_context),
(long)context->count_threads,
context->pt_serv_buf_size);
-
- lwsl_info(" mem: http hdr rsvd: %5lu bytes (%u thr x (%u + %lu) x %u))\n",
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ lwsl_info(" mem: http hdr rsvd: %5lu B (%u thr x (%u + %lu) x %u))\n",
(long)(context->max_http_header_data +
sizeof(struct allocated_headers)) *
context->max_http_header_pool * context->count_threads,
@@ -1092,6 +1275,7 @@ lws_create_context(struct lws_context_creation_info *info)
context->max_http_header_data,
(long)sizeof(struct allocated_headers),
context->max_http_header_pool);
+#endif
n = sizeof(struct lws_pollfd) * context->count_threads *
context->fd_limit_per_thread;
context->pt[0].fds = lws_zalloc(n, "fds table");
@@ -1117,14 +1301,24 @@ lws_create_context(struct lws_context_creation_info *info)
if (lws_plat_init(context, info))
goto bail;
-#if defined(LWS_WITH_HTTP2)
- /*
- * let the user code see what the platform default SETTINGS were, he
- * can modify them when he creates the vhosts.
- */
- for (n = 1; n < LWS_H2_SETTINGS_LEN; n++)
- info->http2_settings[n] = context->set.s[n];
-#endif
+ if (context->event_loop_ops->init_context)
+ if (context->event_loop_ops->init_context(context, info))
+ goto bail;
+
+
+ if (context->event_loop_ops->init_pt)
+ for (n = 0; n < context->count_threads; n++) {
+ void *lp = NULL;
+
+ if (info->foreign_loops)
+ lp = info->foreign_loops[n];
+
+ if (context->event_loop_ops->init_pt(context, lp, n))
+ goto bail;
+ }
+
+ if (lws_create_event_pipes(context))
+ goto bail;
lws_context_init_ssl_library(info);
@@ -1137,6 +1331,14 @@ lws_create_context(struct lws_context_creation_info *info)
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
if (!lws_create_vhost(context, info)) {
lwsl_err("Failed to create default vhost\n");
+ for (n = 0; n < context->count_threads; n++)
+ lws_free_set_NULL(context->pt[n].serv_buf);
+#if defined(LWS_WITH_PEER_LIMITS)
+ lws_free_set_NULL(context->pl_hash_table);
+#endif
+ lws_free_set_NULL(context->pt[0].fds);
+ lws_plat_context_late_destroy(context);
+ lws_free_set_NULL(context);
return NULL;
}
@@ -1164,23 +1366,32 @@ lws_create_context(struct lws_context_creation_info *info)
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
lws_plat_drop_app_privileges(info);
- /*
- * give all extensions a chance to create any per-context
- * allocations they need
- */
- if (info->port != CONTEXT_PORT_NO_LISTEN) {
- if (lws_ext_cb_all_exts(context, NULL,
- LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0)
- goto bail;
- } else
- if (lws_ext_cb_all_exts(context, NULL,
- LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0)
- goto bail;
+ /* expedite post-context init (eg, protocols) */
+ lws_cancel_service(context);
+
+#if defined(LWS_WITH_SELFTESTS)
+ lws_jws_selftest();
+#endif
return context;
bail:
lws_context_destroy(context);
+
+ return NULL;
+
+fail_event_libs:
+ lwsl_err("Requested event library support not configured, available:\n");
+ {
+ const struct lws_event_loop_ops **elops = available_event_libs;
+
+ while (*elops) {
+ lwsl_err(" - %s\n", (*elops)->name);
+ elops++;
+ }
+ }
+ lws_free(context);
+
return NULL;
}
@@ -1205,7 +1416,7 @@ lws_context_deprecate(struct lws_context *context, lws_reload_func cb)
wsi = vh->lserv_wsi;
if (wsi) {
wsi->socket_is_permanently_unusable = 1;
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "ctx deprecate");
wsi->context->deprecation_pending_listen_close_count++;
/*
* other vhosts can share the listen port, they
@@ -1231,11 +1442,7 @@ lws_context_is_deprecated(struct lws_context *context)
return context->deprecated;
}
-LWS_VISIBLE void
-lws_context_destroy2(struct lws_context *context);
-
-
-static void
+void
lws_vhost_destroy1(struct lws_vhost *vh)
{
const struct lws_protocols *protocol = NULL;
@@ -1259,7 +1466,8 @@ lws_vhost_destroy1(struct lws_vhost *vh)
*/
if (vh->lserv_wsi)
- lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
+ lws_start_foreach_ll(struct lws_vhost *, v,
+ context->vhost_list) {
if (v != vh &&
!v->being_destroyed &&
v->listen_port == vh->listen_port &&
@@ -1302,13 +1510,21 @@ lws_vhost_destroy1(struct lws_vhost *vh)
continue;
lws_close_free_wsi(wsi,
- LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY
+ LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY,
+ "vh destroy"
/* no protocol close */);
n--;
}
}
/*
+ * destroy any pending timed events
+ */
+
+ while (vh->timed_vh_protocol_list)
+ lws_timed_callback_remove(vh, vh->timed_vh_protocol_list);
+
+ /*
* let the protocols destroy the per-vhost protocol objects
*/
@@ -1316,7 +1532,7 @@ lws_vhost_destroy1(struct lws_vhost *vh)
wsi.context = vh->context;
wsi.vhost = vh;
protocol = vh->protocols;
- if (protocol) {
+ if (protocol && vh->created_vhost_protocols) {
n = 0;
while (n < vh->count_protocols) {
wsi.protocol = protocol;
@@ -1397,28 +1613,37 @@ lws_vhost_destroy2(struct lws_vhost *vh)
lws_free(vh->protocol_vh_privs);
lws_ssl_SSL_CTX_destroy(vh);
lws_free(vh->same_vh_protocol_list);
-#ifdef LWS_WITH_PLUGINS
- if (LWS_LIBUV_ENABLED(context)) {
- if (context->plugin_list)
- lws_free((void *)vh->protocols);
- } else
-#endif
- {
- if (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS)
- lws_free((void *)vh->protocols);
- }
-#ifdef LWS_WITH_PLUGINS
-#ifndef LWS_NO_EXTENSIONS
- if (context->plugin_extension_count)
- lws_free((void *)vh->extensions);
-#endif
-#endif
+ if (context->plugin_list ||
+ (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
+ lws_free((void *)vh->protocols);
+
+ LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
+ if (ar->destroy_vhost)
+ ar->destroy_vhost(vh);
+ LWS_FOR_EVERY_AVAILABLE_ROLE_END;
+
#ifdef LWS_WITH_ACCESS_LOG
if (vh->log_fd != (int)LWS_INVALID_FILE)
close(vh->log_fd);
#endif
+#if defined (LWS_WITH_TLS)
+ lws_free_set_NULL(vh->tls.alloc_cert_path);
+#endif
+
+#if LWS_MAX_SMP > 1
+ pthread_mutex_destroy(&vh->lock);
+#endif
+
+#if defined(LWS_WITH_UNIX_SOCK)
+ if (LWS_UNIX_SOCK_ENABLED(context)) {
+ n = unlink(vh->iface);
+ if (n)
+ lwsl_info("Closing unix socket %s: errno %d\n",
+ vh->iface, errno);
+ }
+#endif
/*
* although async event callbacks may still come for wsi handles with
* pending close in the case of asycn event library like libuv,
@@ -1439,7 +1664,8 @@ lws_check_deferred_free(struct lws_context *context, int force)
lws_start_foreach_llp(struct lws_deferred_free **, pdf,
context->deferred_free_list) {
- if (now > (*pdf)->deadline || force) {
+ if (force ||
+ lws_compare_time_t(context, now, (*pdf)->deadline) > 5) {
df = *pdf;
*pdf = df->next;
/* finalize vh destruction */
@@ -1466,119 +1692,86 @@ lws_vhost_destroy(struct lws_vhost *vh)
/* part 2 is deferred to allow all the handle closes to complete */
df->next = vh->context->deferred_free_list;
- df->deadline = lws_now_secs() + 5;
+ df->deadline = lws_now_secs();
df->payload = vh;
vh->context->deferred_free_list = df;
}
-LWS_VISIBLE void
-lws_context_destroy(struct lws_context *context)
-{
- struct lws_context_per_thread *pt;
- struct lws_vhost *vh = NULL;
- struct lws wsi;
- int n, m;
-
- if (!context) {
- lwsl_notice("%s: ctx %p\n", __func__, context);
- return;
- }
- if (context->being_destroyed1) {
- lwsl_notice("%s: ctx %p: already being destroyed\n",
- __func__, context);
- return;
- }
-
- lwsl_info("%s: ctx %p\n", __func__, context);
-
- m = context->count_threads;
- context->being_destroyed = 1;
- context->being_destroyed1 = 1;
-
- memset(&wsi, 0, sizeof(wsi));
- wsi.context = context;
-
-#ifdef LWS_LATENCY
- if (context->worst_latency_info[0])
- lwsl_notice("Worst latency: %s\n", context->worst_latency_info);
-#endif
-
- while (m--) {
- pt = &context->pt[m];
-
- for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) {
- struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
- if (!wsi)
- continue;
-
- lws_close_free_wsi(wsi,
- LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY
- /* no protocol close */);
- n--;
- }
- lws_pt_mutex_destroy(pt);
- }
-
- /*
- * give all extensions a chance to clean up any per-context
- * allocations they might have made
- */
-
- n = lws_ext_cb_all_exts(context, NULL,
- LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT, NULL, 0);
+/*
+ * When using an event loop, the context destruction is in three separate
+ * parts. This is to cover both internal and foreign event loops cleanly.
+ *
+ * - lws_context_destroy() simply starts a soft close of all wsi and
+ * related allocations. The event loop continues.
+ *
+ * As the closes complete in the event loop, reference counting is used
+ * to determine when everything is closed. It then calls
+ * lws_context_destroy2().
+ *
+ * - lws_context_destroy2() cleans up the rest of the higher-level logical
+ * lws pieces like vhosts. If the loop was foreign, it then proceeds to
+ * lws_context_destroy3(). If it the loop is internal, it stops the
+ * internal loops and waits for lws_context_destroy() to be called again
+ * outside the event loop (since we cannot destroy the loop from
+ * within the loop). That will cause lws_context_destroy3() to run
+ * directly.
+ *
+ * - lws_context_destroy3() destroys any internal event loops and then
+ * destroys the context itself, setting what was info.pcontext to NULL.
+ */
- n = lws_ext_cb_all_exts(context, NULL,
- LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT, NULL, 0);
+/*
+ * destroy the actual context itself
+ */
- /*
- * inform all the protocols that they are done and will have no more
- * callbacks.
- *
- * We can't free things until after the event loop shuts down.
- */
- if (context->protocol_init_done)
- vh = context->vhost_list;
- while (vh) {
- struct lws_vhost *vhn = vh->vhost_next;
- lws_vhost_destroy1(vh);
- vh = vhn;
- }
+static void
+lws_context_destroy3(struct lws_context *context)
+{
+ struct lws_context **pcontext_finalize = context->pcontext_finalize;
+ struct lws_context_per_thread *pt;
+ int n;
for (n = 0; n < context->count_threads; n++) {
pt = &context->pt[n];
- lws_libev_destroyloop(context, n);
- lws_libuv_destroyloop(context, n);
- lws_libevent_destroyloop(context, n);
+ if (context->event_loop_ops->destroy_pt)
+ context->event_loop_ops->destroy_pt(context, n);
lws_free_set_NULL(context->pt[n].serv_buf);
- while (pt->ah_list)
- _lws_destroy_ah(pt, pt->ah_list);
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ while (pt->http.ah_list)
+ _lws_destroy_ah(pt, pt->http.ah_list);
+#endif
}
- lws_plat_context_early_destroy(context);
- if (context->pt[0].fds)
- lws_free_set_NULL(context->pt[0].fds);
+ lws_free(context);
+ lwsl_info("%s: ctx %p freed\n", __func__, context);
- if (!LWS_LIBUV_ENABLED(context))
- lws_context_destroy2(context);
+ if (pcontext_finalize)
+ *pcontext_finalize = NULL;
}
/*
- * call the second one after the event loop has been shut down cleanly
+ * really start destroying things
*/
-LWS_VISIBLE void
+void
lws_context_destroy2(struct lws_context *context)
{
struct lws_vhost *vh = NULL, *vh1;
#if defined(LWS_WITH_PEER_LIMITS)
- uint32_t n;
+ uint32_t nu;
#endif
+ int n;
lwsl_info("%s: ctx %p\n", __func__, context);
+ context->being_destroyed2 = 1;
+
+ if (context->pt[0].fds)
+ lws_free_set_NULL(context->pt[0].fds);
+
/*
* free all the per-vhost allocations
*/
@@ -1603,9 +1796,9 @@ lws_context_destroy2(struct lws_context *context)
lws_plat_context_late_destroy(context);
#if defined(LWS_WITH_PEER_LIMITS)
- for (n = 0; n < context->pl_hash_elements; n++) {
+ for (nu = 0; nu < context->pl_hash_elements; nu++) {
lws_start_foreach_llp(struct lws_peer **, peer,
- context->pl_hash_table[n]) {
+ context->pl_hash_table[nu]) {
struct lws_peer *df = *peer;
*peer = df->next;
lws_free(df);
@@ -1624,5 +1817,141 @@ lws_context_destroy2(struct lws_context *context)
pthread_mutex_destroy(&context->lock);
#endif
- lws_free(context);
+ if (context->event_loop_ops->destroy_context2)
+ if (context->event_loop_ops->destroy_context2(context)) {
+ context->finalize_destroy_after_internal_loops_stopped = 1;
+ return;
+ }
+
+ if (!context->pt[0].event_loop_foreign)
+ for (n = 0; n < context->count_threads; n++)
+ if (context->pt[n].inside_service)
+ return;
+
+ lws_context_destroy3(context);
+}
+
+/*
+ * Begin the context takedown
+ */
+
+LWS_VISIBLE void
+lws_context_destroy(struct lws_context *context)
+{
+ volatile struct lws_foreign_thread_pollfd *ftp, *next;
+ volatile struct lws_context_per_thread *vpt;
+ struct lws_context_per_thread *pt;
+ struct lws_vhost *vh = NULL;
+ struct lws wsi;
+ int n, m;
+
+ if (!context)
+ return;
+
+ if (context->finalize_destroy_after_internal_loops_stopped) {
+ if (context->event_loop_ops->destroy_context2)
+ context->event_loop_ops->destroy_context2(context);
+
+ lws_context_destroy3(context);
+
+ return;
+ }
+
+ if (context->being_destroyed1) {
+ if (!context->being_destroyed2) {
+ lws_context_destroy2(context);
+
+ return;
+ }
+ lwsl_info("%s: ctx %p: already being destroyed\n",
+ __func__, context);
+
+ lws_context_destroy3(context);
+ return;
+ }
+
+ lwsl_info("%s: ctx %p\n", __func__, context);
+
+ m = context->count_threads;
+ context->being_destroyed = 1;
+ context->being_destroyed1 = 1;
+ context->requested_kill = 1;
+
+ memset(&wsi, 0, sizeof(wsi));
+ wsi.context = context;
+
+#ifdef LWS_LATENCY
+ if (context->worst_latency_info[0])
+ lwsl_notice("Worst latency: %s\n", context->worst_latency_info);
+#endif
+
+ while (m--) {
+ pt = &context->pt[m];
+ vpt = (volatile struct lws_context_per_thread *)pt;
+
+ ftp = vpt->foreign_pfd_list;
+ while (ftp) {
+ next = ftp->next;
+ lws_free((void *)ftp);
+ ftp = next;
+ }
+ vpt->foreign_pfd_list = NULL;
+
+ for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) {
+ struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
+ if (!wsi)
+ continue;
+
+ if (wsi->event_pipe)
+ lws_destroy_event_pipe(wsi);
+ else
+ lws_close_free_wsi(wsi,
+ LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY,
+ "ctx destroy"
+ /* no protocol close */);
+ n--;
+ }
+ lws_pt_mutex_destroy(pt);
+ }
+
+ /*
+ * inform all the protocols that they are done and will have no more
+ * callbacks.
+ *
+ * We can't free things until after the event loop shuts down.
+ */
+ if (context->protocol_init_done)
+ vh = context->vhost_list;
+ while (vh) {
+ struct lws_vhost *vhn = vh->vhost_next;
+ lws_vhost_destroy1(vh);
+ vh = vhn;
+ }
+
+ lws_plat_context_early_destroy(context);
+
+ /*
+ * We face two different needs depending if foreign loop or not.
+ *
+ * 1) If foreign loop, we really want to advance the destroy_context()
+ * past here, and block only for libuv-style async close completion.
+ *
+ * 2a) If poll, and we exited by ourselves and are calling a final
+ * destroy_context() outside of any service already, we want to
+ * advance all the way in one step.
+ *
+ * 2b) If poll, and we are reacting to a SIGINT, service thread(s) may
+ * be in poll wait or servicing. We can't advance the
+ * destroy_context() to the point it's freeing things; we have to
+ * leave that for the final destroy_context() after the service
+ * thread(s) are finished calling for service.
+ */
+
+ if (context->event_loop_ops->destroy_context1) {
+ context->event_loop_ops->destroy_context1(context);
+
+ return;
+ }
+
+ lws_context_destroy2(context);
}
diff --git a/thirdparty/lws/libwebsockets.c b/thirdparty/libwebsockets/core/libwebsockets.c
index 8fe0854041..0da02b17e4 100644
--- a/thirdparty/lws/libwebsockets.c
+++ b/thirdparty/libwebsockets/core/libwebsockets.c
@@ -19,7 +19,7 @@
* MA 02110-1301 USA
*/
-#include "private-libwebsockets.h"
+#include "core/private.h"
#ifdef LWS_HAVE_SYS_TYPES_H
#include <sys/types.h>
@@ -27,6 +27,7 @@
#ifdef LWS_WITH_IPV6
#if defined(WIN32) || defined(_WIN32)
+#include <wincrypt.h>
#include <Iphlpapi.h>
#else
#include <net/if.h>
@@ -57,16 +58,41 @@ static const char * const log_level_names[] = {
};
#endif
-void
-lws_free_wsi(struct lws *wsi)
+#if defined (_DEBUG)
+void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role)
{
- struct lws_context_per_thread *pt;
- struct allocated_headers *ah;
+ wsi->wsistate = (wsi->wsistate & (~LWSI_ROLE_MASK)) | role;
+
+ lwsl_debug("lwsi_set_role(%p, 0x%x)\n", wsi, wsi->wsistate);
+}
+
+void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs)
+{
+ wsi->wsistate = (wsi->wsistate & (~LRS_MASK)) | lrs;
+ lwsl_debug("lwsi_set_state(%p, 0x%x)\n", wsi, wsi->wsistate);
+}
+#endif
+
+signed char char_to_hex(const char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+
+ return -1;
+}
+
+void
+__lws_free_wsi(struct lws *wsi)
+{
if (!wsi)
return;
-
- pt = &wsi->context->pt[(int)wsi->tsi];
/*
* Protocol user data may be allocated either internally by lws
@@ -76,51 +102,32 @@ lws_free_wsi(struct lws *wsi)
wsi->user_space && !wsi->user_space_externally_allocated)
lws_free(wsi->user_space);
- lws_free_set_NULL(wsi->rxflow_buffer);
+ lws_buflist_destroy_all_segments(&wsi->buflist);
lws_free_set_NULL(wsi->trunc_alloc);
+ lws_free_set_NULL(wsi->udp);
- /* we may not have an ah, but may be on the waiting list... */
- lwsl_info("ah det due to close\n");
- /* we're closing, losing some rx is OK */
- lws_header_table_force_to_detachable_state(wsi);
- lws_header_table_detach(wsi, 0);
-
- if (wsi->vhost->lserv_wsi == wsi)
+ if (wsi->vhost && wsi->vhost->lserv_wsi == wsi)
wsi->vhost->lserv_wsi = NULL;
- lws_pt_lock(pt);
- ah = pt->ah_list;
- while (ah) {
- if (ah->in_use && ah->wsi == wsi) {
- lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi);
- ah->in_use = 0;
- ah->wsi = NULL;
- pt->ah_count_in_use--;
- break;
- }
- ah = ah->next;
- }
+ // lws_peer_dump_from_wsi(wsi);
+
+ if (wsi->role_ops->destroy_role)
+ wsi->role_ops->destroy_role(wsi);
#if defined(LWS_WITH_PEER_LIMITS)
lws_peer_track_wsi_close(wsi->context, wsi->peer);
wsi->peer = NULL;
#endif
-#if defined(LWS_WITH_HTTP2)
- if (wsi->upgraded_to_http2 || wsi->http2_substream) {
- lws_hpack_destroy_dynamic_header(wsi);
+ /* since we will destroy the wsi, make absolutely sure now */
- if (wsi->u.h2.h2n)
- lws_free_set_NULL(wsi->u.h2.h2n);
- }
+#if defined(LWS_WITH_OPENSSL)
+ __lws_ssl_remove_wsi_from_buffered_list(wsi);
#endif
+ __lws_remove_from_timeout_list(wsi);
- lws_pt_unlock(pt);
-
- /* since we will destroy the wsi, make absolutely sure now */
-
- lws_ssl_remove_wsi_from_buffered_list(wsi);
- lws_remove_from_timeout_list(wsi);
+ if (wsi->context->event_loop_ops->destroy_wsi)
+ wsi->context->event_loop_ops->destroy_wsi(wsi);
wsi->context->count_wsi_allocated--;
lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi,
@@ -130,63 +137,301 @@ lws_free_wsi(struct lws *wsi)
}
void
-lws_remove_from_timeout_list(struct lws *wsi)
+lws_dll_add_front(struct lws_dll *d, struct lws_dll *phead)
{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ if (d->prev)
+ return;
+
+ /* our next guy is current first guy */
+ d->next = phead->next;
+ /* if there is a next guy, set his prev ptr to our next ptr */
+ if (d->next)
+ d->next->prev = d;
+ /* our prev ptr is first ptr */
+ d->prev = phead;
+ /* set the first guy to be us */
+ phead->next = d;
+}
+
+/* situation is:
+ *
+ * HEAD: struct lws_dll * = &entry1
+ *
+ * Entry 1: struct lws_dll .pprev = &HEAD , .next = Entry 2
+ * Entry 2: struct lws_dll .pprev = &entry1 , .next = &entry2
+ * Entry 3: struct lws_dll .pprev = &entry2 , .next = NULL
+ *
+ * Delete Entry1:
+ *
+ * - HEAD = &entry2
+ * - Entry2: .pprev = &HEAD, .next = &entry3
+ * - Entry3: .pprev = &entry2, .next = NULL
+ *
+ * Delete Entry2:
+ *
+ * - HEAD = &entry1
+ * - Entry1: .pprev = &HEAD, .next = &entry3
+ * - Entry3: .pprev = &entry1, .next = NULL
+ *
+ * Delete Entry3:
+ *
+ * - HEAD = &entry1
+ * - Entry1: .pprev = &HEAD, .next = &entry2
+ * - Entry2: .pprev = &entry1, .next = NULL
+ *
+ */
- if (!wsi->timeout_list_prev) /* ie, not part of the list */
+void
+lws_dll_remove(struct lws_dll *d)
+{
+ if (!d->prev) /* ie, not part of the list */
return;
- lws_pt_lock(pt);
+ /*
+ * remove us
+ *
+ * USp <-> us <-> USn --> USp <-> USn
+ */
+
/* if we have a next guy, set his prev to our prev */
- if (wsi->timeout_list)
- wsi->timeout_list->timeout_list_prev = wsi->timeout_list_prev;
+ if (d->next)
+ d->next->prev = d->prev;
+
/* set our prev guy to our next guy instead of us */
- *wsi->timeout_list_prev = wsi->timeout_list;
+ if (d->prev)
+ d->prev->next = d->next;
/* we're out of the list, we should not point anywhere any more */
- wsi->timeout_list_prev = NULL;
- wsi->timeout_list = NULL;
+ d->prev = NULL;
+ d->next = NULL;
+}
+
+void
+__lws_remove_from_timeout_list(struct lws *wsi)
+{
+ lws_dll_lws_remove(&wsi->dll_timeout);
+}
+
+void
+lws_remove_from_timeout_list(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+ lws_pt_lock(pt, __func__);
+ __lws_remove_from_timeout_list(wsi);
lws_pt_unlock(pt);
}
+void
+lws_dll_dump(struct lws_dll_lws *head, const char *title)
+{
+ int n = 0;
+
+ (void)n;
+ lwsl_notice("%s: %s (head.next %p)\n", __func__, title, head->next);
+
+ lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, head->next) {
+ struct lws *wsi = lws_container_of(d, struct lws, dll_hrtimer);
+
+ (void)wsi;
+
+ lwsl_notice(" %d: wsi %p: %llu\n", n++, wsi,
+ (unsigned long long)wsi->pending_timer);
+ } lws_end_foreach_dll_safe(d, d1);
+}
+
+void
+__lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ struct lws_dll_lws *dd = &pt->dll_head_hrtimer;
+ struct timeval now;
+ struct lws *wsi1;
+ int bef = 0;
+
+ lws_dll_lws_remove(&wsi->dll_hrtimer);
+
+ if (usecs == LWS_SET_TIMER_USEC_CANCEL)
+ return;
+
+ gettimeofday(&now, NULL);
+ wsi->pending_timer = ((now.tv_sec * 1000000ll) + now.tv_usec) + usecs;
+
+ /*
+ * we sort the hrtimer list with the earliest timeout first
+ */
+
+ lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+ pt->dll_head_hrtimer.next) {
+ dd = d;
+ wsi1 = lws_container_of(d, struct lws, dll_hrtimer);
+
+ if (wsi1->pending_timer >= wsi->pending_timer) {
+ /* d, dprev's next, is >= our time */
+ bef = 1;
+ break;
+ }
+ } lws_end_foreach_dll_safe(d, d1);
+
+ if (bef) {
+ /*
+ * we go before dd
+ * DDp <-> DD <-> DDn --> DDp <-> us <-> DD <-> DDn
+ */
+ /* we point forward to dd */
+ wsi->dll_hrtimer.next = dd;
+ /* we point back to what dd used to point back to */
+ wsi->dll_hrtimer.prev = dd->prev;
+ /* DDp points forward to us now */
+ dd->prev->next = &wsi->dll_hrtimer;
+ /* DD points back to us now */
+ dd->prev = &wsi->dll_hrtimer;
+ } else {
+ /*
+ * we go after dd
+ * DDp <-> DD <-> DDn --> DDp <-> DD <-> us <-> DDn
+ */
+ /* we point forward to what dd used to point forward to */
+ wsi->dll_hrtimer.next = dd->next;
+ /* we point back to dd */
+ wsi->dll_hrtimer.prev = dd;
+ /* DDn points back to us */
+ if (dd->next)
+ dd->next->prev = &wsi->dll_hrtimer;
+ /* DD points forward to us */
+ dd->next = &wsi->dll_hrtimer;
+ }
+
+// lws_dll_dump(&pt->dll_head_hrtimer, "after set_timer_usec");
+}
+
LWS_VISIBLE void
-lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
+lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs)
+{
+ __lws_set_timer_usecs(wsi, usecs);
+}
+
+lws_usec_t
+__lws_hrtimer_service(struct lws_context_per_thread *pt)
+{
+ struct timeval now;
+ struct lws *wsi;
+ lws_usec_t t;
+
+ gettimeofday(&now, NULL);
+ t = (now.tv_sec * 1000000ll) + now.tv_usec;
+
+ lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+ pt->dll_head_hrtimer.next) {
+ wsi = lws_container_of(d, struct lws, dll_hrtimer);
+
+ /*
+ * if we met one in the future, we are done, because the list
+ * is sorted by time in the future.
+ */
+ if (wsi->pending_timer > t)
+ break;
+
+ lws_set_timer_usecs(wsi, LWS_SET_TIMER_USEC_CANCEL);
+
+ /* it's time for the timer to be serviced */
+
+ if (wsi->protocol &&
+ wsi->protocol->callback(wsi, LWS_CALLBACK_TIMER,
+ wsi->user_space, NULL, 0))
+ __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
+ "timer cb errored");
+ } lws_end_foreach_dll_safe(d, d1);
+
+ /* return an estimate how many us until next timer hit */
+
+ if (!pt->dll_head_hrtimer.next)
+ return LWS_HRTIMER_NOWAIT;
+
+ wsi = lws_container_of(pt->dll_head_hrtimer.next, struct lws, dll_hrtimer);
+
+ gettimeofday(&now, NULL);
+ t = (now.tv_sec * 1000000ll) + now.tv_usec;
+
+ if (wsi->pending_timer < t)
+ return 0;
+
+ return wsi->pending_timer - t;
+}
+
+void
+__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
time_t now;
+ time(&now);
+
+ lwsl_debug("%s: %p: %d secs\n", __func__, wsi, secs);
+ wsi->pending_timeout_limit = secs;
+ wsi->pending_timeout_set = now;
+ wsi->pending_timeout = reason;
+
+ if (!reason)
+ lws_dll_lws_remove(&wsi->dll_timeout);
+ else
+ lws_dll_lws_add_front(&wsi->dll_timeout, &pt->dll_head_timeout);
+}
+
+LWS_VISIBLE void
+lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
if (secs == LWS_TO_KILL_SYNC) {
lws_remove_from_timeout_list(wsi);
lwsl_debug("synchronously killing %p\n", wsi);
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "to sync kill");
return;
}
- lws_pt_lock(pt);
+ if (secs == LWS_TO_KILL_ASYNC)
+ secs = 0;
- time(&now);
+ lws_pt_lock(pt, __func__);
+ __lws_set_timeout(wsi, reason, secs);
+ lws_pt_unlock(pt);
+}
- if (reason && !wsi->timeout_list_prev) {
- /* our next guy is current first guy */
- wsi->timeout_list = pt->timeout_list;
- /* if there is a next guy, set his prev ptr to our next ptr */
- if (wsi->timeout_list)
- wsi->timeout_list->timeout_list_prev = &wsi->timeout_list;
- /* our prev ptr is first ptr */
- wsi->timeout_list_prev = &pt->timeout_list;
- /* set the first guy to be us */
- *wsi->timeout_list_prev = wsi;
- }
+int
+lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p)
+{
+ lws_start_foreach_llp(struct lws_timed_vh_protocol **, pt,
+ vh->timed_vh_protocol_list) {
+ if (*pt == p) {
+ *pt = p->next;
+ lws_free(p);
- lwsl_debug("%s: %p: %d secs\n", __func__, wsi, secs);
- wsi->pending_timeout_limit = now + secs;
- wsi->pending_timeout = reason;
+ return 0;
+ }
+ } lws_end_foreach_llp(pt, next);
- lws_pt_unlock(pt);
+ return 1;
+}
- if (!reason)
- lws_remove_from_timeout_list(wsi);
+LWS_VISIBLE LWS_EXTERN int
+lws_timed_callback_vh_protocol(struct lws_vhost *vh, const struct lws_protocols *prot,
+ int reason, int secs)
+{
+ struct lws_timed_vh_protocol *p = (struct lws_timed_vh_protocol *)
+ lws_malloc(sizeof(*p), "timed_vh");
+
+ if (!p)
+ return 1;
+
+ p->protocol = prot;
+ p->reason = reason;
+ p->time = lws_now_secs() + secs;
+ p->next = vh->timed_vh_protocol_list;
+
+ vh->timed_vh_protocol_list = p;
+
+ return 0;
}
static void
@@ -229,9 +474,11 @@ lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p)
// return 0;
const struct lws_protocols *vp = wsi->vhost->protocols, *vpo;
- if (wsi->protocol)
+ if (wsi->protocol && wsi->protocol_bind_balance) {
wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_DROP_PROTOCOL,
wsi->user_space, NULL, 0);
+ wsi->protocol_bind_balance = 0;
+ }
if (!wsi->user_space_externally_allocated)
lws_free_set_NULL(wsi->user_space);
@@ -245,7 +492,7 @@ lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p)
return 1;
if (p > vp && p < &vp[wsi->vhost->count_protocols])
- lws_same_vh_protocol_insert(wsi, p - vp);
+ lws_same_vh_protocol_insert(wsi, (int)(p - vp));
else {
int n = wsi->vhost->count_protocols;
int hit = 0;
@@ -255,7 +502,7 @@ lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p)
while (n--) {
if (p->name && vp->name && !strcmp(p->name, vp->name)) {
hit = 1;
- lws_same_vh_protocol_insert(wsi, vp - vpo);
+ lws_same_vh_protocol_insert(wsi, (int)(vp - vpo));
break;
}
vp++;
@@ -269,42 +516,65 @@ lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p)
wsi->user_space, NULL, 0))
return 1;
+ wsi->protocol_bind_balance = 1;
+
return 0;
}
void
-lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
+__lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *caller)
{
struct lws_context_per_thread *pt;
struct lws *wsi1, *wsi2;
struct lws_context *context;
- struct lws_tokens eff_buf;
- int n, m, ret;
+ int n;
- lwsl_debug("%s: %p\n", __func__, wsi);
+ lwsl_info("%s: %p: caller: %s\n", __func__, wsi, caller);
if (!wsi)
return;
lws_access_log(wsi);
-#if defined(LWS_WITH_ESP8266)
- if (wsi->premature_rx)
- lws_free(wsi->premature_rx);
-
- if (wsi->pending_send_completion && !wsi->close_is_pending_send_completion) {
- lwsl_notice("delaying close\n");
- wsi->close_is_pending_send_completion = 1;
- return;
- }
-#endif
-
- /* we're closing, losing some rx is OK */
- lws_header_table_force_to_detachable_state(wsi);
context = wsi->context;
pt = &context->pt[(int)wsi->tsi];
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_CLOSE, 1);
+#if !defined(LWS_NO_CLIENT)
+
+ lws_free_set_NULL(wsi->client_hostname_copy);
+ /* we are no longer an active client connection that can piggyback */
+ lws_dll_lws_remove(&wsi->dll_active_client_conns);
+
+ /*
+ * if we have wsi in our transaction queue, if we are closing we
+ * must go through and close all those first
+ */
+ if (wsi->vhost) {
+ if ((int)reason != -1)
+ lws_vhost_lock(wsi->vhost);
+ lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+ wsi->dll_client_transaction_queue_head.next) {
+ struct lws *w = lws_container_of(d, struct lws,
+ dll_client_transaction_queue);
+
+ __lws_close_free_wsi(w, -1, "trans q leader closing");
+ } lws_end_foreach_dll_safe(d, d1);
+
+ /*
+ * !!! If we are closing, but we have pending pipelined transaction
+ * results we already sent headers for, that's going to destroy sync
+ * for HTTP/1 and leave H2 stream with no live swsi.
+ *
+ * However this is normal if we are being closed because the transaction
+ * queue leader is closing.
+ */
+ lws_dll_lws_remove(&wsi->dll_client_transaction_queue);
+ if ((int)reason !=-1)
+ lws_vhost_unlock(wsi->vhost);
+ }
+#endif
+
/* if we have children, close them first */
if (wsi->child_list) {
wsi2 = wsi->child_list;
@@ -313,302 +583,146 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
wsi2->parent = NULL;
/* stop it doing shutdown processing */
wsi2->socket_is_permanently_unusable = 1;
- lws_close_free_wsi(wsi2, reason);
+ __lws_close_free_wsi(wsi2, reason, "general child recurse");
wsi2 = wsi1;
}
wsi->child_list = NULL;
}
-#if defined(LWS_WITH_HTTP2)
-
- if (wsi->u.h2.parent_wsi) {
- lwsl_info(" wsi: %p, his parent %p: siblings:\n", wsi, wsi->u.h2.parent_wsi);
- lws_start_foreach_llp(struct lws **, w, wsi->u.h2.parent_wsi->u.h2.child_list) {
- lwsl_info(" \\---- child %p\n", *w);
- } lws_end_foreach_llp(w, u.h2.sibling_list);
- }
-
- if (wsi->upgraded_to_http2 || wsi->http2_substream) {
- lwsl_info("closing %p: parent %p\n", wsi, wsi->u.h2.parent_wsi);
-
- if (wsi->u.h2.child_list) {
- lwsl_info(" parent %p: closing children: list:\n", wsi);
- lws_start_foreach_llp(struct lws **, w, wsi->u.h2.child_list) {
- lwsl_info(" \\---- child %p\n", *w);
- } lws_end_foreach_llp(w, u.h2.sibling_list);
- /* trigger closing of all of our http2 children first */
- lws_start_foreach_llp(struct lws **, w, wsi->u.h2.child_list) {
- lwsl_info(" closing child %p\n", *w);
- /* disconnect from siblings */
- wsi2 = (*w)->u.h2.sibling_list;
- (*w)->u.h2.sibling_list = NULL;
- (*w)->socket_is_permanently_unusable = 1;
- lws_close_free_wsi(*w, reason);
- *w = wsi2;
- continue;
- } lws_end_foreach_llp(w, u.h2.sibling_list);
- }
- }
-
- if (wsi->upgraded_to_http2) {
- /* remove pps */
- struct lws_h2_protocol_send *w = wsi->u.h2.h2n->pps, *w1;
- while (w) {
- w1 = wsi->u.h2.h2n->pps->next;
- free(w);
- w = w1;
- }
- wsi->u.h2.h2n->pps = NULL;
- }
-
- if (wsi->http2_substream && wsi->u.h2.parent_wsi) {
- lwsl_info(" %p: disentangling from siblings\n", wsi);
- lws_start_foreach_llp(struct lws **, w,
- wsi->u.h2.parent_wsi->u.h2.child_list) {
- /* disconnect from siblings */
- if (*w == wsi) {
- wsi2 = (*w)->u.h2.sibling_list;
- (*w)->u.h2.sibling_list = NULL;
- *w = wsi2;
- lwsl_info(" %p disentangled from sibling %p\n", wsi, wsi2);
- break;
- }
- } lws_end_foreach_llp(w, u.h2.sibling_list);
- wsi->u.h2.parent_wsi->u.h2.child_count--;
- wsi->u.h2.parent_wsi = NULL;
- if (wsi->u.h2.pending_status_body)
- lws_free_set_NULL(wsi->u.h2.pending_status_body);
- }
-
- if (wsi->upgraded_to_http2 && wsi->u.h2.h2n &&
- wsi->u.h2.h2n->rx_scratch)
- lws_free_set_NULL(wsi->u.h2.h2n->rx_scratch);
-#endif
-
- if (wsi->mode == LWSCM_RAW_FILEDESC) {
+ if (wsi->role_ops == &role_ops_raw_file) {
lws_remove_child_from_any_parent(wsi);
- remove_wsi_socket_from_fds(wsi);
- wsi->protocol->callback(wsi,
- LWS_CALLBACK_RAW_CLOSE_FILE,
+ __remove_wsi_socket_from_fds(wsi);
+ wsi->protocol->callback(wsi, wsi->role_ops->close_cb[0],
wsi->user_space, NULL, 0);
goto async_close;
}
+ wsi->wsistate_pre_close = wsi->wsistate;
+
#ifdef LWS_WITH_CGI
- if (wsi->mode == LWSCM_CGI) {
+ if (wsi->role_ops == &role_ops_cgi) {
/* we are not a network connection, but a handler for CGI io */
- if (wsi->parent && wsi->parent->cgi) {
+ if (wsi->parent && wsi->parent->http.cgi) {
if (wsi->cgi_channel == LWS_STDOUT)
lws_cgi_remove_and_kill(wsi->parent);
/* end the binding between us and master */
- wsi->parent->cgi->stdwsi[(int)wsi->cgi_channel] = NULL;
+ wsi->parent->http.cgi->stdwsi[(int)wsi->cgi_channel] = NULL;
}
wsi->socket_is_permanently_unusable = 1;
goto just_kill_connection;
}
- if (wsi->cgi)
+ if (wsi->http.cgi)
lws_cgi_remove_and_kill(wsi);
#endif
#if !defined(LWS_NO_CLIENT)
- if (wsi->mode == LWSCM_HTTP_CLIENT ||
- wsi->mode == LWSCM_WSCL_WAITING_CONNECT ||
- wsi->mode == LWSCM_WSCL_WAITING_PROXY_REPLY ||
- wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE ||
- wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE2 ||
- wsi->mode == LWSCM_WSCL_WAITING_SSL ||
- wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY ||
- wsi->mode == LWSCM_WSCL_WAITING_EXTENSION_CONNECT ||
- wsi->mode == LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY ||
- wsi->mode == LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY ||
- wsi->mode == LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY)
- if (wsi->u.hdr.stash)
- lws_free_set_NULL(wsi->u.hdr.stash);
-#endif
-
- if (wsi->mode == LWSCM_RAW) {
- wsi->protocol->callback(wsi,
- LWS_CALLBACK_RAW_CLOSE, wsi->user_space, NULL, 0);
+ lws_client_stash_destroy(wsi);
+#endif
+
+ if (wsi->role_ops == &role_ops_raw_skt) {
wsi->socket_is_permanently_unusable = 1;
goto just_kill_connection;
}
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ if (lwsi_role_http(wsi) && lwsi_role_server(wsi) &&
+ wsi->http.fop_fd != NULL)
+ lws_vfs_file_close(&wsi->http.fop_fd);
+#endif
+
+ if (lwsi_state(wsi) == LRS_DEAD_SOCKET)
+ return;
- if ((wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED ||
- wsi->mode == LWSCM_HTTP2_SERVING) &&
- wsi->u.http.fop_fd != NULL) {
- lws_vfs_file_close(&wsi->u.http.fop_fd);
- wsi->vhost->protocols->callback(wsi,
- LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
- wsi->told_user_closed = 1;
- }
if (wsi->socket_is_permanently_unusable ||
reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
- wsi->state == LWSS_SHUTDOWN)
+ lwsi_state(wsi) == LRS_SHUTDOWN)
goto just_kill_connection;
- wsi->state_pre_close = wsi->state;
-
- switch (wsi->state_pre_close) {
- case LWSS_DEAD_SOCKET:
+ switch (lwsi_state_PRE_CLOSE(wsi)) {
+ case LRS_DEAD_SOCKET:
return;
/* we tried the polite way... */
- case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
- case LWSS_AWAITING_CLOSE_ACK:
+ case LRS_WAITING_TO_SEND_CLOSE:
+ case LRS_AWAITING_CLOSE_ACK:
+ case LRS_RETURNED_CLOSE:
goto just_kill_connection;
- case LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE:
+ case LRS_FLUSHING_BEFORE_CLOSE:
if (wsi->trunc_len) {
lws_callback_on_writable(wsi);
return;
}
- lwsl_info("%p: end FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
+ lwsl_info("%p: end LRS_FLUSHING_BEFORE_CLOSE\n", wsi);
goto just_kill_connection;
default:
if (wsi->trunc_len) {
- lwsl_info("%p: start FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
- wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE;
- lws_set_timeout(wsi, PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5);
+ lwsl_info("%p: LRS_FLUSHING_BEFORE_CLOSE\n", wsi);
+ lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
+ __lws_set_timeout(wsi,
+ PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5);
return;
}
break;
}
- if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT ||
- wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
+ if (lwsi_state(wsi) == LRS_WAITING_CONNECT ||
+ lwsi_state(wsi) == LRS_H1C_ISSUE_HANDSHAKE)
goto just_kill_connection;
- if (!wsi->told_user_closed &&
- (wsi->mode == LWSCM_HTTP_SERVING ||
- wsi->mode == LWSCM_HTTP2_SERVING)) {
- if (wsi->user_space)
- wsi->vhost->protocols->callback(wsi,
+ if (!wsi->told_user_closed && lwsi_role_http(wsi) &&
+ lwsi_role_server(wsi)) {
+ if (wsi->user_space && wsi->protocol &&
+ wsi->protocol_bind_balance) {
+ wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_DROP_PROTOCOL,
wsi->user_space, NULL, 0);
- wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
- wsi->user_space, NULL, 0);
- wsi->told_user_closed = 1;
- }
-
- /*
- * are his extensions okay with him closing? Eg he might be a mux
- * parent and just his ch1 aspect is closing?
- */
-
- if (lws_ext_cb_active(wsi, LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) {
- lwsl_ext("extension vetoed close\n");
- return;
- }
-
- /*
- * flush any tx pending from extensions, since we may send close packet
- * if there are problems with send, just nuke the connection
- */
- do {
- ret = 0;
- eff_buf.token = NULL;
- eff_buf.token_len = 0;
-
- /* show every extension the new incoming data */
-
- m = lws_ext_cb_active(wsi,
- LWS_EXT_CB_FLUSH_PENDING_TX, &eff_buf, 0);
- if (m < 0) {
- lwsl_ext("Extension reports fatal error\n");
- goto just_kill_connection;
+ wsi->protocol_bind_balance = 0;
}
- if (m)
- /*
- * at least one extension told us he has more
- * to spill, so we will go around again after
- */
- ret = 1;
-
- /* assuming they left us something to send, send it */
-
- if (eff_buf.token_len)
- if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
- eff_buf.token_len) !=
- eff_buf.token_len) {
- lwsl_debug("close: ext spill failed\n");
- goto just_kill_connection;
- }
- } while (ret);
+ }
/*
* signal we are closing, lws_write will
* add any necessary version-specific stuff. If the write fails,
* no worries we are closing anyway. If we didn't initiate this
* close, then our state has been changed to
- * LWSS_RETURNED_CLOSE_ALREADY and we will skip this.
+ * LRS_RETURNED_CLOSE and we will skip this.
*
* Likewise if it's a second call to close this connection after we
* sent the close indication to the peer already, we are in state
- * LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time.
+ * LRS_AWAITING_CLOSE_ACK and will skip doing this a second time.
*/
- if (wsi->state_pre_close == LWSS_ESTABLISHED &&
- (wsi->u.ws.close_in_ping_buffer_len || /* already a reason */
- (reason != LWS_CLOSE_STATUS_NOSTATUS &&
- (reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) {
- lwsl_debug("sending close indication...\n");
-
- /* if no prepared close reason, use 1000 and no aux data */
- if (!wsi->u.ws.close_in_ping_buffer_len) {
- wsi->u.ws.close_in_ping_buffer_len = 2;
- wsi->u.ws.ping_payload_buf[LWS_PRE] =
- (reason >> 8) & 0xff;
- wsi->u.ws.ping_payload_buf[LWS_PRE + 1] =
- reason & 0xff;
- }
-
-#if defined (LWS_WITH_ESP8266)
- wsi->close_is_pending_send_completion = 1;
-#endif
-
- lwsl_debug("waiting for chance to send close\n");
- wsi->waiting_to_send_close_frame = 1;
- wsi->state = LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION;
- lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 2);
- lws_callback_on_writable(wsi);
-
+ if (wsi->role_ops->close_via_role_protocol &&
+ wsi->role_ops->close_via_role_protocol(wsi, reason))
return;
- }
just_kill_connection:
+ if (wsi->role_ops->close_kill_connection)
+ wsi->role_ops->close_kill_connection(wsi, reason);
+
lws_remove_child_from_any_parent(wsi);
n = 0;
- if (!wsi->told_user_closed && wsi->user_space) {
- lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi,
+ if (!wsi->told_user_closed && wsi->user_space &&
+ wsi->protocol_bind_balance) {
+ lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi,
wsi->protocol->name);
- wsi->protocol->callback(wsi,
- LWS_CALLBACK_HTTP_DROP_PROTOCOL,
+ wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_DROP_PROTOCOL,
wsi->user_space, NULL, 0);
+ wsi->protocol_bind_balance = 0;
}
- if ((wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY ||
- wsi->mode == LWSCM_WSCL_WAITING_CONNECT) &&
- !wsi->already_did_cce) {
- wsi->vhost->protocols[0].callback(wsi,
- LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+ if ((lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY ||
+ lwsi_state(wsi) == LRS_WAITING_CONNECT) && !wsi->already_did_cce)
+ wsi->protocol->callback(wsi,
+ LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
wsi->user_space, NULL, 0);
- }
- if (wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) {
- wsi->vhost->protocols[0].callback(wsi,
- LWS_CALLBACK_CLOSED_CLIENT_HTTP,
- wsi->user_space, NULL, 0);
- wsi->told_user_closed = 1;
- }
-
-
-#if LWS_POSIX
/*
* Testing with ab shows that we have to stage the socket close when
* the system is under stress... shutdown any further TX, change the
@@ -616,55 +730,38 @@ just_kill_connection:
* for the POLLIN to show a zero-size rx before coming back and doing
* the actual close.
*/
- if (wsi->mode != LWSCM_RAW &&
- !(wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) &&
- wsi->state != LWSS_SHUTDOWN &&
- wsi->state != LWSS_CLIENT_UNCONNECTED &&
+ if (wsi->role_ops != &role_ops_raw_skt && !lwsi_role_client(wsi) &&
+ lwsi_state(wsi) != LRS_SHUTDOWN &&
+ lwsi_state(wsi) != LRS_UNCONNECTED &&
reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
!wsi->socket_is_permanently_unusable) {
-#ifdef LWS_OPENSSL_SUPPORT
- if (lws_is_ssl(wsi) && wsi->ssl) {
- n = SSL_shutdown(wsi->ssl);
- /*
- * If finished the SSL shutdown, then do socket
- * shutdown, else need to retry SSL shutdown
- */
- switch (n) {
- case 0:
- lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
- break;
- case 1:
- n = shutdown(wsi->desc.sockfd, SHUT_WR);
- break;
- default:
- if (SSL_want_read(wsi->ssl)) {
- lws_change_pollfd(wsi, 0, LWS_POLLIN);
- n = 0;
- break;
- }
- if (SSL_want_write(wsi->ssl)) {
- lws_change_pollfd(wsi, 0, LWS_POLLOUT);
- n = 0;
- break;
- }
- n = shutdown(wsi->desc.sockfd, SHUT_WR);
- break;
- }
- } else
+
+#if defined(LWS_WITH_TLS)
+ if (lws_is_ssl(wsi) && wsi->tls.ssl) {
+ n = 0;
+ switch (__lws_tls_shutdown(wsi)) {
+ case LWS_SSL_CAPABLE_DONE:
+ case LWS_SSL_CAPABLE_ERROR:
+ case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
+ case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
+ case LWS_SSL_CAPABLE_MORE_SERVICE:
+ break;
+ }
+ } else
#endif
{
- lwsl_info("%s: shutdown conn: %p (sock %d, state %d)\n",
+ lwsl_info("%s: shutdown conn: %p (sock %d, state 0x%x)\n",
__func__, wsi, (int)(long)wsi->desc.sockfd,
- wsi->state);
+ lwsi_state(wsi));
if (!wsi->socket_is_permanently_unusable &&
- lws_sockfd_valid(wsi->desc.sockfd)) {
+ lws_socket_is_valid(wsi->desc.sockfd)) {
wsi->socket_is_permanently_unusable = 1;
n = shutdown(wsi->desc.sockfd, SHUT_WR);
}
}
if (n)
- lwsl_debug("closing: shutdown (state %d) ret %d\n",
- wsi->state, LWS_ERRNO);
+ lwsl_debug("closing: shutdown (state 0x%x) ret %d\n",
+ lwsi_state(wsi), LWS_ERRNO);
/*
* This causes problems on WINCE / ESP32 with disconnection
@@ -673,199 +770,272 @@ just_kill_connection:
#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP32)
/* libuv: no event available to guarantee completion */
if (!wsi->socket_is_permanently_unusable &&
- lws_sockfd_valid(wsi->desc.sockfd) &&
- !LWS_LIBUV_ENABLED(context)) {
- lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
- wsi->state = LWSS_SHUTDOWN;
- lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
- context->timeout_secs);
+ lws_socket_is_valid(wsi->desc.sockfd) &&
+ lwsi_state(wsi) != LRS_SHUTDOWN &&
+ context->event_loop_ops->periodic_events_available) {
+ __lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
+ lwsi_set_state(wsi, LRS_SHUTDOWN);
+ __lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
+ context->timeout_secs);
return;
}
#endif
}
-#endif
lwsl_debug("%s: real just_kill_connection: %p (sockfd %d)\n", __func__,
wsi, wsi->desc.sockfd);
#ifdef LWS_WITH_HTTP_PROXY
- if (wsi->rw) {
- lws_rewrite_destroy(wsi->rw);
- wsi->rw = NULL;
+ if (wsi->http.rw) {
+ lws_rewrite_destroy(wsi->http.rw);
+ wsi->http.rw = NULL;
}
#endif
/*
* we won't be servicing or receiving anything further from this guy
* delete socket from the internal poll list if still present
*/
- lws_ssl_remove_wsi_from_buffered_list(wsi);
- lws_remove_from_timeout_list(wsi);
+ __lws_ssl_remove_wsi_from_buffered_list(wsi);
+ __lws_remove_from_timeout_list(wsi);
+ lws_dll_lws_remove(&wsi->dll_hrtimer);
+
+ /* don't repeat event loop stuff */
+ if (wsi->told_event_loop_closed)
+ return;
/* checking return redundant since we anyway close */
if (wsi->desc.sockfd != LWS_SOCK_INVALID)
- remove_wsi_socket_from_fds(wsi);
+ __remove_wsi_socket_from_fds(wsi);
else
lws_same_vh_protocol_remove(wsi);
-#if defined(LWS_WITH_ESP8266)
- espconn_disconnect(wsi->desc.sockfd);
-#endif
-
- wsi->state = LWSS_DEAD_SOCKET;
-
- lws_free_set_NULL(wsi->rxflow_buffer);
- if (wsi->state_pre_close == LWSS_ESTABLISHED ||
- wsi->mode == LWSCM_WS_SERVING ||
- wsi->mode == LWSCM_WS_CLIENT) {
-
- if (wsi->u.ws.rx_draining_ext) {
- struct lws **w = &pt->rx_draining_ext_list;
-
- wsi->u.ws.rx_draining_ext = 0;
- /* remove us from context draining ext list */
- while (*w) {
- if (*w == wsi) {
- *w = wsi->u.ws.rx_draining_ext_list;
- break;
- }
- w = &((*w)->u.ws.rx_draining_ext_list);
- }
- wsi->u.ws.rx_draining_ext_list = NULL;
- }
-
- if (wsi->u.ws.tx_draining_ext) {
- struct lws **w = &pt->tx_draining_ext_list;
-
- wsi->u.ws.tx_draining_ext = 0;
- /* remove us from context draining ext list */
- while (*w) {
- if (*w == wsi) {
- *w = wsi->u.ws.tx_draining_ext_list;
- break;
- }
- w = &((*w)->u.ws.tx_draining_ext_list);
- }
- wsi->u.ws.tx_draining_ext_list = NULL;
- }
- lws_free_set_NULL(wsi->u.ws.rx_ubuf);
+ lwsi_set_state(wsi, LRS_DEAD_SOCKET);
+ lws_buflist_destroy_all_segments(&wsi->buflist);
+ lws_dll_lws_remove(&wsi->dll_buflist);
- if (wsi->trunc_alloc)
- /* not going to be completed... nuke it */
- lws_free_set_NULL(wsi->trunc_alloc);
-
- wsi->u.ws.ping_payload_len = 0;
- wsi->u.ws.ping_pending_flag = 0;
- }
+ if (wsi->role_ops->close_role)
+ wsi->role_ops->close_role(pt, wsi);
/* tell the user it's all over for this guy */
- if (!wsi->told_user_closed &&
- wsi->mode != LWSCM_RAW && wsi->protocol &&
- wsi->protocol->callback &&
- (wsi->state_pre_close == LWSS_ESTABLISHED ||
- wsi->state_pre_close == LWSS_HTTP2_ESTABLISHED ||
- wsi->state_pre_close == LWSS_HTTP_BODY ||
- wsi->state_pre_close == LWSS_HTTP ||
- wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY ||
- wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK ||
- wsi->state_pre_close == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
- wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE ||
- (wsi->mode == LWSCM_WS_CLIENT && wsi->state_pre_close == LWSS_HTTP) ||
- (wsi->mode == LWSCM_WS_SERVING && wsi->state_pre_close == LWSS_HTTP))) {
- lwsl_debug("calling back CLOSED %d %d\n", wsi->mode, wsi->state);
- wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
- wsi->user_space, NULL, 0);
- } else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) {
- lwsl_debug("calling back CLOSED_HTTP\n");
- wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
- wsi->user_space, NULL, 0 );
- } else
- lwsl_debug("not calling back closed mode=%d state=%d\n",
- wsi->mode, wsi->state_pre_close);
+ if (lwsi_state_est_PRE_CLOSE(wsi) && !wsi->told_user_closed &&
+ wsi->role_ops->close_cb[lwsi_role_server(wsi)]) {
+ const struct lws_protocols *pro = wsi->protocol;
- /* deallocate any active extension contexts */
+ if (!wsi->protocol)
+ pro = &wsi->vhost->protocols[0];
- if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
- lwsl_warn("extension destruction failed\n");
- /*
- * inform all extensions in case they tracked this guy out of band
- * even though not active on him specifically
- */
- if (lws_ext_cb_all_exts(context, wsi,
- LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
- lwsl_warn("ext destroy wsi failed\n");
+ pro->callback(wsi,
+ wsi->role_ops->close_cb[lwsi_role_server(wsi)],
+ wsi->user_space, NULL, 0);
+ wsi->told_user_closed = 1;
+ }
async_close:
wsi->socket_is_permanently_unusable = 1;
-#ifdef LWS_WITH_LIBUV
- if (!wsi->parent_carries_io &&
- lws_sockfd_valid(wsi->desc.sockfd))
- if (LWS_LIBUV_ENABLED(context)) {
- if (wsi->listener) {
- lwsl_debug("%s: stop listener poll\n", __func__);
- uv_poll_stop(&wsi->w_read.uv_watcher);
- }
- lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n",
- __func__, wsi);
- /*
- * libuv has to do his own close handle processing
- * asynchronously
- */
- lws_libuv_closehandle(wsi);
-
+ if (wsi->context->event_loop_ops->wsi_logical_close)
+ if (wsi->context->event_loop_ops->wsi_logical_close(wsi))
return;
- }
-#endif
- lws_close_free_wsi_final(wsi);
+ __lws_close_free_wsi_final(wsi);
}
void
-lws_close_free_wsi_final(struct lws *wsi)
+__lws_close_free_wsi_final(struct lws *wsi)
{
int n;
if (lws_socket_is_valid(wsi->desc.sockfd) && !lws_ssl_close(wsi)) {
-#if LWS_POSIX
n = compatible_close(wsi->desc.sockfd);
if (n)
lwsl_debug("closing: close ret %d\n", LWS_ERRNO);
-#else
- compatible_close(wsi->desc.sockfd);
- (void)n;
-#endif
wsi->desc.sockfd = LWS_SOCK_INVALID;
}
/* outermost destroy notification for wsi (user_space still intact) */
- wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
- wsi->user_space, NULL, 0);
+ if (wsi->vhost)
+ wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
+ wsi->user_space, NULL, 0);
#ifdef LWS_WITH_CGI
- if (wsi->cgi) {
+ if (wsi->http.cgi) {
for (n = 0; n < 3; n++) {
- if (wsi->cgi->pipe_fds[n][!!(n == 0)] == 0)
+ if (wsi->http.cgi->pipe_fds[n][!!(n == 0)] == 0)
lwsl_err("ZERO FD IN CGI CLOSE");
- if (wsi->cgi->pipe_fds[n][!!(n == 0)] >= 0)
- close(wsi->cgi->pipe_fds[n][!!(n == 0)]);
+ if (wsi->http.cgi->pipe_fds[n][!!(n == 0)] >= 0)
+ close(wsi->http.cgi->pipe_fds[n][!!(n == 0)]);
}
- lws_free(wsi->cgi);
+ lws_free(wsi->http.cgi);
}
#endif
- lws_free_wsi(wsi);
+ __lws_free_wsi(wsi);
+}
+
+
+void
+lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *caller)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+ lws_pt_lock(pt, __func__);
+ __lws_close_free_wsi(wsi, reason, caller);
+ lws_pt_unlock(pt);
+}
+
+/* lws_buflist */
+
+int
+lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf,
+ size_t len)
+{
+ struct lws_buflist *nbuf;
+ int first = !*head;
+ void *p = *head;
+ int sanity = 1024;
+
+ assert(buf);
+ assert(len);
+
+ /* append at the tail */
+ while (*head) {
+ if (!--sanity || head == &((*head)->next)) {
+ lwsl_err("%s: corrupt list points to self\n", __func__);
+ return -1;
+ }
+ head = &((*head)->next);
+ }
+
+ lwsl_info("%s: len %u first %d %p\n", __func__, (uint32_t)len, first, p);
+
+ nbuf = (struct lws_buflist *)
+ lws_malloc(sizeof(**head) + len, __func__);
+ if (!nbuf) {
+ lwsl_err("%s: OOM\n", __func__);
+ return -1;
+ }
+
+ nbuf->len = len;
+ nbuf->pos = 0;
+ nbuf->next = NULL;
+
+ p = (void *)nbuf->buf;
+ memcpy(p, buf, len);
+
+ *head = nbuf;
+
+ return first; /* returns 1 if first segment just created */
+}
+
+static int
+lws_buflist_destroy_segment(struct lws_buflist **head)
+{
+ struct lws_buflist *old = *head;
+
+ assert(*head);
+ *head = (*head)->next;
+ old->next = NULL;
+ lws_free(old);
+
+ return !*head; /* returns 1 if last segment just destroyed */
+}
+
+void
+lws_buflist_destroy_all_segments(struct lws_buflist **head)
+{
+ struct lws_buflist *p = *head, *p1;
+
+ while (p) {
+ p1 = p->next;
+ p->next = NULL;
+ lws_free(p);
+ p = p1;
+ }
+
+ *head = NULL;
+}
+
+size_t
+lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf)
+{
+ if (!*head) {
+ if (buf)
+ *buf = NULL;
+
+ return 0;
+ }
+
+ if (!(*head)->len && (*head)->next)
+ lws_buflist_destroy_segment(head);
+
+ if (!*head) {
+ if (buf)
+ *buf = NULL;
+
+ return 0;
+ }
+
+ assert((*head)->pos < (*head)->len);
+
+ if (buf)
+ *buf = (*head)->buf + (*head)->pos;
+
+ return (*head)->len - (*head)->pos;
}
+int
+lws_buflist_use_segment(struct lws_buflist **head, size_t len)
+{
+ assert(*head);
+ assert(len);
+ assert((*head)->pos + len <= (*head)->len);
+
+ (*head)->pos += len;
+ if ((*head)->pos == (*head)->len)
+ lws_buflist_destroy_segment(head);
+
+ if (!*head)
+ return 0;
+
+ return (int)((*head)->len - (*head)->pos);
+}
+
+void
+lws_buflist_describe(struct lws_buflist **head, void *id)
+{
+ struct lws_buflist *old;
+ int n = 0;
+
+ if (*head == NULL)
+ lwsl_notice("%p: buflist empty\n", id);
+
+ while (*head) {
+ lwsl_notice("%p: %d: %llu / %llu (%llu left)\n", id, n,
+ (unsigned long long)(*head)->pos,
+ (unsigned long long)(*head)->len,
+ (unsigned long long)(*head)->len - (*head)->pos);
+ old = *head;
+ head = &((*head)->next);
+ if (*head == old) {
+ lwsl_err("%s: next points to self\n", __func__);
+ break;
+ }
+ n++;
+ }
+}
+
+/* ... */
+
LWS_VISIBLE LWS_EXTERN const char *
lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len)
{
- int n = 0, sl = strlen(name);
+ int n = 0, sl = (int)strlen(name);
while (lws_hdr_copy_fragment(wsi, buf, len,
WSI_TOKEN_HTTP_URI_ARGS, n) >= 0) {
@@ -879,7 +1049,7 @@ lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len)
return NULL;
}
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
+#if !defined(LWS_WITH_ESP32)
LWS_VISIBLE int
interface_to_sa(struct lws_vhost *vh, const char *ifname,
struct sockaddr_in *addr, size_t addrlen)
@@ -895,12 +1065,10 @@ interface_to_sa(struct lws_vhost *vh, const char *ifname,
#endif
#ifndef LWS_PLAT_OPTEE
-#if LWS_POSIX
static int
lws_get_addresses(struct lws_vhost *vh, void *ads, char *name,
int name_len, char *rip, int rip_len)
{
-#if LWS_POSIX
struct addrinfo ai, *res;
struct sockaddr_in addr4;
@@ -921,8 +1089,8 @@ lws_get_addresses(struct lws_vhost *vh, void *ads, char *name,
if (strncmp(rip, "::ffff:", 7) == 0)
memmove(rip, rip + 7, strlen(rip) - 6);
- getnameinfo((struct sockaddr *)ads,
- sizeof(struct sockaddr_in6), name, name_len, NULL, 0, 0);
+ getnameinfo((struct sockaddr *)ads, sizeof(struct sockaddr_in6),
+ name, name_len, NULL, 0, 0);
return 0;
} else
@@ -933,7 +1101,6 @@ lws_get_addresses(struct lws_vhost *vh, void *ads, char *name,
memset(&ai, 0, sizeof ai);
ai.ai_family = PF_UNSPEC;
ai.ai_socktype = SOCK_STREAM;
- ai.ai_flags = AI_CANONNAME;
#if !defined(LWS_WITH_ESP32)
if (getnameinfo((struct sockaddr *)ads,
sizeof(struct sockaddr_in),
@@ -966,24 +1133,12 @@ lws_get_addresses(struct lws_vhost *vh, void *ads, char *name,
return -1;
return 0;
-#else
- (void)vh;
- (void)ads;
- (void)name;
- (void)name_len;
- (void)rip;
- (void)rip_len;
-
- return -1;
-#endif
}
-#endif
LWS_VISIBLE const char *
lws_get_peer_simple(struct lws *wsi, char *name, int namelen)
{
-#if LWS_POSIX
socklen_t len, olen;
#ifdef LWS_WITH_IPV6
struct sockaddr_in6 sin6;
@@ -992,10 +1147,7 @@ lws_get_peer_simple(struct lws *wsi, char *name, int namelen)
int af = AF_INET;
void *p, *q;
-#if defined(LWS_WITH_HTTP2)
- if (wsi->http2_substream)
- wsi = wsi->u.h2.parent_wsi;
-#endif
+ wsi = lws_get_network_wsi(wsi);
if (wsi->parent_carries_io)
wsi = wsi->parent;
@@ -1021,13 +1173,6 @@ lws_get_peer_simple(struct lws *wsi, char *name, int namelen)
}
return lws_plat_inet_ntop(af, q, name, namelen);
-#else
-#if defined(LWS_WITH_ESP8266)
- return lws_plat_get_peer_simple(wsi, name, namelen);
-#else
- return NULL;
-#endif
-#endif
}
#endif
@@ -1036,7 +1181,6 @@ lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name,
int name_len, char *rip, int rip_len)
{
#ifndef LWS_PLAT_OPTEE
-#if LWS_POSIX
socklen_t len;
#ifdef LWS_WITH_IPV6
struct sockaddr_in6 sin6;
@@ -1072,7 +1216,6 @@ lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name,
bail:
lws_latency(context, wsi, "lws_get_peer_addresses", ret, 1);
#endif
-#endif
(void)wsi;
(void)fd;
(void)name;
@@ -1112,6 +1255,12 @@ lws_protocol_get(struct lws *wsi)
return wsi->protocol;
}
+LWS_VISIBLE const struct lws_udp *
+lws_get_udp(const struct lws *wsi)
+{
+ return wsi->udp;
+}
+
LWS_VISIBLE struct lws *
lws_get_network_wsi(struct lws *wsi)
{
@@ -1119,11 +1268,11 @@ lws_get_network_wsi(struct lws *wsi)
return NULL;
#if defined(LWS_WITH_HTTP2)
- if (!wsi->http2_substream)
+ if (!wsi->http2_substream && !wsi->client_h2_substream)
return wsi;
- while (wsi->u.h2.parent_wsi)
- wsi = wsi->u.h2.parent_wsi;
+ while (wsi->h2.parent_wsi)
+ wsi = wsi->h2.parent_wsi;
#endif
return wsi;
@@ -1209,6 +1358,29 @@ lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len)
return 0;
}
+LWS_VISIBLE LWS_EXTERN int
+lws_callback_vhost_protocols_vhost(struct lws_vhost *vh, int reason, void *in,
+ size_t len)
+{
+ int n;
+ struct lws *wsi = lws_zalloc(sizeof(*wsi), "fake wsi");
+
+ wsi->context = vh->context;
+ wsi->vhost = vh;
+
+ for (n = 0; n < wsi->vhost->count_protocols; n++) {
+ wsi->protocol = &vh->protocols[n];
+ if (wsi->protocol->callback(wsi, reason, NULL, in, len)) {
+ lws_free(wsi);
+ return 1;
+ }
+ }
+
+ lws_free(wsi);
+
+ return 0;
+}
+
LWS_VISIBLE LWS_EXTERN void
lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops)
{
@@ -1281,7 +1453,7 @@ lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path,
pf = fops->next;
while (pf) {
n = 0;
- while (n < ARRAY_SIZE(pf->fi) && pf->fi[n].sig) {
+ while (n < (int)ARRAY_SIZE(pf->fi) && pf->fi[n].sig) {
if (p >= vfs_path + pf->fi[n].len)
if (!strncmp(p - (pf->fi[n].len - 1),
pf->fi[n].sig,
@@ -1327,10 +1499,19 @@ lws_now_secs(void)
return tv.tv_sec;
}
+LWS_VISIBLE LWS_EXTERN int
+lws_compare_time_t(struct lws_context *context, time_t t1, time_t t2)
+{
+ if (t1 < context->time_discontiguity)
+ t1 += context->time_fixup;
-#if LWS_POSIX
+ if (t2 < context->time_discontiguity)
+ t2 += context->time_fixup;
-LWS_VISIBLE int
+ return (int)(t1 - t2);
+}
+
+LWS_VISIBLE lws_sockfd_type
lws_get_socket_fd(struct lws *wsi)
{
if (!wsi)
@@ -1338,8 +1519,6 @@ lws_get_socket_fd(struct lws *wsi)
return wsi->desc.sockfd;
}
-#endif
-
#ifdef LWS_LATENCY
void
lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
@@ -1384,8 +1563,14 @@ lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
LWS_VISIBLE int
lws_rx_flow_control(struct lws *wsi, int _enable)
{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int en = _enable;
+ // h2 ignores rx flow control atm
+ if (lwsi_role_h2(wsi) || wsi->http2_substream ||
+ lwsi_role_h2_ENCAPSULATION(wsi))
+ return 0; // !!!
+
lwsl_info("%s: %p 0x%x\n", __func__, wsi, _enable);
if (!(_enable & LWS_RXFLOW_REASON_APPLIES)) {
@@ -1398,6 +1583,8 @@ lws_rx_flow_control(struct lws *wsi, int _enable)
en |= LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT;
}
+ lws_pt_lock(pt, __func__);
+
/* any bit set in rxflow_bitmap DISABLEs rxflow control */
if (en & LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT)
wsi->rxflow_bitmap &= ~(en & 0xff);
@@ -1406,16 +1593,23 @@ lws_rx_flow_control(struct lws *wsi, int _enable)
if ((LWS_RXFLOW_PENDING_CHANGE | (!wsi->rxflow_bitmap)) ==
wsi->rxflow_change_to)
- return 0;
+ goto skip;
wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !wsi->rxflow_bitmap;
- lwsl_info("%s: 0x%p: bitmap 0x%x: en 0x%x, ch 0x%x\n", __func__, wsi,
+ lwsl_info("%s: %p: bitmap 0x%x: en 0x%x, ch 0x%x\n", __func__, wsi,
wsi->rxflow_bitmap, en, wsi->rxflow_change_to);
if (_enable & LWS_RXFLOW_REASON_FLAG_PROCESS_NOW ||
- !wsi->rxflow_will_be_applied)
- return _lws_rx_flow_control(wsi);
+ !wsi->rxflow_will_be_applied) {
+ en = __lws_rx_flow_control(wsi);
+ lws_pt_unlock(pt);
+
+ return en;
+ }
+
+skip:
+ lws_pt_unlock(pt);
return 0;
}
@@ -1440,12 +1634,63 @@ lws_rx_flow_allow_all_protocol(const struct lws_context *context,
}
}
+int
+lws_broadcast(struct lws_context *context, int reason, void *in, size_t len)
+{
+ struct lws_vhost *v = context->vhost_list;
+ struct lws wsi;
+ int n, ret = 0;
+
+ memset(&wsi, 0, sizeof(wsi));
+ wsi.context = context;
+
+ while (v) {
+ const struct lws_protocols *p = v->protocols;
+ wsi.vhost = v;
+
+ for (n = 0; n < v->count_protocols; n++) {
+ wsi.protocol = p;
+ if (p->callback &&
+ p->callback(&wsi, reason, NULL, in, len))
+ ret |= 1;
+ p++;
+ }
+ v = v->vhost_next;
+ }
+
+ return ret;
+}
+
LWS_VISIBLE extern const char *
lws_canonical_hostname(struct lws_context *context)
{
return (const char *)context->canonical_hostname;
}
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_vhost_name(struct lws_vhost *vhost)
+{
+ return vhost->name;
+}
+
+LWS_VISIBLE LWS_EXTERN int
+lws_get_vhost_port(struct lws_vhost *vhost)
+{
+ return vhost->listen_port;
+}
+
+LWS_VISIBLE LWS_EXTERN void *
+lws_get_vhost_user(struct lws_vhost *vhost)
+{
+ return vhost->user;
+}
+
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_vhost_iface(struct lws_vhost *vhost)
+{
+ return vhost->iface;
+}
+
int user_callback_handle_rxflow(lws_callback_function callback_function,
struct lws *wsi,
enum lws_callback_reasons reason, void *user,
@@ -1457,20 +1702,15 @@ int user_callback_handle_rxflow(lws_callback_function callback_function,
n = callback_function(wsi, reason, user, in, len);
wsi->rxflow_will_be_applied = 0;
if (!n)
- n = _lws_rx_flow_control(wsi);
+ n = __lws_rx_flow_control(wsi);
return n;
}
-#if defined(LWS_WITH_ESP8266)
-#undef strchr
-#define strchr ets_strchr
-#endif
-
+#if !defined(LWS_WITHOUT_CLIENT)
LWS_VISIBLE int
lws_set_proxy(struct lws_vhost *vhost, const char *proxy)
{
-#if !defined(LWS_WITH_ESP8266)
char *p;
char authstring[96];
@@ -1481,15 +1721,15 @@ lws_set_proxy(struct lws_vhost *vhost, const char *proxy)
if (!strncmp(proxy, "http://", 7))
proxy += 7;
- p = strchr(proxy, '@');
+ p = strrchr(proxy, '@');
if (p) { /* auth is around */
if ((unsigned int)(p - proxy) > sizeof(authstring) - 1)
goto auth_too_long;
- strncpy(authstring, proxy, p - proxy);
+ lws_strncpy(authstring, proxy, p - proxy + 1);
// null termination not needed on input
- if (lws_b64_encode_string(authstring, (p - proxy),
+ if (lws_b64_encode_string(authstring, lws_ptr_diff(p, proxy),
vhost->proxy_basic_auth_token,
sizeof vhost->proxy_basic_auth_token) < 0)
goto auth_too_long;
@@ -1500,39 +1740,38 @@ lws_set_proxy(struct lws_vhost *vhost, const char *proxy)
} else
vhost->proxy_basic_auth_token[0] = '\0';
- strncpy(vhost->http_proxy_address, proxy,
- sizeof(vhost->http_proxy_address) - 1);
- vhost->http_proxy_address[
- sizeof(vhost->http_proxy_address) - 1] = '\0';
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ lws_strncpy(vhost->http.http_proxy_address, proxy,
+ sizeof(vhost->http.http_proxy_address));
- p = strchr(vhost->http_proxy_address, ':');
- if (!p && !vhost->http_proxy_port) {
+ p = strchr(vhost->http.http_proxy_address, ':');
+ if (!p && !vhost->http.http_proxy_port) {
lwsl_err("http_proxy needs to be ads:port\n");
return -1;
} else {
if (p) {
*p = '\0';
- vhost->http_proxy_port = atoi(p + 1);
+ vhost->http.http_proxy_port = atoi(p + 1);
}
}
- lwsl_info(" Proxy %s:%u\n", vhost->http_proxy_address,
- vhost->http_proxy_port);
-
+ lwsl_info(" Proxy %s:%u\n", vhost->http.http_proxy_address,
+ vhost->http.http_proxy_port);
+#endif
return 0;
auth_too_long:
lwsl_err("proxy auth too long\n");
-#endif
+
return -1;
}
+#endif
#if defined(LWS_WITH_SOCKS5)
LWS_VISIBLE int
lws_set_socks(struct lws_vhost *vhost, const char *socks)
{
-#if !defined(LWS_WITH_ESP8266)
char *p_at, *p_colon;
char user[96];
char password[96];
@@ -1543,7 +1782,7 @@ lws_set_socks(struct lws_vhost *vhost, const char *socks)
vhost->socks_user[0] = '\0';
vhost->socks_password[0] = '\0';
- p_at = strchr(socks, '@');
+ p_at = strrchr(socks, '@');
if (p_at) { /* auth is around */
if ((unsigned int)(p_at - socks) > (sizeof(user)
+ sizeof(password) - 2)) {
@@ -1564,9 +1803,9 @@ lws_set_socks(struct lws_vhost *vhost, const char *socks)
goto bail;
}
- strncpy(vhost->socks_user, socks, p_colon - socks);
- strncpy(vhost->socks_password, p_colon + 1,
- p_at - (p_colon + 1));
+ lws_strncpy(vhost->socks_user, socks, p_colon - socks + 1);
+ lws_strncpy(vhost->socks_password, p_colon + 1,
+ p_at - (p_colon + 1) + 1);
}
lwsl_info(" Socks auth, user: %s, password: %s\n",
@@ -1575,10 +1814,8 @@ lws_set_socks(struct lws_vhost *vhost, const char *socks)
socks = p_at + 1;
}
- strncpy(vhost->socks_proxy_address, socks,
- sizeof(vhost->socks_proxy_address) - 1);
- vhost->socks_proxy_address[sizeof(vhost->socks_proxy_address) - 1]
- = '\0';
+ lws_strncpy(vhost->socks_proxy_address, socks,
+ sizeof(vhost->socks_proxy_address));
p_colon = strchr(vhost->socks_proxy_address, ':');
if (!p_colon && !vhost->socks_proxy_port) {
@@ -1597,7 +1834,6 @@ lws_set_socks(struct lws_vhost *vhost, const char *socks)
return 0;
bail:
-#endif
return -1;
}
#endif
@@ -1608,27 +1844,6 @@ lws_get_protocol(struct lws *wsi)
return wsi->protocol;
}
-LWS_VISIBLE int
-lws_is_final_fragment(struct lws *wsi)
-{
- lwsl_info("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
- wsi->u.ws.final, (long)wsi->u.ws.rx_packet_length,
- (long)wsi->u.ws.rx_draining_ext);
- return wsi->u.ws.final && !wsi->u.ws.rx_packet_length &&
- !wsi->u.ws.rx_draining_ext;
-}
-
-LWS_VISIBLE int
-lws_is_first_fragment(struct lws *wsi)
-{
- return wsi->u.ws.first_fragment;
-}
-
-LWS_VISIBLE unsigned char
-lws_get_reserved_bits(struct lws *wsi)
-{
- return wsi->u.ws.rsv;
-}
int
lws_ensure_user_space(struct lws *wsi)
@@ -1639,7 +1854,8 @@ lws_ensure_user_space(struct lws *wsi)
/* allocate the per-connection user memory (if any) */
if (wsi->protocol->per_session_data_size && !wsi->user_space) {
- wsi->user_space = lws_zalloc(wsi->protocol->per_session_data_size, "user space");
+ wsi->user_space = lws_zalloc(
+ wsi->protocol->per_session_data_size, "user space");
if (wsi->user_space == NULL) {
lwsl_err("%s: OOM\n", __func__);
return 1;
@@ -1704,10 +1920,14 @@ lwsl_timestamp(int level, char *p, int len)
(int)(now % 10000), log_level_names[n]);
return n;
}
+#else
+ p[0] = '\0';
#endif
+
return 0;
}
+#ifndef LWS_PLAT_OPTEE
static const char * const colours[] = {
"[31;1m", /* LLL_ERR */
"[36;1m", /* LLL_WARN */
@@ -1722,17 +1942,14 @@ static const char * const colours[] = {
"[30;1m", /* LLL_USER */
};
-#ifndef LWS_PLAT_OPTEE
LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line)
{
-#if !defined(LWS_WITH_ESP8266)
char buf[50];
- static char tty;
+ static char tty = 3;
int n, m = ARRAY_SIZE(colours) - 1;
if (!tty)
tty = isatty(2) | 2;
-
lwsl_timestamp(level, buf, sizeof(buf));
if (tty == 3) {
@@ -1746,17 +1963,12 @@ LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line)
fprintf(stderr, "%c%s%s%s%c[0m", 27, colours[m], buf, line, 27);
} else
fprintf(stderr, "%s%s", buf, line);
-#endif
}
#endif
LWS_VISIBLE void _lws_logv(int filter, const char *format, va_list vl)
{
-#if defined(LWS_WITH_ESP8266)
- char buf[128];
-#else
char buf[256];
-#endif
int n;
if (!(log_level & filter))
@@ -1764,15 +1976,11 @@ LWS_VISIBLE void _lws_logv(int filter, const char *format, va_list vl)
n = vsnprintf(buf, sizeof(buf) - 1, format, vl);
(void)n;
-#if defined(LWS_WITH_ESP8266)
- buf[sizeof(buf) - 1] = '\0';
-#else
/* vnsprintf returns what it would have written, even if truncated */
- if (n > sizeof(buf) - 1)
+ if (n > (int)sizeof(buf) - 1)
n = sizeof(buf) - 1;
if (n > 0)
buf[n] = '\0';
-#endif
lwsl_emit(filter, buf);
}
@@ -1810,6 +2018,12 @@ lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len)
if (!lwsl_visible(hexdump_level))
return;
+ if (!len)
+ return;
+
+ if (!vbuf)
+ return;
+
_lws_log(hexdump_level, "\n");
for (n = 0; n < len;) {
@@ -1852,19 +2066,19 @@ lwsl_hexdump(const void *vbuf, size_t len)
LWS_VISIBLE int
lws_is_ssl(struct lws *wsi)
{
-#ifdef LWS_OPENSSL_SUPPORT
- return wsi->use_ssl;
+#if defined(LWS_WITH_TLS)
+ return wsi->tls.use_ssl & LCCSCF_USE_SSL;
#else
(void)wsi;
return 0;
#endif
}
-#ifdef LWS_OPENSSL_SUPPORT
-LWS_VISIBLE SSL*
+#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS)
+LWS_VISIBLE lws_tls_conn*
lws_get_ssl(struct lws *wsi)
{
- return wsi->ssl;
+ return wsi->tls.ssl;
}
#endif
@@ -1874,27 +2088,28 @@ lws_partial_buffered(struct lws *wsi)
return !!wsi->trunc_len;
}
-LWS_VISIBLE size_t
+LWS_VISIBLE lws_fileofs_t
lws_get_peer_write_allowance(struct lws *wsi)
{
-#ifdef LWS_WITH_HTTP2
- /* only if we are using HTTP2 on this connection */
- if (wsi->mode != LWSCM_HTTP2_SERVING)
- return -1;
-
- return lws_h2_tx_cr_get(wsi);
-#else
- (void)wsi;
- return -1;
-#endif
+ return wsi->role_ops->tx_credit(wsi);
}
LWS_VISIBLE void
-lws_union_transition(struct lws *wsi, enum connection_mode mode)
+lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state,
+ struct lws_role_ops *ops)
{
- lwsl_debug("%s: %p: mode %d\n", __func__, wsi, mode);
- memset(&wsi->u, 0, sizeof(wsi->u));
- wsi->mode = mode;
+#if defined(_DEBUG)
+ const char *name = "(unset)";
+#endif
+ wsi->wsistate = role | state;
+ if (ops)
+ wsi->role_ops = ops;
+#if defined(_DEBUG)
+ if (wsi->role_ops)
+ name = wsi->role_ops->name;
+ lwsl_debug("%s: %p: wsistate 0x%x, ops %s\n", __func__, wsi,
+ wsi->wsistate, name);
+#endif
}
LWS_VISIBLE struct lws_plat_file_ops *
@@ -1973,48 +2188,21 @@ lws_clear_child_pending_on_writable(struct lws *wsi)
wsi->parent_pending_cb_on_writable = 0;
}
-LWS_VISIBLE LWS_EXTERN int
-lws_get_close_length(struct lws *wsi)
-{
- return wsi->u.ws.close_in_ping_buffer_len;
-}
-
-LWS_VISIBLE LWS_EXTERN unsigned char *
-lws_get_close_payload(struct lws *wsi)
-{
- return &wsi->u.ws.ping_payload_buf[LWS_PRE];
-}
-
-LWS_VISIBLE LWS_EXTERN void
-lws_close_reason(struct lws *wsi, enum lws_close_status status,
- unsigned char *buf, size_t len)
-{
- unsigned char *p, *start;
- int budget = sizeof(wsi->u.ws.ping_payload_buf) - LWS_PRE;
-
- assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT);
-
- start = p = &wsi->u.ws.ping_payload_buf[LWS_PRE];
-
- *p++ = (((int)status) >> 8) & 0xff;
- *p++ = ((int)status) & 0xff;
-
- if (buf)
- while (len-- && p < start + budget)
- *p++ = *buf++;
-
- wsi->u.ws.close_in_ping_buffer_len = p - start;
-}
LWS_EXTERN int
-_lws_rx_flow_control(struct lws *wsi)
+__lws_rx_flow_control(struct lws *wsi)
{
struct lws *wsic = wsi->child_list;
+ // h2 ignores rx flow control atm
+ if (lwsi_role_h2(wsi) || wsi->http2_substream ||
+ lwsi_role_h2_ENCAPSULATION(wsi))
+ return 0; // !!!
+
/* if he has children, do those if they were changed */
while (wsic) {
if (wsic->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)
- _lws_rx_flow_control(wsic);
+ __lws_rx_flow_control(wsic);
wsic = wsic->sibling_list;
}
@@ -2024,13 +2212,13 @@ _lws_rx_flow_control(struct lws *wsi)
return 0;
/* stuff is still buffered, not ready to really accept new input */
- if (wsi->rxflow_buffer) {
+ if (lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
/* get ourselves called back to deal with stashed buffer */
lws_callback_on_writable(wsi);
return 0;
}
- /* pending is cleared, we can change rxflow state */
+ /* now the pending is cleared, we can change rxflow state */
wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE;
@@ -2040,12 +2228,12 @@ _lws_rx_flow_control(struct lws *wsi)
/* adjust the pollfd for this wsi */
if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) {
- if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
+ if (__lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
lwsl_info("%s: fail\n", __func__);
return -1;
}
} else
- if (lws_change_pollfd(wsi, LWS_POLLIN, 0))
+ if (__lws_change_pollfd(wsi, LWS_POLLIN, 0))
return -1;
return 0;
@@ -2162,14 +2350,14 @@ lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
return 0;
}
-#ifdef LWS_NO_EXTENSIONS
+#if defined(LWS_WITHOUT_EXTENSIONS)
/* we need to provide dummy callbacks for internal exts
* so user code runs when faced with a lib compiled with
* extensions disabled.
*/
-int
+LWS_VISIBLE int
lws_extension_callback_pm_deflate(struct lws_context *context,
const struct lws_extension *ext,
struct lws *wsi,
@@ -2186,13 +2374,19 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
return 0;
}
+
+LWS_EXTERN int
+lws_set_extension_option(struct lws *wsi, const char *ext_name,
+ const char *opt_name, const char *opt_val)
+{
+ return -1;
+}
#endif
LWS_EXTERN int
lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
const char *iface)
{
-#if LWS_POSIX
#ifdef LWS_WITH_UNIX_SOCK
struct sockaddr_un serv_unix;
#endif
@@ -2204,6 +2398,9 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
socklen_t len = sizeof(struct sockaddr_storage);
#endif
int n;
+#if !defined(LWS_WITH_ESP32)
+ int m;
+#endif
struct sockaddr_storage sin;
struct sockaddr *v;
@@ -2213,6 +2410,8 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
n = sizeof(struct sockaddr_un);
bzero((char *) &serv_unix, sizeof(serv_unix));
serv_unix.sun_family = AF_UNIX;
+ if (!iface)
+ return -1;
if (sizeof(serv_unix.sun_path) <= strlen(iface)) {
lwsl_err("\"%s\" too long for UNIX domain socket\n",
iface);
@@ -2230,10 +2429,17 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
n = sizeof(struct sockaddr_in6);
bzero((char *) &serv_addr6, sizeof(serv_addr6));
if (iface) {
- if (interface_to_sa(vhost, iface,
- (struct sockaddr_in *)v, n) < 0) {
- lwsl_err("Unable to find if %s\n", iface);
- return -1;
+ m = interface_to_sa(vhost, iface,
+ (struct sockaddr_in *)v, n);
+ if (m == LWS_ITOSA_NOT_USABLE) {
+ lwsl_info("%s: netif %s: Not usable\n",
+ __func__, iface);
+ return m;
+ }
+ if (m == LWS_ITOSA_NOT_EXIST) {
+ lwsl_info("%s: netif %s: Does not exist\n",
+ __func__, iface);
+ return m;
}
serv_addr6.sin6_scope_id = lws_get_addr_scope(iface);
}
@@ -2250,16 +2456,28 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
serv_addr4.sin_family = AF_INET;
#if !defined(LWS_WITH_ESP32)
- if (iface &&
- interface_to_sa(vhost, iface,
- (struct sockaddr_in *)v, n) < 0) {
- lwsl_err("Unable to find interface %s\n", iface);
- return -1;
+ if (iface) {
+ m = interface_to_sa(vhost, iface,
+ (struct sockaddr_in *)v, n);
+ if (m == LWS_ITOSA_NOT_USABLE) {
+ lwsl_info("%s: netif %s: Not usable\n",
+ __func__, iface);
+ return m;
+ }
+ if (m == LWS_ITOSA_NOT_EXIST) {
+ lwsl_info("%s: netif %s: Does not exist\n",
+ __func__, iface);
+ return m;
+ }
}
#endif
serv_addr4.sin_port = htons(port);
} /* ipv4 */
+ /* just checking for the interface extant */
+ if (sockfd == LWS_SOCK_INVALID)
+ return 0;
+
n = bind(sockfd, v, n);
#ifdef LWS_WITH_UNIX_SOCK
if (n < 0 && LWS_UNIX_SOCK_ENABLED(vhost)) {
@@ -2281,8 +2499,8 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
#endif
#if defined(LWS_WITH_IPV6)
port = (sin.ss_family == AF_INET6) ?
- ntohs(((struct sockaddr_in6 *) &sin)->sin6_port) :
- ntohs(((struct sockaddr_in *) &sin)->sin_port);
+ ntohs(((struct sockaddr_in6 *) &sin)->sin6_port) :
+ ntohs(((struct sockaddr_in *) &sin)->sin_port);
#else
{
struct sockaddr_in sain;
@@ -2290,11 +2508,16 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
port = ntohs(sain.sin_port);
}
#endif
-#endif
return port;
}
+LWS_VISIBLE LWS_EXTERN int
+lws_get_vhost_listen_port(struct lws_vhost *vhost)
+{
+ return vhost->listen_port;
+}
+
#if defined(LWS_WITH_IPV6)
LWS_EXTERN unsigned long
lws_get_addr_scope(const char *ipaddr)
@@ -2368,7 +2591,8 @@ lws_get_addr_scope(const char *ipaddr)
while (adapter && !found) {
addr = adapter->FirstUnicastAddress;
while (addr && !found) {
- if (addr->Address.lpSockaddr->sa_family == AF_INET6) {
+ if (addr->Address.lpSockaddr->sa_family ==
+ AF_INET6) {
sockaddr = (struct sockaddr_in6 *)
(addr->Address.lpSockaddr);
@@ -2395,18 +2619,73 @@ lws_get_addr_scope(const char *ipaddr)
}
#endif
-LWS_EXTERN void
-lws_restart_ws_ping_pong_timer(struct lws *wsi)
+#if !defined(LWS_NO_SERVER)
+
+LWS_EXTERN struct lws *
+lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
+ const char *protocol_name, struct lws *parent_wsi)
{
- if (!wsi->context->ws_ping_pong_interval)
- return;
- if (wsi->state != LWSS_ESTABLISHED)
- return;
+ lws_sock_file_fd_type sock;
+ struct addrinfo h, *r, *rp;
+ struct lws *wsi = NULL;
+ char buf[16];
+ int n;
- wsi->u.ws.time_next_ping_check = (time_t)lws_now_secs() +
- wsi->context->ws_ping_pong_interval;
+ memset(&h, 0, sizeof(h));
+ h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
+ h.ai_socktype = SOCK_DGRAM;
+ h.ai_protocol = IPPROTO_UDP;
+ h.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+
+ lws_snprintf(buf, sizeof(buf), "%u", port);
+ n = getaddrinfo(NULL, buf, &h, &r);
+ if (n) {
+ lwsl_info("%s: getaddrinfo error: %s\n", __func__,
+ gai_strerror(n));
+ goto bail;
+ }
+
+ for (rp = r; rp; rp = rp->ai_next) {
+ sock.sockfd = socket(rp->ai_family, rp->ai_socktype,
+ rp->ai_protocol);
+ if (sock.sockfd >= 0)
+ break;
+ }
+ if (!rp) {
+ lwsl_err("%s: unable to create INET socket\n", __func__);
+ goto bail1;
+ }
+
+ if ((flags & LWS_CAUDP_BIND) && bind(sock.sockfd, rp->ai_addr,
+#if defined(_WIN32)
+ (int)rp->ai_addrlen
+#else
+ rp->ai_addrlen
+#endif
+ ) == -1) {
+ lwsl_err("%s: bind failed\n", __func__);
+ goto bail2;
+ }
+
+ wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_SOCKET_UDP, sock,
+ protocol_name, parent_wsi);
+ if (!wsi)
+ lwsl_err("%s: udp adoption failed\n", __func__);
+
+bail2:
+ if (!wsi)
+ close((int)sock.sockfd);
+bail1:
+ freeaddrinfo(r);
+
+bail:
+ return wsi;
}
+#endif
+
+
+
static const char *hex = "0123456789ABCDEF";
LWS_VISIBLE LWS_EXTERN const char *
@@ -2458,6 +2737,27 @@ lws_json_purify(char *escaped, const char *string, int len)
return escaped;
}
+LWS_VISIBLE LWS_EXTERN void
+lws_filename_purify_inplace(char *filename)
+{
+ while (*filename) {
+
+ if (*filename == '.' && filename[1] == '.') {
+ *filename = '_';
+ filename[1] = '_';
+ }
+
+ if (*filename == ':' ||
+ *filename == '/' ||
+ *filename == '\\' ||
+ *filename == '$' ||
+ *filename == '%')
+ *filename = '_';
+
+ filename++;
+ }
+}
+
LWS_VISIBLE LWS_EXTERN const char *
lws_urlencode(char *escaped, const char *string, int len)
{
@@ -2570,32 +2870,42 @@ lws_snprintf(char *str, size_t size, const char *format, ...)
va_end(ap);
if (n >= (int)size)
- return size;
+ return (int)size;
return n;
}
+char *
+lws_strncpy(char *dest, const char *src, size_t size)
+{
+ strncpy(dest, src, size - 1);
+ dest[size - 1] = '\0';
+
+ return dest;
+}
+
LWS_VISIBLE LWS_EXTERN int
lws_is_cgi(struct lws *wsi) {
#ifdef LWS_WITH_CGI
- return !!wsi->cgi;
+ return !!wsi->http.cgi;
#else
return 0;
#endif
}
+const struct lws_protocol_vhost_options *
+lws_pvo_search(const struct lws_protocol_vhost_options *pvo, const char *name)
+{
+ while (pvo) {
+ if (!strcmp(pvo->name, name))
+ break;
+ pvo = pvo->next;
+ }
-#ifdef LWS_NO_EXTENSIONS
-LWS_EXTERN int
-lws_set_extension_option(struct lws *wsi, const char *ext_name,
- const char *opt_name, const char *opt_val)
-{
- return -1;
+ return pvo;
}
-#endif
-
void
lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs)
@@ -2619,11 +2929,34 @@ lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs)
}
}
+const char *
+lws_cmdline_option(int argc, const char **argv, const char *val)
+{
+ int n = (int)strlen(val), c = argc;
+
+ while (--c > 0) {
+
+ if (!strncmp(argv[c], val, n)) {
+ if (!*(argv[c] + n) && c < argc - 1) {
+ /* coverity treats unchecked argv as "tainted" */
+ if (!argv[c + 1] || strlen(argv[c + 1]) > 1024)
+ return NULL;
+ return argv[c + 1];
+ }
+
+ return argv[c] + n;
+ }
+ }
+
+ return NULL;
+}
+
#ifdef LWS_WITH_SERVER_STATUS
LWS_EXTERN int
lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
{
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
static const char * const prots[] = {
"http://",
"https://",
@@ -2633,6 +2966,7 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
">https://",
"callback://"
};
+#endif
char *orig = buf, *end = buf + len - 1, first = 1;
int n = 0;
@@ -2656,8 +2990,8 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
" \"h2_subs\":\"%lu\""
,
vh->name, vh->listen_port,
-#ifdef LWS_OPENSSL_SUPPORT
- vh->use_ssl,
+#if defined(LWS_WITH_TLS)
+ vh->tls.use_ssl & LCCSCF_USE_SSL,
#else
0,
#endif
@@ -2672,9 +3006,9 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
vh->conn_stats.h2_alpn,
vh->conn_stats.h2_subs
);
-
- if (vh->mount_list) {
- const struct lws_http_mount *m = vh->mount_list;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ if (vh->http.mount_list) {
+ const struct lws_http_mount *m = vh->http.mount_list;
buf += lws_snprintf(buf, end - buf, ",\n \"mounts\":[");
while (m) {
@@ -2705,7 +3039,7 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
}
buf += lws_snprintf(buf, end - buf, "\n ]");
}
-
+#endif
if (vh->protocols) {
n = 0;
first = 1;
@@ -2798,8 +3132,8 @@ lws_json_dump_context(const struct lws_context *context, char *buf, int len,
" \"ah_wait_list\":\"%d\"\n"
" }",
pt->fds_count,
- pt->ah_count_in_use,
- pt->ah_wait_list_length);
+ pt->http.ah_count_in_use,
+ pt->http.ah_wait_list_length);
}
buf += lws_snprintf(buf, end - buf, "]");
@@ -2850,7 +3184,7 @@ lws_json_dump_context(const struct lws_context *context, char *buf, int len,
#ifdef LWS_WITH_CGI
for (n = 0; n < context->count_threads; n++) {
pt = &context->pt[n];
- pcgi = &pt->cgi_list;
+ pcgi = &pt->http.cgi_list;
while (*pcgi) {
pcgi = &(*pcgi)->cgi_list;
@@ -2898,63 +3232,117 @@ lws_stats_log_dump(struct lws_context *context)
lwsl_notice("\n");
lwsl_notice("LWS internal statistics dump ----->\n");
- lwsl_notice("LWSSTATS_C_CONNECTIONS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_CONNECTIONS));
- lwsl_notice("LWSSTATS_C_API_CLOSE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_CLOSE));
- lwsl_notice("LWSSTATS_C_API_READ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_READ));
- lwsl_notice("LWSSTATS_C_API_LWS_WRITE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_LWS_WRITE));
- lwsl_notice("LWSSTATS_C_API_WRITE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_WRITE));
- lwsl_notice("LWSSTATS_C_WRITE_PARTIALS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITE_PARTIALS));
- lwsl_notice("LWSSTATS_C_WRITEABLE_CB_REQ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB_REQ));
- lwsl_notice("LWSSTATS_C_WRITEABLE_CB_EFF_REQ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB_EFF_REQ));
- lwsl_notice("LWSSTATS_C_WRITEABLE_CB: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB));
- lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN));
- lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_FAILED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_FAILED));
- lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED));
- lwsl_notice("LWSSTATS_C_SSL_CONNS_HAD_RX: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNS_HAD_RX));
- lwsl_notice("LWSSTATS_C_PEER_LIMIT_AH_DENIED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_PEER_LIMIT_AH_DENIED));
- lwsl_notice("LWSSTATS_C_PEER_LIMIT_WSI_DENIED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_PEER_LIMIT_WSI_DENIED));
-
- lwsl_notice("LWSSTATS_C_TIMEOUTS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_TIMEOUTS));
- lwsl_notice("LWSSTATS_C_SERVICE_ENTRY: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SERVICE_ENTRY));
- lwsl_notice("LWSSTATS_B_READ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_B_READ));
- lwsl_notice("LWSSTATS_B_WRITE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_B_WRITE));
- lwsl_notice("LWSSTATS_B_PARTIALS_ACCEPTED_PARTS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS));
- lwsl_notice("LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY: %8llums\n", (unsigned long long)lws_stats_get(context, LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) / 1000);
+ lwsl_notice("LWSSTATS_C_CONNECTIONS: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_CONNECTIONS));
+ lwsl_notice("LWSSTATS_C_API_CLOSE: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_API_CLOSE));
+ lwsl_notice("LWSSTATS_C_API_READ: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_API_READ));
+ lwsl_notice("LWSSTATS_C_API_LWS_WRITE: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_API_LWS_WRITE));
+ lwsl_notice("LWSSTATS_C_API_WRITE: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_API_WRITE));
+ lwsl_notice("LWSSTATS_C_WRITE_PARTIALS: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_WRITE_PARTIALS));
+ lwsl_notice("LWSSTATS_C_WRITEABLE_CB_REQ: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_WRITEABLE_CB_REQ));
+ lwsl_notice("LWSSTATS_C_WRITEABLE_CB_EFF_REQ: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_WRITEABLE_CB_EFF_REQ));
+ lwsl_notice("LWSSTATS_C_WRITEABLE_CB: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_WRITEABLE_CB));
+ lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN));
+ lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_FAILED: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_SSL_CONNECTIONS_FAILED));
+ lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED));
+ lwsl_notice("LWSSTATS_C_SSL_CONNS_HAD_RX: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_SSL_CONNS_HAD_RX));
+ lwsl_notice("LWSSTATS_C_PEER_LIMIT_AH_DENIED: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_PEER_LIMIT_AH_DENIED));
+ lwsl_notice("LWSSTATS_C_PEER_LIMIT_WSI_DENIED: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_PEER_LIMIT_WSI_DENIED));
+
+ lwsl_notice("LWSSTATS_C_TIMEOUTS: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_TIMEOUTS));
+ lwsl_notice("LWSSTATS_C_SERVICE_ENTRY: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_C_SERVICE_ENTRY));
+ lwsl_notice("LWSSTATS_B_READ: %8llu\n",
+ (unsigned long long)lws_stats_get(context, LWSSTATS_B_READ));
+ lwsl_notice("LWSSTATS_B_WRITE: %8llu\n",
+ (unsigned long long)lws_stats_get(context, LWSSTATS_B_WRITE));
+ lwsl_notice("LWSSTATS_B_PARTIALS_ACCEPTED_PARTS: %8llu\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_B_PARTIALS_ACCEPTED_PARTS));
+ lwsl_notice("LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY: %8llums\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) / 1000);
if (lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED))
lwsl_notice(" Avg accept delay: %8llums\n",
- (unsigned long long)(lws_stats_get(context, LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) /
- lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)) / 1000);
- lwsl_notice("LWSSTATS_MS_SSL_RX_DELAY: %8llums\n", (unsigned long long)lws_stats_get(context, LWSSTATS_MS_SSL_RX_DELAY) / 1000);
+ (unsigned long long)(lws_stats_get(context,
+ LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) /
+ lws_stats_get(context,
+ LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)) / 1000);
+ lwsl_notice("LWSSTATS_MS_SSL_RX_DELAY: %8llums\n",
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_MS_SSL_RX_DELAY) / 1000);
if (lws_stats_get(context, LWSSTATS_C_SSL_CONNS_HAD_RX))
lwsl_notice(" Avg accept-rx delay: %8llums\n",
- (unsigned long long)(lws_stats_get(context, LWSSTATS_MS_SSL_RX_DELAY) /
- lws_stats_get(context, LWSSTATS_C_SSL_CONNS_HAD_RX)) / 1000);
+ (unsigned long long)(lws_stats_get(context,
+ LWSSTATS_MS_SSL_RX_DELAY) /
+ lws_stats_get(context,
+ LWSSTATS_C_SSL_CONNS_HAD_RX)) / 1000);
lwsl_notice("LWSSTATS_MS_WRITABLE_DELAY: %8lluus\n",
- (unsigned long long)lws_stats_get(context, LWSSTATS_MS_WRITABLE_DELAY));
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_MS_WRITABLE_DELAY));
lwsl_notice("LWSSTATS_MS_WORST_WRITABLE_DELAY: %8lluus\n",
- (unsigned long long)lws_stats_get(context, LWSSTATS_MS_WORST_WRITABLE_DELAY));
+ (unsigned long long)lws_stats_get(context,
+ LWSSTATS_MS_WORST_WRITABLE_DELAY));
if (lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB))
lwsl_notice(" Avg writable delay: %8lluus\n",
- (unsigned long long)(lws_stats_get(context, LWSSTATS_MS_WRITABLE_DELAY) /
+ (unsigned long long)(lws_stats_get(context,
+ LWSSTATS_MS_WRITABLE_DELAY) /
lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB)));
- lwsl_notice("Simultaneous SSL restriction: %8d/%d/%d\n", context->simultaneous_ssl,
- context->simultaneous_ssl_restriction, context->ssl_gate_accepts);
+ lwsl_notice("Simultaneous SSL restriction: %8d/%d\n",
+ context->simultaneous_ssl,
+ context->simultaneous_ssl_restriction);
- lwsl_notice("Live wsi: %8d\n", context->count_wsi_allocated);
+ lwsl_notice("Live wsi: %8d\n",
+ context->count_wsi_allocated);
context->updated = 1;
while (v) {
- if (v->lserv_wsi) {
+ if (v->lserv_wsi &&
+ v->lserv_wsi->position_in_fds_table != LWS_NO_FDS_POS) {
- struct lws_context_per_thread *pt = &context->pt[(int)v->lserv_wsi->tsi];
+ struct lws_context_per_thread *pt =
+ &context->pt[(int)v->lserv_wsi->tsi];
struct lws_pollfd *pfd;
pfd = &pt->fds[v->lserv_wsi->position_in_fds_table];
lwsl_notice(" Listen port %d actual POLLIN: %d\n",
- v->listen_port, (int)pfd->events & LWS_POLLIN);
+ v->listen_port,
+ (int)pfd->events & LWS_POLLIN);
}
v = v->vhost_next;
@@ -2967,20 +3355,20 @@ lws_stats_log_dump(struct lws_context *context)
lwsl_notice("PT %d\n", n + 1);
- lws_pt_lock(pt);
+ lws_pt_lock(pt, __func__);
lwsl_notice(" AH in use / max: %d / %d\n",
- pt->ah_count_in_use,
+ pt->http.ah_count_in_use,
context->max_http_header_pool);
- wl = pt->ah_wait_list;
+ wl = pt->http.ah_wait_list;
while (wl) {
m++;
- wl = wl->u.hdr.ah_wait_list;
+ wl = wl->ah_wait_list;
}
lwsl_notice(" AH wait list count / actual: %d / %d\n",
- pt->ah_wait_list_length, m);
+ pt->http.ah_wait_list_length, m);
lws_pt_unlock(pt);
}
@@ -3004,15 +3392,20 @@ lws_stats_log_dump(struct lws_context *context)
for (n = 0; n < (int)context->pl_hash_elements; n++) {
char buf[72];
- lws_start_foreach_llp(struct lws_peer **, peer, context->pl_hash_table[n]) {
+ lws_start_foreach_llp(struct lws_peer **, peer,
+ context->pl_hash_table[n]) {
struct lws_peer *df = *peer;
if (!lws_plat_inet_ntop(df->af, df->addr, buf,
sizeof(buf) - 1))
strcpy(buf, "unknown");
-
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
lwsl_notice(" peer %s: count wsi: %d, count ah: %d\n",
buf, df->count_wsi, df->count_ah);
+#else
+ lwsl_notice(" peer %s: count wsi: %d\n",
+ buf, df->count_wsi);
+#endif
if (!--m)
break;
@@ -3028,23 +3421,23 @@ void
lws_stats_atomic_bump(struct lws_context * context,
struct lws_context_per_thread *pt, int index, uint64_t bump)
{
- lws_pt_lock(pt);
+ lws_pt_stats_lock(pt);
context->lws_stats[index] += bump;
if (index != LWSSTATS_C_SERVICE_ENTRY)
context->updated = 1;
- lws_pt_unlock(pt);
+ lws_pt_stats_unlock(pt);
}
void
lws_stats_atomic_max(struct lws_context * context,
struct lws_context_per_thread *pt, int index, uint64_t val)
{
- lws_pt_lock(pt);
+ lws_pt_stats_lock(pt);
if (val > context->lws_stats[index]) {
context->lws_stats[index] = val;
context->updated = 1;
}
- lws_pt_unlock(pt);
+ lws_pt_stats_unlock(pt);
}
#endif
diff --git a/thirdparty/libwebsockets/core/output.c b/thirdparty/libwebsockets/core/output.c
new file mode 100644
index 0000000000..e2ff18ef27
--- /dev/null
+++ b/thirdparty/libwebsockets/core/output.c
@@ -0,0 +1,308 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+
+/*
+ * notice this returns number of bytes consumed, or -1
+ */
+int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
+{
+ struct lws_context *context = lws_get_context(wsi);
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ size_t real_len = len;
+ unsigned int n;
+
+ // lwsl_hexdump_err(buf, len);
+
+ /*
+ * Detect if we got called twice without going through the
+ * event loop to handle pending. This would be caused by either
+ * back-to-back writes in one WRITABLE (illegal) or calling lws_write()
+ * from outside the WRITABLE callback (illegal).
+ */
+ if (wsi->could_have_pending) {
+ lwsl_hexdump_level(LLL_ERR, buf, len);
+ lwsl_err("** %p: vh: %s, prot: %s, role %s: "
+ "Illegal back-to-back write of %lu detected...\n",
+ wsi, wsi->vhost->name, wsi->protocol->name,
+ wsi->role_ops->name,
+ (unsigned long)len);
+ // assert(0);
+
+ return -1;
+ }
+
+ lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1);
+
+ if (!len)
+ return 0;
+ /* just ignore sends after we cleared the truncation buffer */
+ if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE && !wsi->trunc_len)
+ return (int)len;
+
+ if (wsi->trunc_len && (buf < wsi->trunc_alloc ||
+ buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) {
+ lwsl_hexdump_level(LLL_ERR, buf, len);
+ lwsl_err("** %p: vh: %s, prot: %s, Sending new %lu, pending truncated ...\n"
+ " It's illegal to do an lws_write outside of\n"
+ " the writable callback: fix your code\n",
+ wsi, wsi->vhost->name, wsi->protocol->name,
+ (unsigned long)len);
+ assert(0);
+
+ return -1;
+ }
+
+ if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd))
+ lwsl_warn("** error invalid sock but expected to send\n");
+
+ /* limit sending */
+ if (wsi->protocol->tx_packet_size)
+ n = (int)wsi->protocol->tx_packet_size;
+ else {
+ n = (int)wsi->protocol->rx_buffer_size;
+ if (!n)
+ n = context->pt_serv_buf_size;
+ }
+ n += LWS_PRE + 4;
+ if (n > len)
+ n = (int)len;
+
+ /* nope, send it on the socket directly */
+ lws_latency_pre(context, wsi);
+ n = lws_ssl_capable_write(wsi, buf, n);
+ lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
+
+ /* something got written, it can have been truncated now */
+ wsi->could_have_pending = 1;
+
+ switch (n) {
+ case LWS_SSL_CAPABLE_ERROR:
+ /* we're going to close, let close know sends aren't possible */
+ wsi->socket_is_permanently_unusable = 1;
+ return -1;
+ case LWS_SSL_CAPABLE_MORE_SERVICE:
+ /*
+ * nothing got sent, not fatal. Retry the whole thing later,
+ * ie, implying treat it was a truncated send so it gets
+ * retried
+ */
+ n = 0;
+ break;
+ }
+
+ /*
+ * we were already handling a truncated send?
+ */
+ if (wsi->trunc_len) {
+ lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len);
+ wsi->trunc_offset += n;
+ wsi->trunc_len -= n;
+
+ if (!wsi->trunc_len) {
+ lwsl_info("** %p partial send completed\n", wsi);
+ /* done with it, but don't free it */
+ n = (int)real_len;
+ if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
+ lwsl_info("** %p signalling to close now\n", wsi);
+ return -1; /* retry closing now */
+ }
+ }
+ /* always callback on writeable */
+ lws_callback_on_writable(wsi);
+
+ return n;
+ }
+
+ if ((unsigned int)n == real_len)
+ /* what we just sent went out cleanly */
+ return n;
+
+ /*
+ * Newly truncated send. Buffer the remainder (it will get
+ * first priority next time the socket is writable).
+ */
+ lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n,
+ (unsigned long)real_len);
+
+ lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1);
+ lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n);
+
+ /*
+ * - if we still have a suitable malloc lying around, use it
+ * - or, if too small, reallocate it
+ * - or, if no buffer, create it
+ */
+ if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) {
+ lws_free(wsi->trunc_alloc);
+
+ wsi->trunc_alloc_len = (unsigned int)(real_len - n);
+ wsi->trunc_alloc = lws_malloc(real_len - n,
+ "truncated send alloc");
+ if (!wsi->trunc_alloc) {
+ lwsl_err("truncated send: unable to malloc %lu\n",
+ (unsigned long)(real_len - n));
+ return -1;
+ }
+ }
+ wsi->trunc_offset = 0;
+ wsi->trunc_len = (unsigned int)(real_len - n);
+ memcpy(wsi->trunc_alloc, buf + n, real_len - n);
+
+#if !defined(LWS_WITH_ESP32)
+ if (lws_wsi_is_udp(wsi)) {
+ /* stash original destination for fulfilling UDP partials */
+ wsi->udp->sa_pending = wsi->udp->sa;
+ wsi->udp->salen_pending = wsi->udp->salen;
+ }
+#endif
+
+ /* since something buffered, force it to get another chance to send */
+ lws_callback_on_writable(wsi);
+
+ return (int)real_len;
+}
+
+LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
+ enum lws_write_protocol wp)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+ if (wsi->parent_carries_io) {
+ struct lws_write_passthru pas;
+
+ pas.buf = buf;
+ pas.len = len;
+ pas.wp = wp;
+ pas.wsi = wsi;
+
+ if (wsi->parent->protocol->callback(wsi->parent,
+ LWS_CALLBACK_CHILD_WRITE_VIA_PARENT,
+ wsi->parent->user_space,
+ (void *)&pas, 0))
+ return 1;
+
+ return (int)len;
+ }
+
+ lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1);
+
+ if ((int)len < 0) {
+ lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__,
+ (int)len, (unsigned long)len);
+ return -1;
+ }
+
+ lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_WRITE, len);
+
+#ifdef LWS_WITH_ACCESS_LOG
+ wsi->http.access_log.sent += len;
+#endif
+ if (wsi->vhost)
+ wsi->vhost->conn_stats.tx += len;
+
+ assert(wsi->role_ops);
+ if (!wsi->role_ops->write_role_protocol)
+ return lws_issue_raw(wsi, buf, len);
+
+ return wsi->role_ops->write_role_protocol(wsi, buf, len, &wp);
+}
+
+LWS_VISIBLE int
+lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
+{
+ struct lws_context *context = wsi->context;
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ int n = 0;
+
+ lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
+
+ if (lws_wsi_is_udp(wsi)) {
+#if !defined(LWS_WITH_ESP32)
+ wsi->udp->salen = sizeof(wsi->udp->sa);
+ n = recvfrom(wsi->desc.sockfd, (char *)buf, len, 0,
+ &wsi->udp->sa, &wsi->udp->salen);
+#endif
+ } else
+ n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
+
+ if (n >= 0) {
+ if (wsi->vhost)
+ wsi->vhost->conn_stats.rx += n;
+ lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
+
+ return n;
+ }
+
+ if (LWS_ERRNO == LWS_EAGAIN ||
+ LWS_ERRNO == LWS_EWOULDBLOCK ||
+ LWS_ERRNO == LWS_EINTR)
+ return LWS_SSL_CAPABLE_MORE_SERVICE;
+
+ lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO);
+ return LWS_SSL_CAPABLE_ERROR;
+}
+
+LWS_VISIBLE int
+lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
+{
+ int n = 0;
+
+ if (lws_wsi_is_udp(wsi)) {
+#if !defined(LWS_WITH_ESP32)
+ if (wsi->trunc_len)
+ n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa_pending, wsi->udp->salen_pending);
+ else
+ n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa, wsi->udp->salen);
+#endif
+ } else
+ n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
+// lwsl_info("%s: sent len %d result %d", __func__, len, n);
+ if (n >= 0)
+ return n;
+
+ if (LWS_ERRNO == LWS_EAGAIN ||
+ LWS_ERRNO == LWS_EWOULDBLOCK ||
+ LWS_ERRNO == LWS_EINTR) {
+ if (LWS_ERRNO == LWS_EWOULDBLOCK) {
+ lws_set_blocking_send(wsi);
+ }
+
+ return LWS_SSL_CAPABLE_MORE_SERVICE;
+ }
+
+ lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n",
+ len, wsi->desc.sockfd, n, LWS_ERRNO);
+
+ return LWS_SSL_CAPABLE_ERROR;
+}
+
+LWS_VISIBLE int
+lws_ssl_pending_no_ssl(struct lws *wsi)
+{
+ (void)wsi;
+#if defined(LWS_WITH_ESP32)
+ return 100;
+#else
+ return 0;
+#endif
+}
diff --git a/thirdparty/lws/pollfd.c b/thirdparty/libwebsockets/core/pollfd.c
index 54a4a86057..2a632ce8ec 100644
--- a/thirdparty/lws/pollfd.c
+++ b/thirdparty/libwebsockets/core/pollfd.c
@@ -19,21 +19,31 @@
* MA 02110-1301 USA
*/
-#include "private-libwebsockets.h"
+#include "core/private.h"
int
_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
{
+#if !defined(LWS_WITH_LIBUV) && !defined(LWS_WITH_LIBEV) && !defined(LWS_WITH_LIBEVENT)
+ volatile struct lws_context_per_thread *vpt;
+#endif
struct lws_context_per_thread *pt;
struct lws_context *context;
int ret = 0, pa_events = 1;
struct lws_pollfd *pfd;
int sampled_tid, tid;
- if (!wsi || wsi->position_in_fds_table < 0)
+ if (!wsi)
+ return 0;
+
+ assert(wsi->position_in_fds_table == LWS_NO_FDS_POS ||
+ wsi->position_in_fds_table >= 0);
+
+ if (wsi->position_in_fds_table == LWS_NO_FDS_POS)
return 0;
- if (wsi->handling_pollout && !_and && _or == LWS_POLLOUT) {
+ if (((volatile struct lws *)wsi)->handling_pollout &&
+ !_and && _or == LWS_POLLOUT) {
/*
* Happening alongside service thread handling POLLOUT.
* The danger is when he is finished, he will disable POLLOUT,
@@ -42,7 +52,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
* Instead of changing the fds, inform the service thread
* what happened, and ask it to leave POLLOUT active on exit
*/
- wsi->leave_pollout_active = 1;
+ ((volatile struct lws *)wsi)->leave_pollout_active = 1;
/*
* by definition service thread is not in poll wait, so no need
* to cancel service
@@ -55,42 +65,106 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
context = wsi->context;
pt = &context->pt[(int)wsi->tsi];
- assert(wsi->position_in_fds_table >= 0 &&
- wsi->position_in_fds_table < pt->fds_count);
+
+ assert(wsi->position_in_fds_table < (int)pt->fds_count);
+
+#if !defined(LWS_WITH_LIBUV) && \
+ !defined(LWS_WITH_LIBEV) && \
+ !defined(LWS_WITH_LIBEVENT)
+ /*
+ * This only applies when we use the default poll() event loop.
+ *
+ * BSD can revert pa->events at any time, when the kernel decides to
+ * exit from poll(). We can't protect against it using locking.
+ *
+ * Therefore we must check first if the service thread is in poll()
+ * wait; if so, we know we must be being called from a foreign thread,
+ * and we must keep a strictly ordered list of changes we made instead
+ * of trying to apply them, since when poll() exits, which may happen
+ * at any time it would revert our changes.
+ *
+ * The plat code will apply them when it leaves the poll() wait
+ * before doing anything else.
+ */
+
+ vpt = (volatile struct lws_context_per_thread *)pt;
+
+ vpt->foreign_spinlock = 1;
+ lws_memory_barrier();
+
+ if (vpt->inside_poll) {
+ struct lws_foreign_thread_pollfd *ftp, **ftp1;
+ /*
+ * We are certainly a foreign thread trying to change events
+ * while the service thread is in the poll() wait.
+ *
+ * Create a list of changes to be applied after poll() exit,
+ * instead of trying to apply them now.
+ */
+ ftp = lws_malloc(sizeof(*ftp), "ftp");
+ if (!ftp) {
+ vpt->foreign_spinlock = 0;
+ lws_memory_barrier();
+ ret = -1;
+ goto bail;
+ }
+
+ ftp->_and = _and;
+ ftp->_or = _or;
+ ftp->fd_index = wsi->position_in_fds_table;
+ ftp->next = NULL;
+
+ /* place at END of list to maintain order */
+ ftp1 = (struct lws_foreign_thread_pollfd **)
+ &vpt->foreign_pfd_list;
+ while (*ftp1)
+ ftp1 = &((*ftp1)->next);
+
+ *ftp1 = ftp;
+ vpt->foreign_spinlock = 0;
+ lws_memory_barrier();
+ lws_cancel_service_pt(wsi);
+
+ return 0;
+ }
+
+ vpt->foreign_spinlock = 0;
+ lws_memory_barrier();
+#endif
pfd = &pt->fds[wsi->position_in_fds_table];
pa->fd = wsi->desc.sockfd;
+ lwsl_debug("%s: wsi %p: fd %d events %d -> %d\n", __func__, wsi, pa->fd, pfd->events, (pfd->events & ~_and) | _or);
pa->prev_events = pfd->events;
pa->events = pfd->events = (pfd->events & ~_and) | _or;
if (wsi->http2_substream)
return 0;
- if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD,
- wsi->user_space, (void *)pa, 0)) {
+ if (wsi->vhost &&
+ wsi->vhost->protocols[0].callback(wsi,
+ LWS_CALLBACK_CHANGE_MODE_POLL_FD,
+ wsi->user_space, (void *)pa, 0)) {
ret = -1;
goto bail;
}
- if (_and & LWS_POLLIN) {
- lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ);
- lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ);
- lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ);
- }
- if (_or & LWS_POLLIN) {
- lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
- lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
- lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
- }
- if (_and & LWS_POLLOUT) {
- lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
- lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
- lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
- }
- if (_or & LWS_POLLOUT) {
- lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE);
- lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE);
- lws_libevent_io(wsi, LWS_EV_START | LWS_EV_WRITE);
+ if (context->event_loop_ops->io) {
+ if (_and & LWS_POLLIN)
+ context->event_loop_ops->io(wsi,
+ LWS_EV_STOP | LWS_EV_READ);
+
+ if (_or & LWS_POLLIN)
+ context->event_loop_ops->io(wsi,
+ LWS_EV_START | LWS_EV_READ);
+
+ if (_and & LWS_POLLOUT)
+ context->event_loop_ops->io(wsi,
+ LWS_EV_STOP | LWS_EV_WRITE);
+
+ if (_or & LWS_POLLOUT)
+ context->event_loop_ops->io(wsi,
+ LWS_EV_START | LWS_EV_WRITE);
}
/*
@@ -100,20 +174,16 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
* ... and the service thread is waiting ...
* then cancel it to force a restart with our changed events
*/
-#if LWS_POSIX
pa_events = pa->prev_events != pa->events;
-#endif
if (pa_events) {
-
if (lws_plat_change_pollfd(context, wsi, pfd)) {
lwsl_info("%s failed\n", __func__);
ret = -1;
goto bail;
}
-
sampled_tid = context->service_tid;
- if (sampled_tid) {
+ if (sampled_tid && wsi->vhost) {
tid = wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
if (tid == -1) {
@@ -124,34 +194,39 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
lws_cancel_service_pt(wsi);
}
}
+
bail:
return ret;
}
#ifndef LWS_NO_SERVER
+/*
+ * Enable or disable listen sockets on this pt globally...
+ * it's modulated according to the pt having space for a new accept.
+ */
static void
-lws_accept_modulation(struct lws_context_per_thread *pt, int allow)
+lws_accept_modulation(struct lws_context *context,
+ struct lws_context_per_thread *pt, int allow)
{
-// multithread listen seems broken
-#if 0
struct lws_vhost *vh = context->vhost_list;
struct lws_pollargs pa1;
while (vh) {
- if (allow)
- _lws_change_pollfd(pt->wsi_listening,
+ if (vh->lserv_wsi) {
+ if (allow)
+ _lws_change_pollfd(vh->lserv_wsi,
0, LWS_POLLIN, &pa1);
- else
- _lws_change_pollfd(pt->wsi_listening,
+ else
+ _lws_change_pollfd(vh->lserv_wsi,
LWS_POLLIN, 0, &pa1);
+ }
vh = vh->vhost_next;
}
-#endif
}
#endif
int
-insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
+__insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
{
struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 };
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
@@ -167,52 +242,46 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
return 1;
}
-#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
- if (wsi->desc.sockfd >= context->max_fds) {
- lwsl_err("Socket fd %d is too high (%d)\n",
- wsi->desc.sockfd, context->max_fds);
+#if !defined(_WIN32)
+ if (wsi->desc.sockfd - lws_plat_socket_offset() >= context->max_fds) {
+ lwsl_err("Socket fd %d is too high (%d) offset %d\n",
+ wsi->desc.sockfd, context->max_fds, lws_plat_socket_offset());
return 1;
}
#endif
assert(wsi);
- assert(wsi->vhost);
+ assert(wsi->event_pipe || wsi->vhost);
assert(lws_socket_is_valid(wsi->desc.sockfd));
- if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
+ if (wsi->vhost &&
+ wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 1))
return -1;
- lws_pt_lock(pt);
pt->count_conns++;
insert_wsi(context, wsi);
-#if defined(LWS_WITH_ESP8266)
- if (wsi->position_in_fds_table == -1)
-#endif
- wsi->position_in_fds_table = pt->fds_count;
+ wsi->position_in_fds_table = pt->fds_count;
pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd;
-#if LWS_POSIX
pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN;
-#else
- pt->fds[wsi->position_in_fds_table].events = 0;
-#endif
pa.events = pt->fds[pt->fds_count].events;
lws_plat_insert_socket_into_fds(context, wsi);
/* external POLL support via protocol 0 */
- if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
+ if (wsi->vhost &&
+ wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
wsi->user_space, (void *) &pa, 0))
ret = -1;
#ifndef LWS_NO_SERVER
/* if no more room, defeat accepts on this thread */
if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1)
- lws_accept_modulation(pt, 0);
+ lws_accept_modulation(context, pt, 0);
#endif
- lws_pt_unlock(pt);
- if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
+ if (wsi->vhost &&
+ wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *)&pa, 1))
ret = -1;
@@ -220,15 +289,13 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
}
int
-remove_wsi_socket_from_fds(struct lws *wsi)
+__remove_wsi_socket_from_fds(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_pollargs pa = { wsi->desc.sockfd, 0, 0 };
-#if !defined(LWS_WITH_ESP8266)
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws *end_wsi;
int v;
-#endif
int m, ret = 0;
if (wsi->parent_carries_io) {
@@ -236,15 +303,16 @@ remove_wsi_socket_from_fds(struct lws *wsi)
return 0;
}
-#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
- if (wsi->desc.sockfd > context->max_fds) {
+#if !defined(_WIN32)
+ if (wsi->desc.sockfd - lws_plat_socket_offset() > context->max_fds) {
lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd,
context->max_fds);
return 1;
}
#endif
- if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
+ if (wsi->vhost &&
+ wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *)&pa, 1))
return -1;
@@ -253,101 +321,114 @@ remove_wsi_socket_from_fds(struct lws *wsi)
/* the guy who is to be deleted's slot index in pt->fds */
m = wsi->position_in_fds_table;
-#if !defined(LWS_WITH_ESP8266)
- lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE |
- LWS_EV_PREPARE_DELETION);
- lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE |
- LWS_EV_PREPARE_DELETION);
+ /* these are the only valid possibilities for position_in_fds_table */
+ assert(m == LWS_NO_FDS_POS || (m >= 0 &&
+ (unsigned int)m < pt->fds_count));
- lws_pt_lock(pt);
+ if (context->event_loop_ops->io)
+ context->event_loop_ops->io(wsi,
+ LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE |
+ LWS_EV_PREPARE_DELETION);
lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n",
__func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table,
pt->fds_count, pt->fds[pt->fds_count].fd);
- /* have the last guy take up the now vacant slot */
- pt->fds[m] = pt->fds[pt->fds_count - 1];
-#endif
- /* this decrements pt->fds_count */
- lws_plat_delete_socket_from_fds(context, wsi, m);
-#if !defined(LWS_WITH_ESP8266)
- v = (int) pt->fds[m].fd;
- /* end guy's "position in fds table" is now the deletion guy's old one */
- end_wsi = wsi_from_fd(context, v);
- if (!end_wsi) {
- lwsl_err("no wsi found for sock fd %d at pos %d, pt->fds_count=%d\n",
- (int)pt->fds[m].fd, m, pt->fds_count);
- assert(0);
- } else
- end_wsi->position_in_fds_table = m;
-
- /* deletion guy's lws_lookup entry needs nuking */
- delete_from_fd(context, wsi->desc.sockfd);
- /* removed wsi has no position any more */
- wsi->position_in_fds_table = -1;
+ if (m != LWS_NO_FDS_POS) {
+
+ /* have the last guy take up the now vacant slot */
+ pt->fds[m] = pt->fds[pt->fds_count - 1];
+ /* this decrements pt->fds_count */
+ lws_plat_delete_socket_from_fds(context, wsi, m);
+ v = (int) pt->fds[m].fd;
+ /* end guy's "position in fds table" is now the deletion guy's old one */
+ end_wsi = wsi_from_fd(context, v);
+ if (!end_wsi) {
+ lwsl_err("no wsi for fd %d at pos %d, pt->fds_count=%d\n",
+ (int)pt->fds[m].fd, m, pt->fds_count);
+ assert(0);
+ } else
+ end_wsi->position_in_fds_table = m;
+
+ /* deletion guy's lws_lookup entry needs nuking */
+ delete_from_fd(context, wsi->desc.sockfd);
+
+ /* removed wsi has no position any more */
+ wsi->position_in_fds_table = LWS_NO_FDS_POS;
+ }
/* remove also from external POLL support via protocol 0 */
- if (lws_socket_is_valid(wsi->desc.sockfd))
- if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
- wsi->user_space, (void *) &pa, 0))
- ret = -1;
+ if (lws_socket_is_valid(wsi->desc.sockfd) && wsi->vhost &&
+ wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
+ wsi->user_space, (void *) &pa, 0))
+ ret = -1;
+
#ifndef LWS_NO_SERVER
- if (!context->being_destroyed)
- /* if this made some room, accept connects on this thread */
- if ((unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
- lws_accept_modulation(pt, 1);
+ if (!context->being_destroyed &&
+ /* if this made some room, accept connects on this thread */
+ (unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
+ lws_accept_modulation(context, pt, 1);
#endif
- lws_pt_unlock(pt);
- if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
+ if (wsi->vhost &&
+ wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 1))
ret = -1;
-#endif
+
return ret;
}
int
-lws_change_pollfd(struct lws *wsi, int _and, int _or)
+__lws_change_pollfd(struct lws *wsi, int _and, int _or)
{
- struct lws_context_per_thread *pt;
struct lws_context *context;
struct lws_pollargs pa;
int ret = 0;
- if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0)
- return 1;
+ if (!wsi || (!wsi->protocol && !wsi->event_pipe) ||
+ wsi->position_in_fds_table == LWS_NO_FDS_POS)
+ return 0;
context = lws_get_context(wsi);
if (!context)
return 1;
- if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
- wsi->user_space, (void *) &pa, 0))
+ if (wsi->vhost &&
+ wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
+ wsi->user_space, (void *) &pa, 0))
return -1;
- pt = &context->pt[(int)wsi->tsi];
-
- lws_pt_lock(pt);
ret = _lws_change_pollfd(wsi, _and, _or, &pa);
- lws_pt_unlock(pt);
- if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
+ if (wsi->vhost &&
+ wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 0))
ret = -1;
return ret;
}
+int
+lws_change_pollfd(struct lws *wsi, int _and, int _or)
+{
+ struct lws_context_per_thread *pt;
+ int ret = 0;
+
+ pt = &wsi->context->pt[(int)wsi->tsi];
+
+ lws_pt_lock(pt, __func__);
+ ret = __lws_change_pollfd(wsi, _and, _or);
+ lws_pt_unlock(pt);
+
+ return ret;
+}
+
LWS_VISIBLE int
lws_callback_on_writable(struct lws *wsi)
{
struct lws_context_per_thread *pt;
-#ifdef LWS_WITH_HTTP2
- struct lws *network_wsi, *wsi2;
- int already;
-#endif
int n;
- if (wsi->state == LWSS_SHUTDOWN)
+ if (lwsi_state(wsi) == LRS_SHUTDOWN)
return 0;
if (wsi->socket_is_permanently_unusable)
@@ -375,72 +456,31 @@ lws_callback_on_writable(struct lws *wsi)
#if defined(LWS_WITH_STATS)
if (!wsi->active_writable_req_us) {
wsi->active_writable_req_us = time_in_microseconds();
- lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
}
#endif
-#ifdef LWS_WITH_HTTP2
- lwsl_info("%s: %p\n", __func__, wsi);
-
- if (wsi->mode != LWSCM_HTTP2_SERVING)
- goto network_sock;
- if (wsi->u.h2.requested_POLLOUT) {
- lwsl_info("already pending writable\n");
- return 1;
+ if (wsi->role_ops->callback_on_writable) {
+ if (wsi->role_ops->callback_on_writable(wsi))
+ return 1;
+ wsi = lws_get_network_wsi(wsi);
}
- /* is this for DATA or for control messages? */
- if (wsi->upgraded_to_http2 && !wsi->u.h2.h2n->pps &&
- !lws_h2_tx_cr_get(wsi)) {
- /*
- * other side is not able to cope with us sending DATA
- * anything so no matter if we have POLLOUT on our side if it's
- * DATA we want to send.
- *
- * Delay waiting for our POLLOUT until peer indicates he has
- * space for more using tx window command in http2 layer
- */
- lwsl_notice("%s: %p: skint (%d)\n", __func__, wsi, wsi->u.h2.tx_cr);
- wsi->u.h2.skint = 1;
- return 0;
- }
-
- wsi->u.h2.skint = 0;
- network_wsi = lws_get_network_wsi(wsi);
- already = network_wsi->u.h2.requested_POLLOUT;
-
- /* mark everybody above him as requesting pollout */
-
- wsi2 = wsi;
- while (wsi2) {
- wsi2->u.h2.requested_POLLOUT = 1;
- lwsl_info("mark %p pending writable\n", wsi2);
- wsi2 = wsi2->u.h2.parent_wsi;
- }
-
- /* for network action, act only on the network wsi */
-
- wsi = network_wsi;
- if (already)
- return 1;
-network_sock:
-#endif
-
- if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0))
- return 1;
-
- if (wsi->position_in_fds_table < 0) {
- lwsl_debug("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
+ if (wsi->position_in_fds_table == LWS_NO_FDS_POS) {
+ lwsl_debug("%s: failed to find socket %d\n", __func__,
+ wsi->desc.sockfd);
return -1;
}
- if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
+ if (__lws_change_pollfd(wsi, 0, LWS_POLLOUT))
return -1;
return 1;
}
+
/*
* stitch protocol choice into the vh protocol linked list
* We always insert ourselves at the start of the list
@@ -458,6 +498,8 @@ lws_same_vh_protocol_insert(struct lws *wsi, int n)
lwsl_notice("Attempted to attach wsi twice to same vh prot\n");
}
+ lws_vhost_lock(wsi->vhost);
+
wsi->same_vh_protocol_prev = &wsi->vhost->same_vh_protocol_list[n];
/* old first guy is our next */
wsi->same_vh_protocol_next = wsi->vhost->same_vh_protocol_list[n];
@@ -468,6 +510,10 @@ lws_same_vh_protocol_insert(struct lws *wsi, int n)
/* old first guy points back to us now */
wsi->same_vh_protocol_next->same_vh_protocol_prev =
&wsi->same_vh_protocol_next;
+
+ wsi->on_same_vh_list = 1;
+
+ lws_vhost_unlock(wsi->vhost);
}
void
@@ -482,6 +528,11 @@ lws_same_vh_protocol_remove(struct lws *wsi)
*/
lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi);
+ if (!wsi->vhost || !wsi->on_same_vh_list)
+ return;
+
+ lws_vhost_lock(wsi->vhost);
+
if (wsi->same_vh_protocol_prev) {
assert (*(wsi->same_vh_protocol_prev) == wsi);
lwsl_info("have prev %p, setting him to our next %p\n",
@@ -493,13 +544,15 @@ lws_same_vh_protocol_remove(struct lws *wsi)
}
/* our next should point back to our prev */
- if (wsi->same_vh_protocol_next) {
+ if (wsi->same_vh_protocol_next)
wsi->same_vh_protocol_next->same_vh_protocol_prev =
wsi->same_vh_protocol_prev;
- }
wsi->same_vh_protocol_prev = NULL;
wsi->same_vh_protocol_next = NULL;
+ wsi->on_same_vh_list = 0;
+
+ lws_vhost_unlock(wsi->vhost);
}
@@ -523,7 +576,8 @@ lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
assert(wsi->protocol == protocol);
assert(*wsi->same_vh_protocol_prev == wsi);
if (wsi->same_vh_protocol_next)
- assert(wsi->same_vh_protocol_next->same_vh_protocol_prev ==
+ assert(wsi->same_vh_protocol_next->
+ same_vh_protocol_prev ==
&wsi->same_vh_protocol_next);
lws_callback_on_writable(wsi);
diff --git a/thirdparty/libwebsockets/core/private.h b/thirdparty/libwebsockets/core/private.h
new file mode 100644
index 0000000000..d6b494ac8c
--- /dev/null
+++ b/thirdparty/libwebsockets/core/private.h
@@ -0,0 +1,1751 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "lws_config.h"
+#include "lws_config_private.h"
+
+#if defined(LWS_WITH_CGI) && defined(LWS_HAVE_VFORK)
+ #define _GNU_SOURCE
+#endif
+
+#if defined(__COVERITY__) && !defined(LWS_COVERITY_WORKAROUND)
+ #define LWS_COVERITY_WORKAROUND
+ typedef float _Float32;
+ typedef float _Float64;
+ typedef float _Float128;
+ typedef float _Float32x;
+ typedef float _Float64x;
+ typedef float _Float128x;
+#endif
+
+#ifdef LWS_HAVE_SYS_TYPES_H
+ #include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <inttypes.h>
+
+#if defined(LWS_WITH_ESP32)
+ #define MSG_NOSIGNAL 0
+ #define SOMAXCONN 3
+#endif
+
+#define STORE_IN_ROM
+#include <assert.h>
+#if LWS_MAX_SMP > 1
+ #include <pthread.h>
+#endif
+
+#ifdef LWS_HAVE_SYS_STAT_H
+ #include <sys/stat.h>
+#endif
+
+#if defined(WIN32) || defined(_WIN32)
+
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #endif
+
+ #if (WINVER < 0x0501)
+ #undef WINVER
+ #undef _WIN32_WINNT
+ #define WINVER 0x0501
+ #define _WIN32_WINNT WINVER
+ #endif
+
+ #define LWS_NO_DAEMONIZE
+ #define LWS_ERRNO WSAGetLastError()
+ #define LWS_EAGAIN WSAEWOULDBLOCK
+ #define LWS_EALREADY WSAEALREADY
+ #define LWS_EINPROGRESS WSAEINPROGRESS
+ #define LWS_EINTR WSAEINTR
+ #define LWS_EISCONN WSAEISCONN
+ #define LWS_EWOULDBLOCK WSAEWOULDBLOCK
+ #define MSG_NOSIGNAL 0
+ #define SHUT_RDWR SD_BOTH
+ #define SOL_TCP IPPROTO_TCP
+ #define SHUT_WR SD_SEND
+
+ #define compatible_close(fd) closesocket(fd)
+ #define lws_set_blocking_send(wsi) wsi->sock_send_blocking = 1
+ #define LWS_SOCK_INVALID (INVALID_SOCKET)
+
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #include <windows.h>
+ #include <tchar.h>
+ #ifdef LWS_HAVE_IN6ADDR_H
+ #include <in6addr.h>
+ #endif
+ #include <mstcpip.h>
+ #include <io.h>
+
+ #if !defined(LWS_HAVE_ATOLL)
+ #if defined(LWS_HAVE__ATOI64)
+ #define atoll _atoi64
+ #else
+ #warning No atoll or _atoi64 available, using atoi
+ #define atoll atoi
+ #endif
+ #endif
+
+ #ifndef __func__
+ #define __func__ __FUNCTION__
+ #endif
+
+ #ifdef LWS_HAVE__VSNPRINTF
+ #define vsnprintf _vsnprintf
+ #endif
+
+ /* we don't have an implementation for this on windows... */
+ int kill(int pid, int sig);
+ int fork(void);
+ #ifndef SIGINT
+ #define SIGINT 2
+ #endif
+
+#else /* not windows --> */
+
+ #include <fcntl.h>
+ #include <strings.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+
+ #ifndef __cplusplus
+ #include <errno.h>
+ #endif
+ #include <netdb.h>
+ #include <signal.h>
+ #include <sys/socket.h>
+
+ #if defined(LWS_BUILTIN_GETIFADDRS)
+ #include "./misc/getifaddrs.h"
+ #else
+ #if !defined(LWS_WITH_ESP32)
+ #if defined(__HAIKU__)
+ #define _BSD_SOURCE
+ #endif
+ #include <ifaddrs.h>
+ #endif
+ #endif
+ #if defined (__ANDROID__)
+ #include <syslog.h>
+ #include <sys/resource.h>
+ #elif defined (__sun) || defined(__HAIKU__) || defined(__QNX__)
+ #include <syslog.h>
+ #else
+ #if !defined(LWS_WITH_ESP32)
+ #include <sys/syslog.h>
+ #endif
+ #endif
+ #include <netdb.h>
+ #if !defined(LWS_WITH_ESP32)
+ #include <sys/mman.h>
+ #include <sys/un.h>
+ #include <netinet/in.h>
+ #include <netinet/tcp.h>
+ #include <arpa/inet.h>
+ #include <poll.h>
+ #endif
+ #ifndef LWS_NO_FORK
+ #ifdef LWS_HAVE_SYS_PRCTL_H
+ #include <sys/prctl.h>
+ #endif
+ #endif
+
+ #include <sys/time.h>
+
+ #define LWS_ERRNO errno
+ #define LWS_EAGAIN EAGAIN
+ #define LWS_EALREADY EALREADY
+ #define LWS_EINPROGRESS EINPROGRESS
+ #define LWS_EINTR EINTR
+ #define LWS_EISCONN EISCONN
+ #define LWS_EWOULDBLOCK EWOULDBLOCK
+
+ #define lws_set_blocking_send(wsi)
+
+ #define LWS_SOCK_INVALID (-1)
+#endif /* not windows */
+
+#ifndef LWS_HAVE_BZERO
+ #ifndef bzero
+ #define bzero(b, len) (memset((b), '\0', (len)), (void) 0)
+ #endif
+#endif
+
+#ifndef LWS_HAVE_STRERROR
+ #define strerror(x) ""
+#endif
+
+
+#define lws_socket_is_valid(x) (x != LWS_SOCK_INVALID)
+
+#include "libwebsockets.h"
+
+#include "tls/private.h"
+
+#if defined(WIN32) || defined(_WIN32)
+ #include <gettimeofday.h>
+
+ #ifndef BIG_ENDIAN
+ #define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */
+ #endif
+ #ifndef LITTLE_ENDIAN
+ #define LITTLE_ENDIAN 1234
+ #endif
+ #ifndef BYTE_ORDER
+ #define BYTE_ORDER LITTLE_ENDIAN
+ #endif
+
+ #undef __P
+ #ifndef __P
+ #if __STDC__
+ #define __P(protos) protos
+ #else
+ #define __P(protos) ()
+ #endif
+ #endif
+
+#else /* not windows */
+ static inline int compatible_close(int fd) { return close(fd); }
+
+ #include <sys/stat.h>
+ #include <sys/time.h>
+
+ #if defined(__APPLE__)
+ #include <machine/endian.h>
+ #elif defined(__FreeBSD__)
+ #include <sys/endian.h>
+ #elif defined(__linux__)
+ #include <endian.h>
+ #endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__QNX__)
+ #include <gulliver.h>
+ #if defined(__LITTLEENDIAN__)
+ #define BYTE_ORDER __LITTLEENDIAN__
+ #define LITTLE_ENDIAN __LITTLEENDIAN__
+ #define BIG_ENDIAN 4321 /* to show byte order (taken from gcc); for suppres warning that BIG_ENDIAN is not defined. */
+ #endif
+ #if defined(__BIGENDIAN__)
+ #define BYTE_ORDER __BIGENDIAN__
+ #define LITTLE_ENDIAN 1234 /* to show byte order (taken from gcc); for suppres warning that LITTLE_ENDIAN is not defined. */
+ #define BIG_ENDIAN __BIGENDIAN__
+ #endif
+#endif
+
+#if defined(__sun) && defined(__GNUC__)
+
+ #include <arpa/nameser_compat.h>
+
+ #if !defined (BYTE_ORDER)
+ #define BYTE_ORDER __BYTE_ORDER__
+ #endif
+
+ #if !defined(LITTLE_ENDIAN)
+ #define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
+ #endif
+
+ #if !defined(BIG_ENDIAN)
+ #define BIG_ENDIAN __ORDER_BIG_ENDIAN__
+ #endif
+
+#endif /* sun + GNUC */
+
+#if !defined(BYTE_ORDER)
+ #define BYTE_ORDER __BYTE_ORDER
+#endif
+#if !defined(LITTLE_ENDIAN)
+ #define LITTLE_ENDIAN __LITTLE_ENDIAN
+#endif
+#if !defined(BIG_ENDIAN)
+ #define BIG_ENDIAN __BIG_ENDIAN
+#endif
+
+
+/*
+ * Mac OSX as well as iOS do not define the MSG_NOSIGNAL flag,
+ * but happily have something equivalent in the SO_NOSIGPIPE flag.
+ */
+#ifdef __APPLE__
+#define MSG_NOSIGNAL SO_NOSIGPIPE
+#endif
+
+/*
+ * Solaris 11.X only supports POSIX 2001, MSG_NOSIGNAL appears in
+ * POSIX 2008.
+ */
+#ifdef __sun
+ #define MSG_NOSIGNAL 0
+#endif
+
+#ifdef _WIN32
+ #ifndef FD_HASHTABLE_MODULUS
+ #define FD_HASHTABLE_MODULUS 32
+ #endif
+#endif
+
+#ifndef LWS_DEF_HEADER_LEN
+#define LWS_DEF_HEADER_LEN 4096
+#endif
+#ifndef LWS_DEF_HEADER_POOL
+#define LWS_DEF_HEADER_POOL 4
+#endif
+#ifndef LWS_MAX_PROTOCOLS
+#define LWS_MAX_PROTOCOLS 5
+#endif
+#ifndef LWS_MAX_EXTENSIONS_ACTIVE
+#define LWS_MAX_EXTENSIONS_ACTIVE 1
+#endif
+#ifndef LWS_MAX_EXT_OFFERS
+#define LWS_MAX_EXT_OFFERS 8
+#endif
+#ifndef SPEC_LATEST_SUPPORTED
+#define SPEC_LATEST_SUPPORTED 13
+#endif
+#ifndef AWAITING_TIMEOUT
+#define AWAITING_TIMEOUT 20
+#endif
+#ifndef CIPHERS_LIST_STRING
+#define CIPHERS_LIST_STRING "DEFAULT"
+#endif
+#ifndef LWS_SOMAXCONN
+#define LWS_SOMAXCONN SOMAXCONN
+#endif
+
+#define MAX_WEBSOCKET_04_KEY_LEN 128
+
+#ifndef SYSTEM_RANDOM_FILEPATH
+#define SYSTEM_RANDOM_FILEPATH "/dev/urandom"
+#endif
+
+#define LWS_H2_RX_SCRATCH_SIZE 512
+
+
+
+/*
+ * All lws_tls...() functions must return this type, converting the
+ * native backend result and doing the extra work to determine which one
+ * as needed.
+ *
+ * Native TLS backend return codes are NOT ALLOWED outside the backend.
+ *
+ * Non-SSL mode also uses these types.
+ */
+enum lws_ssl_capable_status {
+ LWS_SSL_CAPABLE_ERROR = -1, /* it failed */
+ LWS_SSL_CAPABLE_DONE = 0, /* it succeeded */
+ LWS_SSL_CAPABLE_MORE_SERVICE_READ = -2, /* retry WANT_READ */
+ LWS_SSL_CAPABLE_MORE_SERVICE_WRITE = -3, /* retry WANT_WRITE */
+ LWS_SSL_CAPABLE_MORE_SERVICE = -4, /* general retry */
+};
+
+#if defined(__clang__)
+#define lws_memory_barrier() __sync_synchronize()
+#elif defined(__GNUC__)
+#define lws_memory_barrier() __sync_synchronize()
+#else
+#define lws_memory_barrier()
+#endif
+
+/*
+ *
+ * ------ roles ------
+ *
+ */
+
+#include "roles/private.h"
+
+/* null-terminated array of pointers to roles lws built with */
+extern const struct lws_role_ops *available_roles[];
+
+#define LWS_FOR_EVERY_AVAILABLE_ROLE_START(xx) { \
+ const struct lws_role_ops **ppxx = available_roles; \
+ while (*ppxx) { \
+ const struct lws_role_ops *xx = *ppxx++;
+
+#define LWS_FOR_EVERY_AVAILABLE_ROLE_END }}
+
+/*
+ *
+ * ------ event_loop ops ------
+ *
+ */
+
+#include "event-libs/private.h"
+
+/* enums of socks version */
+enum socks_version {
+ SOCKS_VERSION_4 = 4,
+ SOCKS_VERSION_5 = 5
+};
+
+/* enums of subnegotiation version */
+enum socks_subnegotiation_version {
+ SOCKS_SUBNEGOTIATION_VERSION_1 = 1,
+};
+
+/* enums of socks commands */
+enum socks_command {
+ SOCKS_COMMAND_CONNECT = 1,
+ SOCKS_COMMAND_BIND = 2,
+ SOCKS_COMMAND_UDP_ASSOCIATE = 3
+};
+
+/* enums of socks address type */
+enum socks_atyp {
+ SOCKS_ATYP_IPV4 = 1,
+ SOCKS_ATYP_DOMAINNAME = 3,
+ SOCKS_ATYP_IPV6 = 4
+};
+
+/* enums of socks authentication methods */
+enum socks_auth_method {
+ SOCKS_AUTH_NO_AUTH = 0,
+ SOCKS_AUTH_GSSAPI = 1,
+ SOCKS_AUTH_USERNAME_PASSWORD = 2
+};
+
+/* enums of subnegotiation status */
+enum socks_subnegotiation_status {
+ SOCKS_SUBNEGOTIATION_STATUS_SUCCESS = 0,
+};
+
+/* enums of socks request reply */
+enum socks_request_reply {
+ SOCKS_REQUEST_REPLY_SUCCESS = 0,
+ SOCKS_REQUEST_REPLY_FAILURE_GENERAL = 1,
+ SOCKS_REQUEST_REPLY_CONNECTION_NOT_ALLOWED = 2,
+ SOCKS_REQUEST_REPLY_NETWORK_UNREACHABLE = 3,
+ SOCKS_REQUEST_REPLY_HOST_UNREACHABLE = 4,
+ SOCKS_REQUEST_REPLY_CONNECTION_REFUSED = 5,
+ SOCKS_REQUEST_REPLY_TTL_EXPIRED = 6,
+ SOCKS_REQUEST_REPLY_COMMAND_NOT_SUPPORTED = 7,
+ SOCKS_REQUEST_REPLY_ATYP_NOT_SUPPORTED = 8
+};
+
+/* enums used to generate socks messages */
+enum socks_msg_type {
+ /* greeting */
+ SOCKS_MSG_GREETING,
+ /* credential, user name and password */
+ SOCKS_MSG_USERNAME_PASSWORD,
+ /* connect command */
+ SOCKS_MSG_CONNECT
+};
+
+enum {
+ LWS_RXFLOW_ALLOW = (1 << 0),
+ LWS_RXFLOW_PENDING_CHANGE = (1 << 1),
+};
+
+struct lws_ring {
+ void *buf;
+ void (*destroy_element)(void *element);
+ uint32_t buflen;
+ uint32_t element_len;
+ uint32_t head;
+ uint32_t oldest_tail;
+};
+
+struct lws_protocols;
+struct lws;
+
+struct lws_io_watcher {
+#ifdef LWS_WITH_LIBEV
+ struct lws_io_watcher_libev ev;
+#endif
+#ifdef LWS_WITH_LIBUV
+ struct lws_io_watcher_libuv uv;
+#endif
+#ifdef LWS_WITH_LIBEVENT
+ struct lws_io_watcher_libevent event;
+#endif
+ struct lws_context *context;
+
+ uint8_t actual_events;
+};
+
+struct lws_signal_watcher {
+#ifdef LWS_WITH_LIBEV
+ struct lws_signal_watcher_libev ev;
+#endif
+#ifdef LWS_WITH_LIBUV
+ struct lws_signal_watcher_libuv uv;
+#endif
+#ifdef LWS_WITH_LIBEVENT
+ struct lws_signal_watcher_libevent event;
+#endif
+ struct lws_context *context;
+};
+
+#ifdef _WIN32
+#define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS)
+struct lws_fd_hashtable {
+ struct lws **wsi;
+ int length;
+};
+#endif
+
+struct lws_foreign_thread_pollfd {
+ struct lws_foreign_thread_pollfd *next;
+ int fd_index;
+ int _and;
+ int _or;
+};
+
+
+#define LWS_HRTIMER_NOWAIT (0x7fffffffffffffffll)
+
+/*
+ * so we can have n connections being serviced simultaneously,
+ * these things need to be isolated per-thread.
+ */
+
+struct lws_context_per_thread {
+#if LWS_MAX_SMP > 1
+ pthread_mutex_t lock;
+ pthread_mutex_t lock_stats;
+ pthread_t lock_owner;
+ const char *last_lock_reason;
+#endif
+
+ struct lws_context *context;
+
+ /*
+ * usable by anything in the service code, but only if the scope
+ * does not last longer than the service action (since next service
+ * of any socket can likewise use it and overwrite)
+ */
+ unsigned char *serv_buf;
+
+ struct lws_dll_lws dll_head_timeout;
+ struct lws_dll_lws dll_head_hrtimer;
+ struct lws_dll_lws dll_head_buflist; /* guys with pending rxflow */
+
+#if defined(LWS_WITH_TLS)
+ struct lws_pt_tls tls;
+#endif
+
+ struct lws_pollfd *fds;
+ volatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list;
+#ifdef _WIN32
+ WSAEVENT *events;
+#endif
+ lws_sockfd_type dummy_pipe_fds[2];
+ struct lws *pipe_wsi;
+
+ /* --- role based members --- */
+
+#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
+ struct lws_pt_role_ws ws;
+#endif
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ struct lws_pt_role_http http;
+#endif
+
+ /* --- event library based members --- */
+
+#if defined(LWS_WITH_LIBEV)
+ struct lws_pt_eventlibs_libev ev;
+#endif
+#if defined(LWS_WITH_LIBUV)
+ struct lws_pt_eventlibs_libuv uv;
+#endif
+#if defined(LWS_WITH_LIBEVENT)
+ struct lws_pt_eventlibs_libevent event;
+#endif
+
+#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
+ struct lws_signal_watcher w_sigint;
+#endif
+
+ /* --- */
+
+ unsigned long count_conns;
+ unsigned int fds_count;
+
+ volatile unsigned char inside_poll;
+ volatile unsigned char foreign_spinlock;
+
+ unsigned char tid;
+
+ unsigned char lock_depth;
+ unsigned char inside_service:1;
+ unsigned char event_loop_foreign:1;
+ unsigned char event_loop_destroy_processing_done:1;
+};
+
+struct lws_conn_stats {
+ unsigned long long rx, tx;
+ unsigned long h1_conn, h1_trans, h2_trans, ws_upg, h2_alpn, h2_subs,
+ h2_upg, rejected;
+};
+
+void
+lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs);
+
+struct lws_timed_vh_protocol {
+ struct lws_timed_vh_protocol *next;
+ const struct lws_protocols *protocol;
+ time_t time;
+ int reason;
+};
+
+/*
+ * virtual host -related context information
+ * vhostwide SSL context
+ * vhostwide proxy
+ *
+ * hierarchy:
+ *
+ * context -> vhost -> wsi
+ *
+ * incoming connection non-SSL vhost binding:
+ *
+ * listen socket -> wsi -> select vhost after first headers
+ *
+ * incoming connection SSL vhost binding:
+ *
+ * SSL SNI -> wsi -> bind after SSL negotiation
+ */
+
+
+struct lws_vhost {
+#if !defined(LWS_WITHOUT_CLIENT)
+ char proxy_basic_auth_token[128];
+#endif
+#if LWS_MAX_SMP > 1
+ pthread_mutex_t lock;
+#endif
+
+#if defined(LWS_ROLE_H2)
+ struct lws_vhost_role_h2 h2;
+#endif
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ struct lws_vhost_role_http http;
+#endif
+#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
+ struct lws_vhost_role_ws ws;
+#endif
+
+#if defined(LWS_WITH_SOCKS5)
+ char socks_proxy_address[128];
+ char socks_user[96];
+ char socks_password[96];
+#endif
+#if defined(LWS_WITH_LIBEV)
+ struct lws_io_watcher w_accept;
+#endif
+ struct lws_conn_stats conn_stats;
+ struct lws_context *context;
+ struct lws_vhost *vhost_next;
+
+ struct lws *lserv_wsi;
+ const char *name;
+ const char *iface;
+
+#if !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32)
+ int bind_iface;
+#endif
+ const struct lws_protocols *protocols;
+ void **protocol_vh_privs;
+ const struct lws_protocol_vhost_options *pvo;
+ const struct lws_protocol_vhost_options *headers;
+ struct lws **same_vh_protocol_list;
+ struct lws_vhost *no_listener_vhost_list;
+#if !defined(LWS_NO_CLIENT)
+ struct lws_dll_lws dll_active_client_conns;
+#endif
+
+#if defined(LWS_WITH_TLS)
+ struct lws_vhost_tls tls;
+#endif
+
+ struct lws_timed_vh_protocol *timed_vh_protocol_list;
+ void *user;
+
+ int listen_port;
+
+#if defined(LWS_WITH_SOCKS5)
+ unsigned int socks_proxy_port;
+#endif
+ unsigned int options;
+ int count_protocols;
+ int ka_time;
+ int ka_probes;
+ int ka_interval;
+ int keepalive_timeout;
+ int timeout_secs_ah_idle;
+
+#ifdef LWS_WITH_ACCESS_LOG
+ int log_fd;
+#endif
+
+ unsigned int created_vhost_protocols:1;
+ unsigned int being_destroyed:1;
+
+ unsigned char default_protocol_index;
+ unsigned char raw_protocol_index;
+};
+
+struct lws_deferred_free
+{
+ struct lws_deferred_free *next;
+ time_t deadline;
+ void *payload;
+};
+
+typedef union {
+#ifdef LWS_WITH_IPV6
+ struct sockaddr_in6 sa6;
+#endif
+ struct sockaddr_in sa4;
+} sockaddr46;
+
+
+#if defined(LWS_WITH_PEER_LIMITS)
+struct lws_peer {
+ struct lws_peer *next;
+ struct lws_peer *peer_wait_list;
+
+ time_t time_created;
+ time_t time_closed_all;
+
+ uint8_t addr[32];
+ uint32_t hash;
+ uint32_t count_wsi;
+ uint32_t total_wsi;
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ struct lws_peer_role_http http;
+#endif
+
+ uint8_t af;
+};
+#endif
+
+/*
+ * the rest is managed per-context, that includes
+ *
+ * - processwide single fd -> wsi lookup
+ * - contextwide headers pool
+ */
+
+struct lws_context {
+ time_t last_timeout_check_s;
+ time_t last_ws_ping_pong_check_s;
+ time_t time_up;
+ time_t time_discontiguity;
+ time_t time_fixup;
+ const struct lws_plat_file_ops *fops;
+ struct lws_plat_file_ops fops_platform;
+ struct lws_context **pcontext_finalize;
+
+ const struct lws_tls_ops *tls_ops;
+
+#if defined(LWS_WITH_HTTP2)
+ struct http2_settings set;
+#endif
+#if defined(LWS_WITH_ZIP_FOPS)
+ struct lws_plat_file_ops fops_zip;
+#endif
+ struct lws_context_per_thread pt[LWS_MAX_SMP];
+ struct lws_conn_stats conn_stats;
+#if LWS_MAX_SMP > 1
+ pthread_mutex_t lock;
+ int lock_depth;
+#endif
+#ifdef _WIN32
+/* different implementation between unix and windows */
+ struct lws_fd_hashtable fd_hashtable[FD_HASHTABLE_MODULUS];
+#else
+ struct lws **lws_lookup; /* fd to wsi */
+#endif
+ struct lws_vhost *vhost_list;
+ struct lws_vhost *no_listener_vhost_list;
+ struct lws_vhost *vhost_pending_destruction_list;
+ struct lws_plugin *plugin_list;
+ struct lws_deferred_free *deferred_free_list;
+#if defined(LWS_WITH_PEER_LIMITS)
+ struct lws_peer **pl_hash_table;
+ struct lws_peer *peer_wait_list;
+ time_t next_cull;
+#endif
+
+ void *external_baggage_free_on_destroy;
+ const struct lws_token_limits *token_limits;
+ void *user_space;
+ const struct lws_protocol_vhost_options *reject_service_keywords;
+ lws_reload_func deprecation_cb;
+ void (*eventlib_signal_cb)(void *event_lib_handle, int signum);
+
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+ cap_value_t caps[4];
+ char count_caps;
+#endif
+
+#if defined(LWS_WITH_LIBEV)
+ struct lws_context_eventlibs_libev ev;
+#endif
+#if defined(LWS_WITH_LIBUV)
+ struct lws_context_eventlibs_libuv uv;
+#endif
+#if defined(LWS_WITH_LIBEVENT)
+ struct lws_context_eventlibs_libevent event;
+#endif
+ struct lws_event_loop_ops *event_loop_ops;
+
+
+#if defined(LWS_WITH_TLS)
+ struct lws_context_tls tls;
+#endif
+
+ char canonical_hostname[128];
+ const char *server_string;
+
+#ifdef LWS_LATENCY
+ unsigned long worst_latency;
+ char worst_latency_info[256];
+#endif
+
+#if defined(LWS_WITH_STATS)
+ uint64_t lws_stats[LWSSTATS_SIZE];
+ uint64_t last_dump;
+ int updated;
+#endif
+#if defined(LWS_WITH_ESP32)
+ unsigned long time_last_state_dump;
+ uint32_t last_free_heap;
+#endif
+
+ int max_fds;
+ int count_event_loop_static_asset_handles;
+ int started_with_parent;
+ int uid, gid;
+
+ int fd_random;
+
+ int count_wsi_allocated;
+ int count_cgi_spawned;
+ unsigned int options;
+ unsigned int fd_limit_per_thread;
+ unsigned int timeout_secs;
+ unsigned int pt_serv_buf_size;
+ int max_http_header_data;
+ int simultaneous_ssl_restriction;
+ int simultaneous_ssl;
+#if defined(LWS_WITH_PEER_LIMITS)
+ uint32_t pl_hash_elements; /* protected by context->lock */
+ uint32_t count_peers; /* protected by context->lock */
+ unsigned short ip_limit_ah;
+ unsigned short ip_limit_wsi;
+#endif
+ unsigned int deprecated:1;
+ unsigned int being_destroyed:1;
+ unsigned int being_destroyed1:1;
+ unsigned int being_destroyed2:1;
+ unsigned int requested_kill:1;
+ unsigned int protocol_init_done:1;
+ unsigned int doing_protocol_init:1;
+ unsigned int done_protocol_destroy_cb:1;
+ unsigned int finalize_destroy_after_internal_loops_stopped:1;
+ /*
+ * set to the Thread ID that's doing the service loop just before entry
+ * to poll indicates service thread likely idling in poll()
+ * volatile because other threads may check it as part of processing
+ * for pollfd event change.
+ */
+ volatile int service_tid;
+ int service_tid_detected;
+
+ short max_http_header_pool;
+ short count_threads;
+ short plugin_protocol_count;
+ short plugin_extension_count;
+ short server_string_len;
+ unsigned short ws_ping_pong_interval;
+ unsigned short deprecation_pending_listen_close_count;
+
+ uint8_t max_fi;
+};
+
+int
+lws_check_deferred_free(struct lws_context *context, int force);
+
+#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x]
+#define lws_get_vh_protocol(vh, x) vh->protocols[x]
+
+LWS_EXTERN void
+__lws_close_free_wsi_final(struct lws *wsi);
+LWS_EXTERN void
+lws_libuv_closehandle(struct lws *wsi);
+LWS_EXTERN int
+lws_libuv_check_watcher_active(struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_plat_plugins_init(struct lws_context * context, const char * const *d);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_plat_plugins_destroy(struct lws_context * context);
+
+LWS_EXTERN void
+lws_restart_ws_ping_pong_timer(struct lws *wsi);
+
+struct lws *
+lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd);
+
+int
+lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max);
+
+void
+lws_vhost_destroy1(struct lws_vhost *vh);
+
+enum {
+ LWS_EV_READ = (1 << 0),
+ LWS_EV_WRITE = (1 << 1),
+ LWS_EV_START = (1 << 2),
+ LWS_EV_STOP = (1 << 3),
+
+ LWS_EV_PREPARE_DELETION = (1 << 31),
+};
+
+
+#if defined(LWS_WITH_ESP32)
+LWS_EXTERN int
+lws_find_string_in_file(const char *filename, const char *string, int stringlen);
+#endif
+
+#ifdef LWS_WITH_IPV6
+#define LWS_IPV6_ENABLED(vh) \
+ (!lws_check_opt(vh->context->options, LWS_SERVER_OPTION_DISABLE_IPV6) && \
+ !lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_IPV6))
+#else
+#define LWS_IPV6_ENABLED(context) (0)
+#endif
+
+#ifdef LWS_WITH_UNIX_SOCK
+#define LWS_UNIX_SOCK_ENABLED(vhost) \
+ (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK)
+#else
+#define LWS_UNIX_SOCK_ENABLED(vhost) (0)
+#endif
+
+enum uri_path_states {
+ URIPS_IDLE,
+ URIPS_SEEN_SLASH,
+ URIPS_SEEN_SLASH_DOT,
+ URIPS_SEEN_SLASH_DOT_DOT,
+};
+
+enum uri_esc_states {
+ URIES_IDLE,
+ URIES_SEEN_PERCENT,
+ URIES_SEEN_PERCENT_H1,
+};
+
+
+#ifndef LWS_NO_CLIENT
+struct client_info_stash {
+ char *address;
+ char *path;
+ char *host;
+ char *origin;
+ char *protocol;
+ char *method;
+ char *iface;
+ char *alpn;
+};
+#endif
+
+
+signed char char_to_hex(const char c);
+
+
+struct lws_buflist {
+ struct lws_buflist *next;
+
+ size_t len;
+ size_t pos;
+
+ uint8_t buf[1]; /* true length of this is set by the oversize malloc */
+};
+
+#define lws_wsi_is_udp(___wsi) (!!___wsi->udp)
+
+#define LWS_H2_FRAME_HEADER_LENGTH 9
+
+
+struct lws {
+ /* structs */
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ struct _lws_http_mode_related http;
+#endif
+#if defined(LWS_ROLE_H2)
+ struct _lws_h2_related h2;
+#endif
+#if defined(LWS_ROLE_WS)
+ struct _lws_websocket_related *ws; /* allocated if we upgrade to ws */
+#endif
+
+ const struct lws_role_ops *role_ops;
+ lws_wsi_state_t wsistate;
+ lws_wsi_state_t wsistate_pre_close;
+
+ /* lifetime members */
+
+#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
+ struct lws_io_watcher w_read;
+#endif
+#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBEVENT)
+ struct lws_io_watcher w_write;
+#endif
+
+ /* pointers */
+
+ struct lws_context *context;
+ struct lws_vhost *vhost;
+ struct lws *parent; /* points to parent, if any */
+ struct lws *child_list; /* points to first child */
+ struct lws *sibling_list; /* subsequent children at same level */
+
+ const struct lws_protocols *protocol;
+ struct lws **same_vh_protocol_prev, *same_vh_protocol_next;
+
+ struct lws_dll_lws dll_timeout;
+ struct lws_dll_lws dll_hrtimer;
+ struct lws_dll_lws dll_buflist; /* guys with pending rxflow */
+
+#if defined(LWS_WITH_PEER_LIMITS)
+ struct lws_peer *peer;
+#endif
+
+ struct lws_udp *udp;
+#ifndef LWS_NO_CLIENT
+ struct client_info_stash *stash;
+ char *client_hostname_copy;
+ struct lws_dll_lws dll_active_client_conns;
+ struct lws_dll_lws dll_client_transaction_queue_head;
+ struct lws_dll_lws dll_client_transaction_queue;
+#endif
+ void *user_space;
+ void *opaque_parent_data;
+
+ struct lws_buflist *buflist;
+
+ /* truncated send handling */
+ unsigned char *trunc_alloc; /* non-NULL means buffering in progress */
+
+#if defined(LWS_WITH_TLS)
+ struct lws_lws_tls tls;
+#endif
+
+ lws_sock_file_fd_type desc; /* .filefd / .sockfd */
+#if defined(LWS_WITH_STATS)
+ uint64_t active_writable_req_us;
+#if defined(LWS_WITH_TLS)
+ uint64_t accept_start_us;
+#endif
+#endif
+
+ lws_usec_t pending_timer; /* hrtimer fires */
+ time_t pending_timeout_set; /* second-resolution timeout start */
+
+#ifdef LWS_LATENCY
+ unsigned long action_start;
+ unsigned long latency_start;
+#endif
+
+ /* ints */
+#define LWS_NO_FDS_POS (-1)
+ int position_in_fds_table;
+ unsigned int trunc_alloc_len; /* size of malloc */
+ unsigned int trunc_offset; /* where we are in terms of spilling */
+ unsigned int trunc_len; /* how much is buffered */
+#ifndef LWS_NO_CLIENT
+ int chunk_remaining;
+#endif
+ unsigned int cache_secs;
+
+ unsigned int hdr_parsing_completed:1;
+ unsigned int http2_substream:1;
+ unsigned int upgraded_to_http2:1;
+ unsigned int h2_stream_carries_ws:1;
+ unsigned int seen_nonpseudoheader:1;
+ unsigned int listener:1;
+ unsigned int user_space_externally_allocated:1;
+ unsigned int socket_is_permanently_unusable:1;
+ unsigned int rxflow_change_to:2;
+ unsigned int conn_stat_done:1;
+ unsigned int cache_reuse:1;
+ unsigned int cache_revalidate:1;
+ unsigned int cache_intermediaries:1;
+ unsigned int favoured_pollin:1;
+ unsigned int sending_chunked:1;
+ unsigned int interpreting:1;
+ unsigned int already_did_cce:1;
+ unsigned int told_user_closed:1;
+ unsigned int told_event_loop_closed:1;
+ unsigned int waiting_to_send_close_frame:1;
+ unsigned int close_needs_ack:1;
+ unsigned int ipv6:1;
+ unsigned int parent_carries_io:1;
+ unsigned int parent_pending_cb_on_writable:1;
+ unsigned int cgi_stdout_zero_length:1;
+ unsigned int seen_zero_length_recv:1;
+ unsigned int rxflow_will_be_applied:1;
+ unsigned int event_pipe:1;
+ unsigned int on_same_vh_list:1;
+ unsigned int handling_404:1;
+ unsigned int protocol_bind_balance:1;
+
+ unsigned int could_have_pending:1; /* detect back-to-back writes */
+ unsigned int outer_will_close:1;
+
+#ifdef LWS_WITH_ACCESS_LOG
+ unsigned int access_log_pending:1;
+#endif
+#ifndef LWS_NO_CLIENT
+ unsigned int do_ws:1; /* whether we are doing http or ws flow */
+ unsigned int chunked:1; /* if the clientside connection is chunked */
+ unsigned int client_rx_avail:1;
+ unsigned int client_http_body_pending:1;
+ unsigned int transaction_from_pipeline_queue:1;
+ unsigned int keepalive_active:1;
+ unsigned int keepalive_rejected:1;
+ unsigned int client_pipeline:1;
+ unsigned int client_h2_alpn:1;
+ unsigned int client_h2_substream:1;
+#endif
+
+#ifdef _WIN32
+ unsigned int sock_send_blocking:1;
+#endif
+
+#ifndef LWS_NO_CLIENT
+ unsigned short c_port;
+#endif
+ unsigned short pending_timeout_limit;
+
+ /* chars */
+
+ char lws_rx_parse_state; /* enum lws_rx_parse_state */
+ char rx_frame_type; /* enum lws_write_protocol */
+ char pending_timeout; /* enum pending_timeout */
+ char tsi; /* thread service index we belong to */
+ char protocol_interpret_idx;
+ char redirects;
+ uint8_t rxflow_bitmap;
+#ifdef LWS_WITH_CGI
+ char cgi_channel; /* which of stdin/out/err */
+ char hdr_state;
+#endif
+#ifndef LWS_NO_CLIENT
+ char chunk_parser; /* enum lws_chunk_parser */
+#endif
+#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT)
+ char reason_bf; /* internal writeable callback reason bitfield */
+#endif
+#if defined(LWS_WITH_STATS) && defined(LWS_WITH_TLS)
+ char seen_rx;
+#endif
+ uint8_t ws_over_h2_count;
+ /* volatile to make sure code is aware other thread can change */
+ volatile char handling_pollout;
+ volatile char leave_pollout_active;
+};
+
+#define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap))
+
+void
+lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt);
+
+LWS_EXTERN int log_level;
+
+LWS_EXTERN int
+lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
+ const char *iface);
+
+#if defined(LWS_WITH_IPV6)
+LWS_EXTERN unsigned long
+lws_get_addr_scope(const char *ipaddr);
+#endif
+
+LWS_EXTERN void
+lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller);
+LWS_EXTERN void
+__lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller);
+
+LWS_EXTERN void
+__lws_free_wsi(struct lws *wsi);
+
+LWS_EXTERN int
+__remove_wsi_socket_from_fds(struct lws *wsi);
+LWS_EXTERN int
+lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len);
+
+#ifndef LWS_LATENCY
+static inline void
+lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
+ int ret, int completion) {
+ do {
+ (void)context; (void)wsi; (void)action; (void)ret;
+ (void)completion;
+ } while (0);
+}
+static inline void
+lws_latency_pre(struct lws_context *context, struct lws *wsi) {
+ do { (void)context; (void)wsi; } while (0);
+}
+#else
+#define lws_latency_pre(_context, _wsi) lws_latency(_context, _wsi, NULL, 0, 0)
+extern void
+lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
+ int ret, int completion);
+#endif
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ws_client_rx_sm(struct lws *wsi, unsigned char c);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_parse(struct lws *wsi, unsigned char *buf, int *len);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_parse_urldecode(struct lws *wsi, uint8_t *_c);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_http_action(struct lws *wsi);
+
+LWS_EXTERN int
+lws_b64_selftest(void);
+
+LWS_EXTERN int
+lws_service_flag_pending(struct lws_context *context, int tsi);
+
+LWS_EXTERN int
+lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p);
+
+#if defined(_WIN32)
+LWS_EXTERN struct lws *
+wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd);
+
+LWS_EXTERN int
+insert_wsi(struct lws_context *context, struct lws *wsi);
+
+LWS_EXTERN int
+delete_from_fd(struct lws_context *context, lws_sockfd_type fd);
+#else
+#define wsi_from_fd(A,B) A->lws_lookup[B - lws_plat_socket_offset()]
+#define insert_wsi(A,B) assert(A->lws_lookup[B->desc.sockfd - lws_plat_socket_offset()] == 0); A->lws_lookup[B->desc.sockfd - lws_plat_socket_offset()]=B
+#define delete_from_fd(A,B) A->lws_lookup[B - lws_plat_socket_offset()]=0
+#endif
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+__insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len);
+
+LWS_EXTERN void
+lws_remove_from_timeout_list(struct lws *wsi);
+
+LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
+lws_client_connect_2(struct lws *wsi);
+
+LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT
+lws_client_reset(struct lws **wsi, int ssl, const char *address, int port,
+ const char *path, const char *host);
+
+LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
+lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi);
+
+LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
+lws_generate_client_handshake(struct lws *wsi, char *pkt);
+
+LWS_EXTERN int
+lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd);
+
+LWS_EXTERN struct lws *
+lws_client_connect_via_info2(struct lws *wsi);
+
+
+
+LWS_EXTERN void
+lws_client_stash_destroy(struct lws *wsi);
+
+/*
+ * EXTENSIONS
+ */
+
+#if defined(LWS_WITHOUT_EXTENSIONS)
+#define lws_any_extension_handled(_a, _b, _c, _d) (0)
+#define lws_ext_cb_active(_a, _b, _c, _d) (0)
+#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0)
+#define lws_issue_raw_ext_access lws_issue_raw
+#define lws_context_init_extensions(_a, _b)
+#endif
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_client_interpret_server_handshake(struct lws *wsi);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len);
+
+LWS_EXTERN void
+lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state,
+ struct lws_role_ops *ops);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+user_callback_handle_rxflow(lws_callback_function, struct lws *wsi,
+ enum lws_callback_reasons reason, void *user,
+ void *in, size_t len);
+
+LWS_EXTERN int
+lws_plat_socket_offset(void);
+
+LWS_EXTERN int
+lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd);
+
+LWS_EXTERN int
+lws_plat_check_connection_error(struct lws *wsi);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_header_table_attach(struct lws *wsi, int autoservice);
+
+LWS_EXTERN int
+lws_header_table_detach(struct lws *wsi, int autoservice);
+LWS_EXTERN int
+__lws_header_table_detach(struct lws *wsi, int autoservice);
+
+LWS_EXTERN void
+lws_header_table_reset(struct lws *wsi, int autoservice);
+
+void
+__lws_header_table_reset(struct lws *wsi, int autoservice);
+
+LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
+lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ensure_user_space(struct lws *wsi);
+
+LWS_EXTERN int
+lws_change_pollfd(struct lws *wsi, int _and, int _or);
+
+#ifndef LWS_NO_SERVER
+ int _lws_vhost_init_server(const struct lws_context_creation_info *info,
+ struct lws_vhost *vhost);
+ LWS_EXTERN struct lws_vhost *
+ lws_select_vhost(struct lws_context *context, int port, const char *servername);
+ LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+ lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len);
+ LWS_EXTERN void
+ lws_server_get_canonical_hostname(struct lws_context *context,
+ const struct lws_context_creation_info *info);
+#else
+ #define _lws_vhost_init_server(_a, _b) (0)
+ #define lws_parse_ws(_a, _b, _c) (0)
+ #define lws_server_get_canonical_hostname(_a, _b)
+#endif
+
+#ifndef LWS_NO_DAEMONIZE
+ LWS_EXTERN int get_daemonize_pid();
+#else
+ #define get_daemonize_pid() (0)
+#endif
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+interface_to_sa(struct lws_vhost *vh, const char *ifname,
+ struct sockaddr_in *addr, size_t addrlen);
+LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
+
+#if !defined(LWS_WITH_TLS)
+ #define LWS_SSL_ENABLED(context) (0)
+ #define lws_context_init_server_ssl(_a, _b) (0)
+ #define lws_ssl_destroy(_a)
+ #define lws_context_init_alpn(_a)
+ #define lws_ssl_capable_read lws_ssl_capable_read_no_ssl
+ #define lws_ssl_capable_write lws_ssl_capable_write_no_ssl
+ #define lws_ssl_pending lws_ssl_pending_no_ssl
+ #define lws_server_socket_service_ssl(_b, _c) (0)
+ #define lws_ssl_close(_a) (0)
+ #define lws_ssl_context_destroy(_a)
+ #define lws_ssl_SSL_CTX_destroy(_a)
+ #define lws_ssl_remove_wsi_from_buffered_list(_a)
+ #define __lws_ssl_remove_wsi_from_buffered_list(_a)
+ #define lws_context_init_ssl_library(_a)
+ #define lws_tls_check_all_cert_lifetimes(_a)
+ #define lws_tls_acme_sni_cert_destroy(_a)
+#endif
+
+
+#if LWS_MAX_SMP > 1
+
+static LWS_INLINE void
+lws_pt_mutex_init(struct lws_context_per_thread *pt)
+{
+ pthread_mutex_init(&pt->lock, NULL);
+ pthread_mutex_init(&pt->lock_stats, NULL);
+}
+
+static LWS_INLINE void
+lws_pt_mutex_destroy(struct lws_context_per_thread *pt)
+{
+ pthread_mutex_destroy(&pt->lock_stats);
+ pthread_mutex_destroy(&pt->lock);
+}
+
+static LWS_INLINE void
+lws_pt_lock(struct lws_context_per_thread *pt, const char *reason)
+{
+ if (pt->lock_owner == pthread_self()) {
+ pt->lock_depth++;
+ return;
+ }
+ pthread_mutex_lock(&pt->lock);
+ pt->last_lock_reason = reason;
+ pt->lock_owner = pthread_self();
+ //lwsl_notice("tid %d: lock %s\n", pt->tid, reason);
+}
+
+static LWS_INLINE void
+lws_pt_unlock(struct lws_context_per_thread *pt)
+{
+ if (pt->lock_depth) {
+ pt->lock_depth--;
+ return;
+ }
+ pt->last_lock_reason = "free";
+ pt->lock_owner = 0;
+ //lwsl_notice("tid %d: unlock %s\n", pt->tid, pt->last_lock_reason);
+ pthread_mutex_unlock(&pt->lock);
+}
+
+static LWS_INLINE void
+lws_pt_stats_lock(struct lws_context_per_thread *pt)
+{
+ pthread_mutex_lock(&pt->lock_stats);
+}
+
+static LWS_INLINE void
+lws_pt_stats_unlock(struct lws_context_per_thread *pt)
+{
+ pthread_mutex_unlock(&pt->lock_stats);
+}
+
+static LWS_INLINE void
+lws_context_lock(struct lws_context *context)
+{
+ pthread_mutex_lock(&context->lock);
+}
+
+static LWS_INLINE void
+lws_context_unlock(struct lws_context *context)
+{
+ pthread_mutex_unlock(&context->lock);
+}
+
+static LWS_INLINE void
+lws_vhost_lock(struct lws_vhost *vhost)
+{
+ pthread_mutex_lock(&vhost->lock);
+}
+
+static LWS_INLINE void
+lws_vhost_unlock(struct lws_vhost *vhost)
+{
+ pthread_mutex_unlock(&vhost->lock);
+}
+
+
+#else
+#define lws_pt_mutex_init(_a) (void)(_a)
+#define lws_pt_mutex_destroy(_a) (void)(_a)
+#define lws_pt_lock(_a, b) (void)(_a)
+#define lws_pt_unlock(_a) (void)(_a)
+#define lws_context_lock(_a) (void)(_a)
+#define lws_context_unlock(_a) (void)(_a)
+#define lws_vhost_lock(_a) (void)(_a)
+#define lws_vhost_unlock(_a) (void)(_a)
+#define lws_pt_stats_lock(_a) (void)(_a)
+#define lws_pt_stats_unlock(_a) (void)(_a)
+#endif
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_pending_no_ssl(struct lws *wsi);
+
+int
+lws_tls_check_cert_lifetime(struct lws_vhost *vhost);
+
+int lws_jws_selftest(void);
+
+
+#ifndef LWS_NO_CLIENT
+LWS_EXTERN int lws_client_socket_service(struct lws *wsi,
+ struct lws_pollfd *pollfd,
+ struct lws *wsi_conn);
+LWS_EXTERN struct lws *
+lws_client_wsi_effective(struct lws *wsi);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_http_transaction_completed_client(struct lws *wsi);
+#if !defined(LWS_WITH_TLS)
+ #define lws_context_init_client_ssl(_a, _b) (0)
+#endif
+LWS_EXTERN void
+lws_decode_ssl_error(void);
+#else
+#define lws_context_init_client_ssl(_a, _b) (0)
+#endif
+
+LWS_EXTERN int
+__lws_rx_flow_control(struct lws *wsi);
+
+LWS_EXTERN int
+_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa);
+
+#ifndef LWS_NO_SERVER
+LWS_EXTERN int
+lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len);
+#else
+#define lws_server_socket_service(_b, _c) (0)
+#define lws_handshake_server(_a, _b, _c) (0)
+#endif
+
+#ifdef LWS_WITH_ACCESS_LOG
+LWS_EXTERN int
+lws_access_log(struct lws *wsi);
+LWS_EXTERN void
+lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth);
+#else
+#define lws_access_log(_a)
+#endif
+
+LWS_EXTERN int
+lws_cgi_kill_terminated(struct lws_context_per_thread *pt);
+
+LWS_EXTERN void
+lws_cgi_remove_and_kill(struct lws *wsi);
+
+int
+lws_protocol_init(struct lws_context *context);
+
+int
+lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p);
+
+const struct lws_http_mount *
+lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len);
+
+/*
+ * custom allocator
+ */
+LWS_EXTERN void *
+lws_realloc(void *ptr, size_t size, const char *reason);
+
+LWS_EXTERN void * LWS_WARN_UNUSED_RESULT
+lws_zalloc(size_t size, const char *reason);
+
+#ifdef LWS_PLAT_OPTEE
+void *lws_malloc(size_t size, const char *reason);
+void lws_free(void *p);
+#define lws_free_set_NULL(P) do { lws_free(P); (P) = NULL; } while(0)
+#else
+#define lws_malloc(S, R) lws_realloc(NULL, S, R)
+#define lws_free(P) lws_realloc(P, 0, "lws_free")
+#define lws_free_set_NULL(P) do { lws_realloc(P, 0, "free"); (P) = NULL; } while(0)
+#endif
+
+int
+lws_plat_pipe_create(struct lws *wsi);
+int
+lws_plat_pipe_signal(struct lws *wsi);
+void
+lws_plat_pipe_close(struct lws *wsi);
+int
+lws_create_event_pipes(struct lws_context *context);
+
+const struct lws_plat_file_ops *
+lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path,
+ const char **vpath);
+
+/* lws_plat_ */
+LWS_EXTERN void
+lws_plat_delete_socket_from_fds(struct lws_context *context,
+ struct lws *wsi, int m);
+LWS_EXTERN void
+lws_plat_insert_socket_into_fds(struct lws_context *context,
+ struct lws *wsi);
+LWS_EXTERN void
+lws_plat_service_periodic(struct lws_context *context);
+
+LWS_EXTERN int
+lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi,
+ struct lws_pollfd *pfd);
+LWS_EXTERN void
+lws_add_wsi_to_draining_ext_list(struct lws *wsi);
+LWS_EXTERN void
+lws_remove_wsi_from_draining_ext_list(struct lws *wsi);
+LWS_EXTERN int
+lws_plat_context_early_init(void);
+LWS_EXTERN void
+lws_plat_context_early_destroy(struct lws_context *context);
+LWS_EXTERN void
+lws_plat_context_late_destroy(struct lws_context *context);
+LWS_EXTERN int
+lws_poll_listen_fd(struct lws_pollfd *fd);
+LWS_EXTERN int
+lws_plat_service(struct lws_context *context, int timeout_ms);
+LWS_EXTERN LWS_VISIBLE int
+_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi);
+LWS_EXTERN int
+lws_plat_init(struct lws_context *context,
+ const struct lws_context_creation_info *info);
+LWS_EXTERN void
+lws_plat_drop_app_privileges(const struct lws_context_creation_info *info);
+LWS_EXTERN unsigned long long
+time_in_microseconds(void);
+LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT
+lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_plat_inet_pton(int af, const char *src, void *dst);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len);
+LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
+ lws_filepos_t *amount);
+
+
+LWS_EXTERN void
+lws_same_vh_protocol_remove(struct lws *wsi);
+LWS_EXTERN void
+lws_same_vh_protocol_insert(struct lws *wsi, int n);
+
+LWS_EXTERN int
+lws_broadcast(struct lws_context *context, int reason, void *in, size_t len);
+
+#if defined(LWS_WITH_STATS)
+ void
+ lws_stats_atomic_bump(struct lws_context * context,
+ struct lws_context_per_thread *pt, int index, uint64_t bump);
+ void
+ lws_stats_atomic_max(struct lws_context * context,
+ struct lws_context_per_thread *pt, int index, uint64_t val);
+#else
+ static inline uint64_t lws_stats_atomic_bump(struct lws_context * context,
+ struct lws_context_per_thread *pt, int index, uint64_t bump) {
+ (void)context; (void)pt; (void)index; (void)bump; return 0; }
+ static inline uint64_t lws_stats_atomic_max(struct lws_context * context,
+ struct lws_context_per_thread *pt, int index, uint64_t val) {
+ (void)context; (void)pt; (void)index; (void)val; return 0; }
+#endif
+
+/* socks */
+void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
+ ssize_t *msg_len);
+
+#if defined(LWS_WITH_PEER_LIMITS)
+void
+lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer);
+int
+lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer);
+void
+lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer);
+void
+lws_peer_cull_peer_wait_list(struct lws_context *context);
+struct lws_peer *
+lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd);
+void
+lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer,
+ struct lws *wsi);
+void
+lws_peer_dump_from_wsi(struct lws *wsi);
+#endif
+
+
+void
+__lws_remove_from_timeout_list(struct lws *wsi);
+
+lws_usec_t
+__lws_hrtimer_service(struct lws_context_per_thread *pt);
+
+void
+__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
+int
+__lws_change_pollfd(struct lws *wsi, int _and, int _or);
+
+
+int
+lws_callback_as_writeable(struct lws *wsi);
+int
+lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi,
+ struct lws_tokens *ebuf);
+int
+lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used,
+ int buffered);
+
+
+char *
+lws_generate_client_ws_handshake(struct lws *wsi, char *p);
+int
+lws_client_ws_upgrade(struct lws *wsi, const char **cce);
+int
+lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi);
+int
+lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len);
+int
+lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn);
+int
+lws_tls_server_conn_alpn(struct lws *wsi);
+
+int
+lws_ws_client_rx_sm_block(struct lws *wsi, unsigned char **buf, size_t len);
+void
+lws_destroy_event_pipe(struct lws *wsi);
+void
+lws_context_destroy2(struct lws_context *context);
+
+#ifdef __cplusplus
+};
+#endif
diff --git a/thirdparty/libwebsockets/core/service.c b/thirdparty/libwebsockets/core/service.c
new file mode 100644
index 0000000000..6523058814
--- /dev/null
+++ b/thirdparty/libwebsockets/core/service.c
@@ -0,0 +1,987 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+
+int
+lws_callback_as_writeable(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ int n, m;
+
+ lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB, 1);
+#if defined(LWS_WITH_STATS)
+ if (wsi->active_writable_req_us) {
+ uint64_t ul = time_in_microseconds() -
+ wsi->active_writable_req_us;
+
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_MS_WRITABLE_DELAY, ul);
+ lws_stats_atomic_max(wsi->context, pt,
+ LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
+ wsi->active_writable_req_us = 0;
+ }
+#endif
+
+ n = wsi->role_ops->writeable_cb[lwsi_role_server(wsi)];
+
+ m = user_callback_handle_rxflow(wsi->protocol->callback,
+ wsi, (enum lws_callback_reasons) n,
+ wsi->user_space, NULL, 0);
+
+ return m;
+}
+
+LWS_VISIBLE int
+lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
+{
+ volatile struct lws *vwsi = (volatile struct lws *)wsi;
+ int n;
+
+ //lwsl_notice("%s: %p\n", __func__, wsi);
+
+ vwsi->leave_pollout_active = 0;
+ vwsi->handling_pollout = 1;
+ /*
+ * if another thread wants POLLOUT on us, from here on while
+ * handling_pollout is set, he will only set leave_pollout_active.
+ * If we are going to disable POLLOUT, we will check that first.
+ */
+ wsi->could_have_pending = 0; /* clear back-to-back write detection */
+
+ /*
+ * user callback is lowest priority to get these notifications
+ * actually, since other pending things cannot be disordered
+ *
+ * Priority 1: pending truncated sends are incomplete ws fragments
+ * If anything else sent first the protocol would be
+ * corrupted.
+ */
+
+ if (wsi->trunc_len) {
+ //lwsl_notice("%s: completing partial\n", __func__);
+ if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
+ wsi->trunc_len) < 0) {
+ lwsl_info("%s signalling to close\n", __func__);
+ goto bail_die;
+ }
+ /* leave POLLOUT active either way */
+ goto bail_ok;
+ } else
+ if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
+ wsi->socket_is_permanently_unusable = 1;
+ goto bail_die; /* retry closing now */
+ }
+
+#ifdef LWS_WITH_CGI
+ /*
+ * A cgi master's wire protocol remains h1 or h2. He is just getting
+ * his data from his child cgis.
+ */
+ if (wsi->http.cgi) {
+ /* also one shot */
+ if (pollfd)
+ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+ lwsl_info("failed at set pollfd\n");
+ return 1;
+ }
+ goto user_service_go_again;
+ }
+#endif
+
+ /* if we got here, we should have wire protocol ops set on the wsi */
+ assert(wsi->role_ops);
+
+ if (!wsi->role_ops->handle_POLLOUT)
+ goto bail_ok;
+
+ switch ((wsi->role_ops->handle_POLLOUT)(wsi)) {
+ case LWS_HP_RET_BAIL_OK:
+ goto bail_ok;
+ case LWS_HP_RET_BAIL_DIE:
+ goto bail_die;
+ case LWS_HP_RET_USER_SERVICE:
+ break;
+ default:
+ assert(0);
+ }
+
+ /* one shot */
+
+ if (wsi->parent_carries_io) {
+ vwsi->handling_pollout = 0;
+ vwsi->leave_pollout_active = 0;
+
+ return lws_callback_as_writeable(wsi);
+ }
+
+ if (pollfd) {
+ int eff = vwsi->leave_pollout_active;
+
+ if (!eff) {
+ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+ lwsl_info("failed at set pollfd\n");
+ goto bail_die;
+ }
+ }
+
+ vwsi->handling_pollout = 0;
+
+ /* cannot get leave_pollout_active set after the above */
+ if (!eff && wsi->leave_pollout_active) {
+ /*
+ * got set inbetween sampling eff and clearing
+ * handling_pollout, force POLLOUT on
+ */
+ lwsl_debug("leave_pollout_active\n");
+ if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
+ lwsl_info("failed at set pollfd\n");
+ goto bail_die;
+ }
+ }
+
+ vwsi->leave_pollout_active = 0;
+ }
+
+ if (lwsi_role_client(wsi) &&
+ !wsi->hdr_parsing_completed &&
+ lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS &&
+ lwsi_state(wsi) != LRS_ISSUE_HTTP_BODY
+ )
+ goto bail_ok;
+
+
+#ifdef LWS_WITH_CGI
+user_service_go_again:
+#endif
+
+ if (wsi->role_ops->perform_user_POLLOUT) {
+ if (wsi->role_ops->perform_user_POLLOUT(wsi) == -1)
+ goto bail_die;
+ else
+ goto bail_ok;
+ }
+
+ lwsl_debug("%s: %p: non mux: wsistate 0x%x, ops %s\n", __func__, wsi,
+ wsi->wsistate, wsi->role_ops->name);
+
+ vwsi = (volatile struct lws *)wsi;
+ vwsi->leave_pollout_active = 0;
+
+ n = lws_callback_as_writeable(wsi);
+ vwsi->handling_pollout = 0;
+
+ if (vwsi->leave_pollout_active)
+ lws_change_pollfd(wsi, 0, LWS_POLLOUT);
+
+ return n;
+
+ /*
+ * since these don't disable the POLLOUT, they are always doing the
+ * right thing for leave_pollout_active whether it was set or not.
+ */
+
+bail_ok:
+ vwsi->handling_pollout = 0;
+ vwsi->leave_pollout_active = 0;
+
+ return 0;
+
+bail_die:
+ vwsi->handling_pollout = 0;
+ vwsi->leave_pollout_active = 0;
+
+ return -1;
+}
+
+static int
+__lws_service_timeout_check(struct lws *wsi, time_t sec)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ int n = 0;
+
+ (void)n;
+
+ /*
+ * if we went beyond the allowed time, kill the
+ * connection
+ */
+ if (wsi->dll_timeout.prev &&
+ lws_compare_time_t(wsi->context, sec, wsi->pending_timeout_set) >
+ wsi->pending_timeout_limit) {
+
+ if (wsi->desc.sockfd != LWS_SOCK_INVALID &&
+ wsi->position_in_fds_table >= 0)
+ n = pt->fds[wsi->position_in_fds_table].events;
+
+ lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_TIMEOUTS, 1);
+
+ /* no need to log normal idle keepalive timeout */
+ if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE)
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ lwsl_info("wsi %p: TIMEDOUT WAITING on %d "
+ "(did hdr %d, ah %p, wl %d, pfd "
+ "events %d) %llu vs %llu\n",
+ (void *)wsi, wsi->pending_timeout,
+ wsi->hdr_parsing_completed, wsi->http.ah,
+ pt->http.ah_wait_list_length, n,
+ (unsigned long long)sec,
+ (unsigned long long)wsi->pending_timeout_limit);
+#if defined(LWS_WITH_CGI)
+ if (wsi->http.cgi)
+ lwsl_notice("CGI timeout: %s\n", wsi->http.cgi->summary);
+#endif
+#else
+ lwsl_info("wsi %p: TIMEDOUT WAITING on %d ", (void *)wsi,
+ wsi->pending_timeout);
+#endif
+
+ /*
+ * Since he failed a timeout, he already had a chance to do
+ * something and was unable to... that includes situations like
+ * half closed connections. So process this "failed timeout"
+ * close as a violent death and don't try to do protocol
+ * cleanup like flush partials.
+ */
+ wsi->socket_is_permanently_unusable = 1;
+ if (lwsi_state(wsi) == LRS_WAITING_SSL && wsi->protocol)
+ wsi->protocol->callback(wsi,
+ LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+ wsi->user_space,
+ (void *)"Timed out waiting SSL", 21);
+
+ __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "timeout");
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ uint8_t *buffered;
+ size_t blen;
+ int ret = 0, m;
+
+ /* his RX is flowcontrolled, don't send remaining now */
+ blen = lws_buflist_next_segment_len(&wsi->buflist, &buffered);
+ if (blen) {
+ if (buf >= buffered && buf + len <= buffered + blen) {
+ /* rxflow while we were spilling prev rxflow */
+ lwsl_info("%s: staying in rxflow buf\n", __func__);
+
+ return 1;
+ }
+ ret = 1;
+ }
+
+ /* a new rxflow, buffer it and warn caller */
+
+ m = lws_buflist_append_segment(&wsi->buflist, buf + n, len - n);
+
+ if (m < 0)
+ return -1;
+ if (m) {
+ lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
+ lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
+ }
+
+ return ret;
+}
+
+/* this is used by the platform service code to stop us waiting for network
+ * activity in poll() when we have something that already needs service
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
+{
+ struct lws_context_per_thread *pt = &context->pt[tsi];
+
+ /* Figure out if we really want to wait in poll()
+ * We only need to wait if really nothing already to do and we have
+ * to wait for something from network
+ */
+#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
+ /* 1) if we know we are draining rx ext, do not wait in poll */
+ if (pt->ws.rx_draining_ext_list)
+ return 0;
+#endif
+
+ /* 2) if we know we have non-network pending data, do not wait in poll */
+
+ if (pt->context->tls_ops &&
+ pt->context->tls_ops->fake_POLLIN_for_buffered)
+ if (pt->context->tls_ops->fake_POLLIN_for_buffered(pt))
+ return 0;
+
+ /* 3) If there is any wsi with rxflow buffered and in a state to process
+ * it, we should not wait in poll
+ */
+
+ lws_start_foreach_dll(struct lws_dll_lws *, d, pt->dll_head_buflist.next) {
+ struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
+
+ if (lwsi_state(wsi) != LRS_DEFERRING_ACTION)
+ return 0;
+
+ } lws_end_foreach_dll(d);
+
+ return timeout_ms;
+}
+
+/*
+ * POLLIN said there is something... we must read it, and either use it; or
+ * if other material already in the buflist append it and return the buflist
+ * head material.
+ */
+int
+lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi,
+ struct lws_tokens *ebuf)
+{
+ int n, prior = (int)lws_buflist_next_segment_len(&wsi->buflist, NULL);
+
+ ebuf->token = (char *)pt->serv_buf;
+ ebuf->len = lws_ssl_capable_read(wsi, pt->serv_buf,
+ wsi->context->pt_serv_buf_size);
+
+ if (ebuf->len == LWS_SSL_CAPABLE_MORE_SERVICE && prior)
+ goto get_from_buflist;
+
+ if (ebuf->len <= 0)
+ return 0;
+
+ /* nothing in buflist already? Then just use what we read */
+
+ if (!prior)
+ return 0;
+
+ /* stash what we read */
+
+ n = lws_buflist_append_segment(&wsi->buflist, (uint8_t *)ebuf->token,
+ ebuf->len);
+ if (n < 0)
+ return -1;
+ if (n) {
+ lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
+ lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
+ }
+
+ /* get the first buflist guy in line */
+
+get_from_buflist:
+
+ ebuf->len = (int)lws_buflist_next_segment_len(&wsi->buflist,
+ (uint8_t **)&ebuf->token);
+
+ return 1; /* came from buflist */
+}
+
+int
+lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used,
+ int buffered)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ int m;
+
+ /* it's in the buflist; we didn't use any */
+
+ if (!used && buffered)
+ return 0;
+
+ if (used && buffered) {
+ m = lws_buflist_use_segment(&wsi->buflist, used);
+ lwsl_info("%s: draining rxflow: used %d, next %d\n",
+ __func__, used, m);
+ if (m)
+ return 0;
+
+ lwsl_info("%s: removed %p from dll_buflist\n", __func__, wsi);
+ lws_dll_lws_remove(&wsi->dll_buflist);
+
+ return 0;
+ }
+
+ /* any remainder goes on the buflist */
+
+ if (used != ebuf->len) {
+ m = lws_buflist_append_segment(&wsi->buflist,
+ (uint8_t *)ebuf->token + used,
+ ebuf->len - used);
+ if (m < 0)
+ return 1; /* OOM */
+ if (m) {
+ lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
+ lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
+ }
+ }
+
+ return 0;
+}
+
+void
+lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt)
+{
+ struct lws_pollfd pfd;
+
+ if (!pt->dll_head_buflist.next)
+ return;
+
+ /*
+ * service all guys with pending rxflow that reached a state they can
+ * accept the pending data
+ */
+
+ lws_pt_lock(pt, __func__);
+
+ lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+ pt->dll_head_buflist.next) {
+ struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
+
+ pfd.events = LWS_POLLIN;
+ pfd.revents = LWS_POLLIN;
+ pfd.fd = -1;
+
+ lwsl_debug("%s: rxflow processing: %p 0x%x\n", __func__, wsi,
+ wsi->wsistate);
+
+ if (!lws_is_flowcontrolled(wsi) &&
+ lwsi_state(wsi) != LRS_DEFERRING_ACTION &&
+ (wsi->role_ops->handle_POLLIN)(pt, wsi, &pfd) ==
+ LWS_HPI_RET_PLEASE_CLOSE_ME)
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
+ "close_and_handled");
+
+ } lws_end_foreach_dll_safe(d, d1);
+
+ lws_pt_unlock(pt);
+}
+
+/*
+ * guys that need POLLIN service again without waiting for network action
+ * can force POLLIN here if not flowcontrolled, so they will get service.
+ *
+ * Return nonzero if anybody got their POLLIN faked
+ */
+int
+lws_service_flag_pending(struct lws_context *context, int tsi)
+{
+ struct lws_context_per_thread *pt = &context->pt[tsi];
+
+#if defined(LWS_WITH_TLS)
+ struct lws *wsi, *wsi_next;
+#endif
+ int forced = 0;
+
+ lws_pt_lock(pt, __func__);
+
+ /*
+ * 1) If there is any wsi with a buflist and in a state to process
+ * it, we should not wait in poll
+ */
+
+ lws_start_foreach_dll(struct lws_dll_lws *, d, pt->dll_head_buflist.next) {
+ struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
+
+ if (lwsi_state(wsi) != LRS_DEFERRING_ACTION) {
+ forced = 1;
+ break;
+ }
+ } lws_end_foreach_dll(d);
+
+#if defined(LWS_ROLE_WS)
+ forced |= role_ops_ws.service_flag_pending(context, tsi);
+#endif
+
+#if defined(LWS_WITH_TLS)
+ /*
+ * 2) For all guys with buffered SSL read data already saved up, if they
+ * are not flowcontrolled, fake their POLLIN status so they'll get
+ * service to use up the buffered incoming data, even though their
+ * network socket may have nothing
+ */
+ wsi = pt->tls.pending_read_list;
+ while (wsi) {
+ wsi_next = wsi->tls.pending_read_list_next;
+ pt->fds[wsi->position_in_fds_table].revents |=
+ pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
+ if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) {
+ forced = 1;
+ /*
+ * he's going to get serviced now, take him off the
+ * list of guys with buffered SSL. If he still has some
+ * at the end of the service, he'll get put back on the
+ * list then.
+ */
+ __lws_ssl_remove_wsi_from_buffered_list(wsi);
+ }
+
+ wsi = wsi_next;
+ }
+#endif
+
+ lws_pt_unlock(pt);
+
+ return forced;
+}
+
+static int
+lws_service_periodic_checks(struct lws_context *context,
+ struct lws_pollfd *pollfd, int tsi)
+{
+ struct lws_context_per_thread *pt = &context->pt[tsi];
+ lws_sockfd_type our_fd = 0, tmp_fd;
+ struct lws *wsi;
+ int timed_out = 0;
+ time_t now;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ struct allocated_headers *ah;
+ int m;
+#endif
+
+ if (!context->protocol_init_done)
+ if (lws_protocol_init(context))
+ return -1;
+
+ time(&now);
+
+ /*
+ * handle case that system time was uninitialized when lws started
+ * at boot, and got initialized a little later
+ */
+ if (context->time_up < 1464083026 && now > 1464083026)
+ context->time_up = now;
+
+ if (context->last_timeout_check_s &&
+ now - context->last_timeout_check_s > 100) {
+ /*
+ * There has been a discontiguity. Any stored time that is
+ * less than context->time_discontiguity should have context->
+ * time_fixup added to it.
+ *
+ * Some platforms with no RTC will experience this as a normal
+ * event when ntp sets their clock, but we can have started
+ * long before that with a 0-based unix time.
+ */
+
+ context->time_discontiguity = now;
+ context->time_fixup = now - context->last_timeout_check_s;
+
+ lwsl_notice("time discontiguity: at old time %llus, "
+ "new time %llus: +%llus\n",
+ (unsigned long long)context->last_timeout_check_s,
+ (unsigned long long)context->time_discontiguity,
+ (unsigned long long)context->time_fixup);
+
+ context->last_timeout_check_s = now - 1;
+ }
+
+ if (!lws_compare_time_t(context, context->last_timeout_check_s, now))
+ return 0;
+
+ context->last_timeout_check_s = now;
+
+#if defined(LWS_WITH_STATS)
+ if (!tsi && now - context->last_dump > 10) {
+ lws_stats_log_dump(context);
+ context->last_dump = now;
+ }
+#endif
+
+ lws_plat_service_periodic(context);
+ lws_check_deferred_free(context, 0);
+
+#if defined(LWS_WITH_PEER_LIMITS)
+ lws_peer_cull_peer_wait_list(context);
+#endif
+
+ /* retire unused deprecated context */
+#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_ESP32)
+#if !defined(_WIN32)
+ if (context->deprecated && !context->count_wsi_allocated) {
+ lwsl_notice("%s: ending deprecated context\n", __func__);
+ kill(getpid(), SIGINT);
+ return 0;
+ }
+#endif
+#endif
+ /* global timeout check once per second */
+
+ if (pollfd)
+ our_fd = pollfd->fd;
+
+ /*
+ * Phase 1: check every wsi on the timeout check list
+ */
+
+ lws_pt_lock(pt, __func__);
+
+ lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+ context->pt[tsi].dll_head_timeout.next) {
+ wsi = lws_container_of(d, struct lws, dll_timeout);
+ tmp_fd = wsi->desc.sockfd;
+ if (__lws_service_timeout_check(wsi, now)) {
+ /* he did time out... */
+ if (tmp_fd == our_fd)
+ /* it was the guy we came to service! */
+ timed_out = 1;
+ /* he's gone, no need to mark as handled */
+ }
+ } lws_end_foreach_dll_safe(d, d1);
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ /*
+ * Phase 2: double-check active ah timeouts independent of wsi
+ * timeout status
+ */
+
+ ah = pt->http.ah_list;
+ while (ah) {
+ int len;
+ char buf[256];
+ const unsigned char *c;
+
+ if (!ah->in_use || !ah->wsi || !ah->assigned ||
+ (ah->wsi->vhost &&
+ lws_compare_time_t(context, now, ah->assigned) <
+ ah->wsi->vhost->timeout_secs_ah_idle + 360)) {
+ ah = ah->next;
+ continue;
+ }
+
+ /*
+ * a single ah session somehow got held for
+ * an unreasonable amount of time.
+ *
+ * Dump info on the connection...
+ */
+ wsi = ah->wsi;
+ buf[0] = '\0';
+#if !defined(LWS_PLAT_OPTEE)
+ lws_get_peer_simple(wsi, buf, sizeof(buf));
+#else
+ buf[0] = '\0';
+#endif
+ lwsl_notice("ah excessive hold: wsi %p\n"
+ " peer address: %s\n"
+ " ah pos %u\n",
+ wsi, buf, ah->pos);
+ buf[0] = '\0';
+ m = 0;
+ do {
+ c = lws_token_to_string(m);
+ if (!c)
+ break;
+ if (!(*c))
+ break;
+
+ len = lws_hdr_total_length(wsi, m);
+ if (!len || len > (int)sizeof(buf) - 1) {
+ m++;
+ continue;
+ }
+
+ if (lws_hdr_copy(wsi, buf,
+ sizeof buf, m) > 0) {
+ buf[sizeof(buf) - 1] = '\0';
+
+ lwsl_notice(" %s = %s\n",
+ (const char *)c, buf);
+ }
+ m++;
+ } while (1);
+
+ /* explicitly detach the ah */
+ lws_header_table_detach(wsi, 0);
+
+ /* ... and then drop the connection */
+
+ m = 0;
+ if (wsi->desc.sockfd == our_fd) {
+ m = timed_out;
+
+ /* it was the guy we came to service! */
+ timed_out = 1;
+ }
+
+ if (!m) /* if he didn't already timeout */
+ __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
+ "excessive ah");
+
+ ah = pt->http.ah_list;
+ }
+#endif
+ lws_pt_unlock(pt);
+
+#if 0
+ {
+ char s[300], *p = s;
+
+ for (n = 0; n < context->count_threads; n++)
+ p += sprintf(p, " %7lu (%5d), ",
+ context->pt[n].count_conns,
+ context->pt[n].fds_count);
+
+ lwsl_notice("load: %s\n", s);
+ }
+#endif
+ /*
+ * Phase 3: vhost / protocol timer callbacks
+ */
+
+ wsi = NULL;
+ lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
+ struct lws_timed_vh_protocol *nx;
+ if (v->timed_vh_protocol_list) {
+ lws_start_foreach_ll(struct lws_timed_vh_protocol *,
+ q, v->timed_vh_protocol_list) {
+ if (now >= q->time) {
+ if (!wsi)
+ wsi = lws_zalloc(sizeof(*wsi), "cbwsi");
+ wsi->context = context;
+ wsi->vhost = v;
+ wsi->protocol = q->protocol;
+ lwsl_debug("timed cb: vh %s, protocol %s, reason %d\n", v->name, q->protocol->name, q->reason);
+ q->protocol->callback(wsi, q->reason, NULL, NULL, 0);
+ nx = q->next;
+ lws_timed_callback_remove(v, q);
+ q = nx;
+ continue; /* we pointed ourselves to the next from the now-deleted guy */
+ }
+ } lws_end_foreach_ll(q, next);
+ }
+ } lws_end_foreach_ll(v, vhost_next);
+ if (wsi)
+ lws_free(wsi);
+
+ /*
+ * Phase 4: check for unconfigured vhosts due to required
+ * interface missing before
+ */
+
+ lws_context_lock(context);
+ lws_start_foreach_llp(struct lws_vhost **, pv,
+ context->no_listener_vhost_list) {
+ struct lws_vhost *v = *pv;
+ lwsl_debug("deferred iface: checking if on vh %s\n", (*pv)->name);
+ if (_lws_vhost_init_server(NULL, *pv) == 0) {
+ /* became happy */
+ lwsl_notice("vh %s: became connected\n", v->name);
+ *pv = v->no_listener_vhost_list;
+ v->no_listener_vhost_list = NULL;
+ break;
+ }
+ } lws_end_foreach_llp(pv, no_listener_vhost_list);
+ lws_context_unlock(context);
+
+ /*
+ * Phase 5: role periodic checks
+ */
+#if defined(LWS_ROLE_WS)
+ role_ops_ws.periodic_checks(context, tsi, now);
+#endif
+#if defined(LWS_ROLE_CGI)
+ role_ops_cgi.periodic_checks(context, tsi, now);
+#endif
+
+ /*
+ * Phase 6: check the remaining cert lifetime daily
+ */
+
+ if (context->tls_ops &&
+ context->tls_ops->periodic_housekeeping)
+ context->tls_ops->periodic_housekeeping(context, now);
+
+ return timed_out;
+}
+
+LWS_VISIBLE int
+lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
+ int tsi)
+{
+ struct lws_context_per_thread *pt = &context->pt[tsi];
+ struct lws *wsi;
+
+ if (!context || context->being_destroyed1)
+ return -1;
+
+ /* the socket we came to service timed out, nothing to do */
+ if (lws_service_periodic_checks(context, pollfd, tsi) || !pollfd)
+ return 0;
+
+ /* no, here to service a socket descriptor */
+ wsi = wsi_from_fd(context, pollfd->fd);
+ if (!wsi)
+ /* not lws connection ... leave revents alone and return */
+ return 0;
+
+ /*
+ * so that caller can tell we handled, past here we need to
+ * zero down pollfd->revents after handling
+ */
+
+ /* handle session socket closed */
+
+ if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
+ (pollfd->revents & LWS_POLLHUP)) {
+ wsi->socket_is_permanently_unusable = 1;
+ lwsl_debug("Session Socket %p (fd=%d) dead\n",
+ (void *)wsi, pollfd->fd);
+
+ goto close_and_handled;
+ }
+
+#ifdef _WIN32
+ if (pollfd->revents & LWS_POLLOUT)
+ wsi->sock_send_blocking = FALSE;
+#endif
+
+ if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
+ (pollfd->revents & LWS_POLLHUP)) {
+ lwsl_debug("pollhup\n");
+ wsi->socket_is_permanently_unusable = 1;
+ goto close_and_handled;
+ }
+
+#if defined(LWS_WITH_TLS)
+ if (lwsi_state(wsi) == LRS_SHUTDOWN &&
+ lws_is_ssl(wsi) && wsi->tls.ssl) {
+ switch (__lws_tls_shutdown(wsi)) {
+ case LWS_SSL_CAPABLE_DONE:
+ case LWS_SSL_CAPABLE_ERROR:
+ goto close_and_handled;
+
+ case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
+ case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
+ case LWS_SSL_CAPABLE_MORE_SERVICE:
+ goto handled;
+ }
+ }
+#endif
+ wsi->could_have_pending = 0; /* clear back-to-back write detection */
+
+ /* okay, what we came here to do... */
+
+ /* if we got here, we should have wire protocol ops set on the wsi */
+ assert(wsi->role_ops);
+
+ // lwsl_notice("%s: %s: wsistate 0x%x\n", __func__, wsi->role_ops->name,
+ // wsi->wsistate);
+
+ switch ((wsi->role_ops->handle_POLLIN)(pt, wsi, pollfd)) {
+ case LWS_HPI_RET_WSI_ALREADY_DIED:
+ return 1;
+ case LWS_HPI_RET_HANDLED:
+ break;
+ case LWS_HPI_RET_PLEASE_CLOSE_ME:
+close_and_handled:
+ lwsl_debug("%p: Close and handled\n", wsi);
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
+ "close_and_handled");
+#if defined(_DEBUG) && defined(LWS_WITH_LIBUV)
+ /*
+ * confirm close has no problem being called again while
+ * it waits for libuv service to complete the first async
+ * close
+ */
+ if (context->event_loop_ops == &event_loop_ops_uv)
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
+ "close_and_handled uv repeat test");
+#endif
+ /*
+ * pollfd may point to something else after the close
+ * due to pollfd swapping scheme on delete on some platforms
+ * we can't clear revents now because it'd be the wrong guy's
+ * revents
+ */
+ return 1;
+ default:
+ assert(0);
+ }
+#if defined(LWS_WITH_TLS)
+handled:
+#endif
+ pollfd->revents = 0;
+
+ lws_pt_lock(pt, __func__);
+ __lws_hrtimer_service(pt);
+ lws_pt_unlock(pt);
+
+ return 0;
+}
+
+LWS_VISIBLE int
+lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd)
+{
+ return lws_service_fd_tsi(context, pollfd, 0);
+}
+
+LWS_VISIBLE int
+lws_service(struct lws_context *context, int timeout_ms)
+{
+ struct lws_context_per_thread *pt = &context->pt[0];
+ int n;
+
+ if (!context)
+ return 1;
+
+ pt->inside_service = 1;
+
+ if (context->event_loop_ops->run_pt) {
+ /* we are configured for an event loop */
+ context->event_loop_ops->run_pt(context, 0);
+
+ pt->inside_service = 0;
+
+ return 1;
+ }
+ n = lws_plat_service(context, timeout_ms);
+
+ pt->inside_service = 0;
+
+ return n;
+}
+
+LWS_VISIBLE int
+lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
+{
+ struct lws_context_per_thread *pt = &context->pt[tsi];
+ int n;
+
+ pt->inside_service = 1;
+
+ if (context->event_loop_ops->run_pt) {
+ /* we are configured for an event loop */
+ context->event_loop_ops->run_pt(context, tsi);
+
+ pt->inside_service = 0;
+
+ return 1;
+ }
+
+ n = _lws_plat_service_tsi(context, timeout_ms, tsi);
+
+ pt->inside_service = 0;
+
+ return n;
+}
diff --git a/thirdparty/libwebsockets/event-libs/poll/poll.c b/thirdparty/libwebsockets/event-libs/poll/poll.c
new file mode 100644
index 0000000000..09af5b15d8
--- /dev/null
+++ b/thirdparty/libwebsockets/event-libs/poll/poll.c
@@ -0,0 +1,43 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * This is included from core/private.h if LWS_ROLE_WS
+ */
+
+#include <core/private.h>
+
+struct lws_event_loop_ops event_loop_ops_poll = {
+ /* name */ "poll",
+ /* init_context */ NULL,
+ /* destroy_context1 */ NULL,
+ /* destroy_context2 */ NULL,
+ /* init_vhost_listen_wsi */ NULL,
+ /* init_pt */ NULL,
+ /* wsi_logical_close */ NULL,
+ /* check_client_connect_ok */ NULL,
+ /* close_handle_manually */ NULL,
+ /* accept */ NULL,
+ /* io */ NULL,
+ /* run */ NULL,
+ /* destroy_pt */ NULL,
+ /* destroy wsi */ NULL,
+
+ /* periodic_events_available */ 1,
+}; \ No newline at end of file
diff --git a/thirdparty/libwebsockets/event-libs/poll/private.h b/thirdparty/libwebsockets/event-libs/poll/private.h
new file mode 100644
index 0000000000..ca313ebfb0
--- /dev/null
+++ b/thirdparty/libwebsockets/event-libs/poll/private.h
@@ -0,0 +1,23 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ */
+
+extern struct lws_event_loop_ops event_loop_ops_poll;
diff --git a/thirdparty/libwebsockets/event-libs/private.h b/thirdparty/libwebsockets/event-libs/private.h
new file mode 100644
index 0000000000..c36d39c8c2
--- /dev/null
+++ b/thirdparty/libwebsockets/event-libs/private.h
@@ -0,0 +1,74 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * This is included from core/private.h
+ */
+
+struct lws_event_loop_ops {
+ const char *name;
+ /* event loop-specific context init during context creation */
+ int (*init_context)(struct lws_context *context,
+ const struct lws_context_creation_info *info);
+ /* called during lws_destroy_context */
+ int (*destroy_context1)(struct lws_context *context);
+ /* called during lws_destroy_context2 */
+ int (*destroy_context2)(struct lws_context *context);
+ /* init vhost listening wsi */
+ int (*init_vhost_listen_wsi)(struct lws *wsi);
+ /* init the event loop for a pt */
+ int (*init_pt)(struct lws_context *context, void *_loop, int tsi);
+ /* called at end of first phase of close_free_wsi() */
+ int (*wsi_logical_close)(struct lws *wsi);
+ /* return nonzero if client connect not allowed */
+ int (*check_client_connect_ok)(struct lws *wsi);
+ /* close handle manually */
+ void (*close_handle_manually)(struct lws *wsi);
+ /* event loop accept processing */
+ void (*accept)(struct lws *wsi);
+ /* control wsi active events */
+ void (*io)(struct lws *wsi, int flags);
+ /* run the event loop for a pt */
+ void (*run_pt)(struct lws_context *context, int tsi);
+ /* called before pt is destroyed */
+ void (*destroy_pt)(struct lws_context *context, int tsi);
+ /* called just before wsi is freed */
+ void (*destroy_wsi)(struct lws *wsi);
+
+ unsigned int periodic_events_available:1;
+};
+
+/* bring in event libs private declarations */
+
+#if defined(LWS_WITH_POLL)
+#include "event-libs/poll/private.h"
+#endif
+
+#if defined(LWS_WITH_LIBUV)
+#include "event-libs/libuv/private.h"
+#endif
+
+#if defined(LWS_WITH_LIBEVENT)
+#include "event-libs/libevent/private.h"
+#endif
+
+#if defined(LWS_WITH_LIBEV)
+#include "event-libs/libev/private.h"
+#endif
+
diff --git a/thirdparty/lws/libwebsockets.h b/thirdparty/libwebsockets/libwebsockets.h
index 460c732602..7ae563d582 100644
--- a/thirdparty/lws/libwebsockets.h
+++ b/thirdparty/libwebsockets/libwebsockets.h
@@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
- * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -27,25 +27,21 @@
#ifdef __cplusplus
#include <cstddef>
#include <cstdarg>
-#
+
extern "C" {
#else
#include <stdarg.h>
#endif
+#include <string.h>
+#include <stdlib.h>
+
#include "lws_config.h"
/*
* CARE: everything using cmake defines needs to be below here
*/
-#if defined(LWS_WITH_ESP8266)
-struct sockaddr_in;
-#define LWS_POSIX 0
-#else
-#define LWS_POSIX 1
-#endif
-
#if defined(LWS_HAS_INTPTR_T)
#include <stdint.h>
#define lws_intptr_t intptr_t
@@ -62,6 +58,7 @@ typedef unsigned long long lws_intptr_t;
#include <ws2tcpip.h>
#include <stddef.h>
#include <basetsd.h>
+#include <io.h>
#ifndef _WIN32_WCE
#include <fcntl.h>
#else
@@ -99,25 +96,18 @@ typedef unsigned long long lws_intptr_t;
#define LWS_O_CREAT _O_CREAT
#define LWS_O_TRUNC _O_TRUNC
-#if !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER < 1900) /* Visual Studio 2015 already defines this in <stdio.h> */
-#define lws_snprintf _snprintf
-#endif
-
#ifndef __func__
#define __func__ __FUNCTION__
#endif
-#if !defined(__MINGW32__) &&(!defined(_MSC_VER) || _MSC_VER < 1900) && !defined(snprintf)
-#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__)
-#endif
-
#else /* NOT WIN32 */
#include <unistd.h>
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
#include <sys/capability.h>
#endif
-#if defined(__NetBSD__) || defined(__FreeBSD__)
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__QNX__) || defined(__OpenBSD__)
+#include <sys/socket.h>
#include <netinet/in.h>
#endif
@@ -127,7 +117,7 @@ typedef unsigned long long lws_intptr_t;
#define LWS_O_CREAT O_CREAT
#define LWS_O_TRUNC O_TRUNC
-#if !defined(LWS_WITH_ESP8266) && !defined(OPTEE_TA) && !defined(LWS_WITH_ESP32)
+#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_TA) && !defined(LWS_WITH_ESP32)
#include <poll.h>
#include <netdb.h>
#define LWS_INVALID_FILE -1
@@ -166,7 +156,7 @@ typedef unsigned long long lws_intptr_t;
#endif
-#ifdef LWS_WITH_LIBEV
+#if defined(LWS_WITH_LIBEV)
#include <ev.h>
#endif /* LWS_WITH_LIBEV */
#ifdef LWS_WITH_LIBUV
@@ -175,7 +165,7 @@ typedef unsigned long long lws_intptr_t;
#include <uv-version.h>
#endif
#endif /* LWS_WITH_LIBUV */
-#ifdef LWS_WITH_LIBEVENT
+#if defined(LWS_WITH_LIBEVENT)
#include <event2/event.h>
#endif /* LWS_WITH_LIBEVENT */
@@ -192,13 +182,34 @@ typedef unsigned long long lws_intptr_t;
#endif
#endif
-#ifdef LWS_OPENSSL_SUPPORT
+#if defined(LWS_WITH_TLS)
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
+#ifdef _WIN32
+/*
+ * Include user-controlled settings for windows from
+ * <wolfssl-root>/IDE/WIN/user_settings.h
+ */
+#include <IDE/WIN/user_settings.h>
+#include <cyassl/ctaocrypt/settings.h>
+#else
+#include <cyassl/options.h>
+#endif
#include <cyassl/openssl/ssl.h>
#include <cyassl/error-ssl.h>
+
#else
+#ifdef _WIN32
+/*
+ * Include user-controlled settings for windows from
+ * <wolfssl-root>/IDE/WIN/user_settings.h
+ */
+#include <IDE/WIN/user_settings.h>
+#include <wolfssl/wolfcrypt/settings.h>
+#else
+#include <wolfssl/options.h>
+#endif
#include <wolfssl/openssl/ssl.h>
#include <wolfssl/error-ssl.h>
#endif /* not USE_OLD_CYASSL */
@@ -210,14 +221,60 @@ typedef unsigned long long lws_intptr_t;
#define MBEDTLS_CONFIG_FILE <mbedtls/esp_config.h>
#endif
#include <mbedtls/ssl.h>
-#endif
+#else
#include <openssl/ssl.h>
#if !defined(LWS_WITH_MBEDTLS)
#include <openssl/err.h>
#endif
+#endif
#endif /* not USE_WOLFSSL */
#endif
+/*
+ * Helpers for pthread mutex in user code... if lws is built for
+ * multiple service threads, these resolve to pthread mutex
+ * operations. In the case LWS_MAX_SMP is 1 (the default), they
+ * are all NOPs and no pthread type or api is referenced.
+ */
+
+#if LWS_MAX_SMP > 1
+
+#include <pthread.h>
+
+#define lws_pthread_mutex(name) pthread_mutex_t name;
+
+static LWS_INLINE void
+lws_pthread_mutex_init(pthread_mutex_t *lock)
+{
+ pthread_mutex_init(lock, NULL);
+}
+
+static LWS_INLINE void
+lws_pthread_mutex_destroy(pthread_mutex_t *lock)
+{
+ pthread_mutex_destroy(lock);
+}
+
+static LWS_INLINE void
+lws_pthread_mutex_lock(pthread_mutex_t *lock)
+{
+ pthread_mutex_lock(lock);
+}
+
+static LWS_INLINE void
+lws_pthread_mutex_unlock(pthread_mutex_t *lock)
+{
+ pthread_mutex_unlock(lock);
+}
+
+#else
+#define lws_pthread_mutex(name)
+#define lws_pthread_mutex_init(_a)
+#define lws_pthread_mutex_destroy(_a)
+#define lws_pthread_mutex_lock(_a)
+#define lws_pthread_mutex_unlock(_a)
+#endif
+
#define CONTEXT_PORT_NO_LISTEN -1
#define CONTEXT_PORT_NO_LISTEN_SERVER -2
@@ -280,10 +337,6 @@ lwsl_timestamp(int level, char *p, int len);
* active
*/
-#if defined(LWS_WITH_ESP8266)
-#undef _DEBUG
-#endif
-
#ifdef _DEBUG
#if defined(LWS_WITH_NO_LOGS)
/* notice, warn and log are always compiled in */
@@ -313,14 +366,20 @@ lwsl_timestamp(int level, char *p, int len);
#endif
+#define lwsl_hexdump_err(...) lwsl_hexdump_level(LLL_ERR, __VA_ARGS__)
+#define lwsl_hexdump_warn(...) lwsl_hexdump_level(LLL_WARN, __VA_ARGS__)
+#define lwsl_hexdump_notice(...) lwsl_hexdump_level(LLL_NOTICE, __VA_ARGS__)
+#define lwsl_hexdump_info(...) lwsl_hexdump_level(LLL_INFO, __VA_ARGS__)
+#define lwsl_hexdump_debug(...) lwsl_hexdump_level(LLL_DEBUG, __VA_ARGS__)
+
/**
- * lwsl_hexdump() - helper to hexdump a buffer
+ * lwsl_hexdump_level() - helper to hexdump a buffer at a selected debug level
*
* \param level: one of LLL_ constants
- * \param buf: buffer start to dump
+ * \param vbuf: buffer start to dump
* \param len: length of buffer to dump
*
- * If \p level is visible, does a nice hexdump -C style dump of \p buf for
+ * If \p level is visible, does a nice hexdump -C style dump of \p vbuf for
* \p len bytes. This can be extremely convenient while debugging.
*/
LWS_VISIBLE LWS_EXTERN void
@@ -396,12 +455,13 @@ lwsl_visible(int level);
#define lws_container_of(P,T,M) ((T *)((char *)(P) - offsetof(T, M)))
#endif
-
struct lws;
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#endif
+typedef int64_t lws_usec_t;
+
/* api change list for user code to test against */
#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_ARG
@@ -422,7 +482,7 @@ struct lws;
#if defined(_WIN32)
typedef SOCKET lws_sockfd_type;
typedef HANDLE lws_filefd_type;
-#define lws_sockfd_valid(sfd) (!!sfd)
+
struct lws_pollfd {
lws_sockfd_type fd; /**< file descriptor */
SHORT events; /**< which events to respond to */
@@ -434,77 +494,11 @@ struct lws_pollfd {
#else
-#if defined(LWS_WITH_ESP8266)
-
-#include <user_interface.h>
-#include <espconn.h>
-
-typedef struct espconn * lws_sockfd_type;
-typedef void * lws_filefd_type;
-#define lws_sockfd_valid(sfd) (!!sfd)
-struct pollfd {
- lws_sockfd_type fd; /**< fd related to */
- short events; /**< which POLL... events to respond to */
- short revents; /**< which POLL... events occurred */
-};
-#define POLLIN 0x0001
-#define POLLPRI 0x0002
-#define POLLOUT 0x0004
-#define POLLERR 0x0008
-#define POLLHUP 0x0010
-#define POLLNVAL 0x0020
-
-struct lws_vhost;
-
-lws_sockfd_type esp8266_create_tcp_listen_socket(struct lws_vhost *vh);
-void esp8266_tcp_stream_accept(lws_sockfd_type fd, struct lws *wsi);
-
-#include <os_type.h>
-#include <osapi.h>
-#include "ets_sys.h"
-
-int ets_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3);
-#define snprintf ets_snprintf
-
-typedef os_timer_t uv_timer_t;
-typedef void uv_cb_t(uv_timer_t *);
-
-void os_timer_disarm(void *);
-void os_timer_setfn(os_timer_t *, os_timer_func_t *, void *);
-
-void ets_timer_arm_new(os_timer_t *, int, int, int);
-
-//void os_timer_arm(os_timer_t *, int, int);
-
-#define UV_VERSION_MAJOR 1
-
-#define lws_uv_getloop(a, b) (NULL)
-
-static inline void uv_timer_init(void *l, uv_timer_t *t)
-{
- (void)l;
- memset(t, 0, sizeof(*t));
- os_timer_disarm(t);
-}
-
-static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep)
-{
- os_timer_setfn(t, (os_timer_func_t *)cb, t);
- /* ms, repeat */
- os_timer_arm(t, first, !!rep);
-}
-
-static inline void uv_timer_stop(uv_timer_t *t)
-{
- os_timer_disarm(t);
-}
-
-#else
#if defined(LWS_WITH_ESP32)
typedef int lws_sockfd_type;
typedef int lws_filefd_type;
-#define lws_sockfd_valid(sfd) (sfd >= 0)
+
struct pollfd {
lws_sockfd_type fd; /**< fd related to */
short events; /**< which POLL... events to respond to */
@@ -638,13 +632,14 @@ struct lws_esp32 {
char model[16];
char group[16];
char role[16];
- char ssid[4][16];
- char password[4][32];
- char active_ssid[32];
+ char ssid[4][64];
+ char password[4][64];
+ char active_ssid[64];
char access_pw[16];
char hostname[32];
char mac[20];
- mdns_server_t *mdns;
+ char le_dns[64];
+ char le_email[64];
char region;
char inet;
char conn_ap;
@@ -656,6 +651,11 @@ struct lws_esp32 {
void *scan_consumer_arg;
struct lws_group_member *first;
int extant_group_members;
+
+ char acme;
+ char upload;
+
+ volatile char button_is_down;
};
struct lws_esp32_image {
@@ -702,8 +702,6 @@ extern void lws_esp32_leds_timer_cb(TimerHandle_t th);
#else
typedef int lws_sockfd_type;
typedef int lws_filefd_type;
-#define lws_sockfd_valid(sfd) (sfd >= 0)
-#endif
#endif
#define lws_pollfd pollfd
@@ -830,6 +828,8 @@ enum lws_close_status {
connection was closed due to a failure to perform a TLS handshake
(e.g., the server certificate can't be verified). */
+ LWS_CLOSE_STATUS_CLIENT_TRANSACTION_DONE = 2000,
+
/****** add new things just above ---^ ******/
LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY = 9999,
@@ -858,37 +858,6 @@ struct lws_context;
/* needed even with extensions disabled for create context */
struct lws_extension;
-/*! \defgroup lwsmeta lws-meta
- *
- * ##lws-meta protocol
- *
- * The protocol wraps other muxed connections inside one tcp connection.
- *
- * Commands are assigned from 0x41 up (so they are valid unicode)
- */
-///@{
-
-enum lws_meta_commands {
- LWS_META_CMD_OPEN_SUBCHANNEL = 'A',
- /**< Client requests to open new subchannel
- */
- LWS_META_CMD_OPEN_RESULT,
- /**< Result of client request to open new subchannel */
- LWS_META_CMD_CLOSE_NOTIFY,
- /**< Notification of subchannel closure */
- LWS_META_CMD_CLOSE_RQ,
- /**< client requests to close a subchannel */
- LWS_META_CMD_WRITE,
- /**< connection writes something to specific channel index */
-
- /****** add new things just above ---^ ******/
-};
-
-/* channel numbers are transported offset by 0x20 so they are valid unicode */
-
-#define LWS_META_TRANSPORT_OFFSET 0x20
-
-///@}
/*! \defgroup usercb User Callback
*
@@ -908,16 +877,388 @@ struct lws_ssl_info {
int ret;
};
+enum lws_cert_update_state {
+ LWS_CUS_IDLE,
+ LWS_CUS_STARTING,
+ LWS_CUS_SUCCESS,
+ LWS_CUS_FAILED,
+
+ LWS_CUS_CREATE_KEYS,
+ LWS_CUS_REG,
+ LWS_CUS_AUTH,
+ LWS_CUS_CHALLENGE,
+ LWS_CUS_CREATE_REQ,
+ LWS_CUS_REQ,
+ LWS_CUS_CONFIRM,
+ LWS_CUS_ISSUE,
+};
+
+enum {
+ LWS_TLS_REQ_ELEMENT_COUNTRY,
+ LWS_TLS_REQ_ELEMENT_STATE,
+ LWS_TLS_REQ_ELEMENT_LOCALITY,
+ LWS_TLS_REQ_ELEMENT_ORGANIZATION,
+ LWS_TLS_REQ_ELEMENT_COMMON_NAME,
+ LWS_TLS_REQ_ELEMENT_EMAIL,
+
+ LWS_TLS_REQ_ELEMENT_COUNT,
+
+ LWS_TLS_SET_DIR_URL = LWS_TLS_REQ_ELEMENT_COUNT,
+ LWS_TLS_SET_AUTH_PATH,
+ LWS_TLS_SET_CERT_PATH,
+ LWS_TLS_SET_KEY_PATH,
+
+ LWS_TLS_TOTAL_COUNT
+};
+
+struct lws_acme_cert_aging_args {
+ struct lws_vhost *vh;
+ const char *element_overrides[LWS_TLS_TOTAL_COUNT]; /* NULL = use pvo */
+};
+
/*
* NOTE: These public enums are part of the abi. If you want to add one,
* add it at where specified so existing users are unaffected.
*/
/** enum lws_callback_reasons - reason you're getting a protocol callback */
enum lws_callback_reasons {
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to wsi and protocol binding lifecycle -----
+ */
+
+ LWS_CALLBACK_PROTOCOL_INIT = 27,
+ /**< One-time call per protocol, per-vhost using it, so it can
+ * do initial setup / allocations etc */
+
+ LWS_CALLBACK_PROTOCOL_DESTROY = 28,
+ /**< One-time call per protocol, per-vhost using it, indicating
+ * this protocol won't get used at all after this callback, the
+ * vhost is getting destroyed. Take the opportunity to
+ * deallocate everything that was allocated by the protocol. */
+
+ LWS_CALLBACK_WSI_CREATE = 29,
+ /**< outermost (earliest) wsi create notification to protocols[0] */
+
+ LWS_CALLBACK_WSI_DESTROY = 30,
+ /**< outermost (latest) wsi destroy notification to protocols[0] */
+
+ LWS_CALLBACK_HTTP_BIND_PROTOCOL = 49,
+ /**< By default, all HTTP handling is done in protocols[0].
+ * However you can bind different protocols (by name) to
+ * different parts of the URL space using callback mounts. This
+ * callback occurs in the new protocol when a wsi is bound
+ * to that protocol. Any protocol allocation related to the
+ * http transaction processing should be created then.
+ * These specific callbacks are necessary because with HTTP/1.1,
+ * a single connection may perform at series of different
+ * transactions at different URLs, thus the lifetime of the
+ * protocol bind is just for one transaction, not connection. */
+
+ LWS_CALLBACK_HTTP_DROP_PROTOCOL = 50,
+ /**< This is called when a transaction is unbound from a protocol.
+ * It indicates the connection completed its transaction and may
+ * do something different now. Any protocol allocation related
+ * to the http transaction processing should be destroyed. */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to Server TLS -----
+ */
+
+ LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS = 21,
+ /**< if configured for
+ * including OpenSSL support, this callback allows your user code
+ * to perform extra SSL_CTX_load_verify_locations() or similar
+ * calls to direct OpenSSL where to find certificates the client
+ * can use to confirm the remote server identity. user is the
+ * OpenSSL SSL_CTX* */
+
+ LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22,
+ /**< if configured for
+ * including OpenSSL support, this callback allows your user code
+ * to load extra certificates into the server which allow it to
+ * verify the validity of certificates returned by clients. user
+ * is the server's OpenSSL SSL_CTX* and in is the lws_vhost */
+
+ LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23,
+ /**< if the libwebsockets vhost was created with the option
+ * LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT, then this
+ * callback is generated during OpenSSL verification of the cert
+ * sent from the client. It is sent to protocol[0] callback as
+ * no protocol has been negotiated on the connection yet.
+ * Notice that the libwebsockets context and wsi are both NULL
+ * during this callback. See
+ * http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
+ * to understand more detail about the OpenSSL callback that
+ * generates this libwebsockets callback and the meanings of the
+ * arguments passed. In this callback, user is the x509_ctx,
+ * in is the ssl pointer and len is preverify_ok
+ * Notice that this callback maintains libwebsocket return
+ * conventions, return 0 to mean the cert is OK or 1 to fail it.
+ * This also means that if you don't handle this callback then
+ * the default callback action of returning 0 allows the client
+ * certificates. */
+
+ LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY = 37,
+ /**< if configured for including OpenSSL support but no private key
+ * file has been specified (ssl_private_key_filepath is NULL), this is
+ * called to allow the user to set the private key directly via
+ * libopenssl and perform further operations if required; this might be
+ * useful in situations where the private key is not directly accessible
+ * by the OS, for example if it is stored on a smartcard.
+ * user is the server's OpenSSL SSL_CTX* */
+
+ LWS_CALLBACK_SSL_INFO = 67,
+ /**< SSL connections only. An event you registered an
+ * interest in at the vhost has occurred on a connection
+ * using the vhost. in is a pointer to a
+ * struct lws_ssl_info containing information about the
+ * event*/
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to Client TLS -----
+ */
+
+ LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION = 58,
+ /**< Similar to LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION
+ * this callback is called during OpenSSL verification of the cert
+ * sent from the server to the client. It is sent to protocol[0]
+ * callback as no protocol has been negotiated on the connection yet.
+ * Notice that the wsi is set because lws_client_connect_via_info was
+ * successful.
+ *
+ * See http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
+ * to understand more detail about the OpenSSL callback that
+ * generates this libwebsockets callback and the meanings of the
+ * arguments passed. In this callback, user is the x509_ctx,
+ * in is the ssl pointer and len is preverify_ok.
+ *
+ * THIS IS NOT RECOMMENDED BUT if a cert validation error shall be
+ * overruled and cert shall be accepted as ok,
+ * X509_STORE_CTX_set_error((X509_STORE_CTX*)user, X509_V_OK); must be
+ * called and return value must be 0 to mean the cert is OK;
+ * returning 1 will fail the cert in any case.
+ *
+ * This also means that if you don't handle this callback then
+ * the default callback action of returning 0 will not accept the
+ * certificate in case of a validation error decided by the SSL lib.
+ *
+ * This is expected and secure behaviour when validating certificates.
+ *
+ * Note: LCCSCF_ALLOW_SELFSIGNED and
+ * LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK still work without this
+ * callback being implemented.
+ */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to HTTP Server -----
+ */
+
+ LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED = 19,
+ /**< A new client has been accepted by the ws server. This
+ * callback allows setting any relevant property to it. Because this
+ * happens immediately after the instantiation of a new client,
+ * there's no websocket protocol selected yet so this callback is
+ * issued only to protocol 0. Only wsi is defined, pointing to the
+ * new client, and the return value is ignored. */
+
+ LWS_CALLBACK_HTTP = 12,
+ /**< an http request has come from a client that is not
+ * asking to upgrade the connection to a websocket
+ * one. This is a chance to serve http content,
+ * for example, to send a script to the client
+ * which will then open the websockets connection.
+ * in points to the URI path requested and
+ * lws_serve_http_file() makes it very
+ * simple to send back a file to the client.
+ * Normally after sending the file you are done
+ * with the http connection, since the rest of the
+ * activity will come by websockets from the script
+ * that was delivered by http, so you will want to
+ * return 1; to close and free up the connection. */
+
+ LWS_CALLBACK_HTTP_BODY = 13,
+ /**< the next len bytes data from the http
+ * request body HTTP connection is now available in in. */
+
+ LWS_CALLBACK_HTTP_BODY_COMPLETION = 14,
+ /**< the expected amount of http request body has been delivered */
+
+ LWS_CALLBACK_HTTP_FILE_COMPLETION = 15,
+ /**< a file requested to be sent down http link has completed. */
+
+ LWS_CALLBACK_HTTP_WRITEABLE = 16,
+ /**< you can write more down the http protocol link now. */
+
+ LWS_CALLBACK_CLOSED_HTTP = 5,
+ /**< when a HTTP (non-websocket) session ends */
+
+ LWS_CALLBACK_FILTER_HTTP_CONNECTION = 18,
+ /**< called when the request has
+ * been received and parsed from the client, but the response is
+ * not sent yet. Return non-zero to disallow the connection.
+ * user is a pointer to the connection user space allocation,
+ * in is the URI, eg, "/"
+ * In your handler you can use the public APIs
+ * lws_hdr_total_length() / lws_hdr_copy() to access all of the
+ * headers using the header enums lws_token_indexes from
+ * libwebsockets.h to check for and read the supported header
+ * presence and content before deciding to allow the http
+ * connection to proceed or to kill the connection. */
+
+ LWS_CALLBACK_ADD_HEADERS = 53,
+ /**< This gives your user code a chance to add headers to a server
+ * transaction bound to your protocol. `in` points to a
+ * `struct lws_process_html_args` describing a buffer and length
+ * you can add headers into using the normal lws apis.
+ *
+ * (see LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to add headers to
+ * a client transaction)
+ *
+ * Only `args->p` and `args->len` are valid, and `args->p` should
+ * be moved on by the amount of bytes written, if any. Eg
+ *
+ * case LWS_CALLBACK_ADD_HEADERS:
+ *
+ * struct lws_process_html_args *args =
+ * (struct lws_process_html_args *)in;
+ *
+ * if (lws_add_http_header_by_name(wsi,
+ * (unsigned char *)"set-cookie:",
+ * (unsigned char *)cookie, cookie_len,
+ * (unsigned char **)&args->p,
+ * (unsigned char *)args->p + args->max_len))
+ * return 1;
+ *
+ * break;
+ */
+
+ LWS_CALLBACK_CHECK_ACCESS_RIGHTS = 51,
+ /**< This gives the user code a chance to forbid an http access.
+ * `in` points to a `struct lws_process_html_args`, which
+ * describes the URL, and a bit mask describing the type of
+ * authentication required. If the callback returns nonzero,
+ * the transaction ends with HTTP_STATUS_UNAUTHORIZED. */
+
+ LWS_CALLBACK_PROCESS_HTML = 52,
+ /**< This gives your user code a chance to mangle outgoing
+ * HTML. `in` points to a `struct lws_process_html_args`
+ * which describes the buffer containing outgoing HTML.
+ * The buffer may grow up to `.max_len` (currently +128
+ * bytes per buffer).
+ */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to HTTP Client -----
+ */
+
+ LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44,
+ /**< The HTTP client connection has succeeded, and is now
+ * connected to the server */
+
+ LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45,
+ /**< The HTTP client connection is closing */
+
+ LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48,
+ /**< This is generated by lws_http_client_read() used to drain
+ * incoming data. In the case the incoming data was chunked, it will
+ * be split into multiple smaller callbacks for each chunk block,
+ * removing the chunk headers. If not chunked, it will appear all in
+ * one callback. */
+
+ LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46,
+ /**< This simply indicates data was received on the HTTP client
+ * connection. It does NOT drain or provide the data.
+ * This exists to neatly allow a proxying type situation,
+ * where this incoming data will go out on another connection.
+ * If the outgoing connection stalls, we should stall processing
+ * the incoming data. So a handler for this in that case should
+ * simply set a flag to indicate there is incoming data ready
+ * and ask for a writeable callback on the outgoing connection.
+ * In the writable callback he can check the flag and then get
+ * and drain the waiting incoming data using lws_http_client_read().
+ * This will use callbacks to LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ
+ * to get and drain the incoming data, where it should be sent
+ * back out on the outgoing connection. */
+ LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47,
+ /**< The client transaction completed... at the moment this
+ * is the same as closing since transaction pipelining on
+ * client side is not yet supported. */
+
+ LWS_CALLBACK_CLIENT_HTTP_WRITEABLE = 57,
+ /**< when doing an HTTP type client connection, you can call
+ * lws_client_http_body_pending(wsi, 1) from
+ * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to get these callbacks
+ * sending the HTTP headers.
+ *
+ * From this callback, when you have sent everything, you should let
+ * lws know by calling lws_client_http_body_pending(wsi, 0)
+ */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to Websocket Server -----
+ */
+
LWS_CALLBACK_ESTABLISHED = 0,
/**< (VH) after the server completes a handshake with an incoming
* client. If you built the library with ssl support, in is a
- * pointer to the ssl struct associated with the connection or NULL.*/
+ * pointer to the ssl struct associated with the connection or NULL.
+ *
+ * b0 of len is set if the connection was made using ws-over-h2
+ */
+
+ LWS_CALLBACK_CLOSED = 4,
+ /**< when the websocket session ends */
+
+ LWS_CALLBACK_SERVER_WRITEABLE = 11,
+ /**< See LWS_CALLBACK_CLIENT_WRITEABLE */
+
+ LWS_CALLBACK_RECEIVE = 6,
+ /**< data has appeared for this server endpoint from a
+ * remote client, it can be found at *in and is
+ * len bytes long */
+
+ LWS_CALLBACK_RECEIVE_PONG = 7,
+ /**< servers receive PONG packets with this callback reason */
+
+ LWS_CALLBACK_WS_PEER_INITIATED_CLOSE = 38,
+ /**< The peer has sent an unsolicited Close WS packet. in and
+ * len are the optional close code (first 2 bytes, network
+ * order) and the optional additional information which is not
+ * defined in the standard, and may be a string or non human-readable
+ * data.
+ * If you return 0 lws will echo the close and then close the
+ * connection. If you return nonzero lws will just close the
+ * connection. */
+
+ LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION = 20,
+ /**< called when the handshake has
+ * been received and parsed from the client, but the response is
+ * not sent yet. Return non-zero to disallow the connection.
+ * user is a pointer to the connection user space allocation,
+ * in is the requested protocol name
+ * In your handler you can use the public APIs
+ * lws_hdr_total_length() / lws_hdr_copy() to access all of the
+ * headers using the header enums lws_token_indexes from
+ * libwebsockets.h to check for and read the supported header
+ * presence and content before deciding to allow the handshake
+ * to proceed or to kill the connection. */
+
+ LWS_CALLBACK_CONFIRM_EXTENSION_OKAY = 25,
+ /**< When the server handshake code
+ * sees that it does support a requested extension, before
+ * accepting the extension by additing to the list sent back to
+ * the client it gives this callback just to check that it's okay
+ * to use that extension. It calls back to the requested protocol
+ * and with in being the extension name, len is 0 and user is
+ * valid. Note though at this time the ESTABLISHED callback hasn't
+ * happened yet so if you initialize user content there, user
+ * content during this callback might not be useful for anything. */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to Websocket Client -----
+ */
+
LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 1,
/**< the request client connection has been unable to complete a
* handshake with the remote server. If in is non-NULL, you can
@@ -962,6 +1303,7 @@ enum lws_callback_reasons {
* "HS: SO_SNDBUF failed"
* "HS: Rejected at CLIENT_ESTABLISHED"
*/
+
LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH = 2,
/**< this is the last chance for the client user code to examine the
* http headers and decide to reject the connection. If the
@@ -969,131 +1311,14 @@ enum lws_callback_reasons {
* client (url, etc) it needs to copy it out at
* this point since it will be destroyed before
* the CLIENT_ESTABLISHED call */
+
LWS_CALLBACK_CLIENT_ESTABLISHED = 3,
- /**< after your client connection completed
- * a handshake with the remote server */
- LWS_CALLBACK_CLOSED = 4,
- /**< when the websocket session ends */
- LWS_CALLBACK_CLOSED_HTTP = 5,
- /**< when a HTTP (non-websocket) session ends */
- LWS_CALLBACK_RECEIVE = 6,
- /**< data has appeared for this server endpoint from a
- * remote client, it can be found at *in and is
- * len bytes long */
- LWS_CALLBACK_RECEIVE_PONG = 7,
- /**< servers receive PONG packets with this callback reason */
- LWS_CALLBACK_CLIENT_RECEIVE = 8,
- /**< data has appeared from the server for the client connection, it
- * can be found at *in and is len bytes long */
- LWS_CALLBACK_CLIENT_RECEIVE_PONG = 9,
- /**< clients receive PONG packets with this callback reason */
- LWS_CALLBACK_CLIENT_WRITEABLE = 10,
- /**< If you call lws_callback_on_writable() on a connection, you will
- * get one of these callbacks coming when the connection socket
- * is able to accept another write packet without blocking.
- * If it already was able to take another packet without blocking,
- * you'll get this callback at the next call to the service loop
- * function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE
- * and servers get LWS_CALLBACK_SERVER_WRITEABLE. */
- LWS_CALLBACK_SERVER_WRITEABLE = 11,
- /**< See LWS_CALLBACK_CLIENT_WRITEABLE */
- LWS_CALLBACK_HTTP = 12,
- /**< an http request has come from a client that is not
- * asking to upgrade the connection to a websocket
- * one. This is a chance to serve http content,
- * for example, to send a script to the client
- * which will then open the websockets connection.
- * in points to the URI path requested and
- * lws_serve_http_file() makes it very
- * simple to send back a file to the client.
- * Normally after sending the file you are done
- * with the http connection, since the rest of the
- * activity will come by websockets from the script
- * that was delivered by http, so you will want to
- * return 1; to close and free up the connection. */
- LWS_CALLBACK_HTTP_BODY = 13,
- /**< the next len bytes data from the http
- * request body HTTP connection is now available in in. */
- LWS_CALLBACK_HTTP_BODY_COMPLETION = 14,
- /**< the expected amount of http request body has been delivered */
- LWS_CALLBACK_HTTP_FILE_COMPLETION = 15,
- /**< a file requested to be sent down http link has completed. */
- LWS_CALLBACK_HTTP_WRITEABLE = 16,
- /**< you can write more down the http protocol link now. */
- LWS_CALLBACK_FILTER_NETWORK_CONNECTION = 17,
- /**< called when a client connects to
- * the server at network level; the connection is accepted but then
- * passed to this callback to decide whether to hang up immediately
- * or not, based on the client IP. in contains the connection
- * socket's descriptor. Since the client connection information is
- * not available yet, wsi still pointing to the main server socket.
- * Return non-zero to terminate the connection before sending or
- * receiving anything. Because this happens immediately after the
- * network connection from the client, there's no websocket protocol
- * selected yet so this callback is issued only to protocol 0. */
- LWS_CALLBACK_FILTER_HTTP_CONNECTION = 18,
- /**< called when the request has
- * been received and parsed from the client, but the response is
- * not sent yet. Return non-zero to disallow the connection.
- * user is a pointer to the connection user space allocation,
- * in is the URI, eg, "/"
- * In your handler you can use the public APIs
- * lws_hdr_total_length() / lws_hdr_copy() to access all of the
- * headers using the header enums lws_token_indexes from
- * libwebsockets.h to check for and read the supported header
- * presence and content before deciding to allow the http
- * connection to proceed or to kill the connection. */
- LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED = 19,
- /**< A new client just had
- * been connected, accepted, and instantiated into the pool. This
- * callback allows setting any relevant property to it. Because this
- * happens immediately after the instantiation of a new client,
- * there's no websocket protocol selected yet so this callback is
- * issued only to protocol 0. Only wsi is defined, pointing to the
- * new client, and the return value is ignored. */
- LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION = 20,
- /**< called when the handshake has
- * been received and parsed from the client, but the response is
- * not sent yet. Return non-zero to disallow the connection.
- * user is a pointer to the connection user space allocation,
- * in is the requested protocol name
- * In your handler you can use the public APIs
- * lws_hdr_total_length() / lws_hdr_copy() to access all of the
- * headers using the header enums lws_token_indexes from
- * libwebsockets.h to check for and read the supported header
- * presence and content before deciding to allow the handshake
- * to proceed or to kill the connection. */
- LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS = 21,
- /**< if configured for
- * including OpenSSL support, this callback allows your user code
- * to perform extra SSL_CTX_load_verify_locations() or similar
- * calls to direct OpenSSL where to find certificates the client
- * can use to confirm the remote server identity. user is the
- * OpenSSL SSL_CTX* */
- LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22,
- /**< if configured for
- * including OpenSSL support, this callback allows your user code
- * to load extra certificates into the server which allow it to
- * verify the validity of certificates returned by clients. user
- * is the server's OpenSSL SSL_CTX* */
- LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23,
- /**< if the libwebsockets vhost was created with the option
- * LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT, then this
- * callback is generated during OpenSSL verification of the cert
- * sent from the client. It is sent to protocol[0] callback as
- * no protocol has been negotiated on the connection yet.
- * Notice that the libwebsockets context and wsi are both NULL
- * during this callback. See
- * http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
- * to understand more detail about the OpenSSL callback that
- * generates this libwebsockets callback and the meanings of the
- * arguments passed. In this callback, user is the x509_ctx,
- * in is the ssl pointer and len is preverify_ok
- * Notice that this callback maintains libwebsocket return
- * conventions, return 0 to mean the cert is OK or 1 to fail it.
- * This also means that if you don't handle this callback then
- * the default callback action of returning 0 allows the client
- * certificates. */
+ /**< after your client connection completed the websocket upgrade
+ * handshake with the remote server */
+
+ LWS_CALLBACK_CLIENT_CLOSED = 75,
+ /**< when a client websocket session ends */
+
LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER = 24,
/**< this callback happens
* when a client handshake is being compiled. user is NULL,
@@ -1117,19 +1342,30 @@ enum lws_callback_reasons {
* optional, if you don't handle it everything is fine.
*
* Notice the callback is coming to protocols[0] all the time,
- * because there is no specific protocol negotiated yet. */
- LWS_CALLBACK_CONFIRM_EXTENSION_OKAY = 25,
- /**< When the server handshake code
- * sees that it does support a requested extension, before
- * accepting the extension by additing to the list sent back to
- * the client it gives this callback just to check that it's okay
- * to use that extension. It calls back to the requested protocol
- * and with in being the extension name, len is 0 and user is
- * valid. Note though at this time the ESTABLISHED callback hasn't
- * happened yet so if you initialize user content there, user
- * content during this callback might not be useful for anything. */
+ * because there is no specific protocol negotiated yet.
+ *
+ * See LWS_CALLBACK_ADD_HEADERS for adding headers to server
+ * transactions.
+ */
+
+ LWS_CALLBACK_CLIENT_RECEIVE = 8,
+ /**< data has appeared from the server for the client connection, it
+ * can be found at *in and is len bytes long */
+
+ LWS_CALLBACK_CLIENT_RECEIVE_PONG = 9,
+ /**< clients receive PONG packets with this callback reason */
+
+ LWS_CALLBACK_CLIENT_WRITEABLE = 10,
+ /**< If you call lws_callback_on_writable() on a connection, you will
+ * get one of these callbacks coming when the connection socket
+ * is able to accept another write packet without blocking.
+ * If it already was able to take another packet without blocking,
+ * you'll get this callback at the next call to the service loop
+ * function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE
+ * and servers get LWS_CALLBACK_SERVER_WRITEABLE. */
+
LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED = 26,
- /**< When a client
+ /**< When a ws client
* connection is being prepared to start a handshake to a server,
* each supported extension is checked with protocols[0] callback
* with this reason, giving the user code a chance to suppress the
@@ -1137,18 +1373,32 @@ enum lws_callback_reasons {
* unhandled, by default 0 will be returned and the extension
* support included in the header to the server. Notice this
* callback comes to protocols[0]. */
- LWS_CALLBACK_PROTOCOL_INIT = 27,
- /**< One-time call per protocol, per-vhost using it, so it can
- * do initial setup / allocations etc */
- LWS_CALLBACK_PROTOCOL_DESTROY = 28,
- /**< One-time call per protocol, per-vhost using it, indicating
- * this protocol won't get used at all after this callback, the
- * vhost is getting destroyed. Take the opportunity to
- * deallocate everything that was allocated by the protocol. */
- LWS_CALLBACK_WSI_CREATE = 29,
- /**< outermost (earliest) wsi create notification to protocols[0] */
- LWS_CALLBACK_WSI_DESTROY = 30,
- /**< outermost (latest) wsi destroy notification to protocols[0] */
+
+ LWS_CALLBACK_WS_EXT_DEFAULTS = 39,
+ /**< Gives client connections an opportunity to adjust negotiated
+ * extension defaults. `user` is the extension name that was
+ * negotiated (eg, "permessage-deflate"). `in` points to a
+ * buffer and `len` is the buffer size. The user callback can
+ * set the buffer to a string describing options the extension
+ * should parse. Or just ignore for defaults. */
+
+
+ LWS_CALLBACK_FILTER_NETWORK_CONNECTION = 17,
+ /**< called when a client connects to
+ * the server at network level; the connection is accepted but then
+ * passed to this callback to decide whether to hang up immediately
+ * or not, based on the client IP. in contains the connection
+ * socket's descriptor. Since the client connection information is
+ * not available yet, wsi still pointing to the main server socket.
+ * Return non-zero to terminate the connection before sending or
+ * receiving anything. Because this happens immediately after the
+ * network connection from the client, there's no websocket protocol
+ * selected yet so this callback is issued only to protocol 0. */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to external poll loop integration -----
+ */
+
LWS_CALLBACK_GET_THREAD_ID = 31,
/**< lws can accept callback when writable requests from other
* threads, if you implement this callback and return an opaque
@@ -1171,12 +1421,14 @@ enum lws_callback_reasons {
*
* If you are using the internal lws polling / event loop
* you can just ignore these callbacks. */
+
LWS_CALLBACK_DEL_POLL_FD = 33,
/**< This callback happens when a socket descriptor
* needs to be removed from an external polling array. in is
* again the struct lws_pollargs containing the fd member
* to be removed. If you are using the internal polling
* loop, you can just ignore it. */
+
LWS_CALLBACK_CHANGE_MODE_POLL_FD = 34,
/**< This callback happens when lws wants to modify the events for
* a connection.
@@ -1185,6 +1437,7 @@ enum lws_callback_reasons {
* the prev_events member.
* If you are using the internal polling loop, you can just ignore
* it. */
+
LWS_CALLBACK_LOCK_POLL = 35,
/**< These allow the external poll changes driven
* by lws to participate in an external thread locking
@@ -1197,135 +1450,46 @@ enum lws_callback_reasons {
* len == 1 allows external threads to be synchronized against
* wsi lifecycle changes if it acquires the same lock for the
* duration of wsi dereference from the other thread context. */
+
LWS_CALLBACK_UNLOCK_POLL = 36,
/**< See LWS_CALLBACK_LOCK_POLL, ignore if using lws internal poll */
- LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY = 37,
- /**< if configured for including OpenSSL support but no private key
- * file has been specified (ssl_private_key_filepath is NULL), this is
- * called to allow the user to set the private key directly via
- * libopenssl and perform further operations if required; this might be
- * useful in situations where the private key is not directly accessible
- * by the OS, for example if it is stored on a smartcard.
- * user is the server's OpenSSL SSL_CTX* */
- LWS_CALLBACK_WS_PEER_INITIATED_CLOSE = 38,
- /**< The peer has sent an unsolicited Close WS packet. in and
- * len are the optional close code (first 2 bytes, network
- * order) and the optional additional information which is not
- * defined in the standard, and may be a string or non-human- readable data.
- * If you return 0 lws will echo the close and then close the
- * connection. If you return nonzero lws will just close the
- * connection. */
-
- LWS_CALLBACK_WS_EXT_DEFAULTS = 39,
- /**< Gives client connections an opportunity to adjust negotiated
- * extension defaults. `user` is the extension name that was
- * negotiated (eg, "permessage-deflate"). `in` points to a
- * buffer and `len` is the buffer size. The user callback can
- * set the buffer to a string describing options the extension
- * should parse. Or just ignore for defaults. */
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to CGI serving -----
+ */
LWS_CALLBACK_CGI = 40,
/**< CGI: CGI IO events on stdin / out / err are sent here on
* protocols[0]. The provided `lws_callback_http_dummy()`
* handles this and the callback should be directed there if
* you use CGI. */
+
LWS_CALLBACK_CGI_TERMINATED = 41,
/**< CGI: The related CGI process ended, this is called before
* the wsi is closed. Used to, eg, terminate chunking.
* The provided `lws_callback_http_dummy()`
* handles this and the callback should be directed there if
* you use CGI. The child PID that terminated is in len. */
+
LWS_CALLBACK_CGI_STDIN_DATA = 42,
/**< CGI: Data is, to be sent to the CGI process stdin, eg from
* a POST body. The provided `lws_callback_http_dummy()`
* handles this and the callback should be directed there if
* you use CGI. */
+
LWS_CALLBACK_CGI_STDIN_COMPLETED = 43,
/**< CGI: no more stdin is coming. The provided
* `lws_callback_http_dummy()` handles this and the callback
* should be directed there if you use CGI. */
- LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44,
- /**< The HTTP client connection has succeeded, and is now
- * connected to the server */
- LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45,
- /**< The HTTP client connection is closing */
- LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46,
- /**< This simply indicates data was received on the HTTP client
- * connection. It does NOT drain or provide the data.
- * This exists to neatly allow a proxying type situation,
- * where this incoming data will go out on another connection.
- * If the outgoing connection stalls, we should stall processing
- * the incoming data. So a handler for this in that case should
- * simply set a flag to indicate there is incoming data ready
- * and ask for a writeable callback on the outgoing connection.
- * In the writable callback he can check the flag and then get
- * and drain the waiting incoming data using lws_http_client_read().
- * This will use callbacks to LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ
- * to get and drain the incoming data, where it should be sent
- * back out on the outgoing connection. */
- LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47,
- /**< The client transaction completed... at the moment this
- * is the same as closing since transaction pipelining on
- * client side is not yet supported. */
- LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48,
- /**< This is generated by lws_http_client_read() used to drain
- * incoming data. In the case the incoming data was chunked,
- * it will be split into multiple smaller callbacks for each
- * chunk block, removing the chunk headers. If not chunked,
- * it will appear all in one callback. */
- LWS_CALLBACK_HTTP_BIND_PROTOCOL = 49,
- /**< By default, all HTTP handling is done in protocols[0].
- * However you can bind different protocols (by name) to
- * different parts of the URL space using callback mounts. This
- * callback occurs in the new protocol when a wsi is bound
- * to that protocol. Any protocol allocation related to the
- * http transaction processing should be created then.
- * These specific callbacks are necessary because with HTTP/1.1,
- * a single connection may perform at series of different
- * transactions at different URLs, thus the lifetime of the
- * protocol bind is just for one transaction, not connection. */
- LWS_CALLBACK_HTTP_DROP_PROTOCOL = 50,
- /**< This is called when a transaction is unbound from a protocol.
- * It indicates the connection completed its transaction and may
- * do something different now. Any protocol allocation related
- * to the http transaction processing should be destroyed. */
- LWS_CALLBACK_CHECK_ACCESS_RIGHTS = 51,
- /**< This gives the user code a chance to forbid an http access.
- * `in` points to a `struct lws_process_html_args`, which
- * describes the URL, and a bit mask describing the type of
- * authentication required. If the callback returns nonzero,
- * the transaction ends with HTTP_STATUS_UNAUTHORIZED. */
- LWS_CALLBACK_PROCESS_HTML = 52,
- /**< This gives your user code a chance to mangle outgoing
- * HTML. `in` points to a `struct lws_process_html_args`
- * which describes the buffer containing outgoing HTML.
- * The buffer may grow up to `.max_len` (currently +128
- * bytes per buffer).
- * */
- LWS_CALLBACK_ADD_HEADERS = 53,
- /**< This gives your user code a chance to add headers to a
- * transaction bound to your protocol. `in` points to a
- * `struct lws_process_html_args` describing a buffer and length
- * you can add headers into using the normal lws apis.
- *
- * Only `args->p` and `args->len` are valid, and `args->p` should
- * be moved on by the amount of bytes written, if any. Eg
- *
- * case LWS_CALLBACK_ADD_HEADERS:
- *
- * struct lws_process_html_args *args =
- * (struct lws_process_html_args *)in;
- *
- * if (lws_add_http_header_by_name(wsi,
- * (unsigned char *)"set-cookie:",
- * (unsigned char *)cookie, cookie_len,
- * (unsigned char **)&args->p,
- * (unsigned char *)args->p + args->max_len))
- * return 1;
- *
- * break;
+
+ LWS_CALLBACK_CGI_PROCESS_ATTACH = 70,
+ /**< CGI: Sent when the CGI process is spawned for the wsi. The
+ * len parameter is the PID of the child process */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to Generic Sessions -----
*/
+
LWS_CALLBACK_SESSION_INFO = 54,
/**< This is only generated by user code using generic sessions.
* It's used to get a `struct lws_session_info` filled in by
@@ -1335,85 +1499,103 @@ enum lws_callback_reasons {
LWS_CALLBACK_GS_EVENT = 55,
/**< Indicates an event happened to the Generic Sessions session.
* `in` contains a `struct lws_gs_event_args` describing the event. */
+
LWS_CALLBACK_HTTP_PMO = 56,
/**< per-mount options for this connection, called before
* the normal LWS_CALLBACK_HTTP when the mount has per-mount
* options.
*/
- LWS_CALLBACK_CLIENT_HTTP_WRITEABLE = 57,
- /**< when doing an HTTP type client connection, you can call
- * lws_client_http_body_pending(wsi, 1) from
- * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to get these callbacks
- * sending the HTTP headers.
- *
- * From this callback, when you have sent everything, you should let
- * lws know by calling lws_client_http_body_pending(wsi, 0)
- */
- LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION = 58,
- /**< Similar to LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION
- * this callback is called during OpenSSL verification of the cert
- * sent from the server to the client. It is sent to protocol[0]
- * callback as no protocol has been negotiated on the connection yet.
- * Notice that the wsi is set because lws_client_connect_via_info was
- * successful.
- *
- * See http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
- * to understand more detail about the OpenSSL callback that
- * generates this libwebsockets callback and the meanings of the
- * arguments passed. In this callback, user is the x509_ctx,
- * in is the ssl pointer and len is preverify_ok.
- *
- * THIS IS NOT RECOMMENDED BUT if a cert validation error shall be
- * overruled and cert shall be accepted as ok,
- * X509_STORE_CTX_set_error((X509_STORE_CTX*)user, X509_V_OK); must be
- * called and return value must be 0 to mean the cert is OK;
- * returning 1 will fail the cert in any case.
- *
- * This also means that if you don't handle this callback then
- * the default callback action of returning 0 will not accept the
- * certificate in case of a validation error decided by the SSL lib.
- *
- * This is expected and secure behaviour when validating certificates.
- *
- * Note: LCCSCF_ALLOW_SELFSIGNED and
- * LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK still work without this
- * callback being implemented.
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to RAW sockets -----
*/
+
LWS_CALLBACK_RAW_RX = 59,
/**< RAW mode connection RX */
+
LWS_CALLBACK_RAW_CLOSE = 60,
/**< RAW mode connection is closing */
+
LWS_CALLBACK_RAW_WRITEABLE = 61,
/**< RAW mode connection may be written */
+
LWS_CALLBACK_RAW_ADOPT = 62,
/**< RAW mode connection was adopted (equivalent to 'wsi created') */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to RAW file handles -----
+ */
+
LWS_CALLBACK_RAW_ADOPT_FILE = 63,
/**< RAW mode file was adopted (equivalent to 'wsi created') */
+
LWS_CALLBACK_RAW_RX_FILE = 64,
- /**< RAW mode file has something to read */
+ /**< This is the indication the RAW mode file has something to read.
+ * This doesn't actually do the read of the file and len is always
+ * 0... your code should do the read having been informed there is
+ * something to read now. */
+
LWS_CALLBACK_RAW_WRITEABLE_FILE = 65,
/**< RAW mode file is writeable */
+
LWS_CALLBACK_RAW_CLOSE_FILE = 66,
/**< RAW mode wsi that adopted a file is closing */
- LWS_CALLBACK_SSL_INFO = 67,
- /**< SSL connections only. An event you registered an
- * interest in at the vhost has occurred on a connection
- * using the vhost. in is a pointer to a
- * struct lws_ssl_info containing information about the
- * event*/
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to generic wsi events -----
+ */
+
+ LWS_CALLBACK_TIMER = 73,
+ /**< When the time elapsed after a call to
+ * lws_set_timer_usecs(wsi, usecs) is up, the wsi will get one of
+ * these callbacks. The deadline can be continuously extended into the
+ * future by later calls to lws_set_timer_usecs() before the deadline
+ * expires, or cancelled by lws_set_timer_usecs(wsi, -1);
+ * See the note on lws_set_timer_usecs() about which event loops are
+ * supported. */
+
+ LWS_CALLBACK_EVENT_WAIT_CANCELLED = 71,
+ /**< This is sent to every protocol of every vhost in response
+ * to lws_cancel_service() or lws_cancel_service_pt(). This
+ * callback is serialized in the lws event loop normally, even
+ * if the lws_cancel_service[_pt]() call was from a different
+ * thread. */
+
+ LWS_CALLBACK_CHILD_CLOSING = 69,
+ /**< Sent to parent to notify them a child is closing / being
+ * destroyed. in is the child wsi.
+ */
+
LWS_CALLBACK_CHILD_WRITE_VIA_PARENT = 68,
/**< Child has been marked with parent_carries_io attribute, so
* lws_write directs the to this callback at the parent,
* in is a struct lws_write_passthru containing the args
* the lws_write() was called with.
*/
- LWS_CALLBACK_CHILD_CLOSING = 69,
- /**< Sent to parent to notify them a child is closing / being
- * destroyed. in is the child wsi.
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to TLS certificate management -----
*/
- LWS_CALLBACK_CGI_PROCESS_ATTACH = 70,
- /**< CGI: Sent when the CGI process is spawned for the wsi. The
- * len parameter is the PID of the child process */
+
+ LWS_CALLBACK_VHOST_CERT_AGING = 72,
+ /**< When a vhost TLS cert has its expiry checked, this callback
+ * is broadcast to every protocol of every vhost in case the
+ * protocol wants to take some action with this information.
+ * \p in is a pointer to a struct lws_acme_cert_aging_args,
+ * and \p len is the number of days left before it expires, as
+ * a (ssize_t). In the struct lws_acme_cert_aging_args, vh
+ * points to the vhost the cert aging information applies to,
+ * and element_overrides[] is an optional way to update information
+ * from the pvos... NULL in an index means use the information from
+ * from the pvo for the cert renewal, non-NULL in the array index
+ * means use that pointer instead for the index. */
+
+ LWS_CALLBACK_VHOST_CERT_UPDATE = 74,
+ /**< When a vhost TLS cert is being updated, progress is
+ * reported to the vhost in question here, including completion
+ * and failure. in points to optional JSON, and len represents the
+ * connection state using enum lws_cert_update_state */
+
/****** add new things just above ---^ ******/
@@ -1448,6 +1630,8 @@ lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason,
#define LWS_CB_REASON_AUX_BF__CGI_HEADERS 8
///@}
+struct lws_vhost;
+
/*! \defgroup generic hash
* ## Generic Hash related functions
*
@@ -1459,7 +1643,7 @@ lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason,
*/
///@{
-#ifdef LWS_OPENSSL_SUPPORT
+#if defined(LWS_WITH_TLS)
#if defined(LWS_WITH_MBEDTLS)
#include <mbedtls/sha1.h>
@@ -1467,9 +1651,20 @@ lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason,
#include <mbedtls/sha512.h>
#endif
-#define LWS_GENHASH_TYPE_SHA1 0
-#define LWS_GENHASH_TYPE_SHA256 1
-#define LWS_GENHASH_TYPE_SHA512 2
+enum lws_genhash_types {
+ LWS_GENHASH_TYPE_SHA1,
+ LWS_GENHASH_TYPE_SHA256,
+ LWS_GENHASH_TYPE_SHA384,
+ LWS_GENHASH_TYPE_SHA512,
+};
+
+enum lws_genhmac_types {
+ LWS_GENHMAC_TYPE_SHA256,
+ LWS_GENHMAC_TYPE_SHA384,
+ LWS_GENHMAC_TYPE_SHA512,
+};
+
+#define LWS_GENHASH_LARGEST 64
struct lws_genhash_ctx {
uint8_t type;
@@ -1477,7 +1672,8 @@ struct lws_genhash_ctx {
union {
mbedtls_sha1_context sha1;
mbedtls_sha256_context sha256;
- mbedtls_sha512_context sha512;
+ mbedtls_sha512_context sha512; /* 384 also uses this */
+ const mbedtls_md_info_t *hmac;
} u;
#else
const EVP_MD *evp_type;
@@ -1485,6 +1681,17 @@ struct lws_genhash_ctx {
#endif
};
+struct lws_genhmac_ctx {
+ uint8_t type;
+#if defined(LWS_WITH_MBEDTLS)
+ const mbedtls_md_info_t *hmac;
+ mbedtls_md_context_t ctx;
+#else
+ const EVP_MD *evp_type;
+ EVP_MD_CTX *ctx;
+#endif
+};
+
/** lws_genhash_size() - get hash size in bytes
*
* \param type: one of LWS_GENHASH_TYPE_...
@@ -1492,7 +1699,16 @@ struct lws_genhash_ctx {
* Returns number of bytes in this type of hash
*/
LWS_VISIBLE LWS_EXTERN size_t LWS_WARN_UNUSED_RESULT
-lws_genhash_size(int type);
+lws_genhash_size(enum lws_genhash_types type);
+
+/** lws_genhmac_size() - get hash size in bytes
+ *
+ * \param type: one of LWS_GENHASH_TYPE_...
+ *
+ * Returns number of bytes in this type of hmac
+ */
+LWS_VISIBLE LWS_EXTERN size_t LWS_WARN_UNUSED_RESULT
+lws_genhmac_size(enum lws_genhmac_types type);
/** lws_genhash_init() - prepare your struct lws_genhash_ctx for use
*
@@ -1502,7 +1718,7 @@ lws_genhash_size(int type);
* Initializes the hash context for the type you requested
*/
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_genhash_init(struct lws_genhash_ctx *ctx, int type);
+lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type);
/** lws_genhash_update() - digest len bytes of the buffer starting at in
*
@@ -1529,10 +1745,386 @@ lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len);
LWS_VISIBLE LWS_EXTERN int
lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result);
+/** lws_genhmac_init() - prepare your struct lws_genhmac_ctx for use
+ *
+ * \param ctx: your struct lws_genhmac_ctx
+ * \param type: one of LWS_GENHMAC_TYPE_...
+ * \param key: pointer to the start of the HMAC key
+ * \param key_len: length of the HMAC key
+ *
+ * Initializes the hash context for the type you requested
+ *
+ * If the return is nonzero, it failed and there is nothing needing to be
+ * destroyed.
+ */
+int
+lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
+ const uint8_t *key, size_t key_len);
+
+/** lws_genhmac_update() - digest len bytes of the buffer starting at in
+ *
+ * \param ctx: your struct lws_genhmac_ctx
+ * \param in: start of the bytes to digest
+ * \param len: count of bytes to digest
+ *
+ * Updates the state of your hash context to reflect digesting len bytes from in
+ *
+ * If the return is nonzero, it failed and needs destroying.
+ */
+int
+lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len);
+
+/** lws_genhmac_destroy() - copy out the result digest and destroy the ctx
+ *
+ * \param ctx: your struct lws_genhmac_ctx
+ * \param result: NULL, or where to copy the result hash
+ *
+ * Finalizes the hash and copies out the digest. Destroys any allocations such
+ * that ctx can safely go out of scope after calling this.
+ *
+ * NULL result is supported so that you can destroy the ctx cleanly on error
+ * conditions, where there is no valid result.
+ */
+int
+lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result);
+///@}
+
+/*! \defgroup generic RSA
+ * ## Generic RSA related functions
+ *
+ * Lws provides generic RSA functions that abstract the ones
+ * provided by whatever OpenSSL library you are linking against.
+ *
+ * It lets you use the same code if you build against mbedtls or OpenSSL
+ * for example.
+ */
+///@{
+
+enum enum_jwk_tok {
+ JWK_KEY_E,
+ JWK_KEY_N,
+ JWK_KEY_D,
+ JWK_KEY_P,
+ JWK_KEY_Q,
+ JWK_KEY_DP,
+ JWK_KEY_DQ,
+ JWK_KEY_QI,
+ JWK_KTY, /* also serves as count of real elements */
+ JWK_KEY,
+};
+
+#define LWS_COUNT_RSA_ELEMENTS JWK_KTY
+
+struct lws_genrsa_ctx {
+#if defined(LWS_WITH_MBEDTLS)
+ mbedtls_rsa_context *ctx;
+#else
+ BIGNUM *bn[LWS_COUNT_RSA_ELEMENTS];
+ RSA *rsa;
#endif
+};
+
+struct lws_genrsa_element {
+ uint8_t *buf;
+ uint16_t len;
+};
+
+struct lws_genrsa_elements {
+ struct lws_genrsa_element e[LWS_COUNT_RSA_ELEMENTS];
+};
+
+/** lws_jwk_destroy_genrsa_elements() - Free allocations in genrsa_elements
+ *
+ * \param el: your struct lws_genrsa_elements
+ *
+ * This is a helper for user code making use of struct lws_genrsa_elements
+ * where the elements are allocated on the heap, it frees any non-NULL
+ * buf element and sets the buf to NULL.
+ *
+ * NB: lws_genrsa_public_... apis do not need this as they take care of the key
+ * creation and destruction themselves.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el);
+
+/** lws_genrsa_public_decrypt_create() - Create RSA public decrypt context
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param el: struct prepared with key element data
+ *
+ * Creates an RSA context with a public key associated with it, formed from
+ * the key elements in \p el.
+ *
+ * Returns 0 for OK or nonzero for error.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el);
+
+/** lws_genrsa_new_keypair() - Create new RSA keypair
+ *
+ * \param context: your struct lws_context (may be used for RNG)
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param el: struct to get the new key element data allocated into it
+ * \param bits: key size, eg, 4096
+ *
+ * Creates a new RSA context and generates a new keypair into it, with \p bits
+ * bits.
+ *
+ * Returns 0 for OK or nonzero for error.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
+ struct lws_genrsa_elements *el, int bits);
+
+/** lws_genrsa_public_decrypt() - Perform RSA public decryption
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: encrypted input
+ * \param in_len: length of encrypted input
+ * \param out: decrypted output
+ * \param out_max: size of output buffer
+ *
+ * Performs the decryption.
+ *
+ * Returns <0 for error, or length of decrypted data.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out, size_t out_max);
+
+/** lws_genrsa_public_verify() - Perform RSA public verification
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: unencrypted payload (usually a recomputed hash)
+ * \param hash_type: one of LWS_GENHASH_TYPE_
+ * \param sig: pointer to the signature we received with the payload
+ * \param sig_len: length of the signature we are checking in bytes
+ *
+ * Returns <0 for error, or 0 if signature matches the payload + key.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_public_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type,
+ const uint8_t *sig, size_t sig_len);
+
+/** lws_genrsa_public_sign() - Create RSA signature
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: precomputed hash
+ * \param hash_type: one of LWS_GENHASH_TYPE_
+ * \param sig: pointer to buffer to take signature
+ * \param sig_len: length of the buffer (must be >= length of key N)
+ *
+ * Returns <0 for error, or 0 for success.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_public_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type, uint8_t *sig,
+ size_t sig_len);
+
+/** lws_genrsa_public_decrypt_destroy() - Destroy RSA public decrypt context
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ *
+ * Destroys any allocations related to \p ctx.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_genrsa_destroy(struct lws_genrsa_ctx *ctx);
+/** lws_genrsa_render_pkey_asn1() - Exports public or private key to ASN1/DER
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param _private: 0 = public part only, 1 = all parts of the key
+ * \param pkey_asn1: pointer to buffer to take the ASN1
+ * \param pkey_asn1_len: max size of the pkey_asn1_len
+ *
+ * Returns length of pkey_asn1 written, or -1 for error.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private,
+ uint8_t *pkey_asn1, size_t pkey_asn1_len);
///@}
+/*! \defgroup jwk JSON Web Keys
+ * ## JSON Web Keys API
+ *
+ * Lws provides an API to parse JSON Web Keys into a struct lws_genrsa_elements.
+ *
+ * "oct" and "RSA" type keys are supported. For "oct" keys, they are held in
+ * the "e" member of the struct lws_genrsa_elements.
+ *
+ * Keys elements are allocated on the heap. You must destroy the allocations
+ * in the struct lws_genrsa_elements by calling
+ * lws_jwk_destroy_genrsa_elements() when you are finished with it.
+ */
+///@{
+
+struct lws_jwk {
+ char keytype[5]; /**< "oct" or "RSA" */
+ struct lws_genrsa_elements el; /**< OCTet key is in el.e */
+};
+
+/** lws_jwk_import() - Create a JSON Web key from the textual representation
+ *
+ * \param s: the JWK object to create
+ * \param in: a single JWK JSON stanza in utf-8
+ * \param len: the length of the JWK JSON stanza in bytes
+ *
+ * Creates an lws_jwk struct filled with data from the JSON representation.
+ * "oct" and "rsa" key types are supported.
+ *
+ * For "oct" type keys, it is loaded into el.e.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwk_import(struct lws_jwk *s, const char *in, size_t len);
+
+/** lws_jwk_destroy() - Destroy a JSON Web key
+ *
+ * \param s: the JWK object to destroy
+ *
+ * All allocations in the lws_jwk are destroyed
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_jwk_destroy(struct lws_jwk *s);
+
+/** lws_jwk_export() - Export a JSON Web key to a textual representation
+ *
+ * \param s: the JWK object to export
+ * \param _private: 0 = just export public parts, 1 = export everything
+ * \param p: the buffer to write the exported JWK to
+ * \param len: the length of the buffer \p p in bytes
+ *
+ * Returns length of the used part of the buffer if OK, or -1 for error.
+ *
+ * Serializes the content of the JWK into a char buffer.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwk_export(struct lws_jwk *s, int _private, char *p, size_t len);
+
+/** lws_jwk_load() - Import a JSON Web key from a file
+ *
+ * \param s: the JWK object to load into
+ * \param filename: filename to load from
+ *
+ * Returns 0 for OK or -1 for failure
+ */
+LWS_VISIBLE int
+lws_jwk_load(struct lws_jwk *s, const char *filename);
+
+/** lws_jwk_save() - Export a JSON Web key to a file
+ *
+ * \param s: the JWK object to save from
+ * \param filename: filename to save to
+ *
+ * Returns 0 for OK or -1 for failure
+ */
+LWS_VISIBLE int
+lws_jwk_save(struct lws_jwk *s, const char *filename);
+
+/** lws_jwk_rfc7638_fingerprint() - jwk to RFC7638 compliant fingerprint
+ *
+ * \param s: the JWK object to fingerprint
+ * \param digest32: buffer to take 32-byte digest
+ *
+ * Returns 0 for OK or -1 for failure
+ */
+LWS_VISIBLE int
+lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32);
+///@}
+
+
+/*! \defgroup jws JSON Web Signature
+ * ## JSON Web Signature API
+ *
+ * Lws provides an API to check and create RFC7515 JSON Web Signatures
+ *
+ * SHA256/384/512 HMAC, and RSA 256/384/512 are supported.
+ *
+ * The API uses your TLS library crypto, but works exactly the same no matter
+ * what you TLS backend is.
+ */
+///@{
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk);
+
+/**
+ * lws_jws_sign_from_b64() - add b64 sig to b64 hdr + payload
+ *
+ * \param b64_hdr: protected header encoded in b64, may be NULL
+ * \param hdr_len: bytes in b64 coding of protected header
+ * \param b64_pay: payload encoded in b64
+ * \param pay_len: bytes in b64 coding of payload
+ * \param b64_sig: buffer to write the b64 encoded signature into
+ * \param sig_len: max bytes we can write at b64_sig
+ * \param hash_type: one of LWS_GENHASH_TYPE_SHA[256|384|512]
+ * \param jwk: the struct lws_jwk containing the signing key
+ *
+ * This adds a b64-coded JWS signature of the b64-encoded protected header
+ * and b64-encoded payload, at \p b64_sig. The signature will be as large
+ * as the N element of the RSA key when the RSA key is used, eg, 512 bytes for
+ * a 4096-bit key, and then b64-encoding on top.
+ *
+ * In some special cases, there is only payload to sign and no header, in that
+ * case \p b64_hdr may be NULL, and only the payload will be hashed before
+ * signing.
+ *
+ * Returns the length of the encoded signature written to \p b64_sig, or -1.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
+ size_t pay_len, char *b64_sig, size_t sig_len,
+ enum lws_genhash_types hash_type, struct lws_jwk *jwk);
+
+/**
+ * lws_jws_create_packet() - add b64 sig to b64 hdr + payload
+ *
+ * \param jwk: the struct lws_jwk containing the signing key
+ * \param payload: unencoded payload JSON
+ * \param len: length of unencoded payload JSON
+ * \param nonce: Nonse string to include in protected header
+ * \param out: buffer to take signed packet
+ * \param out_len: size of \p out buffer
+ *
+ * This creates a "flattened" JWS packet from the jwk and the plaintext
+ * payload, and signs it. The packet is written into \p out.
+ *
+ * This does the whole packet assembly and signing, calling through to
+ * lws_jws_sign_from_b64() as part of the process.
+ *
+ * Returns the length written to \p out, or -1.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_create_packet(struct lws_jwk *jwk, const char *payload, size_t len,
+ const char *nonce, char *out, size_t out_len);
+
+/**
+ * lws_jws_base64_enc() - encode input data into b64url data
+ *
+ * \param in: the incoming plaintext
+ * \param in_len: the length of the incoming plaintext in bytes
+ * \param out: the buffer to store the b64url encoded data to
+ * \param out_max: the length of \p out in bytes
+ *
+ * Returns either -1 if problems, or the number of bytes written to \p out.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max);
+///@}
+#endif
+
/*! \defgroup extensions Extension related functions
* ##Extension releated functions
*
@@ -1548,27 +2140,10 @@ lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result);
* add it at where specified so existing users are unaffected.
*/
enum lws_extension_callback_reasons {
- LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT = 0,
- LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT = 1,
- LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT = 2,
- LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT = 3,
LWS_EXT_CB_CONSTRUCT = 4,
LWS_EXT_CB_CLIENT_CONSTRUCT = 5,
- LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE = 6,
- LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION = 7,
LWS_EXT_CB_DESTROY = 8,
- LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING = 9,
- LWS_EXT_CB_ANY_WSI_ESTABLISHED = 10,
- LWS_EXT_CB_PACKET_RX_PREPARSE = 11,
LWS_EXT_CB_PACKET_TX_PRESEND = 12,
- LWS_EXT_CB_PACKET_TX_DO_SEND = 13,
- LWS_EXT_CB_HANDSHAKE_REPLY_TX = 14,
- LWS_EXT_CB_FLUSH_PENDING_TX = 15,
- LWS_EXT_CB_EXTENDED_PAYLOAD_RX = 16,
- LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION = 17,
- LWS_EXT_CB_1HZ = 18,
- LWS_EXT_CB_REQUEST_ON_WRITEABLE = 19,
- LWS_EXT_CB_IS_WRITEABLE = 20,
LWS_EXT_CB_PAYLOAD_TX = 21,
LWS_EXT_CB_PAYLOAD_RX = 22,
LWS_EXT_CB_OPTION_DEFAULT = 23,
@@ -1646,18 +2221,6 @@ struct lws_ext_option_arg {
* user data is deleted. This same callback is used whether you
* are in client or server instantiation context.
*
- * LWS_EXT_CB_PACKET_RX_PREPARSE: when this extension was active on
- * a connection, and a packet of data arrived at the connection,
- * it is passed to this callback to give the extension a chance to
- * change the data, eg, decompress it. user is pointing to the
- * extension's private connection context data, in is pointing
- * to an lws_tokens struct, it consists of a char * pointer called
- * token, and an int called token_len. At entry, these are
- * set to point to the received buffer and set to the content
- * length. If the extension will grow the content, it should use
- * a new buffer allocated in its private user context data and
- * set the pointed-to lws_tokens members to point to its buffer.
- *
* LWS_EXT_CB_PACKET_TX_PRESEND: this works the same way as
* LWS_EXT_CB_PACKET_RX_PREPARSE above, except it gives the
* extension a chance to change websocket data just before it will
@@ -1697,16 +2260,6 @@ LWS_VISIBLE LWS_EXTERN int
lws_set_extension_option(struct lws *wsi, const char *ext_name,
const char *opt_name, const char *opt_val);
-#ifndef LWS_NO_EXTENSIONS
-/* lws_get_internal_extensions() - DEPRECATED
- *
- * \Deprecated There is no longer a set internal extensions table. The table is provided
- * by user code along with application-specific settings. See the test
- * client and server for how to do.
- */
-static LWS_INLINE LWS_WARN_DEPRECATED const struct lws_extension *
-lws_get_internal_extensions(void) { return NULL; }
-
/**
* lws_ext_parse_options() - deal with parsing negotiated extension options
*
@@ -1721,7 +2274,6 @@ LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
void *ext_user, const struct lws_ext_options *opts,
const char *o, int len);
-#endif
/** lws_extension_callback_pm_deflate() - extension for RFC7692
*
@@ -1785,8 +2337,8 @@ struct lws_protocols {
* be able to consume it all without having to return to the event
* loop. That is supported in lws.
*
- * If .tx_packet_size is 0, this also controls how much may be sent at once
- * for backwards compatibility.
+ * If .tx_packet_size is 0, this also controls how much may be sent at
+ * once for backwards compatibility.
*/
unsigned int id;
/**< ignored by lws, but useful to contain user information bound
@@ -1811,8 +2363,6 @@ struct lws_protocols {
* This is part of the ABI, don't needlessly break compatibility */
};
-struct lws_vhost;
-
/**
* lws_vhost_name_to_protocol() - get vhost's protocol object from its name
*
@@ -1894,6 +2444,17 @@ lws_adjust_protocol_psds(struct lws *wsi, size_t new_size);
LWS_VISIBLE LWS_EXTERN int
lws_finalize_startup(struct lws_context *context);
+/**
+ * lws_pvo_search() - helper to find a named pvo in a linked-list
+ *
+ * \param pvo: the first pvo in the linked-list
+ * \param name: the name of the pvo to return if found
+ *
+ * Returns NULL, or a pointer to the name pvo in the linked-list
+ */
+LWS_VISIBLE LWS_EXTERN const struct lws_protocol_vhost_options *
+lws_pvo_search(const struct lws_protocol_vhost_options *pvo, const char *name);
+
LWS_VISIBLE LWS_EXTERN int
lws_protocol_init(struct lws_context *context);
@@ -2088,6 +2649,17 @@ enum lws_context_options {
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS callback, which
* provides the vhost SSL_CTX * in the user parameter.
*/
+ LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT = (1 << 25),
+ /**< (VH) You probably don't want this. It forces this vhost to not
+ * call LWS_CALLBACK_PROTOCOL_INIT on its protocols. It's used in the
+ * special case of a temporary vhost bound to a single protocol.
+ */
+ LWS_SERVER_OPTION_IGNORE_MISSING_CERT = (1 << 26),
+ /**< (VH) Don't fail if the vhost TLS cert or key are missing, just
+ * continue. The vhost won't be able to serve anything, but if for
+ * example the ACME plugin was configured to fetch a cert, this lets
+ * you bootstrap your vhost from having no cert to start with.
+ */
/****** add new things just above ---^ ******/
};
@@ -2110,7 +2682,11 @@ struct lws_context_creation_info {
/**< VHOST: Port to listen on. Use CONTEXT_PORT_NO_LISTEN to suppress
* listening for a client. Use CONTEXT_PORT_NO_LISTEN_SERVER if you are
* writing a server but you are using \ref sock-adopt instead of the
- * built-in listener */
+ * built-in listener.
+ *
+ * You can also set port to 0, in which case the kernel will pick
+ * a random port that is not already in use. You can find out what
+ * port the vhost is listening on using lws_get_vhost_listen_port() */
const char *iface;
/**< VHOST: NULL to bind the listen socket to all interfaces, or the
* interface name, eg, "eth2"
@@ -2191,12 +2767,12 @@ struct lws_context_creation_info {
int ka_interval;
/**< CONTEXT: if ka_time was nonzero, how long to wait before each ka_probes
* attempt */
-#ifdef LWS_OPENSSL_SUPPORT
+#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS)
SSL_CTX *provided_client_ssl_ctx;
/**< CONTEXT: If non-null, swap out libwebsockets ssl
- * implementation for the one provided by provided_ssl_ctx.
- * Libwebsockets no longer is responsible for freeing the context
- * if this option is selected. */
+ * implementation for the one provided by provided_ssl_ctx.
+ * Libwebsockets no longer is responsible for freeing the context
+ * if this option is selected. */
#else /* maintain structure layout either way */
void *provided_client_ssl_ctx; /**< dummy if ssl disabled */
#endif
@@ -2207,9 +2783,10 @@ struct lws_context_creation_info {
short max_http_header_pool;
/**< CONTEXT: The max number of connections with http headers that
* can be processed simultaneously (the corresponding memory is
- * allocated for the lifetime of the context). If the pool is
- * busy new incoming connections must wait for accept until one
- * becomes free. */
+ * allocated and deallocated dynamically as needed). If the pool is
+ * fully busy new incoming connections must wait for accept until one
+ * becomes free. 0 = allow as many ah as number of availble fds for
+ * the process */
unsigned int count_threads;
/**< CONTEXT: how many contexts to create in an array, 0 = 1 */
@@ -2372,13 +2949,42 @@ struct lws_context_creation_info {
* given here.
*/
uint32_t http2_settings[7];
- /**< CONTEXT: after context creation http2_settings[1] thru [6] have
- * been set to the lws platform default values.
- * VHOST: if http2_settings[0] is nonzero, the values given in
+ /**< VHOST: if http2_settings[0] is nonzero, the values given in
* http2_settings[1]..[6] are used instead of the lws
* platform default values.
* Just leave all at 0 if you don't care.
*/
+ const char *error_document_404;
+ /**< VHOST: If non-NULL, when asked to serve a non-existent file,
+ * lws attempts to server this url path instead. Eg,
+ * "/404.html" */
+ const char *alpn;
+ /**< CONTEXT: If non-NULL, default list of advertised alpn, comma-
+ * separated
+ *
+ * VHOST: If non-NULL, per-vhost list of advertised alpn, comma-
+ * separated
+ */
+ void **foreign_loops;
+ /**< CONTEXT: This is ignored if the context is not being started with
+ * an event loop, ie, .options has a flag like
+ * LWS_SERVER_OPTION_LIBUV.
+ *
+ * NULL indicates lws should start its own even loop for
+ * each service thread, and deal with closing the loops
+ * when the context is destroyed.
+ *
+ * Non-NULL means it points to an array of external
+ * ("foreign") event loops that are to be used in turn for
+ * each service thread. In the default case of 1 service
+ * thread, it can just point to one foreign event loop.
+ */
+ void (*signal_cb)(void *event_lib_handle, int signum);
+ /**< CONTEXT: NULL: default signal handling. Otherwise this receives
+ * the signal handler callback. event_lib_handle is the
+ * native event library signal handle, eg uv_signal_t *
+ * for libuv.
+ */
/* Add new things just above here ---^
* This is part of the ABI, don't needlessly break compatibility
@@ -2387,8 +2993,14 @@ struct lws_context_creation_info {
* members added above will see 0 (default) even if the app
* was not built against the newer headers.
*/
+ struct lws_context **pcontext;
+ /**< CONTEXT: if non-NULL, at the end of context destroy processing,
+ * the pointer pointed to by pcontext is written with NULL. You can
+ * use this to let foreign event loops know that lws context destruction
+ * is fully completed.
+ */
- void *_unused[8]; /**< dummy */
+ void *_unused[4]; /**< dummy */
};
/**
@@ -2426,7 +3038,8 @@ struct lws_context_creation_info {
* one place; they're all handled in the user callback.
*/
LWS_VISIBLE LWS_EXTERN struct lws_context *
-lws_create_context(struct lws_context_creation_info *info);
+lws_create_context(const struct lws_context_creation_info *info);
+
/**
* lws_context_destroy() - Destroy the websocket context
@@ -2439,9 +3052,6 @@ lws_create_context(struct lws_context_creation_info *info);
LWS_VISIBLE LWS_EXTERN void
lws_context_destroy(struct lws_context *context);
-LWS_VISIBLE LWS_EXTERN void
-lws_context_destroy2(struct lws_context *context);
-
typedef int (*lws_reload_func)(void);
/**
@@ -2529,7 +3139,7 @@ struct lws_vhost;
*/
LWS_VISIBLE LWS_EXTERN struct lws_vhost *
lws_create_vhost(struct lws_context *context,
- struct lws_context_creation_info *info);
+ const struct lws_context_creation_info *info);
/**
* lws_vhost_destroy() - Destroy a vhost (virtual server context)
@@ -2598,6 +3208,38 @@ LWS_VISIBLE LWS_EXTERN struct lws_vhost *
lws_get_vhost(struct lws *wsi);
/**
+ * lws_get_vhost_name() - returns the name of a vhost
+ *
+ * \param vhost: which vhost
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_vhost_name(struct lws_vhost *vhost);
+
+/**
+ * lws_get_vhost_port() - returns the port a vhost listens on, or -1
+ *
+ * \param vhost: which vhost
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_get_vhost_port(struct lws_vhost *vhost);
+
+/**
+ * lws_get_vhost_user() - returns the user pointer for the vhost
+ *
+ * \param vhost: which vhost
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lws_get_vhost_user(struct lws_vhost *vhost);
+
+/**
+ * lws_get_vhost_iface() - returns the binding for the vhost listen socket
+ *
+ * \param vhost: which vhost
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_vhost_iface(struct lws_vhost *vhost);
+
+/**
* lws_json_dump_vhost() - describe vhost state and stats in JSON
*
* \param vh: the vhost
@@ -2748,7 +3390,16 @@ enum lws_client_connect_ssl_connection_flags {
LCCSCF_USE_SSL = (1 << 0),
LCCSCF_ALLOW_SELFSIGNED = (1 << 1),
LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK = (1 << 2),
- LCCSCF_ALLOW_EXPIRED = (1 << 3)
+ LCCSCF_ALLOW_EXPIRED = (1 << 3),
+
+ LCCSCF_PIPELINE = (1 << 16),
+ /**< Serialize / pipeline multiple client connections
+ * on a single connection where possible.
+ *
+ * HTTP/1.0: possible if Keep-Alive: yes sent by server
+ * HTTP/1.1: always possible... uses pipelining
+ * HTTP/2: always possible... uses parallel streams
+ * */
};
/** struct lws_client_connect_info - parameters to connect with when using
@@ -2762,7 +3413,7 @@ struct lws_client_connect_info {
int port;
/**< remote port to connect to */
int ssl_connection;
- /**< nonzero for ssl */
+ /**< 0, or a combination of LCCSCF_ flags */
const char *path;
/**< uri path */
const char *host;
@@ -2779,7 +3430,9 @@ struct lws_client_connect_info {
/**< UNUSED... provide in info.extensions at context creation time */
const char *method;
/**< if non-NULL, do this http method instead of ws[s] upgrade.
- * use "GET" to be a simple http client connection */
+ * use "GET" to be a simple http client connection. "RAW" gets
+ * you a connected socket that lws itself will leave alone once
+ * connected. */
struct lws *parent_wsi;
/**< if another wsi is responsible for this connection, give it here.
* this is used to make sure if the parent closes so do any
@@ -2805,6 +3458,13 @@ struct lws_client_connect_info {
const char *iface;
/**< NULL to allow routing on any interface, or interface name or IP
* to bind the socket to */
+ const char *local_protocol_name;
+ /**< NULL: .protocol is used both to select the local protocol handler
+ * to bind to and as the list of remote ws protocols we could
+ * accept.
+ * non-NULL: this protocol name is used to bind the connection to
+ * the local protocol handler. .protocol is used for the
+ * list of remote ws protocols we could accept */
/* Add new things just above here ---^
* This is part of the ABI, don't needlessly break compatibility
@@ -2813,6 +3473,12 @@ struct lws_client_connect_info {
* members added above will see 0 (default) even if the app
* was not built against the newer headers.
*/
+ const char *alpn;
+ /* NULL: allow lws default ALPN list, from vhost if present or from
+ * list of roles built into lws
+ * non-NULL: require one from provided comma-separated list of alpn
+ * tokens
+ */
void *_unused[4]; /**< dummy */
};
@@ -2938,6 +3604,10 @@ lws_http_client_read(struct lws *wsi, char **buf, int *len);
* \param wsi: client connection
*
* Returns the last server response code, eg, 200 for client http connections.
+ *
+ * You should capture this during the LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP
+ * callback, because after that the memory reserved for storing the related
+ * headers is freed and this value is lost.
*/
LWS_VISIBLE LWS_EXTERN unsigned int
lws_http_client_http_response(struct lws *wsi);
@@ -3030,15 +3700,8 @@ lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi);
* on one thread
* \param wsi: Cancel service on the thread this wsi is serviced by
*
- * This function lets a call to lws_service() waiting for a timeout
- * immediately return.
- *
- * It works by creating a phony event and then swallowing it silently.
- *
- * The reason it may be needed is when waiting in poll(), changes to
- * the event masks are ignored by the OS until poll() is reentered. This
- * lets you halt the poll() wait and make the reentry happen immediately
- * instead of having the wait out the rest of the poll timeout.
+ * Same as lws_cancel_service(), but targets a single service thread, the one
+ * the wsi belongs to. You probably want to use lws_cancel_service() instead.
*/
LWS_VISIBLE LWS_EXTERN void
lws_cancel_service_pt(struct lws *wsi);
@@ -3047,12 +3710,13 @@ lws_cancel_service_pt(struct lws *wsi);
* lws_cancel_service() - Cancel wait for new pending socket activity
* \param context: Websocket context
*
- * This function let a call to lws_service() waiting for a timeout
- * immediately return.
+ * This function creates an immediate "synchronous interrupt" to the lws poll()
+ * wait or event loop. As soon as possible in the serialzed service sequencing,
+ * a LWS_CALLBACK_EVENT_WAIT_CANCELLED callback is sent to every protocol on
+ * every vhost.
*
- * What it basically does is provide a fake event that will be swallowed,
- * so the wait in poll() is ended. That's useful because poll() doesn't
- * attend to changes in POLLIN/OUT/ERR until it re-enters the wait.
+ * lws_cancel_service() may be called from another thread while the context
+ * exists, and its effect will be immediately serialized.
*/
LWS_VISIBLE LWS_EXTERN void
lws_cancel_service(struct lws_context *context);
@@ -3235,6 +3899,7 @@ struct lws_process_html_args {
int len; /**< length of the original data at p */
int max_len; /**< maximum length we can grow the data to */
int final; /**< set if this is the last chunk of the file */
+ int chunked; /**< 0 == unchunked, 1 == produce chunk headers (incompatible with HTTP/2) */
};
typedef const char *(*lws_process_html_state_cb)(void *data, int index);
@@ -3296,12 +3961,12 @@ lws_chunked_html_process(struct lws_process_html_args *args,
/** struct lws_tokens
* you need these to look at headers that have been parsed if using the
* LWS_CALLBACK_FILTER_CONNECTION callback. If a header from the enum
- * list below is absent, .token = NULL and token_len = 0. Otherwise .token
- * points to .token_len chars containing that header content.
+ * list below is absent, .token = NULL and len = 0. Otherwise .token
+ * points to .len chars containing that header content.
*/
struct lws_tokens {
char *token; /**< pointer to start of the token */
- int token_len; /**< length of the token's value */
+ int len; /**< length of the token's value */
};
/* enum lws_token_indexes
@@ -3399,6 +4064,10 @@ enum lws_token_indexes {
WSI_TOKEN_CONNECT = 81,
WSI_TOKEN_HEAD_URI = 82,
WSI_TOKEN_TE = 83,
+ WSI_TOKEN_REPLAY_NONCE = 84,
+ WSI_TOKEN_COLON_PROTOCOL = 85,
+ WSI_TOKEN_X_AUTH_TOKEN = 86,
+
/****** add new things just above ---^ ******/
/* use token storage to stash these internally, not for
@@ -3411,6 +4080,7 @@ enum lws_token_indexes {
_WSI_TOKEN_CLIENT_ORIGIN,
_WSI_TOKEN_CLIENT_METHOD,
_WSI_TOKEN_CLIENT_IFACE,
+ _WSI_TOKEN_CLIENT_ALPN,
/* always last real token index*/
WSI_TOKEN_COUNT,
@@ -3605,6 +4275,54 @@ lws_add_http_header_content_length(struct lws *wsi,
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_finalize_http_header(struct lws *wsi, unsigned char **p,
unsigned char *end);
+
+/**
+ * lws_finalize_write_http_header() - Helper finializing and writing http headers
+ *
+ * \param wsi: the connection to check
+ * \param start: pointer to the start of headers in the buffer, eg &buf[LWS_PRE]
+ * \param p: pointer to current position in buffer pointer
+ * \param end: pointer to end of buffer
+ *
+ * Terminates the headers correctly accoring to the protocol in use (h1 / h2)
+ * and writes the headers. Returns nonzero for error.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_finalize_write_http_header(struct lws *wsi, unsigned char *start,
+ unsigned char **p, unsigned char *end);
+
+#define LWS_ILLEGAL_HTTP_CONTENT_LEN ((lws_filepos_t)-1ll)
+
+/**
+ * lws_add_http_common_headers() - Helper preparing common http headers
+ *
+ * \param wsi: the connection to check
+ * \param code: an HTTP code like 200, 404 etc (see enum http_status)
+ * \param content_type: the content type, like "text/html"
+ * \param content_len: the content length, in bytes
+ * \param p: pointer to current position in buffer pointer
+ * \param end: pointer to end of buffer
+ *
+ * Adds the initial response code, so should be called first.
+ *
+ * Code may additionally take OR'd flags:
+ *
+ * LWSAHH_FLAG_NO_SERVER_NAME: don't apply server name header this time
+ *
+ * This helper just calls public apis to simplify adding headers that are
+ * commonly needed. If it doesn't fit your case, or you want to add additional
+ * headers just call the public apis directly yourself for what you want.
+ *
+ * You can miss out the content length header by providing the constant
+ * LWS_ILLEGAL_HTTP_CONTENT_LEN for the content_len.
+ *
+ * It does not call lws_finalize_http_header(), to allow you to add further
+ * headers after calling this. You will need to call that yourself at the end.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_add_http_common_headers(struct lws *wsi, unsigned int code,
+ const char *content_type, lws_filepos_t content_len,
+ unsigned char **p, unsigned char *end);
///@}
/** \defgroup form-parsing Form Parsing
@@ -3787,7 +4505,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
const char *html_body);
/**
- * lws_http_redirect() - write http redirect into buffer
+ * lws_http_redirect() - write http redirect out on wsi
*
* \param wsi: websocket connection
* \param code: HTTP response code (eg, 301)
@@ -3795,6 +4513,8 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
* \param len: length of loc
* \param p: pointer current position in buffer (updated as we write)
* \param end: pointer to end of buffer
+ *
+ * Returns amount written, or < 0 indicating fatal write failure.
*/
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
@@ -3845,30 +4565,29 @@ lws_sql_purify(char *escaped, const char *string, int len);
*/
LWS_VISIBLE LWS_EXTERN const char *
lws_json_purify(char *escaped, const char *string, int len);
-///@}
-/*! \defgroup ev libev helpers
+/**
+ * lws_filename_purify_inplace() - replace scary filename chars with underscore
*
- * ##libev helpers
+ * \param filename: filename to be purified
*
- * APIs specific to libev event loop itegration
+ * Replace scary characters in the filename (it should not be a path)
+ * with underscore, so it's safe to use.
*/
-///@{
-
-#ifdef LWS_WITH_LIBEV
-typedef void (lws_ev_signal_cb_t)(EV_P_ struct ev_signal *w, int revents);
+LWS_VISIBLE LWS_EXTERN void
+lws_filename_purify_inplace(char *filename);
LWS_VISIBLE LWS_EXTERN int
-lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint,
- lws_ev_signal_cb_t *cb);
-
+lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
+ int len);
LWS_VISIBLE LWS_EXTERN int
-lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi);
+lws_plat_write_file(const char *filename, void *buf, int len);
-LWS_VISIBLE LWS_EXTERN void
-lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents);
-#endif /* LWS_WITH_LIBEV */
+LWS_VISIBLE LWS_EXTERN int
+lws_plat_read_file(const char *filename, void *buf, int len);
+LWS_VISIBLE LWS_EXTERN int
+lws_plat_recommended_rsa_bits(void);
///@}
/*! \defgroup uv libuv helpers
@@ -3879,60 +4598,38 @@ lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents);
*/
///@{
#ifdef LWS_WITH_LIBUV
-LWS_VISIBLE LWS_EXTERN int
-lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint,
- uv_signal_cb cb);
-
-LWS_VISIBLE LWS_EXTERN void
-lws_libuv_run(const struct lws_context *context, int tsi);
-
-LWS_VISIBLE LWS_EXTERN void
-lws_libuv_stop(struct lws_context *context);
-
-LWS_VISIBLE LWS_EXTERN void
-lws_libuv_stop_without_kill(const struct lws_context *context, int tsi);
-
-LWS_VISIBLE LWS_EXTERN int
-lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi);
+/*
+ * Any direct libuv allocations in lws protocol handlers must participate in the
+ * lws reference counting scheme. Two apis are provided:
+ *
+ * - lws_libuv_static_refcount_add(handle, context) to mark the handle with
+ * a pointer to the context and increment the global uv object counter
+ *
+ * - lws_libuv_static_refcount_del() which should be used as the close callback
+ * for your own libuv objects declared in the protocol scope.
+ *
+ * Using the apis allows lws to detach itself from a libuv loop completely
+ * cleanly and at the moment all of its libuv objects have completed close.
+ */
LWS_VISIBLE LWS_EXTERN uv_loop_t *
lws_uv_getloop(struct lws_context *context, int tsi);
LWS_VISIBLE LWS_EXTERN void
-lws_uv_sigint_cb(uv_signal_t *watcher, int signum);
+lws_libuv_static_refcount_add(uv_handle_t *, struct lws_context *context);
LWS_VISIBLE LWS_EXTERN void
-lws_close_all_handles_in_loop(uv_loop_t *loop);
-#endif /* LWS_WITH_LIBUV */
-///@}
-
-/*! \defgroup event libevent helpers
- *
- * ##libevent helpers
- *
- * APIs specific to libevent event loop itegration
- */
-///@{
-
-#ifdef LWS_WITH_LIBEVENT
-typedef void (lws_event_signal_cb_t) (evutil_socket_t sock_fd, short revents,
- void *ctx);
-
-LWS_VISIBLE LWS_EXTERN int
-lws_event_sigint_cfg(struct lws_context *context, int use_event_sigint,
- lws_event_signal_cb_t cb);
-
-LWS_VISIBLE LWS_EXTERN int
-lws_event_initloop(struct lws_context *context, struct event_base *loop,
- int tsi);
+lws_libuv_static_refcount_del(uv_handle_t *);
-LWS_VISIBLE LWS_EXTERN void
-lws_event_sigint_cb(evutil_socket_t sock_fd, short revents,
- void *ctx);
-#endif /* LWS_WITH_LIBEVENT */
+#endif /* LWS_WITH_LIBUV */
+#if defined(LWS_WITH_ESP32)
+#define lws_libuv_static_refcount_add(_a, _b)
+#define lws_libuv_static_refcount_del NULL
+#endif
///@}
+
/*! \defgroup timeout Connection timeouts
APIs related to setting connection timeouts
@@ -3951,7 +4648,7 @@ enum pending_timeout {
PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE = 4,
PENDING_TIMEOUT_AWAITING_PING = 5,
PENDING_TIMEOUT_CLOSE_ACK = 6,
- PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE = 7,
+ PENDING_TIMEOUT_UNUSED1 = 7,
PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE = 8,
PENDING_TIMEOUT_SSL_ACCEPT = 9,
PENDING_TIMEOUT_HTTP_CONTENT = 10,
@@ -3970,6 +4667,9 @@ enum pending_timeout {
PENDING_TIMEOUT_KILLED_BY_PARENT = 23,
PENDING_TIMEOUT_CLOSE_SEND = 24,
PENDING_TIMEOUT_HOLDING_AH = 25,
+ PENDING_TIMEOUT_UDP_IDLE = 26,
+ PENDING_TIMEOUT_CLIENT_CONN_IDLE = 27,
+ PENDING_TIMEOUT_LAGGING = 28,
/****** add new things just above ---^ ******/
@@ -4003,6 +4703,60 @@ enum pending_timeout {
*/
LWS_VISIBLE LWS_EXTERN void
lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
+
+#define LWS_SET_TIMER_USEC_CANCEL ((lws_usec_t)-1ll)
+#define LWS_USEC_PER_SEC (1000000ll)
+
+/**
+ * lws_set_timer_usecs() - schedules a callback on the wsi in the future
+ *
+ * \param wsi: Websocket connection instance
+ * \param usecs: LWS_SET_TIMER_USEC_CANCEL removes any existing scheduled
+ * callback, otherwise number of microseconds in the future
+ * the callback will occur at.
+ *
+ * NOTE: event loop support for this:
+ *
+ * default poll() loop: yes
+ * libuv event loop: yes
+ * libev: not implemented (patch welcome)
+ * libevent: not implemented (patch welcome)
+ *
+ * After the deadline expires, the wsi will get a callback of type
+ * LWS_CALLBACK_TIMER and the timer is exhausted. The deadline may be
+ * continuously deferred by further calls to lws_set_timer_usecs() with a later
+ * deadline, or cancelled by lws_set_timer_usecs(wsi, -1).
+ *
+ * If the timer should repeat, lws_set_timer_usecs() must be called again from
+ * LWS_CALLBACK_TIMER.
+ *
+ * Accuracy depends on the platform and the load on the event loop or system...
+ * all that's guaranteed is the callback will come after the requested wait
+ * period.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs);
+
+/*
+ * lws_timed_callback_vh_protocol() - calls back a protocol on a vhost after
+ * the specified delay
+ *
+ * \param vh: the vhost to call back
+ * \param protocol: the protocol to call back
+ * \param reason: callback reason
+ * \param secs: how many seconds in the future to do the callback. Set to
+ * -1 to cancel the timer callback.
+ *
+ * Callback the specified protocol with a fake wsi pointing to the specified
+ * vhost and protocol, with the specified reason, at the specified time in the
+ * future.
+ *
+ * Returns 0 if OK.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_timed_callback_vh_protocol(struct lws_vhost *vh,
+ const struct lws_protocols *prot,
+ int reason, int secs);
///@}
/*! \defgroup sending-data Sending data
@@ -4011,7 +4765,7 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
*/
//@{
#if !defined(LWS_SIZEOFPTR)
-#define LWS_SIZEOFPTR (sizeof (void *))
+#define LWS_SIZEOFPTR ((int)sizeof (void *))
#endif
#if defined(__x86_64__)
@@ -4027,6 +4781,8 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
#define LWS_SEND_BUFFER_PRE_PADDING LWS_PRE
#define LWS_SEND_BUFFER_POST_PADDING 0
+#define LWS_WRITE_RAW LWS_WRITE_HTTP
+
/*
* NOTE: These public enums are part of the abi. If you want to add one,
* add it at where specified so existing users are unaffected.
@@ -4185,6 +4941,23 @@ lws_write(struct lws *wsi, unsigned char *buf, size_t len,
/* helper for case where buffer may be const */
#define lws_write_http(wsi, buf, len) \
lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP)
+
+/* helper for multi-frame ws message flags */
+static inline int
+lws_write_ws_flags(int initial, int is_start, int is_end)
+{
+ int r;
+
+ if (is_start)
+ r = initial;
+ else
+ r = LWS_WRITE_CONTINUATION;
+
+ if (!is_end)
+ r |= LWS_WRITE_NO_FIN;
+
+ return r;
+}
///@}
/** \defgroup callback-when-writeable Callback when writeable
@@ -4324,9 +5097,31 @@ lws_callback_all_protocol_vhost_args(struct lws_vhost *vh,
* - Which: connections using this protocol on same VHOST as wsi ONLY
* - When: now
* - What: reason
+ *
+ * This is deprecated since v2.5, use lws_callback_vhost_protocols_vhost()
+ * which takes the pointer to the vhost directly without using or needing the
+ * wsi.
*/
LWS_VISIBLE LWS_EXTERN int
-lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len);
+lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len)
+LWS_WARN_DEPRECATED;
+
+/**
+ * lws_callback_vhost_protocols_vhost() - Callback all protocols enabled on a vhost
+ * with the given reason
+ *
+ * \param vh: vhost that will get callbacks
+ * \param reason: Callback reason index
+ * \param in: in argument to callback
+ * \param len: len argument to callback
+ *
+ * - Which: connections using this protocol on same VHOST as wsi ONLY
+ * - When: now
+ * - What: reason
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_callback_vhost_protocols_vhost(struct lws_vhost *vh, int reason, void *in,
+ size_t len);
LWS_VISIBLE LWS_EXTERN int
lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
@@ -4335,11 +5130,11 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
/**
* lws_get_socket_fd() - returns the socket file descriptor
*
- * You will not need this unless you are doing something special
+ * This is needed to use sendto() on UDP raw sockets
*
* \param wsi: Websocket connection instance
*/
-LWS_VISIBLE LWS_EXTERN int
+LWS_VISIBLE LWS_EXTERN lws_sockfd_type
lws_get_socket_fd(struct lws *wsi);
/**
@@ -4363,7 +5158,7 @@ lws_get_socket_fd(struct lws *wsi);
* automatically, so this number reflects the situation at the peer or
* intermediary dynamically.
*/
-LWS_VISIBLE LWS_EXTERN size_t
+LWS_VISIBLE LWS_EXTERN lws_fileofs_t
lws_get_peer_write_allowance(struct lws *wsi);
///@}
@@ -4422,19 +5217,22 @@ lws_rx_flow_allow_all_protocol(const struct lws_context *context,
/**
* lws_remaining_packet_payload() - Bytes to come before "overall"
- * rx packet is complete
+ * rx fragment is complete
* \param wsi: Websocket instance (available from user callback)
*
- * This function is intended to be called from the callback if the
- * user code is interested in "complete packets" from the client.
- * libwebsockets just passes through payload as it comes and issues a buffer
- * additionally when it hits a built-in limit. The LWS_CALLBACK_RECEIVE
- * callback handler can use this API to find out if the buffer it has just
- * been given is the last piece of a "complete packet" from the client --
- * when that is the case lws_remaining_packet_payload() will return
- * 0.
+ * This tracks how many bytes are left in the current ws fragment, according
+ * to the ws length given in the fragment header.
+ *
+ * If the message was in a single fragment, and there is no compression, this
+ * is the same as "how much data is left to read for this message".
*
- * Many protocols won't care becuse their packets are always small.
+ * However, if the message is being sent in multiple fragments, this will
+ * reflect the unread amount of the current **fragment**, not the message. With
+ * ws, it is legal to not know the length of the message before it completes.
+ *
+ * Additionally if the message is sent via the negotiated permessage-deflate
+ * extension, this number only tells the amount of **compressed** data left to
+ * be read, since that is the only information available at the ws layer.
*/
LWS_VISIBLE LWS_EXTERN size_t
lws_remaining_packet_payload(struct lws *wsi);
@@ -4488,8 +5286,10 @@ typedef enum {
LWS_ADOPT_ALLOW_SSL = 4, /* flag: if set requires LWS_ADOPT_SOCKET */
LWS_ADOPT_WS_PARENTIO = 8, /* flag: ws mode parent handles IO
* if given must be only flag
- * wsi put directly into ws mode
- */
+ * wsi put directly into ws mode */
+ LWS_ADOPT_FLAG_UDP = 16, /* flag: socket is UDP */
+
+ LWS_ADOPT_RAW_SOCKET_UDP = LWS_ADOPT_SOCKET | LWS_ADOPT_FLAG_UDP,
} lws_adoption_type;
typedef union {
@@ -4497,6 +5297,16 @@ typedef union {
lws_filefd_type filefd;
} lws_sock_file_fd_type;
+#if !defined(LWS_WITH_ESP32)
+struct lws_udp {
+ struct sockaddr sa;
+ socklen_t salen;
+
+ struct sockaddr sa_pending;
+ socklen_t salen_pending;
+};
+#endif
+
/*
* lws_adopt_descriptor_vhost() - adopt foreign socket or file descriptor
* if socket descriptor, should already have been accepted from listen socket
@@ -4573,6 +5383,24 @@ lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
LWS_VISIBLE LWS_EXTERN struct lws *
lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, lws_sockfd_type accept_fd,
const char *readbuf, size_t len);
+
+#define LWS_CAUDP_BIND 1
+
+/**
+ * lws_create_adopt_udp() - create, bind and adopt a UDP socket
+ *
+ * \param vhost: lws vhost
+ * \param port: UDP port to bind to, -1 means unbound
+ * \param flags: 0 or LWS_CAUDP_NO_BIND
+ * \param protocol_name: Name of protocol on vhost to bind wsi to
+ * \param parent_wsi: NULL or parent wsi new wsi will be a child of
+ *
+ * Either returns new wsi bound to accept_fd, or closes accept_fd and
+ * returns NULL, having cleaned up any new wsi pieces.
+ * */
+LWS_VISIBLE LWS_EXTERN struct lws *
+lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
+ const char *protocol_name, struct lws *parent_wsi);
///@}
/** \defgroup net Network related helper APIs
@@ -4624,17 +5452,31 @@ lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name,
*/
LWS_VISIBLE LWS_EXTERN const char *
lws_get_peer_simple(struct lws *wsi, char *name, int namelen);
-#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
+
+
+#define LWS_ITOSA_NOT_EXIST -1
+#define LWS_ITOSA_NOT_USABLE -2
+#define LWS_ITOSA_USABLE 0
+#if !defined(LWS_WITH_ESP32)
/**
* lws_interface_to_sa() - Convert interface name or IP to sockaddr struct
*
- * \param ipv6: Allow IPV6 addresses
+ * \param ipv6: Allow IPV6 addresses
* \param ifname: Interface name or IP
- * \param addr: struct sockaddr_in * to be written
+ * \param addr: struct sockaddr_in * to be written
* \param addrlen: Length of addr
*
* This converts a textual network interface name to a sockaddr usable by
- * other network functions
+ * other network functions.
+ *
+ * If the network interface doesn't exist, it will return LWS_ITOSA_NOT_EXIST.
+ *
+ * If the network interface is not usable, eg ethernet cable is removed, it
+ * may logically exist but not have any IP address. As such it will return
+ * LWS_ITOSA_NOT_USABLE.
+ *
+ * If the network interface exists and is usable, it will return
+ * LWS_ITOSA_USABLE.
*/
LWS_VISIBLE LWS_EXTERN int
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
@@ -4702,6 +5544,13 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
type it = &(start); \
while (*(it)) {
+#define lws_start_foreach_llp_safe(type, it, start, nxt)\
+{ \
+ type it = &(start); \
+ type next; \
+ while (*(it)) { \
+ next = &((*(it))->nxt); \
+
/**
* lws_end_foreach_llp(): linkedlist pointer iterator helper end
*
@@ -4717,6 +5566,170 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
} \
}
+#define lws_end_foreach_llp_safe(it) \
+ it = next; \
+ } \
+}
+
+#define lws_ll_fwd_insert(\
+ ___new_object, /* pointer to new object */ \
+ ___m_list, /* member for next list object ptr */ \
+ ___list_head /* list head */ \
+ ) {\
+ ___new_object->___m_list = ___list_head; \
+ ___list_head = ___new_object; \
+ }
+
+#define lws_ll_fwd_remove(\
+ ___type, /* type of listed object */ \
+ ___m_list, /* member for next list object ptr */ \
+ ___target, /* object to remove from list */ \
+ ___list_head /* list head */ \
+ ) { \
+ lws_start_foreach_llp(___type **, ___ppss, ___list_head) { \
+ if (*___ppss == ___target) { \
+ *___ppss = ___target->___m_list; \
+ break; \
+ } \
+ } lws_end_foreach_llp(___ppss, ___m_list); \
+ }
+
+/*
+ * doubly linked-list
+ */
+
+struct lws_dll { /* abstract */
+ struct lws_dll *prev;
+ struct lws_dll *next;
+};
+
+/*
+ * these all point to the composed list objects... you have to use the
+ * lws_container_of() helper to recover the start of the containing struct
+ */
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll_add_front(struct lws_dll *d, struct lws_dll *phead);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll_remove(struct lws_dll *d);
+
+struct lws_dll_lws { /* typed as struct lws * */
+ struct lws_dll_lws *prev;
+ struct lws_dll_lws *next;
+};
+
+#define lws_dll_is_null(___dll) (!(___dll)->prev && !(___dll)->next)
+
+static inline void
+lws_dll_lws_add_front(struct lws_dll_lws *_a, struct lws_dll_lws *_head)
+{
+ lws_dll_add_front((struct lws_dll *)_a, (struct lws_dll *)_head);
+}
+
+static inline void
+lws_dll_lws_remove(struct lws_dll_lws *_a)
+{
+ lws_dll_remove((struct lws_dll *)_a);
+}
+
+/*
+ * these are safe against the current container object getting deleted,
+ * since the hold his next in a temp and go to that next. ___tmp is
+ * the temp.
+ */
+
+#define lws_start_foreach_dll_safe(___type, ___it, ___tmp, ___start) \
+{ \
+ ___type ___it = ___start; \
+ while (___it) { \
+ ___type ___tmp = (___it)->next;
+
+#define lws_end_foreach_dll_safe(___it, ___tmp) \
+ ___it = ___tmp; \
+ } \
+}
+
+#define lws_start_foreach_dll(___type, ___it, ___start) \
+{ \
+ ___type ___it = ___start; \
+ while (___it) {
+
+#define lws_end_foreach_dll(___it) \
+ ___it = (___it)->next; \
+ } \
+}
+
+struct lws_buflist;
+
+/**
+ * lws_buflist_append_segment(): add buffer to buflist at head
+ *
+ * \param head: list head
+ * \param buf: buffer to stash
+ * \param len: length of buffer to stash
+ *
+ * Returns -1 on OOM, 1 if this was the first segment on the list, and 0 if
+ * it was a subsequent segment.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf,
+ size_t len);
+/**
+ * lws_buflist_next_segment_len(): number of bytes left in current segment
+ *
+ * \param head: list head
+ * \param buf: if non-NULL, *buf is written with the address of the start of
+ * the remaining data in the segment
+ *
+ * Returns the number of bytes left in the current segment. 0 indicates
+ * that the buflist is empty (there are no segments on the buflist).
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf);
+/**
+ * lws_buflist_use_segment(): remove len bytes from the current segment
+ *
+ * \param head: list head
+ * \param len: number of bytes to mark as used
+ *
+ * If len is less than the remaining length of the current segment, the position
+ * in the current segment is simply advanced and it returns.
+ *
+ * If len uses up the remaining length of the current segment, then the segment
+ * is deleted and the list head moves to the next segment if any.
+ *
+ * Returns the number of bytes left in the current segment. 0 indicates
+ * that the buflist is empty (there are no segments on the buflist).
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_buflist_use_segment(struct lws_buflist **head, size_t len);
+/**
+ * lws_buflist_destroy_all_segments(): free all segments on the list
+ *
+ * \param head: list head
+ *
+ * This frees everything on the list unconditionally. *head is always
+ * NULL after this.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_buflist_destroy_all_segments(struct lws_buflist **head);
+
+void
+lws_buflist_describe(struct lws_buflist **head, void *id);
+
+/**
+ * lws_ptr_diff(): helper to report distance between pointers as an int
+ *
+ * \param head: the pointer with the larger address
+ * \param tail: the pointer with the smaller address
+ *
+ * This helper gives you an int representing the number of bytes further
+ * forward the first pointer is compared to the second pointer.
+ */
+#define lws_ptr_diff(head, tail) \
+ ((int)((char *)(head) - (char *)(tail)))
+
/**
* lws_snprintf(): snprintf that truncates the returned length too
*
@@ -4732,6 +5745,19 @@ LWS_VISIBLE LWS_EXTERN int
lws_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3);
/**
+ * lws_strncpy(): strncpy that guarantees NUL on truncated copy
+ *
+ * \param dest: destination buffer
+ * \param src: source buffer
+ * \param size: bytes left in destination buffer
+ *
+ * This lets you correctly truncate buffers by concatenating lengths, if you
+ * reach the limit the reported length doesn't exceed the limit.
+ */
+LWS_VISIBLE LWS_EXTERN char *
+lws_strncpy(char *dest, const char *src, size_t size);
+
+/**
* lws_get_random(): fill a buffer with platform random data
*
* \param context: the lws context
@@ -4797,6 +5823,25 @@ lws_set_wsi_user(struct lws *wsi, void *user);
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
const char **path);
+/**
+ * lws_cmdline_option(): simple commandline parser
+ *
+ * \param argc: count of argument strings
+ * \param argv: argument strings
+ * \param val: string to find
+ *
+ * Returns NULL if the string \p val is not found in the arguments.
+ *
+ * If it is found, then it returns a pointer to the next character after \p val.
+ * So if \p val is "-d", then for the commandlines "myapp -d15" and
+ * "myapp -d 15", in both cases the return will point to the "15".
+ *
+ * In the case there is no argument, like "myapp -d", the return will
+ * either point to the '\\0' at the end of -d, or to the start of the
+ * next argument, ie, will be non-NULL.
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_cmdline_option(int argc, const char **argv, const char *val);
/**
* lws_now_secs(): return seconds since 1970-1-1
@@ -4805,6 +5850,26 @@ LWS_VISIBLE LWS_EXTERN unsigned long
lws_now_secs(void);
/**
+ * lws_compare_time_t(): return relationship between two time_t
+ *
+ * \param context: struct lws_context
+ * \param t1: time_t 1
+ * \param t2: time_t 2
+ *
+ * returns <0 if t2 > t1; >0 if t1 > t2; or == 0 if t1 == t2.
+ *
+ * This is aware of clock discontiguities that may have affected either t1 or
+ * t2 and adapts the comparison for them.
+ *
+ * For the discontiguity detection to work, you must avoid any arithmetic on
+ * the times being compared. For example to have a timeout that triggers
+ * 15s from when it was set, store the time it was set and compare like
+ * `if (lws_compare_time_t(context, now, set_time) > 15)`
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_compare_time_t(struct lws_context *context, time_t t1, time_t t2);
+
+/**
* lws_get_context - Allow getting lws_context from a Websocket connection
* instance
*
@@ -4817,6 +5882,18 @@ LWS_VISIBLE LWS_EXTERN struct lws_context * LWS_WARN_UNUSED_RESULT
lws_get_context(const struct lws *wsi);
/**
+ * lws_get_vhost_listen_port - Find out the port number a vhost is listening on
+ *
+ * In the case you passed 0 for the port number at context creation time, you
+ * can discover the port number that was actually chosen for the vhost using
+ * this api.
+ *
+ * \param vhost: Vhost to get listen port from
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_get_vhost_listen_port(struct lws_vhost *vhost);
+
+/**
* lws_get_count_threads(): how many service threads the context uses
*
* \param context: the lws context
@@ -4848,6 +5925,16 @@ LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
lws_get_child(const struct lws *wsi);
/**
+ * lws_get_udp() - get wsi's udp struct
+ *
+ * \param wsi: lws connection
+ *
+ * Returns NULL or pointer to the wsi's UDP-specific information
+ */
+LWS_VISIBLE LWS_EXTERN const struct lws_udp * LWS_WARN_UNUSED_RESULT
+lws_get_udp(const struct lws *wsi);
+
+/**
* lws_parent_carries_io() - mark wsi as needing to send messages via parent
*
* \param wsi: child lws connection
@@ -4887,14 +5974,6 @@ lws_get_close_payload(struct lws *wsi);
LWS_VISIBLE LWS_EXTERN
struct lws *lws_get_network_wsi(struct lws *wsi);
-/*
- * \deprecated DEPRECATED Note: this is not normally needed as a user api.
- * It's provided in case it is
- * useful when integrating with other app poll loop service code.
- */
-LWS_VISIBLE LWS_EXTERN int
-lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
-
/**
* lws_set_allocator() - custom allocator support
*
@@ -4992,7 +6071,18 @@ lws_is_ssl(struct lws *wsi);
LWS_VISIBLE LWS_EXTERN int
lws_is_cgi(struct lws *wsi);
-#ifdef LWS_OPENSSL_SUPPORT
+
+struct lws_wifi_scan { /* generic wlan scan item */
+ struct lws_wifi_scan *next;
+ char ssid[32];
+ int32_t rssi; /* divide by .count to get db */
+ uint8_t bssid[6];
+ uint8_t count;
+ uint8_t channel;
+ uint8_t authmode;
+};
+
+#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS)
/**
* lws_get_ssl() - Return wsi's SSL context structure
* \param wsi: websocket connection
@@ -5002,6 +6092,160 @@ lws_is_cgi(struct lws *wsi);
LWS_VISIBLE LWS_EXTERN SSL*
lws_get_ssl(struct lws *wsi);
#endif
+
+enum lws_tls_cert_info {
+ LWS_TLS_CERT_INFO_VALIDITY_FROM,
+ /**< fills .time with the time_t the cert validity started from */
+ LWS_TLS_CERT_INFO_VALIDITY_TO,
+ /**< fills .time with the time_t the cert validity ends at */
+ LWS_TLS_CERT_INFO_COMMON_NAME,
+ /**< fills up to len bytes of .ns.name with the cert common name */
+ LWS_TLS_CERT_INFO_ISSUER_NAME,
+ /**< fills up to len bytes of .ns.name with the cert issuer name */
+ LWS_TLS_CERT_INFO_USAGE,
+ /**< fills verified with a bitfield asserting the valid uses */
+ LWS_TLS_CERT_INFO_VERIFIED,
+ /**< fills .verified with a bool representing peer cert validity,
+ * call returns -1 if no cert */
+ LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY,
+ /**< the certificate's public key, as an opaque bytestream. These
+ * opaque bytestreams can only be compared with each other using the
+ * same tls backend, ie, OpenSSL or mbedTLS. The different backends
+ * produce different, incompatible representations for the same cert.
+ */
+};
+
+union lws_tls_cert_info_results {
+ unsigned int verified;
+ time_t time;
+ unsigned int usage;
+ struct {
+ int len;
+ /* KEEP LAST... notice the [64] is only there because
+ * name[] is not allowed in a union. The actual length of
+ * name[] is arbitrary and is passed into the api using the
+ * len parameter. Eg
+ *
+ * char big[1024];
+ * union lws_tls_cert_info_results *buf =
+ * (union lws_tls_cert_info_results *)big;
+ *
+ * lws_tls_peer_cert_info(wsi, type, buf, sizeof(big) -
+ * sizeof(*buf) + sizeof(buf->ns.name));
+ */
+ char name[64];
+ } ns;
+};
+
+/**
+ * lws_tls_peer_cert_info() - get information from the peer's TLS cert
+ *
+ * \param wsi: the connection to query
+ * \param type: one of LWS_TLS_CERT_INFO_
+ * \param buf: pointer to union to take result
+ * \param len: when result is a string, the true length of buf->ns.name[]
+ *
+ * lws_tls_peer_cert_info() lets you get hold of information from the peer
+ * certificate.
+ *
+ * Return 0 if there is a result in \p buf, or -1 indicating there was no cert
+ * or another problem.
+ *
+ * This function works the same no matter if the TLS backend is OpenSSL or
+ * mbedTLS.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
+ union lws_tls_cert_info_results *buf, size_t len);
+
+/**
+ * lws_tls_vhost_cert_info() - get information from the vhost's own TLS cert
+ *
+ * \param vhost: the vhost to query
+ * \param type: one of LWS_TLS_CERT_INFO_
+ * \param buf: pointer to union to take result
+ * \param len: when result is a string, the true length of buf->ns.name[]
+ *
+ * lws_tls_vhost_cert_info() lets you get hold of information from the vhost
+ * certificate.
+ *
+ * Return 0 if there is a result in \p buf, or -1 indicating there was no cert
+ * or another problem.
+ *
+ * This function works the same no matter if the TLS backend is OpenSSL or
+ * mbedTLS.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type,
+ union lws_tls_cert_info_results *buf, size_t len);
+
+/**
+ * lws_tls_acme_sni_cert_create() - creates a temp selfsigned cert
+ * and attaches to a vhost
+ *
+ * \param vhost: the vhost to acquire the selfsigned cert
+ * \param san_a: SAN written into the certificate
+ * \param san_b: second SAN written into the certificate
+ *
+ *
+ * Returns 0 if created and attached to the vhost. Returns -1 if problems and
+ * frees all allocations before returning.
+ *
+ * On success, any allocations are destroyed at vhost destruction automatically.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a,
+ const char *san_b);
+
+/**
+ * lws_tls_acme_sni_csr_create() - creates a CSR and related private key PEM
+ *
+ * \param context: lws_context used for random
+ * \param elements: array of LWS_TLS_REQ_ELEMENT_COUNT const char *
+ * \param csr: buffer that will get the b64URL(ASN-1 CSR)
+ * \param csr_len: max length of the csr buffer
+ * \param privkey_pem: pointer to pointer allocated to hold the privkey_pem
+ * \param privkey_len: pointer to size_t set to the length of the privkey_pem
+ *
+ * Creates a CSR according to the information in \p elements, and a private
+ * RSA key used to sign the CSR.
+ *
+ * The outputs are the b64URL(ASN-1 CSR) into csr, and the PEM private key into
+ * privkey_pem.
+ *
+ * Notice that \p elements points to an array of const char *s pointing to the
+ * information listed in the enum above. If an entry is NULL or an empty
+ * string, the element is set to "none" in the CSR.
+ *
+ * Returns 0 on success or nonzero for failure.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[],
+ uint8_t *csr, size_t csr_len, char **privkey_pem,
+ size_t *privkey_len);
+
+/**
+ * lws_tls_cert_updated() - update every vhost using the given cert path
+ *
+ * \param context: our lws_context
+ * \param certpath: the filepath to the certificate
+ * \param keypath: the filepath to the private key of the certificate
+ * \param mem_cert: copy of the cert in memory
+ * \param len_mem_cert: length of the copy of the cert in memory
+ * \param mem_privkey: copy of the private key in memory
+ * \param len_mem_privkey: length of the copy of the private key in memory
+ *
+ * Checks every vhost to see if it is the using certificate described by the
+ * the given filepaths. If so, it attempts to update the vhost ssl_ctx to use
+ * the new certificate.
+ *
+ * Returns 0 on success or nonzero for failure.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_cert_updated(struct lws_context *context, const char *certpath,
+ const char *keypath,
+ const char *mem_cert, size_t len_mem_cert,
+ const char *mem_privkey, size_t len_mem_privkey);
///@}
/** \defgroup lws_ring LWS Ringbuffer APIs
@@ -5226,6 +6470,65 @@ lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start,
*/
LWS_VISIBLE LWS_EXTERN void
lws_ring_bump_head(struct lws_ring *ring, size_t bytes);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_ring_dump(struct lws_ring *ring, uint32_t *tail);
+
+/*
+ * This is a helper that combines the common pattern of needing to consume
+ * some ringbuffer elements, move the consumer tail on, and check if that
+ * has moved any ringbuffer elements out of scope, because it was the last
+ * consumer that had not already consumed them.
+ *
+ * Elements that go out of scope because the oldest tail is now after them
+ * get garbage-collected by calling the destroy_element callback on them
+ * defined when the ringbuffer was created.
+ */
+
+#define lws_ring_consume_and_update_oldest_tail(\
+ ___ring, /* the lws_ring object */ \
+ ___type, /* type of objects with tails */ \
+ ___ptail, /* ptr to tail of obj with tail doing consuming */ \
+ ___count, /* count of payload objects being consumed */ \
+ ___list_head, /* head of list of objects with tails */ \
+ ___mtail, /* member name of tail in ___type */ \
+ ___mlist /* member name of next list member ptr in ___type */ \
+ ) { \
+ int ___n, ___m; \
+ \
+ ___n = lws_ring_get_oldest_tail(___ring) == *(___ptail); \
+ lws_ring_consume(___ring, ___ptail, NULL, ___count); \
+ if (___n) { \
+ uint32_t ___oldest; \
+ ___n = 0; \
+ ___oldest = *(___ptail); \
+ lws_start_foreach_llp(___type **, ___ppss, ___list_head) { \
+ ___m = lws_ring_get_count_waiting_elements( \
+ ___ring, &(*___ppss)->tail); \
+ if (___m >= ___n) { \
+ ___n = ___m; \
+ ___oldest = (*___ppss)->tail; \
+ } \
+ } lws_end_foreach_llp(___ppss, ___mlist); \
+ \
+ lws_ring_update_oldest_tail(___ring, ___oldest); \
+ } \
+}
+
+/*
+ * This does the same as the lws_ring_consume_and_update_oldest_tail()
+ * helper, but for the simpler case there is only one consumer, so one
+ * tail, and that tail is always the oldest tail.
+ */
+
+#define lws_ring_consume_single_tail(\
+ ___ring, /* the lws_ring object */ \
+ ___ptail, /* ptr to tail of obj with tail doing consuming */ \
+ ___count /* count of payload objects being consumed */ \
+ ) { \
+ lws_ring_consume(___ring, ___ptail, NULL, ___count); \
+ lws_ring_update_oldest_tail(___ring, *(___ptail)); \
+}
///@}
/** \defgroup sha SHA and B64 helpers
@@ -5262,16 +6565,40 @@ lws_SHA1(const unsigned char *d, size_t n, unsigned char *md);
LWS_VISIBLE LWS_EXTERN int
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size);
/**
+ * lws_b64_encode_string_url(): encode a string into base 64
+ *
+ * \param in: incoming buffer
+ * \param in_len: length of incoming buffer
+ * \param out: result buffer
+ * \param out_size: length of result buffer
+ *
+ * Encodes a string using b64 with the "URL" variant (+ -> -, and / -> _)
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_b64_encode_string_url(const char *in, int in_len, char *out, int out_size);
+/**
* lws_b64_decode_string(): decode a string from base 64
*
* \param in: incoming buffer
* \param out: result buffer
* \param out_size: length of result buffer
*
- * Decodes a string using b64
+ * Decodes a NUL-terminated string using b64
*/
LWS_VISIBLE LWS_EXTERN int
lws_b64_decode_string(const char *in, char *out, int out_size);
+/**
+ * lws_b64_decode_string_len(): decode a string from base 64
+ *
+ * \param in: incoming buffer
+ * \param in_len: length of incoming buffer
+ * \param out: result buffer
+ * \param out_size: length of result buffer
+ *
+ * Decodes a range of chars using b64
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_b64_decode_string_len(const char *in, int in_len, char *out, int out_size);
///@}
@@ -5727,6 +7054,248 @@ lws_email_destroy(struct lws_email *email);
#endif
//@}
+
+/** \defgroup lejp JSON parser
+ * ##JSON parsing related functions
+ * \ingroup lwsapi
+ *
+ * LEJP is an extremely lightweight JSON stream parser included in lws.
+ */
+//@{
+struct lejp_ctx;
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
+#endif
+#define LWS_ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
+#define LEJP_FLAG_WS_KEEP 64
+#define LEJP_FLAG_WS_COMMENTLINE 32
+
+enum lejp_states {
+ LEJP_IDLE = 0,
+ LEJP_MEMBERS = 1,
+ LEJP_M_P = 2,
+ LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3,
+ LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4,
+ LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5,
+ LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6,
+ LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7,
+ LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8,
+ LEJP_MP_DELIM = 9,
+ LEJP_MP_VALUE = 10,
+ LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11,
+ LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12,
+ LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13,
+ LEJP_MP_COMMA_OR_END = 14,
+ LEJP_MP_ARRAY_END = 15,
+};
+
+enum lejp_reasons {
+ LEJP_CONTINUE = -1,
+ LEJP_REJECT_IDLE_NO_BRACE = -2,
+ LEJP_REJECT_MEMBERS_NO_CLOSE = -3,
+ LEJP_REJECT_MP_NO_OPEN_QUOTE = -4,
+ LEJP_REJECT_MP_STRING_UNDERRUN = -5,
+ LEJP_REJECT_MP_ILLEGAL_CTRL = -6,
+ LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7,
+ LEJP_REJECT_ILLEGAL_HEX = -8,
+ LEJP_REJECT_MP_DELIM_MISSING_COLON = -9,
+ LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10,
+ LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11,
+ LEJP_REJECT_MP_VAL_NUM_FORMAT = -12,
+ LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13,
+ LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14,
+ LEJP_REJECT_MP_C_OR_E_UNDERF = -15,
+ LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16,
+ LEJP_REJECT_MP_ARRAY_END_MISSING = -17,
+ LEJP_REJECT_STACK_OVERFLOW = -18,
+ LEJP_REJECT_MP_DELIM_ISTACK = -19,
+ LEJP_REJECT_NUM_TOO_LONG = -20,
+ LEJP_REJECT_MP_C_OR_E_NEITHER = -21,
+ LEJP_REJECT_UNKNOWN = -22,
+ LEJP_REJECT_CALLBACK = -23
+};
+
+#define LEJP_FLAG_CB_IS_VALUE 64
+
+enum lejp_callbacks {
+ LEJPCB_CONSTRUCTED = 0,
+ LEJPCB_DESTRUCTED = 1,
+
+ LEJPCB_START = 2,
+ LEJPCB_COMPLETE = 3,
+ LEJPCB_FAILED = 4,
+
+ LEJPCB_PAIR_NAME = 5,
+
+ LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6,
+ LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7,
+ LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8,
+ LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9,
+ LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10,
+ LEJPCB_VAL_STR_START = 11, /* notice handle separately */
+ LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12,
+ LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13,
+
+ LEJPCB_ARRAY_START = 14,
+ LEJPCB_ARRAY_END = 15,
+
+ LEJPCB_OBJECT_START = 16,
+ LEJPCB_OBJECT_END = 17
+};
+
+/**
+ * _lejp_callback() - User parser actions
+ * \param ctx: LEJP context
+ * \param reason: Callback reason
+ *
+ * Your user callback is associated with the context at construction time,
+ * and receives calls as the parsing progresses.
+ *
+ * All of the callbacks may be ignored and just return 0.
+ *
+ * The reasons it might get called, found in @reason, are:
+ *
+ * LEJPCB_CONSTRUCTED: The context was just constructed... you might want to
+ * perform one-time allocation for the life of the context.
+ *
+ * LEJPCB_DESTRUCTED: The context is being destructed... if you made any
+ * allocations at construction-time, you can free them now
+ *
+ * LEJPCB_START: Parsing is beginning at the first byte of input
+ *
+ * LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or
+ * positive return code from lejp_parse indicating the
+ * amount of unused bytes left in the input buffer
+ *
+ * LEJPCB_FAILED: Parsing failed. You'll get a negative error code
+ * returned from lejp_parse
+ *
+ * LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed,
+ * this callback occurs. You can find the new name at
+ * the end of ctx->path[]
+ *
+ * LEJPCB_VAL_TRUE: The "true" value appeared
+ *
+ * LEJPCB_VAL_FALSE: The "false" value appeared
+ *
+ * LEJPCB_VAL_NULL: The "null" value appeared
+ *
+ * LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf
+ *
+ * LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf
+ *
+ * LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet
+ *
+ * LEJPCB_VAL_STR_CHUNK: We parsed LEJP_STRING_CHUNK -1 bytes of string data in
+ * ctx->buf, which is as much as we can buffer, so we are
+ * spilling it. If all your strings are less than
+ * LEJP_STRING_CHUNK - 1 bytes, you will never see this
+ * callback.
+ *
+ * LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the
+ * string is in ctx->buf.
+ *
+ * LEJPCB_ARRAY_START: An array started
+ *
+ * LEJPCB_ARRAY_END: An array ended
+ *
+ * LEJPCB_OBJECT_START: An object started
+ *
+ * LEJPCB_OBJECT_END: An object ended
+ */
+LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason);
+
+typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason);
+
+#ifndef LEJP_MAX_DEPTH
+#define LEJP_MAX_DEPTH 12
+#endif
+#ifndef LEJP_MAX_INDEX_DEPTH
+#define LEJP_MAX_INDEX_DEPTH 5
+#endif
+#ifndef LEJP_MAX_PATH
+#define LEJP_MAX_PATH 128
+#endif
+#ifndef LEJP_STRING_CHUNK
+/* must be >= 30 to assemble floats */
+#define LEJP_STRING_CHUNK 255
+#endif
+
+enum num_flags {
+ LEJP_SEEN_MINUS = (1 << 0),
+ LEJP_SEEN_POINT = (1 << 1),
+ LEJP_SEEN_POST_POINT = (1 << 2),
+ LEJP_SEEN_EXP = (1 << 3)
+};
+
+struct _lejp_stack {
+ char s; /* lejp_state stack*/
+ char p; /* path length */
+ char i; /* index array length */
+ char b; /* user bitfield */
+};
+
+struct lejp_ctx {
+
+ /* sorted by type for most compact alignment
+ *
+ * pointers
+ */
+
+ signed char (*callback)(struct lejp_ctx *ctx, char reason);
+ void *user;
+ const char * const *paths;
+
+ /* arrays */
+
+ struct _lejp_stack st[LEJP_MAX_DEPTH];
+ uint16_t i[LEJP_MAX_INDEX_DEPTH]; /* index array */
+ uint16_t wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
+ char path[LEJP_MAX_PATH];
+ char buf[LEJP_STRING_CHUNK];
+
+ /* int */
+
+ uint32_t line;
+
+ /* short */
+
+ uint16_t uni;
+
+ /* char */
+
+ uint8_t npos;
+ uint8_t dcount;
+ uint8_t f;
+ uint8_t sp; /* stack head */
+ uint8_t ipos; /* index stack depth */
+ uint8_t ppos;
+ uint8_t count_paths;
+ uint8_t path_match;
+ uint8_t path_match_len;
+ uint8_t wildcount;
+};
+
+LWS_VISIBLE LWS_EXTERN void
+lejp_construct(struct lejp_ctx *ctx,
+ signed char (*callback)(struct lejp_ctx *ctx, char reason),
+ void *user, const char * const *paths, unsigned char paths_count);
+
+LWS_VISIBLE LWS_EXTERN void
+lejp_destruct(struct lejp_ctx *ctx);
+
+LWS_VISIBLE LWS_EXTERN int
+lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len);
+
+LWS_VISIBLE LWS_EXTERN void
+lejp_change_callback(struct lejp_ctx *ctx,
+ signed char (*callback)(struct lejp_ctx *ctx, char reason));
+
+LWS_VISIBLE LWS_EXTERN int
+lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);
+//@}
+
/*
* Stats are all uint64_t numbers that start at 0.
* Index names here have the convention
@@ -5775,9 +7344,9 @@ LWS_VISIBLE LWS_EXTERN void
lws_stats_log_dump(struct lws_context *context);
#else
static LWS_INLINE uint64_t
-lws_stats_get(struct lws_context *context, int index) { return 0; }
+lws_stats_get(struct lws_context *context, int index) { (void)context; (void)index; return 0; }
static LWS_INLINE void
-lws_stats_log_dump(struct lws_context *context) { }
+lws_stats_log_dump(struct lws_context *context) { (void)context; }
#endif
#ifdef __cplusplus
diff --git a/thirdparty/lws/lws_config.h b/thirdparty/libwebsockets/lws_config.h
index 6005d94ec6..7185a806a5 100644
--- a/thirdparty/lws/lws_config.h
+++ b/thirdparty/libwebsockets/lws_config.h
@@ -14,6 +14,12 @@
#define LWS_INSTALL_DATADIR "/usr/local/share"
+#define LWS_ROLE_H1
+#define LWS_ROLE_WS
+#define LWS_ROLE_RAW
+/* #undef LWS_ROLE_H2 */
+/* #undef LWS_ROLE_CGI */
+
/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL.
* LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
/* #undef USE_WOLFSSL */
@@ -25,26 +31,26 @@
#define LWS_WITH_MBEDTLS
/* #undef LWS_WITH_POLARSSL */
-/* #undef LWS_WITH_ESP8266 */
/* #undef LWS_WITH_ESP32 */
/* #undef LWS_WITH_PLUGINS */
/* #undef LWS_WITH_NO_LOGS */
/* The Libwebsocket version */
-#define LWS_LIBRARY_VERSION "2.4.2"
+#define LWS_LIBRARY_VERSION "3.0.0"
-#define LWS_LIBRARY_VERSION_MAJOR 2
-#define LWS_LIBRARY_VERSION_MINOR 4
-#define LWS_LIBRARY_VERSION_PATCH 2
+#define LWS_LIBRARY_VERSION_MAJOR 3
+#define LWS_LIBRARY_VERSION_MINOR 0
+#define LWS_LIBRARY_VERSION_PATCH 0
/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH
/* The current git commit hash that we're building from */
-#define LWS_BUILD_HASH "8964ce9db75a98e463dfafd2e89f2bc8a95ec6ed"
+#define LWS_BUILD_HASH "v2.0.0-948-geaa935a8"
-/* Build with OpenSSL support */
+/* Build with OpenSSL support ... alias of LWS_WITH_TLS for compatibility*/
#define LWS_OPENSSL_SUPPORT
+#define LWS_WITH_TLS
/* The client should load and trust CA root certs it finds in the OS */
/* #undef LWS_SSL_CLIENT_USE_OS_CA_CERTS */
@@ -53,7 +59,13 @@
/* #undef LWS_OPENSSL_CLIENT_CERTS "../share" */
/* Turn off websocket extensions */
-/* #undef LWS_NO_EXTENSIONS */
+#define LWS_WITHOUT_EXTENSIONS
+
+/* notice if client or server gone */
+/* #undef LWS_WITHOUT_SERVER */
+/* #undef LWS_WITHOUT_CLIENT */
+
+#define LWS_WITH_POLL
/* Enable libev io loop */
/* #undef LWS_WITH_LIBEV */
@@ -99,8 +111,11 @@
/* #undef LWS_HAVE_SSL_CTX_set1_param */
#define LWS_HAVE_X509_VERIFY_PARAM_set1_host
/* #undef LWS_HAVE_RSA_SET0_KEY */
+/* #undef LWS_HAVE_X509_get_key_usage */
+/* #undef LWS_HAVE_SSL_CTX_get0_certificate */
/* #undef LWS_HAVE_UV_VERSION_H */
+/* #undef LWS_HAVE_PTHREAD_H */
/* CGI apis */
/* #undef LWS_WITH_CGI */
@@ -112,7 +127,7 @@
/* #undef LWS_WITH_HTTP_PROXY */
/* HTTP Ranges support */
-#define LWS_WITH_RANGES
+/* #undef LWS_WITH_RANGES */
/* Http access log support */
/* #undef LWS_WITH_ACCESS_LOG */
@@ -134,7 +149,7 @@
/* #undef LWS_PLAT_OPTEE */
/* ZIP FOPS */
-#define LWS_WITH_ZIP_FOPS
+/* #undef LWS_WITH_ZIP_FOPS */
#define LWS_HAVE_STDINT_H
/* #undef LWS_AVOID_SIGPIPE_IGN */
@@ -151,11 +166,26 @@
/* #undef LWS_HAVE__ATOI64 */
/* #undef LWS_HAVE__STAT32I64 */
+/* #undef LWS_WITH_JWS */
+/* #undef LWS_WITH_ACME */
+/* #undef LWS_WITH_SELFTESTS */
+
+#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
+#define LWS_HAVE_MALLOC_H
+#endif
+
+#if !defined(IPHONE_ENABLED) && !defined(OSX_ENABLED)
+#define LWS_HAVE_PIPE2
+#endif
+
/* OpenSSL various APIs */
#define LWS_HAVE_TLS_CLIENT_METHOD
/* #undef LWS_HAVE_TLSV1_2_CLIENT_METHOD */
/* #undef LWS_HAVE_SSL_SET_INFO_CALLBACK */
+/* #undef LWS_HAVE_SSL_EXTRA_CHAIN_CERTS */
+/* #undef LWS_HAVE_SSL_get0_alpn_selected */
+/* #undef LWS_HAVE_SSL_set_alpn_protos */
#define LWS_HAS_INTPTR_T
diff --git a/thirdparty/lws/lws_config_private.h b/thirdparty/libwebsockets/lws_config_private.h
index 475d1bd3f8..9d04078fef 100644
--- a/thirdparty/lws/lws_config_private.h
+++ b/thirdparty/libwebsockets/lws_config_private.h
@@ -100,6 +100,8 @@
/* Define to 1 if you have the <unistd.h> header file. */
#define LWS_HAVE_UNISTD_H
+#define LWS_HAVE_TCP_USER_TIMEOUT
+
/* Define to 1 if you have the `vfork' function. */
#define LWS_HAVE_VFORK
diff --git a/thirdparty/lws/misc/base64-decode.c b/thirdparty/libwebsockets/misc/base64-decode.c
index c8f11d21b8..64b84d78fa 100644
--- a/thirdparty/lws/misc/base64-decode.c
+++ b/thirdparty/libwebsockets/misc/base64-decode.c
@@ -40,15 +40,18 @@
#include <stdio.h>
#include <string.h>
-#include "private-libwebsockets.h"
+#include "core/private.h"
-static const char encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+static const char encode_orig[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char encode_url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz0123456789-_";
static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW"
"$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
-LWS_VISIBLE int
-lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
+static int
+_lws_b64_encode_string(const char *encode, const char *in, int in_len,
+ char *out, int out_size)
{
unsigned char triple[3];
int i;
@@ -89,26 +92,47 @@ lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
return done;
}
+LWS_VISIBLE int
+lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
+{
+ return _lws_b64_encode_string(encode_orig, in, in_len, out, out_size);
+}
+
+LWS_VISIBLE int
+lws_b64_encode_string_url(const char *in, int in_len, char *out, int out_size)
+{
+ return _lws_b64_encode_string(encode_url, in, in_len, out, out_size);
+}
+
/*
* returns length of decoded string in out, or -1 if out was too small
* according to out_size
+ *
+ * Only reads up to in_len chars, otherwise if in_len is -1 on entry reads until
+ * the first NUL in the input.
*/
-LWS_VISIBLE int
-lws_b64_decode_string(const char *in, char *out, int out_size)
+static int
+_lws_b64_decode_string(const char *in, int in_len, char *out, int out_size)
{
int len, i, c = 0, done = 0;
unsigned char v, quad[4];
- while (*in) {
+ while (in_len && *in) {
len = 0;
- for (i = 0; i < 4 && *in; i++) {
+ for (i = 0; i < 4 && in_len && *in; i++) {
v = 0;
c = 0;
- while (*in && !v) {
+ while (in_len && *in && !v) {
c = v = *in++;
+ in_len--;
+ /* support the url base64 variant too */
+ if (v == '-')
+ c = v = '+';
+ if (v == '_')
+ c = v = '/';
v = (v < 43 || v > 122) ? 0 : decode[v - 43];
if (v)
v = (v == '$') ? 0 : v - 61;
@@ -131,7 +155,7 @@ lws_b64_decode_string(const char *in, char *out, int out_size)
* bytes." (wikipedia)
*/
- if (!*in && c == '=')
+ if ((!in_len || !*in) && c == '=')
len--;
if (len >= 2)
@@ -152,6 +176,18 @@ lws_b64_decode_string(const char *in, char *out, int out_size)
return done;
}
+LWS_VISIBLE int
+lws_b64_decode_string(const char *in, char *out, int out_size)
+{
+ return _lws_b64_decode_string(in, -1, out, out_size);
+}
+
+LWS_VISIBLE int
+lws_b64_decode_string_len(const char *in, int in_len, char *out, int out_size)
+{
+ return _lws_b64_decode_string(in, in_len, out, out_size);
+}
+
#if 0
int
lws_b64_selftest(void)
diff --git a/thirdparty/lws/misc/getifaddrs.c b/thirdparty/libwebsockets/misc/getifaddrs.c
index 4f42ab4595..735b899f48 100644
--- a/thirdparty/lws/misc/getifaddrs.c
+++ b/thirdparty/libwebsockets/misc/getifaddrs.c
@@ -43,7 +43,7 @@
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
-#include "private-libwebsockets.h"
+#include "core/private.h"
#ifdef LWS_HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
diff --git a/thirdparty/lws/misc/getifaddrs.h b/thirdparty/libwebsockets/misc/getifaddrs.h
index d26670c082..d26670c082 100644
--- a/thirdparty/lws/misc/getifaddrs.h
+++ b/thirdparty/libwebsockets/misc/getifaddrs.h
diff --git a/thirdparty/lws/misc/lejp.c b/thirdparty/libwebsockets/misc/lejp.c
index 38efa8b122..00d350f81e 100644
--- a/thirdparty/lws/misc/lejp.c
+++ b/thirdparty/libwebsockets/misc/lejp.c
@@ -1,14 +1,26 @@
/*
* Lightweight Embedded JSON Parser
*
- * Copyright (C) 2013 Andy Green <andy@warmcat.com>
- * This code is licensed under LGPL 2.1
- * http://www.gnu.org/licenses/lgpl-2.1.html
+ * Copyright (C) 2013-2017 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
*/
+#include <libwebsockets.h>
#include <string.h>
-#include "lejp.h"
-
#include <stdio.h>
/**
@@ -148,7 +160,8 @@ lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len)
n = ctx->wild[wildcard];
- while (--len && n < ctx->ppos && (n == ctx->wild[wildcard] || ctx->path[n] != '.'))
+ while (--len && n < ctx->ppos &&
+ (n == ctx->wild[wildcard] || ctx->path[n] != '.'))
*dest++ = ctx->path[n++];
*dest = '\0';
@@ -186,7 +199,6 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
while (len--) {
c = *json++;
-
s = ctx->st[ctx->sp].s;
/* skip whitespace unless we should care */
@@ -411,6 +423,26 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
}
goto add_stack_level;
+ case ']':
+ /* pop */
+ ctx->sp--;
+ if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) {
+ ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY;
+ goto reject;
+ }
+ /* drop the path [n] bit */
+ ctx->ppos = ctx->st[ctx->sp - 1].p;
+ ctx->ipos = ctx->st[ctx->sp - 1].i;
+ ctx->path[ctx->ppos] = '\0';
+ if (ctx->path_match &&
+ ctx->ppos <= ctx->path_match_len)
+ /*
+ * we shrank the path to be
+ * smaller than the matching point
+ */
+ ctx->path_match = 0;
+ goto array_end;
+
case 't': /* true */
ctx->uni = 0;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
@@ -582,8 +614,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto reject;
}
/* drop the path [n] bit */
- ctx->ppos = ctx->st[ctx->sp - 1].p;
- ctx->ipos = ctx->st[ctx->sp - 1].i;
+ if (ctx->sp) {
+ ctx->ppos = ctx->st[ctx->sp - 1].p;
+ ctx->ipos = ctx->st[ctx->sp - 1].i;
+ }
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len)
@@ -609,8 +643,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
}
/* pop */
ctx->sp--;
- ctx->ppos = ctx->st[ctx->sp - 1].p;
- ctx->ipos = ctx->st[ctx->sp - 1].i;
+ if (ctx->sp) {
+ ctx->ppos = ctx->st[ctx->sp - 1].p;
+ ctx->ipos = ctx->st[ctx->sp - 1].i;
+ }
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len)
@@ -631,6 +667,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto reject;
case LEJP_MP_ARRAY_END:
+array_end:
ctx->path[ctx->ppos] = '\0';
if (c == ',') {
/* increment this stack level's index */
diff --git a/thirdparty/lws/misc/sha-1.c b/thirdparty/libwebsockets/misc/sha-1.c
index 50205a0100..2e4db52693 100644
--- a/thirdparty/lws/misc/sha-1.c
+++ b/thirdparty/libwebsockets/misc/sha-1.c
@@ -32,7 +32,7 @@
* implemented by Jun-ichiro itojun Itoh <itojun@itojun.org>
*/
-#include "private-libwebsockets.h"
+#include "core/private.h"
#ifdef LWS_HAVE_SYS_TYPES_H
#include <sys/types.h>
diff --git a/thirdparty/lws/plat/lws-plat-unix.c b/thirdparty/libwebsockets/plat/lws-plat-unix.c
index a51e67bb81..bacc6af647 100644
--- a/thirdparty/lws/plat/lws-plat-unix.c
+++ b/thirdparty/libwebsockets/plat/lws-plat-unix.c
@@ -19,7 +19,8 @@
* MA 02110-1301 USA
*/
-#include "private-libwebsockets.h"
+#define _GNU_SOURCE
+#include "core/private.h"
#include <pwd.h>
#include <grp.h>
@@ -29,6 +30,56 @@
#endif
#include <dirent.h>
+int
+lws_plat_socket_offset(void)
+{
+ return 0;
+}
+
+int
+lws_plat_pipe_create(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+#if defined(LWS_HAVE_PIPE2)
+ return pipe2(pt->dummy_pipe_fds, O_NONBLOCK);
+#else
+ return pipe(pt->dummy_pipe_fds);
+#endif
+}
+
+int
+lws_plat_pipe_signal(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ char buf = 0;
+ int n;
+
+ n = write(pt->dummy_pipe_fds[1], &buf, 1);
+
+ return n != 1;
+}
+
+void
+lws_plat_pipe_close(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+ if (pt->dummy_pipe_fds[0] && pt->dummy_pipe_fds[0] != -1)
+ close(pt->dummy_pipe_fds[0]);
+ if (pt->dummy_pipe_fds[1] && pt->dummy_pipe_fds[1] != -1)
+ close(pt->dummy_pipe_fds[1]);
+
+ pt->dummy_pipe_fds[0] = pt->dummy_pipe_fds[1] = -1;
+}
+
+#ifdef __QNX__
+# include "netinet/tcp_var.h"
+# define TCP_KEEPINTVL TCPCTL_KEEPINTVL
+# define TCP_KEEPIDLE TCPCTL_KEEPIDLE
+# define TCP_KEEPCNT TCPCTL_KEEPCNT
+#endif
+
unsigned long long time_in_microseconds(void)
{
struct timeval tv;
@@ -52,6 +103,10 @@ lws_send_pipe_choked(struct lws *wsi)
#if defined(LWS_WITH_HTTP2)
wsi_eff = lws_get_network_wsi(wsi);
#endif
+
+ /* the fact we checked implies we avoided back-to-back writes */
+ wsi_eff->could_have_pending = 0;
+
/* treat the fact we got a truncated send pending as if we're choked */
if (wsi_eff->trunc_len)
return 1;
@@ -77,29 +132,6 @@ lws_poll_listen_fd(struct lws_pollfd *fd)
return poll(fd, 1, 0);
}
-LWS_VISIBLE void
-lws_cancel_service_pt(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- char buf = 0;
-
- if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
- lwsl_err("Cannot write to dummy pipe");
-}
-
-LWS_VISIBLE void
-lws_cancel_service(struct lws_context *context)
-{
- struct lws_context_per_thread *pt = &context->pt[0];
- char buf = 0, m = context->count_threads;
-
- while (m--) {
- if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
- lwsl_err("Cannot write to dummy pipe");
- pt++;
- }
-}
-
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{
int syslog_level = LOG_DEBUG;
@@ -124,9 +156,10 @@ LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
LWS_VISIBLE LWS_EXTERN int
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
{
+ volatile struct lws_foreign_thread_pollfd *ftp, *next;
+ volatile struct lws_context_per_thread *vpt;
struct lws_context_per_thread *pt;
int n = -1, m, c;
- char buf;
/* stay dead once we are dead */
@@ -134,15 +167,15 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
return 1;
pt = &context->pt[tsi];
+ vpt = (volatile struct lws_context_per_thread *)pt;
lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1);
if (timeout_ms < 0)
goto faked_service;
- lws_libev_run(context, tsi);
- lws_libuv_run(context, tsi);
- lws_libevent_run(context, tsi);
+ if (context->event_loop_ops->run_pt)
+ context->event_loop_ops->run_pt(context, tsi);
if (!context->service_tid_detected) {
struct lws _lws;
@@ -169,15 +202,73 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
timeout_ms = 0;
}
+ if (timeout_ms) {
+ lws_pt_lock(pt, __func__);
+ /* don't stay in poll wait longer than next hr timeout */
+ lws_usec_t t = __lws_hrtimer_service(pt);
+ if ((lws_usec_t)timeout_ms * 1000 > t)
+ timeout_ms = t / 1000;
+ lws_pt_unlock(pt);
+ }
+
+ vpt->inside_poll = 1;
+ lws_memory_barrier();
n = poll(pt->fds, pt->fds_count, timeout_ms);
+ vpt->inside_poll = 0;
+ lws_memory_barrier();
-#ifdef LWS_OPENSSL_SUPPORT
- if (!n && !pt->rx_draining_ext_list &&
- !lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) {
-#else
- if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
+ /* Collision will be rare and brief. Just spin until it completes */
+ while (vpt->foreign_spinlock)
+ ;
+
+ /*
+ * At this point we are not inside a foreign thread pollfd change,
+ * and we have marked ourselves as outside the poll() wait. So we
+ * are the only guys that can modify the lws_foreign_thread_pollfd
+ * list on the pt. Drain the list and apply the changes to the
+ * affected pollfds in the correct order.
+ */
+
+ lws_pt_lock(pt, __func__);
+
+ ftp = vpt->foreign_pfd_list;
+ //lwsl_notice("cleared list %p\n", ftp);
+ while (ftp) {
+ struct lws *wsi;
+ struct lws_pollfd *pfd;
+
+ next = ftp->next;
+ pfd = &vpt->fds[ftp->fd_index];
+ if (lws_socket_is_valid(pfd->fd)) {
+ wsi = wsi_from_fd(context, pfd->fd);
+ if (wsi)
+ __lws_change_pollfd(wsi, ftp->_and, ftp->_or);
+ }
+ lws_free((void *)ftp);
+ ftp = next;
+ }
+ vpt->foreign_pfd_list = NULL;
+ lws_memory_barrier();
+
+ /* we have come out of a poll wait... check the hrtimer list */
+
+ __lws_hrtimer_service(pt);
+
+ lws_pt_unlock(pt);
+
+ m = 0;
+#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
+ m |= !!pt->ws.rx_draining_ext_list;
#endif
+
+ if (pt->context->tls_ops &&
+ pt->context->tls_ops->fake_POLLIN_for_buffered)
+ m |= pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
+
+ if (!m && !n) { /* nothing to do */
lws_service_fd_tsi(context, NULL, tsi);
+ lws_service_do_ripe_rxflow(pt);
+
return 0;
}
@@ -194,18 +285,12 @@ faked_service:
c = n;
/* any socket with events to service? */
- for (n = 0; n < pt->fds_count && c; n++) {
+ for (n = 0; n < (int)pt->fds_count && c; n++) {
if (!pt->fds[n].revents)
continue;
c--;
- if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) {
- if (read(pt->fds[n].fd, &buf, 1) != 1)
- lwsl_err("Cannot read from dummy pipe.");
- continue;
- }
-
m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
if (m < 0)
return -1;
@@ -214,6 +299,8 @@ faked_service:
n--;
}
+ lws_service_do_ripe_rxflow(pt);
+
return 0;
}
@@ -262,6 +349,14 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
*/
#else
/* set the keepalive conditions we want on it too */
+
+#if defined(LWS_HAVE_TCP_USER_TIMEOUT)
+ optval = 1000 * (vhost->ka_time +
+ (vhost->ka_interval * vhost->ka_probes));
+ if (setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT,
+ (const void *)&optval, optlen) < 0)
+ return 1;
+#endif
optval = vhost->ka_time;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
(const void *)&optval, optlen) < 0)
@@ -292,7 +387,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
/* Disable Nagle */
optval = 1;
-#if defined (__sun)
+#if defined (__sun) || defined(__QNX__)
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
return 1;
#elif !defined(__APPLE__) && \
@@ -317,7 +412,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
static void
-_lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
+_lws_plat_apply_caps(int mode, const cap_value_t *cv, int count)
{
cap_t caps;
@@ -334,7 +429,7 @@ _lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
#endif
LWS_VISIBLE void
-lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
+lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
{
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
int n;
@@ -449,8 +544,8 @@ lws_plat_plugins_init(struct lws_context * context, const char * const *d)
}
plugin->list = context->plugin_list;
context->plugin_list = plugin;
- strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1);
- plugin->name[sizeof(plugin->name) - 1] = '\0';
+ lws_strncpy(plugin->name, namelist[i]->d_name,
+ sizeof(plugin->name));
plugin->l = l;
plugin->caps = lcaps;
context->plugin_protocol_count += lcaps.count_protocols;
@@ -543,9 +638,6 @@ lws_plat_context_early_destroy(struct lws_context *context)
LWS_VISIBLE void
lws_plat_context_late_destroy(struct lws_context *context)
{
- struct lws_context_per_thread *pt = &context->pt[0];
- int m = context->count_threads;
-
#ifdef LWS_WITH_PLUGINS
if (context->plugin_list)
lws_plat_plugins_destroy(context);
@@ -554,13 +646,6 @@ lws_plat_context_late_destroy(struct lws_context *context)
if (context->lws_lookup)
lws_free(context->lws_lookup);
- while (m--) {
- if (pt->dummy_pipe_fds[0])
- close(pt->dummy_pipe_fds[0]);
- if (pt->dummy_pipe_fds[1])
- close(pt->dummy_pipe_fds[1]);
- pt++;
- }
if (!context->fd_random)
lwsl_err("ZERO RANDOM FD\n");
if (context->fd_random != LWS_INVALID_FILE)
@@ -573,7 +658,7 @@ LWS_VISIBLE int
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
size_t addrlen)
{
- int rc = -1;
+ int rc = LWS_ITOSA_NOT_EXIST;
struct ifaddrs *ifr;
struct ifaddrs *ifc;
@@ -586,12 +671,19 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
if (!ifc->ifa_addr)
continue;
- lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname);
+ lwsl_debug(" interface %s vs %s (fam %d) ipv6 %d\n", ifc->ifa_name, ifname, ifc->ifa_addr->sa_family, ipv6);
if (strcmp(ifc->ifa_name, ifname))
continue;
switch (ifc->ifa_addr->sa_family) {
+#if defined(AF_PACKET)
+ case AF_PACKET:
+ /* interface exists but is not usable */
+ rc = LWS_ITOSA_NOT_USABLE;
+ continue;
+#endif
+
case AF_INET:
#ifdef LWS_WITH_IPV6
if (ipv6) {
@@ -619,20 +711,20 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
default:
continue;
}
- rc = 0;
+ rc = LWS_ITOSA_USABLE;
}
freeifaddrs(ifr);
- if (rc == -1) {
+ if (rc) {
/* check if bind to IP address */
#ifdef LWS_WITH_IPV6
if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
- rc = 0;
+ rc = LWS_ITOSA_USABLE;
else
#endif
if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1)
- rc = 0;
+ rc = LWS_ITOSA_USABLE;
}
return rc;
@@ -643,9 +735,8 @@ lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
- lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
- lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
+ if (context->event_loop_ops->io)
+ context->event_loop_ops->io(wsi, LWS_EV_START | LWS_EV_READ);
pt->fds[pt->fds_count++].revents = 0;
}
@@ -656,9 +747,9 @@ lws_plat_delete_socket_from_fds(struct lws_context *context,
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
- lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
- lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
+ if (context->event_loop_ops->io)
+ context->event_loop_ops->io(wsi,
+ LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
pt->fds_count--;
}
@@ -739,7 +830,8 @@ _lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
{
lws_fileofs_t r;
- if (offset > 0 && offset > fop_fd->len - fop_fd->pos)
+ if (offset > 0 &&
+ offset > (lws_fileofs_t)fop_fd->len - (lws_fileofs_t)fop_fd->pos)
offset = fop_fd->len - fop_fd->pos;
if ((lws_fileofs_t)fop_fd->pos + offset < 0)
@@ -793,13 +885,11 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
return 0;
}
-
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
- struct lws_context_creation_info *info)
+ const struct lws_context_creation_info *info)
{
- struct lws_context_per_thread *pt = &context->pt[0];
- int n = context->count_threads, fd;
+ int fd;
/* master context has the global fd lookup array */
context->lws_lookup = lws_zalloc(sizeof(struct lws *) *
@@ -821,26 +911,6 @@ lws_plat_init(struct lws_context *context,
return 1;
}
- if (!lws_libev_init_fd_table(context) &&
- !lws_libuv_init_fd_table(context) &&
- !lws_libevent_init_fd_table(context)) {
- /* otherwise libev/uv/event handled it instead */
-
- while (n--) {
- if (pipe(pt->dummy_pipe_fds)) {
- lwsl_err("Unable to create pipe\n");
- return 1;
- }
-
- /* use the read end of pipe as first item */
- pt->fds[0].fd = pt->dummy_pipe_fds[0];
- pt->fds[0].events = LWS_POLLIN;
- pt->fds[0].revents = 0;
- pt->fds_count = 1;
- pt++;
- }
- }
-
#ifdef LWS_WITH_PLUGINS
if (info->plugin_dirs)
lws_plat_plugins_init(context, info->plugin_dirs);
@@ -848,3 +918,52 @@ lws_plat_init(struct lws_context *context,
return 0;
}
+
+LWS_VISIBLE int
+lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
+ int len)
+{
+ int n;
+
+ n = write(fd, buf, len);
+
+ fsync(fd);
+ lseek(fd, 0, SEEK_SET);
+
+ return n != len;
+}
+
+LWS_VISIBLE int
+lws_plat_write_file(const char *filename, void *buf, int len)
+{
+ int m, fd;
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+
+ if (fd == -1)
+ return 1;
+
+ m = write(fd, buf, len);
+ close(fd);
+
+ return m != len;
+}
+
+LWS_VISIBLE int
+lws_plat_read_file(const char *filename, void *buf, int len)
+{
+ int n, fd = open(filename, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ n = read(fd, buf, len);
+ close(fd);
+
+ return n;
+}
+
+LWS_VISIBLE int
+lws_plat_recommended_rsa_bits(void)
+{
+ return 4096;
+}
diff --git a/thirdparty/lws/plat/lws-plat-win.c b/thirdparty/libwebsockets/plat/lws-plat-win.c
index f5b178ce85..948db62896 100644
--- a/thirdparty/lws/plat/lws-plat-win.c
+++ b/thirdparty/libwebsockets/plat/lws-plat-win.c
@@ -1,7 +1,34 @@
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif
-#include "private-libwebsockets.h"
+#include "core/private.h"
+
+int
+lws_plat_socket_offset(void)
+{
+ return 0;
+}
+
+int
+lws_plat_pipe_create(struct lws *wsi)
+{
+ return 1;
+}
+
+int
+lws_plat_pipe_signal(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+ WSASetEvent(pt->events[0]); /* trigger the cancel event */
+
+ return 0;
+}
+
+void
+lws_plat_pipe_close(struct lws *wsi)
+{
+}
unsigned long long
time_in_microseconds()
@@ -19,9 +46,10 @@ time_in_microseconds()
#endif
/*
- * As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a
- * ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can
- * prevent alignment faults on 64-bit Windows).
+ * As per Windows documentation for FILETIME, copy the resulting
+ * FILETIME structure to a ULARGE_INTEGER structure using memcpy
+ * (using memcpy instead of direct assignment can prevent alignment
+ * faults on 64-bit Windows).
*/
memcpy(&datetime, &filetime, sizeof(datetime));
@@ -81,7 +109,7 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) {
while (n < context->fd_hashtable[h].length) {
context->fd_hashtable[h].wsi[n] =
- context->fd_hashtable[h].wsi[n + 1];
+ context->fd_hashtable[h].wsi[n + 1];
n++;
}
context->fd_hashtable[h].length--;
@@ -94,8 +122,8 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
return 1;
}
-LWS_VISIBLE int lws_get_random(struct lws_context *context,
- void *buf, int len)
+LWS_VISIBLE int
+lws_get_random(struct lws_context *context, void *buf, int len)
{
int n;
char *p = (char *)buf;
@@ -106,16 +134,25 @@ LWS_VISIBLE int lws_get_random(struct lws_context *context,
return n;
}
-LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi)
-{
+LWS_VISIBLE int
+lws_send_pipe_choked(struct lws *wsi)
+{ struct lws *wsi_eff = wsi;
+
+#if defined(LWS_WITH_HTTP2)
+ wsi_eff = lws_get_network_wsi(wsi);
+#endif
+ /* the fact we checked implies we avoided back-to-back writes */
+ wsi_eff->could_have_pending = 0;
+
/* treat the fact we got a truncated send pending as if we're choked */
- if (wsi->trunc_len)
+ if (wsi_eff->trunc_len)
return 1;
- return (int)wsi->sock_send_blocking;
+ return (int)wsi_eff->sock_send_blocking;
}
-LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
+LWS_VISIBLE int
+lws_poll_listen_fd(struct lws_pollfd *fd)
{
fd_set readfds;
struct timeval tv = { 0, 0 };
@@ -125,29 +162,11 @@ LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
FD_ZERO(&readfds);
FD_SET(fd->fd, &readfds);
- return select(fd->fd + 1, &readfds, NULL, NULL, &tv);
+ return select(((int)fd->fd) + 1, &readfds, NULL, NULL, &tv);
}
LWS_VISIBLE void
-lws_cancel_service(struct lws_context *context)
-{
- struct lws_context_per_thread *pt = &context->pt[0];
- int n = context->count_threads;
-
- while (n--) {
- WSASetEvent(pt->events[0]);
- pt++;
- }
-}
-
-LWS_VISIBLE void
-lws_cancel_service_pt(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- WSASetEvent(pt->events[0]);
-}
-
-LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
+lwsl_emit_syslog(int level, const char *line)
{
lwsl_emit_stderr(level, line);
}
@@ -182,9 +201,8 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
context->service_tid_detected = 1;
}
- if (timeout_ms < 0)
- {
- if (lws_service_flag_pending(context, tsi)) {
+ if (timeout_ms < 0) {
+ if (lws_service_flag_pending(context, tsi)) {
/* any socket with events to service? */
for (n = 0; n < (int)pt->fds_count; n++) {
if (!pt->fds[n].revents)
@@ -201,6 +219,9 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
return 0;
}
+ if (context->event_loop_ops->run_pt)
+ context->event_loop_ops->run_pt(context, tsi);
+
for (i = 0; i < pt->fds_count; ++i) {
pfd = &pt->fds[i];
@@ -220,6 +241,9 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
if (n)
i--;
+ /*
+ * any wsi has truncated, force him signalled
+ */
if (wsi->trunc_len)
WSASetEvent(pt->events[0]);
}
@@ -236,29 +260,44 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
timeout_ms = 0;
}
- ev = WSAWaitForMultipleEvents( 1, pt->events , FALSE, timeout_ms, FALSE);
+ if (timeout_ms) {
+ lws_pt_lock(pt, __func__);
+ /* don't stay in poll wait longer than next hr timeout */
+ lws_usec_t t = __lws_hrtimer_service(pt);
+
+ if ((lws_usec_t)timeout_ms * 1000 > t)
+ timeout_ms = (int)(t / 1000);
+ lws_pt_unlock(pt);
+ }
+
+ ev = WSAWaitForMultipleEvents(1, pt->events, FALSE, timeout_ms, FALSE);
if (ev == WSA_WAIT_EVENT_0) {
- unsigned int eIdx;
+ unsigned int eIdx, err;
WSAResetEvent(pt->events[0]);
+ if (pt->context->tls_ops &&
+ pt->context->tls_ops->fake_POLLIN_for_buffered)
+ pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
+
for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) {
- if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0, &networkevents) == SOCKET_ERROR) {
- lwsl_err("WSAEnumNetworkEvents() failed with error %d\n", LWS_ERRNO);
+ if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0,
+ &networkevents) == SOCKET_ERROR) {
+ lwsl_err("WSAEnumNetworkEvents() failed "
+ "with error %d\n", LWS_ERRNO);
return -1;
}
pfd = &pt->fds[eIdx];
pfd->revents = (short)networkevents.lNetworkEvents;
+ err = networkevents.iErrorCode[FD_CONNECT_BIT];
+
if ((networkevents.lNetworkEvents & FD_CONNECT) &&
- networkevents.iErrorCode[FD_CONNECT_BIT] &&
- networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EALREADY &&
- networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EINPROGRESS &&
- networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EWOULDBLOCK &&
- networkevents.iErrorCode[FD_CONNECT_BIT] != WSAEINVAL) {
- lwsl_debug("Unable to connect errno=%d\n",
- networkevents.iErrorCode[FD_CONNECT_BIT]);
+ err && err != LWS_EALREADY &&
+ err != LWS_EINPROGRESS && err != LWS_EWOULDBLOCK &&
+ err != WSAEINVAL) {
+ lwsl_debug("Unable to connect errno=%d\n", err);
pfd->revents |= LWS_POLLHUP;
}
@@ -269,21 +308,19 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
}
/* if something closed, retry this slot */
if (pfd->revents & LWS_POLLHUP)
- --eIdx;
+ --eIdx;
- if( pfd->revents != 0 ) {
+ if (pfd->revents)
lws_service_fd_tsi(context, pfd, tsi);
-
- }
}
}
context->service_tid = 0;
- if (ev == WSA_WAIT_TIMEOUT) {
+ if (ev == WSA_WAIT_TIMEOUT)
lws_service_fd(context, NULL);
- }
- return 0;;
+
+ return 0;
}
LWS_VISIBLE int
@@ -309,7 +346,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
/* enable keepalive on this socket */
optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
- (const char *)&optval, optlen) < 0)
+ (const char *)&optval, optlen) < 0)
return 1;
alive.onoff = TRUE;
@@ -317,7 +354,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
alive.keepaliveinterval = vhost->ka_interval;
if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
- NULL, 0, &dwBytesRet, NULL, NULL))
+ NULL, 0, &dwBytesRet, NULL, NULL))
return 1;
}
@@ -343,7 +380,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
}
LWS_VISIBLE void
-lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
+lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
{
}
@@ -406,7 +443,7 @@ lws_interface_to_sa(int ipv6,
if (ipv6) {
if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) {
- return 0;
+ return LWS_ITOSA_USABLE;
}
}
#endif
@@ -420,11 +457,11 @@ lws_interface_to_sa(int ipv6,
}
if (address == INADDR_NONE)
- return -1;
+ return LWS_ITOSA_NOT_EXIST;
- addr->sin_addr.s_addr = (lws_intptr_t)address;
+ addr->sin_addr.s_addr = (unsigned long)(lws_intptr_t)address;
- return 0;
+ return LWS_ITOSA_USABLE;
}
LWS_VISIBLE void
@@ -542,7 +579,7 @@ LWS_VISIBLE int
lws_plat_inet_pton(int af, const char *src, void *dst)
{
WCHAR *buffer;
- DWORD bufferlen = strlen(src) + 1;
+ DWORD bufferlen = (int)strlen(src) + 1;
BOOL ok = FALSE;
buffer = lws_malloc(bufferlen * 2, "inet_pton");
@@ -692,7 +729,7 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
- struct lws_context_creation_info *info)
+ const struct lws_context_creation_info *info)
{
struct lws_context_per_thread *pt = &context->pt[0];
int i, n = context->count_threads;
@@ -715,7 +752,7 @@ lws_plat_init(struct lws_context *context,
}
pt->fds_count = 0;
- pt->events[0] = WSACreateEvent();
+ pt->events[0] = WSACreateEvent(); /* the cancel event */
pt++;
}
@@ -743,3 +780,50 @@ int fork(void)
exit(0);
}
+LWS_VISIBLE int
+lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
+ int len)
+{
+ int n;
+
+ n = write(fd, buf, len);
+
+ lseek(fd, 0, SEEK_SET);
+
+ return n != len;
+}
+
+LWS_VISIBLE int
+lws_plat_write_file(const char *filename, void *buf, int len)
+{
+ int m, fd;
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+
+ if (fd == -1)
+ return -1;
+
+ m = write(fd, buf, len);
+ close(fd);
+
+ return m != len;
+}
+
+LWS_VISIBLE int
+lws_plat_read_file(const char *filename, void *buf, int len)
+{
+ int n, fd = open(filename, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ n = read(fd, buf, len);
+ close(fd);
+
+ return n;
+}
+
+LWS_VISIBLE int
+lws_plat_recommended_rsa_bits(void)
+{
+ return 4096;
+}
diff --git a/thirdparty/libwebsockets/roles/h1/ops-h1.c b/thirdparty/libwebsockets/roles/h1/ops-h1.c
new file mode 100644
index 0000000000..d3b16f4d1f
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/h1/ops-h1.c
@@ -0,0 +1,687 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <core/private.h>
+
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+
+/*
+ * We have to take care about parsing because the headers may be split
+ * into multiple fragments. They may contain unknown headers with arbitrary
+ * argument lengths. So, we parse using a single-character at a time state
+ * machine that is completely independent of packet size.
+ *
+ * Returns <0 for error or length of chars consumed from buf (up to len)
+ */
+
+int
+lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
+{
+ unsigned char *last_char, *oldbuf = buf;
+ lws_filepos_t body_chunk_len;
+ size_t n;
+
+ // lwsl_notice("%s: h1 path: wsi state 0x%x\n", __func__, lwsi_state(wsi));
+
+ switch (lwsi_state(wsi)) {
+
+ case LRS_ISSUING_FILE:
+ return 0;
+
+ case LRS_ESTABLISHED:
+
+ if (lwsi_role_ws(wsi))
+ goto ws_mode;
+
+ if (lwsi_role_client(wsi))
+ break;
+
+ wsi->hdr_parsing_completed = 0;
+
+ /* fallthru */
+
+ case LRS_HEADERS:
+ if (!wsi->http.ah) {
+ lwsl_err("%s: LRS_HEADERS: NULL ah\n", __func__);
+ assert(0);
+ }
+ lwsl_parser("issuing %d bytes to parser\n", (int)len);
+#if defined(LWS_ROLE_WS) && !defined(LWS_NO_CLIENT)
+ if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
+ goto bail;
+#endif
+ last_char = buf;
+ if (lws_handshake_server(wsi, &buf, (size_t)len))
+ /* Handshake indicates this session is done. */
+ goto bail;
+
+ /* we might have transitioned to RAW */
+ if (wsi->role_ops == &role_ops_raw_skt ||
+ wsi->role_ops == &role_ops_raw_file)
+ /* we gave the read buffer to RAW handler already */
+ goto read_ok;
+
+ /*
+ * It's possible that we've exhausted our data already, or
+ * rx flow control has stopped us dealing with this early,
+ * but lws_handshake_server doesn't update len for us.
+ * Figure out how much was read, so that we can proceed
+ * appropriately:
+ */
+ len -= (buf - last_char);
+// lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
+
+ if (!wsi->hdr_parsing_completed)
+ /* More header content on the way */
+ goto read_ok;
+
+ switch (lwsi_state(wsi)) {
+ case LRS_ESTABLISHED:
+ case LRS_HEADERS:
+ goto read_ok;
+ case LRS_ISSUING_FILE:
+ goto read_ok;
+ case LRS_BODY:
+ wsi->http.rx_content_remain =
+ wsi->http.rx_content_length;
+ if (wsi->http.rx_content_remain)
+ goto http_postbody;
+
+ /* there is no POST content */
+ goto postbody_completion;
+ default:
+ break;
+ }
+ break;
+
+ case LRS_BODY:
+http_postbody:
+ lwsl_debug("%s: http post body: remain %d\n", __func__,
+ (int)wsi->http.rx_content_remain);
+ while (len && wsi->http.rx_content_remain) {
+ /* Copy as much as possible, up to the limit of:
+ * what we have in the read buffer (len)
+ * remaining portion of the POST body (content_remain)
+ */
+ body_chunk_len = min(wsi->http.rx_content_remain, len);
+ wsi->http.rx_content_remain -= body_chunk_len;
+ len -= body_chunk_len;
+#ifdef LWS_WITH_CGI
+ if (wsi->http.cgi) {
+ struct lws_cgi_args args;
+
+ args.ch = LWS_STDIN;
+ args.stdwsi = &wsi->http.cgi->stdwsi[0];
+ args.data = buf;
+ args.len = body_chunk_len;
+
+ /* returns how much used */
+ n = user_callback_handle_rxflow(
+ wsi->protocol->callback,
+ wsi, LWS_CALLBACK_CGI_STDIN_DATA,
+ wsi->user_space,
+ (void *)&args, 0);
+ if ((int)n < 0)
+ goto bail;
+ } else {
+#endif
+ n = wsi->protocol->callback(wsi,
+ LWS_CALLBACK_HTTP_BODY, wsi->user_space,
+ buf, (size_t)body_chunk_len);
+ if (n)
+ goto bail;
+ n = (size_t)body_chunk_len;
+#ifdef LWS_WITH_CGI
+ }
+#endif
+ buf += n;
+
+ if (wsi->http.rx_content_remain) {
+ lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
+ wsi->context->timeout_secs);
+ break;
+ }
+ /* he sent all the content in time */
+postbody_completion:
+#ifdef LWS_WITH_CGI
+ /*
+ * If we're running a cgi, we can't let him off the
+ * hook just because he sent his POST data
+ */
+ if (wsi->http.cgi)
+ lws_set_timeout(wsi, PENDING_TIMEOUT_CGI,
+ wsi->context->timeout_secs);
+ else
+#endif
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+#ifdef LWS_WITH_CGI
+ if (!wsi->http.cgi)
+#endif
+ {
+ lwsl_info("HTTP_BODY_COMPLETION: %p (%s)\n",
+ wsi, wsi->protocol->name);
+ n = wsi->protocol->callback(wsi,
+ LWS_CALLBACK_HTTP_BODY_COMPLETION,
+ wsi->user_space, NULL, 0);
+ if (n)
+ goto bail;
+
+ if (wsi->http2_substream)
+ lwsi_set_state(wsi, LRS_ESTABLISHED);
+ }
+
+ break;
+ }
+ break;
+
+ case LRS_AWAITING_CLOSE_ACK:
+ case LRS_WAITING_TO_SEND_CLOSE:
+ case LRS_SHUTDOWN:
+
+ws_mode:
+#if !defined(LWS_NO_CLIENT) && defined(LWS_ROLE_WS)
+ // lwsl_notice("%s: ws_mode\n", __func__);
+ if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
+ goto bail;
+#endif
+#if defined(LWS_ROLE_WS)
+ if (lwsi_role_ws(wsi) && lwsi_role_server(wsi) &&
+ /*
+ * for h2 we are on the swsi
+ */
+ lws_parse_ws(wsi, &buf, (size_t)len) < 0) {
+ lwsl_info("%s: lws_parse_ws bailed\n", __func__);
+ goto bail;
+ }
+#endif
+ // lwsl_notice("%s: ws_mode: buf moved on by %d\n", __func__,
+ // lws_ptr_diff(buf, oldbuf));
+ break;
+
+ case LRS_DEFERRING_ACTION:
+ lwsl_debug("%s: LRS_DEFERRING_ACTION\n", __func__);
+ break;
+
+ case LRS_SSL_ACK_PENDING:
+ break;
+
+ case LRS_DEAD_SOCKET:
+ lwsl_err("%s: Unhandled state LRS_DEAD_SOCKET\n", __func__);
+ goto bail;
+ // assert(0);
+ /* fallthru */
+
+ default:
+ lwsl_err("%s: Unhandled state %d\n", __func__, lwsi_state(wsi));
+ assert(0);
+ goto bail;
+ }
+
+read_ok:
+ /* Nothing more to do for now */
+// lwsl_info("%s: %p: read_ok, used %ld (len %d, state %d)\n", __func__,
+// wsi, (long)(buf - oldbuf), (int)len, wsi->state);
+
+ return lws_ptr_diff(buf, oldbuf);
+
+bail:
+ /*
+ * h2 / h2-ws calls us recursively in
+ *
+ * lws_read_h1()->
+ * lws_h2_parser()->
+ * lws_read_h1()
+ *
+ * pattern, having stripped the h2 framing in the middle.
+ *
+ * When taking down the whole connection, make sure that only the
+ * outer lws_read() does the wsi close.
+ */
+ if (!wsi->outer_will_close)
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
+ "lws_read_h1 bail");
+
+ return -1;
+}
+#if !defined(LWS_NO_SERVER)
+static int
+lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ struct lws_tokens ebuf;
+ int n, buffered;
+
+ if (lwsi_state(wsi) == LRS_DEFERRING_ACTION)
+ goto try_pollout;
+
+ /* any incoming data ready? */
+
+ if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
+ goto try_pollout;
+
+ /*
+ * If we previously just did POLLIN when IN and OUT were signaled
+ * (because POLLIN processing may have used up the POLLOUT), don't let
+ * that happen twice in a row... next time we see the situation favour
+ * POLLOUT
+ */
+
+ if (wsi->favoured_pollin &&
+ (pollfd->revents & pollfd->events & LWS_POLLOUT)) {
+ // lwsl_notice("favouring pollout\n");
+ wsi->favoured_pollin = 0;
+ goto try_pollout;
+ }
+
+ /*
+ * We haven't processed that the tunnel is set up yet, so
+ * defer reading
+ */
+
+ if (lwsi_state(wsi) == LRS_SSL_ACK_PENDING)
+ return LWS_HPI_RET_HANDLED;
+
+ /* these states imply we MUST have an ah attached */
+
+ if ((lwsi_state(wsi) == LRS_ESTABLISHED ||
+ lwsi_state(wsi) == LRS_ISSUING_FILE ||
+ lwsi_state(wsi) == LRS_HEADERS ||
+ lwsi_state(wsi) == LRS_BODY)) {
+
+ if (!wsi->http.ah && lws_header_table_attach(wsi, 0)) {
+ lwsl_info("%s: wsi %p: ah not available\n", __func__, wsi);
+ goto try_pollout;
+ }
+
+ /*
+ * We got here because there was specifically POLLIN...
+ * regardless of our buflist state, we need to get it,
+ * and either use it, or append to the buflist and use
+ * buflist head material.
+ *
+ * We will not notice a connection close until the buflist is
+ * exhausted and we tried to do a read of some kind.
+ */
+
+ buffered = lws_buflist_aware_read(pt, wsi, &ebuf);
+ switch (ebuf.len) {
+ case 0:
+ lwsl_info("%s: read 0 len a\n", __func__);
+ wsi->seen_zero_length_recv = 1;
+ lws_change_pollfd(wsi, LWS_POLLIN, 0);
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ /*
+ * autobahn requires us to win the race between close
+ * and draining the extensions
+ */
+ if (wsi->ws &&
+ (wsi->ws->rx_draining_ext || wsi->ws->tx_draining_ext))
+ goto try_pollout;
+#endif
+ /*
+ * normally, we respond to close with logically closing
+ * our side immediately
+ */
+ goto fail;
+
+ case LWS_SSL_CAPABLE_ERROR:
+ goto fail;
+ case LWS_SSL_CAPABLE_MORE_SERVICE:
+ goto try_pollout;
+ }
+
+ /* just ignore incoming if waiting for close */
+ if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
+ lwsl_notice("%s: just ignoring\n", __func__);
+ goto try_pollout;
+ }
+
+ if (lwsi_state(wsi) == LRS_ISSUING_FILE) {
+ // lwsl_notice("stashing: wsi %p: bd %d\n", wsi, buffered);
+ if (lws_buflist_aware_consume(wsi, &ebuf, 0, buffered))
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+
+ goto try_pollout;
+ }
+
+ /*
+ * Otherwise give it to whoever wants it according to the
+ * connection state
+ */
+#if defined(LWS_ROLE_H2)
+ if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
+ n = lws_read_h2(wsi, (uint8_t *)ebuf.token, ebuf.len);
+ else
+#endif
+ n = lws_read_h1(wsi, (uint8_t *)ebuf.token, ebuf.len);
+ if (n < 0) /* we closed wsi */
+ return LWS_HPI_RET_WSI_ALREADY_DIED;
+
+ lwsl_debug("%s: consumed %d\n", __func__, n);
+
+ if (lws_buflist_aware_consume(wsi, &ebuf, n, buffered))
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+
+ /*
+ * during the parsing our role changed to something non-http,
+ * so the ah has no further meaning
+ */
+
+ if (wsi->http.ah &&
+ !lwsi_role_h1(wsi) &&
+ !lwsi_role_h2(wsi) &&
+ !lwsi_role_cgi(wsi))
+ lws_header_table_detach(wsi, 0);
+
+ /*
+ * He may have used up the writability above, if we will defer
+ * POLLOUT processing in favour of POLLIN, note it
+ */
+
+ if (pollfd->revents & LWS_POLLOUT)
+ wsi->favoured_pollin = 1;
+
+ return LWS_HPI_RET_HANDLED;
+ }
+
+ /*
+ * He may have used up the writability above, if we will defer POLLOUT
+ * processing in favour of POLLIN, note it
+ */
+
+ if (pollfd->revents & LWS_POLLOUT)
+ wsi->favoured_pollin = 1;
+
+try_pollout:
+
+ /* this handles POLLOUT for http serving fragments */
+
+ if (!(pollfd->revents & LWS_POLLOUT))
+ return LWS_HPI_RET_HANDLED;
+
+ /* one shot */
+ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+ lwsl_notice("%s a\n", __func__);
+ goto fail;
+ }
+
+ /* clear back-to-back write detection */
+ wsi->could_have_pending = 0;
+
+ if (lwsi_state(wsi) == LRS_DEFERRING_ACTION) {
+ lwsl_debug("%s: LRS_DEFERRING_ACTION now writable\n", __func__);
+
+ lwsi_set_state(wsi, LRS_ESTABLISHED);
+ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+ lwsl_info("failed at set pollfd\n");
+ goto fail;
+ }
+ }
+
+ if (!wsi->hdr_parsing_completed)
+ return LWS_HPI_RET_HANDLED;
+
+ if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
+
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_C_WRITEABLE_CB, 1);
+#if defined(LWS_WITH_STATS)
+ if (wsi->active_writable_req_us) {
+ uint64_t ul = time_in_microseconds() -
+ wsi->active_writable_req_us;
+
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_MS_WRITABLE_DELAY, ul);
+ lws_stats_atomic_max(wsi->context, pt,
+ LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
+ wsi->active_writable_req_us = 0;
+ }
+#endif
+
+ n = user_callback_handle_rxflow(wsi->protocol->callback, wsi,
+ LWS_CALLBACK_HTTP_WRITEABLE,
+ wsi->user_space, NULL, 0);
+ if (n < 0) {
+ lwsl_info("writeable_fail\n");
+ goto fail;
+ }
+
+ return LWS_HPI_RET_HANDLED;
+ }
+
+ /* >0 == completion, <0 == error
+ *
+ * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
+ * it's done. That's the case even if we just completed the
+ * send, so wait for that.
+ */
+ n = lws_serve_http_file_fragment(wsi);
+ if (n < 0)
+ goto fail;
+
+ return LWS_HPI_RET_HANDLED;
+
+
+fail:
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
+ "server socket svc fail");
+
+ return LWS_HPI_RET_WSI_ALREADY_DIED;
+}
+#endif
+
+static int
+rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
+ struct lws_pollfd *pollfd)
+{
+
+// lwsl_notice("%s: %p: wsistate 0x%x %s, revents 0x%x\n", __func__, wsi,
+// wsi->wsistate, wsi->role_ops->name, pollfd->revents);
+
+#ifdef LWS_WITH_CGI
+ if (wsi->http.cgi && (pollfd->revents & LWS_POLLOUT)) {
+ if (lws_handle_POLLOUT_event(wsi, pollfd))
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+
+ return LWS_HPI_RET_HANDLED;
+ }
+#endif
+
+ if (lws_is_flowcontrolled(wsi))
+ /* We cannot deal with any kind of new RX because we are
+ * RX-flowcontrolled.
+ */
+ return LWS_HPI_RET_HANDLED;
+
+#if !defined(LWS_NO_SERVER)
+ if (!lwsi_role_client(wsi)) {
+ int n;
+
+ lwsl_debug("%s: %p: wsistate 0x%x\n", __func__, wsi, wsi->wsistate);
+ n = lws_h1_server_socket_service(wsi, pollfd);
+ if (n != LWS_HPI_RET_HANDLED)
+ return n;
+ if (lwsi_state(wsi) != LRS_SSL_INIT)
+ if (lws_server_socket_service_ssl(wsi, LWS_SOCK_INVALID))
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+
+ return LWS_HPI_RET_HANDLED;
+ }
+#endif
+
+#ifndef LWS_NO_CLIENT
+ if ((pollfd->revents & LWS_POLLIN) &&
+ wsi->hdr_parsing_completed && !wsi->told_user_closed) {
+
+ /*
+ * In SSL mode we get POLLIN notification about
+ * encrypted data in.
+ *
+ * But that is not necessarily related to decrypted
+ * data out becoming available; in may need to perform
+ * other in or out before that happens.
+ *
+ * simply mark ourselves as having readable data
+ * and turn off our POLLIN
+ */
+ wsi->client_rx_avail = 1;
+ lws_change_pollfd(wsi, LWS_POLLIN, 0);
+
+ //lwsl_notice("calling back %s\n", wsi->protocol->name);
+
+ /* let user code know, he'll usually ask for writeable
+ * callback and drain / re-enable it there
+ */
+ if (user_callback_handle_rxflow(
+ wsi->protocol->callback,
+ wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
+ wsi->user_space, NULL, 0)) {
+ lwsl_info("RECEIVE_CLIENT_HTTP closed it\n");
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ }
+
+ return LWS_HPI_RET_HANDLED;
+ }
+#endif
+
+// if (lwsi_state(wsi) == LRS_ESTABLISHED)
+// return LWS_HPI_RET_HANDLED;
+
+#if !defined(LWS_NO_CLIENT)
+ if ((pollfd->revents & LWS_POLLOUT) &&
+ lws_handle_POLLOUT_event(wsi, pollfd)) {
+ lwsl_debug("POLLOUT event closed it\n");
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ }
+
+ if (lws_client_socket_service(wsi, pollfd, NULL))
+ return LWS_HPI_RET_WSI_ALREADY_DIED;
+#endif
+
+ return LWS_HPI_RET_HANDLED;
+}
+
+int rops_handle_POLLOUT_h1(struct lws *wsi)
+{
+ if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY)
+ return LWS_HP_RET_USER_SERVICE;
+
+ if (lwsi_role_client(wsi))
+ return LWS_HP_RET_USER_SERVICE;
+
+ return LWS_HP_RET_BAIL_OK;
+}
+
+static int
+rops_write_role_protocol_h1(struct lws *wsi, unsigned char *buf, size_t len,
+ enum lws_write_protocol *wp)
+{
+#if 0
+ /* if not in a state to send stuff, then just send nothing */
+
+ if ((lwsi_state(wsi) != LRS_RETURNED_CLOSE &&
+ lwsi_state(wsi) != LRS_WAITING_TO_SEND_CLOSE &&
+ lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK)) {
+ //assert(0);
+ lwsl_debug("binning %d %d\n", lwsi_state(wsi), *wp);
+ return 0;
+ }
+#endif
+
+ return lws_issue_raw(wsi, (unsigned char *)buf, len);
+}
+
+static int
+rops_alpn_negotiated_h1(struct lws *wsi, const char *alpn)
+{
+ lwsl_debug("%s: client %d\n", __func__, lwsi_role_client(wsi));
+#if !defined(LWS_NO_CLIENT)
+ if (lwsi_role_client(wsi)) {
+ /*
+ * If alpn asserts it is http/1.1, server support for KA is
+ * mandatory.
+ *
+ * Knowing this lets us proceed with sending pipelined headers
+ * before we received the first response headers.
+ */
+ wsi->keepalive_active = 1;
+ }
+#endif
+
+ return 0;
+}
+
+static int
+rops_destroy_role_h1(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ struct allocated_headers *ah;
+
+ /* we may not have an ah, but may be on the waiting list... */
+ lwsl_info("%s: ah det due to close\n", __func__);
+ __lws_header_table_detach(wsi, 0);
+
+ ah = pt->http.ah_list;
+
+ while (ah) {
+ if (ah->in_use && ah->wsi == wsi) {
+ lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi);
+ ah->in_use = 0;
+ ah->wsi = NULL;
+ pt->http.ah_count_in_use--;
+ break;
+ }
+ ah = ah->next;
+ }
+
+ return 0;
+}
+
+struct lws_role_ops role_ops_h1 = {
+ /* role name */ "h1",
+ /* alpn id */ "http/1.1",
+ /* check_upgrades */ NULL,
+ /* init_context */ NULL,
+ /* init_vhost */ NULL,
+ /* destroy_vhost */ NULL,
+ /* periodic_checks */ NULL,
+ /* service_flag_pending */ NULL,
+ /* handle_POLLIN */ rops_handle_POLLIN_h1,
+ /* handle_POLLOUT */ rops_handle_POLLOUT_h1,
+ /* perform_user_POLLOUT */ NULL,
+ /* callback_on_writable */ NULL,
+ /* tx_credit */ NULL,
+ /* write_role_protocol */ rops_write_role_protocol_h1,
+ /* encapsulation_parent */ NULL,
+ /* alpn_negotiated */ rops_alpn_negotiated_h1,
+ /* close_via_role_protocol */ NULL,
+ /* close_role */ NULL,
+ /* close_kill_connection */ NULL,
+ /* destroy_role */ rops_destroy_role_h1,
+ /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE,
+ LWS_CALLBACK_HTTP_WRITEABLE },
+ /* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP,
+ LWS_CALLBACK_CLOSED_HTTP },
+ /* file_handle */ 0,
+};
diff --git a/thirdparty/libwebsockets/roles/h1/private.h b/thirdparty/libwebsockets/roles/h1/private.h
new file mode 100644
index 0000000000..3f53954d33
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/h1/private.h
@@ -0,0 +1,27 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * This is included from core/private.h if LWS_ROLE_H1
+ *
+ * Most of the h1 business is defined in the h1 / h2 common roles/http dir
+ */
+
+extern struct lws_role_ops role_ops_h1;
+#define lwsi_role_h1(wsi) (wsi->role_ops == &role_ops_h1)
diff --git a/thirdparty/lws/client/client-handshake.c b/thirdparty/libwebsockets/roles/http/client/client-handshake.c
index c2720d9283..4830fc9eca 100644
--- a/thirdparty/lws/client/client-handshake.c
+++ b/thirdparty/libwebsockets/roles/http/client/client-handshake.c
@@ -1,4 +1,4 @@
-#include "private-libwebsockets.h"
+#include "core/private.h"
static int
lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
@@ -20,7 +20,6 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
{
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_CANONNAME;
}
return getaddrinfo(ads, NULL, &hints, result);
@@ -29,15 +28,20 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
struct lws *
lws_client_connect_2(struct lws *wsi)
{
- sockaddr46 sa46;
- struct addrinfo *result;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ const char *adsin;
+ struct lws *wsi_piggyback = NULL;
struct lws_pollfd pfd;
- const char *cce = "", *iface;
- int n, port;
ssize_t plen = 0;
+#endif
+ struct addrinfo *result;
const char *ads;
+ sockaddr46 sa46;
+ int n, port;
+ const char *cce = "", *iface;
+ const char *meth = NULL;
#ifdef LWS_WITH_IPV6
char ipv6only = lws_check_opt(wsi->vhost->options,
LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY |
@@ -48,24 +52,148 @@ lws_client_connect_2(struct lws *wsi)
#endif
#endif
- lwsl_client("%s\n", __func__);
+ lwsl_client("%s: %p\n", __func__, wsi);
- if (!wsi->u.hdr.ah) {
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ if (!wsi->http.ah) {
cce = "ah was NULL at cc2";
lwsl_err("%s\n", cce);
goto oom4;
}
+ /* we can only piggyback GET or POST */
+
+ meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
+ if (meth && strcmp(meth, "GET") && strcmp(meth, "POST"))
+ goto create_new_conn;
+
+ /* we only pipeline connections that said it was okay */
+
+ if (!wsi->client_pipeline)
+ goto create_new_conn;
+
+ /*
+ * let's take a look first and see if there are any already-active
+ * client connections we can piggy-back on.
+ */
+
+ adsin = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
+
+ lws_vhost_lock(wsi->vhost); /* ----------------------------------- { */
+
+ lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+ wsi->vhost->dll_active_client_conns.next) {
+ struct lws *w = lws_container_of(d, struct lws,
+ dll_active_client_conns);
+
+ lwsl_debug("%s: check %s %s %d %d\n", __func__, adsin,
+ w->client_hostname_copy, wsi->c_port, w->c_port);
+
+ if (w != wsi && w->client_hostname_copy &&
+ !strcmp(adsin, w->client_hostname_copy) &&
+#if defined(LWS_WITH_TLS)
+ (wsi->tls.use_ssl & LCCSCF_USE_SSL) ==
+ (w->tls.use_ssl & LCCSCF_USE_SSL) &&
+#endif
+ wsi->c_port == w->c_port) {
+
+ /* someone else is already connected to the right guy */
+
+ /* do we know for a fact pipelining won't fly? */
+ if (w->keepalive_rejected) {
+ lwsl_info("defeating pipelining due to no "
+ "keepalive on server\n");
+ lws_vhost_unlock(wsi->vhost); /* } ---------- */
+ goto create_new_conn;
+ }
+#if defined (LWS_WITH_HTTP2)
+ /*
+ * h2: in usable state already: just use it without
+ * going through the queue
+ */
+ if (w->client_h2_alpn &&
+ (lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS ||
+ lwsi_state(w) == LRS_ESTABLISHED)) {
+
+ lwsl_info("%s: just join h2 directly\n",
+ __func__);
+
+ wsi->client_h2_alpn = 1;
+ lws_wsi_h2_adopt(w, wsi);
+ lws_vhost_unlock(wsi->vhost); /* } ---------- */
+
+ return wsi;
+ }
+#endif
+
+ lwsl_info("applying %p to txn queue on %p (wsistate 0x%x)\n",
+ wsi, w, w->wsistate);
+ /*
+ * ...let's add ourselves to his transaction queue...
+ * we are adding ourselves at the HEAD
+ */
+ lws_dll_lws_add_front(&wsi->dll_client_transaction_queue,
+ &w->dll_client_transaction_queue_head);
+
+ /*
+ * h1: pipeline our headers out on him,
+ * and wait for our turn at client transaction_complete
+ * to take over parsing the rx.
+ */
+
+ wsi_piggyback = w;
+
+ lws_vhost_unlock(wsi->vhost); /* } ---------- */
+ goto send_hs;
+ }
+
+ } lws_end_foreach_dll_safe(d, d1);
+
+ lws_vhost_unlock(wsi->vhost); /* } ---------------------------------- */
+
+create_new_conn:
+#endif
+
+ /*
+ * clients who will create their own fresh connection keep a copy of
+ * the hostname they originally connected to, in case other connections
+ * want to use it too
+ */
+
+ if (!wsi->client_hostname_copy)
+ wsi->client_hostname_copy =
+ strdup(lws_hdr_simple_ptr(wsi,
+ _WSI_TOKEN_CLIENT_PEER_ADDRESS));
+
+ /*
+ * If we made our own connection, and we're doing a method that can take
+ * a pipeline, we are an "active client connection".
+ *
+ * Add ourselves to the vhost list of those so that others can
+ * piggyback on our transaction queue
+ */
+
+ if (meth && (!strcmp(meth, "GET") || !strcmp(meth, "POST")) &&
+ lws_dll_is_null(&wsi->dll_client_transaction_queue) &&
+ lws_dll_is_null(&wsi->dll_active_client_conns)) {
+ lws_vhost_lock(wsi->vhost);
+ lws_dll_lws_add_front(&wsi->dll_active_client_conns,
+ &wsi->vhost->dll_active_client_conns);
+ lws_vhost_unlock(wsi->vhost);
+ }
+
/*
* start off allowing ipv6 on connection if vhost allows it
*/
wsi->ipv6 = LWS_IPV6_ENABLED(wsi->vhost);
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+
/* Decide what it is we need to connect to:
*
* Priority 1: connect to http proxy */
- if (wsi->vhost->http_proxy_port) {
+ if (wsi->vhost->http.http_proxy_port) {
plen = sprintf((char *)pt->serv_buf,
"CONNECT %s:%u HTTP/1.0\x0d\x0a"
"User-agent: libwebsockets\x0d\x0a",
@@ -78,8 +206,11 @@ lws_client_connect_2(struct lws *wsi)
wsi->vhost->proxy_basic_auth_token);
plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a");
- ads = wsi->vhost->http_proxy_address;
- port = wsi->vhost->http_proxy_port;
+ ads = wsi->vhost->http.http_proxy_address;
+ port = wsi->vhost->http.http_proxy_port;
+#else
+ if (0) {
+#endif
#if defined(LWS_WITH_SOCKS5)
@@ -104,12 +235,14 @@ lws_client_connect_2(struct lws *wsi)
* to whatever we decided to connect to
*/
- lwsl_notice("%s: %p: address %s\n", __func__, wsi, ads);
+ lwsl_info("%s: %p: address %s\n", __func__, wsi, ads);
n = lws_getaddrinfo46(wsi, ads, &result);
#ifdef LWS_WITH_IPV6
if (wsi->ipv6) {
+ struct sockaddr_in6 *sa6 =
+ ((struct sockaddr_in6 *)result->ai_addr);
if (n) {
/* lws_getaddrinfo46 failed, there is no usable result */
@@ -138,11 +271,10 @@ lws_client_connect_2(struct lws *wsi)
break;
case AF_INET6:
- memcpy(&sa46.sa6.sin6_addr,
- &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr,
+ memcpy(&sa46.sa6.sin6_addr, &sa6->sin6_addr,
sizeof(struct in6_addr));
- sa46.sa6.sin6_scope_id = ((struct sockaddr_in6 *)result->ai_addr)->sin6_scope_id;
- sa46.sa6.sin6_flowinfo = ((struct sockaddr_in6 *)result->ai_addr)->sin6_flowinfo;
+ sa46.sa6.sin6_scope_id = sa6->sin6_scope_id;
+ sa46.sa6.sin6_flowinfo = sa6->sin6_flowinfo;
break;
default:
lwsl_err("Unknown address family\n");
@@ -211,14 +343,11 @@ lws_client_connect_2(struct lws *wsi)
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
-#if defined(LWS_WITH_LIBUV)
- if (LWS_LIBUV_ENABLED(context))
- if (lws_libuv_check_watcher_active(wsi)) {
- lwsl_warn("Waiting for libuv watcher to close\n");
- cce = "waiting for libuv watcher to close";
- goto oom4;
- }
-#endif
+ if (wsi->context->event_loop_ops->check_client_connect_ok &&
+ wsi->context->event_loop_ops->check_client_connect_ok(wsi)) {
+ cce = "waiting for event loop watcher to close";
+ goto oom4;
+ }
#ifdef LWS_WITH_IPV6
if (wsi->ipv6)
@@ -240,13 +369,12 @@ lws_client_connect_2(struct lws *wsi)
goto oom4;
}
- wsi->mode = LWSCM_WSCL_WAITING_CONNECT;
+ lwsi_set_state(wsi, LRS_WAITING_CONNECT);
- lws_libev_accept(wsi, wsi->desc);
- lws_libuv_accept(wsi, wsi->desc);
- lws_libevent_accept(wsi, wsi->desc);
+ if (wsi->context->event_loop_ops->accept)
+ wsi->context->event_loop_ops->accept(wsi);
- if (insert_wsi_socket_into_fds(context, wsi)) {
+ if (__insert_wsi_socket_into_fds(wsi->context, wsi)) {
compatible_close(wsi->desc.sockfd);
cce = "insert wsi failed";
goto oom4;
@@ -328,10 +456,11 @@ lws_client_connect_2(struct lws *wsi)
lwsl_client("connected\n");
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
/* we are connected to server, or proxy */
/* http proxy */
- if (wsi->vhost->http_proxy_port) {
+ if (wsi->vhost->http.http_proxy_port) {
/*
* OK from now on we talk via the proxy, so connect to that
@@ -340,11 +469,11 @@ lws_client_connect_2(struct lws *wsi)
* leaving old string/frag there but unreferenced)
*/
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
- wsi->vhost->http_proxy_address))
+ wsi->vhost->http.http_proxy_address))
goto failed;
- wsi->c_port = wsi->vhost->http_proxy_port;
+ wsi->c_port = wsi->vhost->http.http_proxy_port;
- n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen,
+ n = send(wsi->desc.sockfd, (char *)pt->serv_buf, (int)plen,
MSG_NOSIGNAL);
if (n < 0) {
lwsl_debug("ERROR writing to proxy socket\n");
@@ -355,10 +484,11 @@ lws_client_connect_2(struct lws *wsi)
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
AWAITING_TIMEOUT);
- wsi->mode = LWSCM_WSCL_WAITING_PROXY_REPLY;
+ lwsi_set_state(wsi, LRS_WAITING_PROXY_REPLY);
return wsi;
}
+#endif
#if defined(LWS_WITH_SOCKS5)
/* socks proxy */
else if (wsi->vhost->socks_proxy_port) {
@@ -373,72 +503,105 @@ lws_client_connect_2(struct lws *wsi)
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY,
AWAITING_TIMEOUT);
- wsi->mode = LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY;
+ lwsi_set_state(wsi, LRS_WAITING_SOCKS_GREETING_REPLY);
return wsi;
}
#endif
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+send_hs:
- /*
- * provoke service to issue the handshake directly
- * we need to do it this way because in the proxy case, this is the
- * next state and executed only if and when we get a good proxy
- * response inside the state machine... but notice in SSL case this
- * may not have sent anything yet with 0 return, and won't until some
- * many retries from main loop. To stop that becoming endless,
- * cover with a timeout.
- */
+ if (wsi_piggyback &&
+ !lws_dll_is_null(&wsi->dll_client_transaction_queue)) {
+ /*
+ * We are pipelining on an already-established connection...
+ * we can skip tls establishment.
+ */
+
+ lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
+
+ /*
+ * we can't send our headers directly, because they have to
+ * be sent when the parent is writeable. The parent will check
+ * for anybody on his client transaction queue that is in
+ * LRS_H1C_ISSUE_HANDSHAKE2, and let them write.
+ *
+ * If we are trying to do this too early, before the master
+ * connection has written his own headers, then it will just
+ * wait in the queue until it's possible to send them.
+ */
+ lws_callback_on_writable(wsi_piggyback);
+ lwsl_info("%s: wsi %p: waiting to send headers (parent state %x)\n",
+ __func__, wsi, lwsi_state(wsi_piggyback));
+ } else {
+ lwsl_info("%s: wsi %p: client creating own connection\n",
+ __func__, wsi);
- lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE,
- AWAITING_TIMEOUT);
+ /* we are making our own connection */
+ lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE);
+
+ /*
+ * provoke service to issue the handshake directly.
+ *
+ * we need to do it this way because in the proxy case, this is
+ * the next state and executed only if and when we get a good
+ * proxy response inside the state machine... but notice in
+ * SSL case this may not have sent anything yet with 0 return,
+ * and won't until many retries from main loop. To stop that
+ * becoming endless, cover with a timeout.
+ */
- wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE;
- pfd.fd = wsi->desc.sockfd;
- pfd.events = LWS_POLLIN;
- pfd.revents = LWS_POLLIN;
+ lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE,
+ AWAITING_TIMEOUT);
- n = lws_service_fd(context, &pfd);
- if (n < 0) {
- cce = "first service failed";
- goto failed;
- }
- if (n) /* returns 1 on failure after closing wsi */
- return NULL;
+ pfd.fd = wsi->desc.sockfd;
+ pfd.events = LWS_POLLIN;
+ pfd.revents = LWS_POLLIN;
+ n = lws_service_fd(context, &pfd);
+ if (n < 0) {
+ cce = "first service failed";
+ goto failed;
+ }
+ if (n) /* returns 1 on failure after closing wsi */
+ return NULL;
+ }
+#endif
return wsi;
oom4:
- /* we're closing, losing some rx is OK */
- lws_header_table_force_to_detachable_state(wsi);
-
- if (wsi->mode == LWSCM_HTTP_CLIENT ||
- wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED ||
- wsi->mode == LWSCM_WSCL_WAITING_CONNECT) {
- wsi->vhost->protocols[0].callback(wsi,
+ if (lwsi_role_client(wsi) && lwsi_state_est(wsi)) {
+ wsi->protocol->callback(wsi,
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
wsi->user_space, (void *)cce, strlen(cce));
wsi->already_did_cce = 1;
}
/* take care that we might be inserted in fds already */
- if (wsi->position_in_fds_table != -1)
+ if (wsi->position_in_fds_table != LWS_NO_FDS_POS)
goto failed1;
lws_remove_from_timeout_list(wsi);
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
lws_header_table_detach(wsi, 0);
+#endif
+ lws_client_stash_destroy(wsi);
+ lws_free_set_NULL(wsi->client_hostname_copy);
lws_free(wsi);
return NULL;
failed:
- wsi->vhost->protocols[0].callback(wsi,
+ wsi->protocol->callback(wsi,
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
wsi->user_space, (void *)cce, strlen(cce));
wsi->already_did_cce = 1;
failed1:
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2");
return NULL;
}
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+
/**
* lws_client_reset() - retarget a connected wsi to start over with a new connection (ie, redirect)
* this only works if still in HTTP, ie, not upgraded yet
@@ -452,7 +615,8 @@ LWS_VISIBLE struct lws *
lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
const char *path, const char *host)
{
- char origin[300] = "", protocol[300] = "", method[32] = "", iface[16] = "", *p;
+ char origin[300] = "", protocol[300] = "", method[32] = "",
+ iface[16] = "", alpn[32] = "", *p;
struct lws *wsi = *pwsi;
if (wsi->redirects == 3) {
@@ -463,49 +627,42 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN);
if (p)
- strncpy(origin, p, sizeof(origin) - 1);
+ lws_strncpy(origin, p, sizeof(origin));
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
if (p)
- strncpy(protocol, p, sizeof(protocol) - 1);
+ lws_strncpy(protocol, p, sizeof(protocol));
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
if (p)
- strncpy(method, p, sizeof(method) - 1);
+ lws_strncpy(method, p, sizeof(method));
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
if (p)
- strncpy(method, p, sizeof(iface) - 1);
+ lws_strncpy(iface, p, sizeof(iface));
+
+ p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ALPN);
+ if (p)
+ lws_strncpy(alpn, p, sizeof(alpn));
lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d\n",
address, port, path, ssl);
/* close the connection by hand */
-#ifdef LWS_OPENSSL_SUPPORT
+#if defined(LWS_WITH_TLS)
lws_ssl_close(wsi);
#endif
-#ifdef LWS_WITH_LIBUV
- if (LWS_LIBUV_ENABLED(wsi->context)) {
- lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi);
- /*
- * libuv has to do his own close handle processing asynchronously
- * but once it starts we can do everything else synchronously,
- * including trash wsi->desc.sockfd since it took a copy.
- *
- * When it completes it will call compatible_close()
- */
- lws_libuv_closehandle_manually(wsi);
- } else
-#else
- compatible_close(wsi->desc.sockfd);
-#endif
+ if (wsi->context->event_loop_ops->close_handle_manually)
+ wsi->context->event_loop_ops->close_handle_manually(wsi);
+ else
+ compatible_close(wsi->desc.sockfd);
- remove_wsi_socket_from_fds(wsi);
+ __remove_wsi_socket_from_fds(wsi);
-#ifdef LWS_OPENSSL_SUPPORT
- wsi->use_ssl = ssl;
+#if defined(LWS_WITH_TLS)
+ wsi->tls.use_ssl = ssl;
#else
if (ssl) {
lwsl_err("%s: not configured for ssl\n", __func__);
@@ -514,12 +671,12 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
#endif
wsi->desc.sockfd = LWS_SOCK_INVALID;
- wsi->state = LWSS_CLIENT_UNCONNECTED;
+ lwsi_set_state(wsi, LRS_UNCONNECTED);
wsi->protocol = NULL;
wsi->pending_timeout = NO_PENDING_TIMEOUT;
wsi->c_port = port;
wsi->hdr_parsing_completed = 0;
- _lws_header_table_reset(wsi->u.hdr.ah);
+ _lws_header_table_reset(wsi->http.ah);
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address))
return NULL;
@@ -544,6 +701,10 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE,
iface))
return NULL;
+ if (alpn[0])
+ if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ALPN,
+ alpn))
+ return NULL;
origin[0] = '/';
strncpy(&origin[1], path, sizeof(origin) - 2);
@@ -683,26 +844,68 @@ html_parser_cb(const hubbub_token *token, void *pw)
}
#endif
+#endif
+
+static char *
+lws_strdup(const char *s)
+{
+ char *d = lws_malloc(strlen(s) + 1, "strdup");
+
+ if (d)
+ strcpy(d, s);
+
+ return d;
+}
+
+void
+lws_client_stash_destroy(struct lws *wsi)
+{
+ if (!wsi || !wsi->stash)
+ return;
+
+ lws_free_set_NULL(wsi->stash->address);
+ lws_free_set_NULL(wsi->stash->path);
+ lws_free_set_NULL(wsi->stash->host);
+ lws_free_set_NULL(wsi->stash->origin);
+ lws_free_set_NULL(wsi->stash->protocol);
+ lws_free_set_NULL(wsi->stash->method);
+ lws_free_set_NULL(wsi->stash->iface);
+ lws_free_set_NULL(wsi->stash->alpn);
+
+ lws_free_set_NULL(wsi->stash);
+}
+
LWS_VISIBLE struct lws *
lws_client_connect_via_info(struct lws_client_connect_info *i)
{
struct lws *wsi;
- int v = SPEC_LATEST_SUPPORTED;
const struct lws_protocols *p;
+ const char *local = i->protocol;
if (i->context->requested_kill)
return NULL;
if (!i->context->protocol_init_done)
lws_protocol_init(i->context);
+ /*
+ * If we have .local_protocol_name, use it to select the
+ * local protocol handler to bind to. Otherwise use .protocol if
+ * http[s].
+ */
+ if (i->local_protocol_name)
+ local = i->local_protocol_name;
wsi = lws_zalloc(sizeof(struct lws), "client wsi");
if (wsi == NULL)
goto bail;
wsi->context = i->context;
+#if defined(LWS_ROLE_H1)
/* assert the mode and union status (hdr) clearly */
- lws_union_transition(wsi, LWSCM_HTTP_CLIENT);
+ lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1);
+#else
+ lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_raw_skt);
+#endif
wsi->desc.sockfd = LWS_SOCK_INVALID;
/* 1) fill up the wsi with stuff from the connect_info as far as it
@@ -710,26 +913,52 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
* not even be able to get ahold of an ah at this point.
*/
- /* -1 means just use latest supported */
- if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one)
- v = i->ietf_version_or_minus_one;
+ if (!i->method) /* ie, ws */
+#if defined(LWS_ROLE_WS)
+ if (lws_create_client_ws_object(i, wsi))
+ return NULL;
+#else
+ return NULL;
+#endif
- wsi->ietf_spec_revision = v;
wsi->user_space = NULL;
- wsi->state = LWSS_CLIENT_UNCONNECTED;
wsi->pending_timeout = NO_PENDING_TIMEOUT;
- wsi->position_in_fds_table = -1;
+ wsi->position_in_fds_table = LWS_NO_FDS_POS;
wsi->c_port = i->port;
wsi->vhost = i->vhost;
if (!wsi->vhost)
wsi->vhost = i->context->vhost_list;
+ if (!wsi->vhost) {
+ lwsl_err("At least one vhost in the context is required\n");
+
+ goto bail;
+ }
+
wsi->protocol = &wsi->vhost->protocols[0];
+ wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE);
- /* for http[s] connection, allow protocol selection by name */
+ /* reasonable place to start */
+ lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
+#if defined(LWS_ROLE_H1)
+ &role_ops_h1);
+#else
+ &role_ops_raw_skt);
+#endif
- if (i->method && i->vhost && i->protocol) {
- p = lws_vhost_name_to_protocol(i->vhost, i->protocol);
+ /*
+ * 1) for http[s] connection, allow protocol selection by name
+ * 2) for ws[s], if local_protocol_name given also use it for
+ * local protocol binding... this defeats the server
+ * protocol negotiation if so
+ *
+ * Otherwise leave at protocols[0]... the server will tell us
+ * which protocol we are associated with since we can give it a
+ * list.
+ */
+ if (/*(i->method || i->local_protocol_name) && */local) {
+ lwsl_info("binding to %s\n", local);
+ p = lws_vhost_name_to_protocol(wsi->vhost, local);
if (p)
wsi->protocol = p;
}
@@ -745,10 +974,10 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
if (lws_ensure_user_space(wsi))
goto bail;
-#ifdef LWS_OPENSSL_SUPPORT
- wsi->use_ssl = i->ssl_connection;
+#if defined(LWS_WITH_TLS)
+ wsi->tls.use_ssl = i->ssl_connection;
#else
- if (i->ssl_connection) {
+ if (i->ssl_connection & LCCSCF_USE_SSL) {
lwsl_err("libwebsockets not configured for ssl\n");
goto bail;
}
@@ -760,47 +989,63 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
* things pointed to have gone out of scope.
*/
- wsi->u.hdr.stash = lws_malloc(sizeof(*wsi->u.hdr.stash), "client stash");
- if (!wsi->u.hdr.stash) {
+ wsi->stash = lws_zalloc(sizeof(*wsi->stash), "client stash");
+ if (!wsi->stash) {
lwsl_err("%s: OOM\n", __func__);
- goto bail;
+ goto bail1;
}
- wsi->u.hdr.stash->origin[0] = '\0';
- wsi->u.hdr.stash->protocol[0] = '\0';
- wsi->u.hdr.stash->method[0] = '\0';
- wsi->u.hdr.stash->iface[0] = '\0';
-
- strncpy(wsi->u.hdr.stash->address, i->address,
- sizeof(wsi->u.hdr.stash->address) - 1);
- strncpy(wsi->u.hdr.stash->path, i->path,
- sizeof(wsi->u.hdr.stash->path) - 1);
- strncpy(wsi->u.hdr.stash->host, i->host,
- sizeof(wsi->u.hdr.stash->host) - 1);
- if (i->origin)
- strncpy(wsi->u.hdr.stash->origin, i->origin,
- sizeof(wsi->u.hdr.stash->origin) - 1);
- if (i->protocol)
- strncpy(wsi->u.hdr.stash->protocol, i->protocol,
- sizeof(wsi->u.hdr.stash->protocol) - 1);
- if (i->method)
- strncpy(wsi->u.hdr.stash->method, i->method,
- sizeof(wsi->u.hdr.stash->method) - 1);
- if (i->iface)
- strncpy(wsi->u.hdr.stash->iface, i->iface,
- sizeof(wsi->u.hdr.stash->iface) - 1);
-
- wsi->u.hdr.stash->address[sizeof(wsi->u.hdr.stash->address) - 1] = '\0';
- wsi->u.hdr.stash->path[sizeof(wsi->u.hdr.stash->path) - 1] = '\0';
- wsi->u.hdr.stash->host[sizeof(wsi->u.hdr.stash->host) - 1] = '\0';
- wsi->u.hdr.stash->origin[sizeof(wsi->u.hdr.stash->origin) - 1] = '\0';
- wsi->u.hdr.stash->protocol[sizeof(wsi->u.hdr.stash->protocol) - 1] = '\0';
- wsi->u.hdr.stash->method[sizeof(wsi->u.hdr.stash->method) - 1] = '\0';
- wsi->u.hdr.stash->iface[sizeof(wsi->u.hdr.stash->iface) - 1] = '\0';
+ wsi->stash->address = lws_strdup(i->address);
+ wsi->stash->path = lws_strdup(i->path);
+ wsi->stash->host = lws_strdup(i->host);
+
+ if (!wsi->stash->address || !wsi->stash->path || !wsi->stash->host)
+ goto bail1;
+
+ if (i->origin) {
+ wsi->stash->origin = lws_strdup(i->origin);
+ if (!wsi->stash->origin)
+ goto bail1;
+ }
+ if (i->protocol) {
+ wsi->stash->protocol = lws_strdup(i->protocol);
+ if (!wsi->stash->protocol)
+ goto bail1;
+ }
+ if (i->method) {
+ wsi->stash->method = lws_strdup(i->method);
+ if (!wsi->stash->method)
+ goto bail1;
+ }
+ if (i->iface) {
+ wsi->stash->iface = lws_strdup(i->iface);
+ if (!wsi->stash->iface)
+ goto bail1;
+ }
+ /*
+ * For ws, default to http/1.1 only. If i->alpn is set, defer to
+ * whatever he has set in there (eg, "h2").
+ *
+ * The problem is he has to commit to h2 before he can find out if the
+ * server has the SETTINGS for ws-over-h2 enabled; if not then ws is
+ * not possible on that connection. So we only try it if he
+ * assertively said to use h2 alpn.
+ */
+ if (!i->method && !i->alpn) {
+ wsi->stash->alpn = lws_strdup("http/1.1");
+ if (!wsi->stash->alpn)
+ goto bail1;
+ } else
+ if (i->alpn) {
+ wsi->stash->alpn = lws_strdup(i->alpn);
+ if (!wsi->stash->alpn)
+ goto bail1;
+ }
if (i->pwsi)
*i->pwsi = wsi;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
/* if we went on the waiting list, no probs just return the wsi
* when we get the ah, now or later, he will call
* lws_client_connect_via_info2() below.
@@ -810,9 +1055,11 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
* if we failed here, the connection is already closed
* and freed.
*/
- goto bail1;
+ goto bail2;
}
+#endif
+
if (i->parent_wsi) {
lwsl_info("%s: created child %p of parent %p\n", __func__,
wsi, i->parent_wsi);
@@ -822,17 +1069,21 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
}
#ifdef LWS_WITH_HTTP_PROXY
if (i->uri_replace_to)
- wsi->rw = lws_rewrite_create(wsi, html_parser_cb,
+ wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb,
i->uri_replace_from,
i->uri_replace_to);
#endif
return wsi;
+bail1:
+ lws_client_stash_destroy(wsi);
+
bail:
lws_free(wsi);
-
-bail1:
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+bail2:
+#endif
if (i->pwsi)
*i->pwsi = NULL;
@@ -842,28 +1093,27 @@ bail1:
struct lws *
lws_client_connect_via_info2(struct lws *wsi)
{
- struct client_info_stash *stash = wsi->u.hdr.stash;
+ struct client_info_stash *stash = wsi->stash;
if (!stash)
return wsi;
/*
* we're not necessarily in a position to action these right away,
- * stash them... we only need during connect phase so u.hdr is fine
+ * stash them... we only need during connect phase so into a temp
+ * allocated stash
*/
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
stash->address))
goto bail1;
- /* these only need u.hdr lifetime as well */
-
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path))
goto bail1;
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host))
goto bail1;
- if (stash->origin[0])
+ if (stash->origin)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN,
stash->origin))
goto bail1;
@@ -871,45 +1121,28 @@ lws_client_connect_via_info2(struct lws *wsi)
* this is a list of protocols we tell the server we're okay with
* stash it for later when we compare server response with it
*/
- if (stash->protocol[0])
+ if (stash->protocol)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
stash->protocol))
goto bail1;
- if (stash->method[0])
+ if (stash->method)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD,
stash->method))
goto bail1;
- if (stash->iface[0])
+ if (stash->iface)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE,
stash->iface))
goto bail1;
+ if (stash->alpn)
+ if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ALPN,
+ stash->alpn))
+ goto bail1;
#if defined(LWS_WITH_SOCKS5)
if (!wsi->vhost->socks_proxy_port)
- lws_free_set_NULL(wsi->u.hdr.stash);
+ lws_client_stash_destroy(wsi);
#endif
- /*
- * Check with each extension if it is able to route and proxy this
- * connection for us. For example, an extension like x-google-mux
- * can handle this and then we don't need an actual socket for this
- * connection.
- */
-
- if (lws_ext_cb_all_exts(wsi->context, wsi,
- LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION,
- (void *)stash->address,
- wsi->c_port) > 0) {
- lwsl_client("lws_client_connect: ext handling conn\n");
-
- lws_set_timeout(wsi,
- PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE,
- AWAITING_TIMEOUT);
-
- wsi->mode = LWSCM_WSCL_WAITING_EXTENSION_CONNECT;
- return wsi;
- }
- lwsl_client("lws_client_connect: direct conn\n");
wsi->context->count_wsi_allocated++;
return lws_client_connect_2(wsi);
@@ -917,7 +1150,7 @@ lws_client_connect_via_info2(struct lws *wsi)
bail1:
#if defined(LWS_WITH_SOCKS5)
if (!wsi->vhost->socks_proxy_port)
- lws_free_set_NULL(wsi->u.hdr.stash);
+ lws_free_set_NULL(wsi->stash);
#endif
return NULL;
@@ -1003,14 +1236,14 @@ void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
/* length of the user name */
pt->serv_buf[len++] = n;
/* user name */
- strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user,
- context->pt_serv_buf_size - len);
+ lws_strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user,
+ context->pt_serv_buf_size - len + 1);
len += n;
/* length of the password */
pt->serv_buf[len++] = passwd_len;
/* password */
- strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password,
- context->pt_serv_buf_size - len);
+ lws_strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password,
+ context->pt_serv_buf_size - len + 1);
len += passwd_len;
break;
@@ -1029,9 +1262,9 @@ void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
n = len++;
/* the address we tell SOCKS proxy to connect to */
- strncpy((char *)&(pt->serv_buf[len]), wsi->u.hdr.stash->address,
- context->pt_serv_buf_size - len);
- len += strlen(wsi->u.hdr.stash->address);
+ lws_strncpy((char *)&(pt->serv_buf[len]), wsi->stash->address,
+ context->pt_serv_buf_size - len + 1);
+ len += strlen(wsi->stash->address);
net_num = htons(wsi->c_port);
/* the port we tell SOCKS proxy to connect to */
@@ -1039,7 +1272,7 @@ void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
pt->serv_buf[len++] = p[1];
/* the length of the address, excluding port */
- pt->serv_buf[n] = strlen(wsi->u.hdr.stash->address);
+ pt->serv_buf[n] = strlen(wsi->stash->address);
break;
default:
diff --git a/thirdparty/libwebsockets/roles/http/client/client.c b/thirdparty/libwebsockets/roles/http/client/client.c
new file mode 100644
index 0000000000..ce42dc6cd3
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/http/client/client.c
@@ -0,0 +1,1231 @@
+/*
+ * libwebsockets - lib/client/client.c
+ *
+ * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+
+LWS_VISIBLE LWS_EXTERN void
+lws_client_http_body_pending(struct lws *wsi, int something_left_to_send)
+{
+ wsi->client_http_body_pending = !!something_left_to_send;
+}
+
+/*
+ * return self, or queued client wsi we are acting on behalf of
+ *
+ * That is the TAIL of the queue (new queue elements are added at the HEAD)
+ */
+
+struct lws *
+lws_client_wsi_effective(struct lws *wsi)
+{
+ struct lws_dll_lws *tail = NULL;
+
+ if (!wsi->transaction_from_pipeline_queue ||
+ !wsi->dll_client_transaction_queue_head.next)
+ return wsi;
+
+ lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+ wsi->dll_client_transaction_queue_head.next) {
+ tail = d;
+ } lws_end_foreach_dll_safe(d, d1);
+
+ return lws_container_of(tail, struct lws,
+ dll_client_transaction_queue);
+}
+
+/*
+ * return self or the guy we are queued under
+ *
+ * REQUIRES VHOST LOCK HELD
+ */
+
+static struct lws *
+_lws_client_wsi_master(struct lws *wsi)
+{
+ struct lws *wsi_eff = wsi;
+ struct lws_dll_lws *d;
+
+ d = wsi->dll_client_transaction_queue.prev;
+ while (d) {
+ wsi_eff = lws_container_of(d, struct lws,
+ dll_client_transaction_queue_head);
+
+ d = d->prev;
+ }
+
+ return wsi_eff;
+}
+
+int
+lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
+ struct lws *wsi_conn)
+{
+ struct lws_context *context = wsi->context;
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ char *p = (char *)&pt->serv_buf[0];
+ struct lws *w;
+#if defined(LWS_WITH_TLS)
+ char ebuf[128];
+#endif
+ const char *cce = NULL;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ ssize_t len = 0;
+ unsigned char c;
+#endif
+ char *sb = p;
+ int n = 0;
+#if defined(LWS_WITH_SOCKS5)
+ char conn_mode = 0, pending_timeout = 0;
+#endif
+
+ if ((pollfd->revents & LWS_POLLOUT) &&
+ wsi->keepalive_active &&
+ wsi->dll_client_transaction_queue_head.next) {
+ struct lws *wfound = NULL;
+
+ lwsl_debug("%s: pollout HANDSHAKE2\n", __func__);
+
+ /*
+ * We have a transaction queued that wants to pipeline.
+ *
+ * We have to allow it to send headers strictly in the order
+ * that it was queued, ie, tail-first.
+ */
+ lws_vhost_lock(wsi->vhost);
+ lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+ wsi->dll_client_transaction_queue_head.next) {
+ struct lws *w = lws_container_of(d, struct lws,
+ dll_client_transaction_queue);
+
+ lwsl_debug("%s: %p states 0x%x\n", __func__, w, w->wsistate);
+ if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2)
+ wfound = w;
+ } lws_end_foreach_dll_safe(d, d1);
+
+ if (wfound) {
+ /*
+ * pollfd has the master sockfd in it... we
+ * need to use that in HANDSHAKE2 to understand
+ * which wsi to actually write on
+ */
+ lws_client_socket_service(wfound, pollfd, wsi);
+ lws_callback_on_writable(wsi);
+ } else
+ lwsl_debug("%s: didn't find anything in txn q in HS2\n",
+ __func__);
+
+ lws_vhost_unlock(wsi->vhost);
+
+ return 0;
+ }
+
+ switch (lwsi_state(wsi)) {
+
+ case LRS_WAITING_CONNECT:
+
+ /*
+ * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
+ * timeout protection set in client-handshake.c
+ */
+
+ if (!lws_client_connect_2(wsi)) {
+ /* closed */
+ lwsl_client("closed\n");
+ return -1;
+ }
+
+ /* either still pending connection, or changed mode */
+ return 0;
+
+#if defined(LWS_WITH_SOCKS5)
+ /* SOCKS Greeting Reply */
+ case LRS_WAITING_SOCKS_GREETING_REPLY:
+ case LRS_WAITING_SOCKS_AUTH_REPLY:
+ case LRS_WAITING_SOCKS_CONNECT_REPLY:
+
+ /* handle proxy hung up on us */
+
+ if (pollfd->revents & LWS_POLLHUP) {
+ lwsl_warn("SOCKS connection %p (fd=%d) dead\n",
+ (void *)wsi, pollfd->fd);
+ goto bail3;
+ }
+
+ n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
+ if (n < 0) {
+ if (LWS_ERRNO == LWS_EAGAIN) {
+ lwsl_debug("SOCKS read EAGAIN, retrying\n");
+ return 0;
+ }
+ lwsl_err("ERROR reading from SOCKS socket\n");
+ goto bail3;
+ }
+
+ switch (lwsi_state(wsi)) {
+
+ case LRS_WAITING_SOCKS_GREETING_REPLY:
+ if (pt->serv_buf[0] != SOCKS_VERSION_5)
+ goto socks_reply_fail;
+
+ if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) {
+ lwsl_client("SOCKS GR: No Auth Method\n");
+ socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
+ conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
+ pending_timeout =
+ PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
+ goto socks_send;
+ }
+
+ if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) {
+ lwsl_client("SOCKS GR: User/Pw Method\n");
+ socks_generate_msg(wsi,
+ SOCKS_MSG_USERNAME_PASSWORD,
+ &len);
+ conn_mode = LRS_WAITING_SOCKS_AUTH_REPLY;
+ pending_timeout =
+ PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY;
+ goto socks_send;
+ }
+ goto socks_reply_fail;
+
+ case LRS_WAITING_SOCKS_AUTH_REPLY:
+ if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 ||
+ pt->serv_buf[1] != SOCKS_SUBNEGOTIATION_STATUS_SUCCESS)
+ goto socks_reply_fail;
+
+ lwsl_client("SOCKS password OK, sending connect\n");
+ socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
+ conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
+ pending_timeout =
+ PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
+socks_send:
+ n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len,
+ MSG_NOSIGNAL);
+ if (n < 0) {
+ lwsl_debug("ERROR writing to socks proxy\n");
+ goto bail3;
+ }
+
+ lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT);
+ lwsi_set_state(wsi, conn_mode);
+ break;
+
+socks_reply_fail:
+ lwsl_notice("socks reply: v%d, err %d\n",
+ pt->serv_buf[0], pt->serv_buf[1]);
+ goto bail3;
+
+ case LRS_WAITING_SOCKS_CONNECT_REPLY:
+ if (pt->serv_buf[0] != SOCKS_VERSION_5 ||
+ pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS)
+ goto socks_reply_fail;
+
+ lwsl_client("socks connect OK\n");
+
+ /* free stash since we are done with it */
+ lws_client_stash_destroy(wsi);
+ if (lws_hdr_simple_create(wsi,
+ _WSI_TOKEN_CLIENT_PEER_ADDRESS,
+ wsi->vhost->socks_proxy_address))
+ goto bail3;
+
+ wsi->c_port = wsi->vhost->socks_proxy_port;
+
+ /* clear his proxy connection timeout */
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+ goto start_ws_handshake;
+ }
+ break;
+#endif
+
+ case LRS_WAITING_PROXY_REPLY:
+
+ /* handle proxy hung up on us */
+
+ if (pollfd->revents & LWS_POLLHUP) {
+
+ lwsl_warn("Proxy connection %p (fd=%d) dead\n",
+ (void *)wsi, pollfd->fd);
+
+ goto bail3;
+ }
+
+ n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
+ if (n < 0) {
+ if (LWS_ERRNO == LWS_EAGAIN) {
+ lwsl_debug("Proxy read EAGAIN... retrying\n");
+ return 0;
+ }
+ lwsl_err("ERROR reading from proxy socket\n");
+ goto bail3;
+ }
+
+ pt->serv_buf[13] = '\0';
+ if (strcmp(sb, "HTTP/1.0 200 ") &&
+ strcmp(sb, "HTTP/1.1 200 ")) {
+ lwsl_err("ERROR proxy: %s\n", sb);
+ goto bail3;
+ }
+
+ /* clear his proxy connection timeout */
+
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+ /* fallthru */
+
+ case LRS_H1C_ISSUE_HANDSHAKE:
+
+ /*
+ * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
+ * timeout protection set in client-handshake.c
+ *
+ * take care of our lws_callback_on_writable
+ * happening at a time when there's no real connection yet
+ */
+#if defined(LWS_WITH_SOCKS5)
+start_ws_handshake:
+#endif
+ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
+ return -1;
+
+#if defined(LWS_WITH_TLS)
+ /* we can retry this... just cook the SSL BIO the first time */
+
+ if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && !wsi->tls.ssl &&
+ lws_ssl_client_bio_create(wsi) < 0) {
+ cce = "bio_create failed";
+ goto bail3;
+ }
+
+ if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
+ n = lws_ssl_client_connect1(wsi);
+ if (!n)
+ return 0;
+ if (n < 0) {
+ cce = "lws_ssl_client_connect1 failed";
+ goto bail3;
+ }
+ } else
+ wsi->tls.ssl = NULL;
+
+ /* fallthru */
+
+ case LRS_WAITING_SSL:
+
+ if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
+ n = lws_ssl_client_connect2(wsi, ebuf, sizeof(ebuf));
+ if (!n)
+ return 0;
+ if (n < 0) {
+ cce = ebuf;
+ goto bail3;
+ }
+ } else
+ wsi->tls.ssl = NULL;
+#endif
+#if defined (LWS_WITH_HTTP2)
+ if (wsi->client_h2_alpn) {
+ /*
+ * We connected to the server and set up tls, and
+ * negotiated "h2".
+ *
+ * So this is it, we are an h2 master client connection
+ * now, not an h1 client connection.
+ */
+ lws_tls_server_conn_alpn(wsi);
+
+ /* send the H2 preface to legitimize the connection */
+ if (lws_h2_issue_preface(wsi)) {
+ cce = "error sending h2 preface";
+ goto bail3;
+ }
+
+ break;
+ }
+#endif
+ lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
+ lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
+ context->timeout_secs);
+
+ /* fallthru */
+
+ case LRS_H1C_ISSUE_HANDSHAKE2:
+ p = lws_generate_client_handshake(wsi, p);
+ if (p == NULL) {
+ if (wsi->role_ops == &role_ops_raw_skt ||
+ wsi->role_ops == &role_ops_raw_file)
+ return 0;
+
+ lwsl_err("Failed to generate handshake for client\n");
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "chs");
+ return 0;
+ }
+
+ /* send our request to the server */
+ lws_latency_pre(context, wsi);
+
+ w = _lws_client_wsi_master(wsi);
+ lwsl_info("%s: HANDSHAKE2: %p: sending headers on %p (wsistate 0x%x 0x%x)\n",
+ __func__, wsi, w, wsi->wsistate, w->wsistate);
+
+ n = lws_ssl_capable_write(w, (unsigned char *)sb, (int)(p - sb));
+ lws_latency(context, wsi, "send lws_issue_raw", n,
+ n == p - sb);
+ switch (n) {
+ case LWS_SSL_CAPABLE_ERROR:
+ lwsl_debug("ERROR writing to client socket\n");
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cws");
+ return 0;
+ case LWS_SSL_CAPABLE_MORE_SERVICE:
+ lws_callback_on_writable(wsi);
+ break;
+ }
+
+ if (wsi->client_http_body_pending) {
+ lwsi_set_state(wsi, LRS_ISSUE_HTTP_BODY);
+ lws_set_timeout(wsi,
+ PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
+ context->timeout_secs);
+ /* user code must ask for writable callback */
+ break;
+ }
+
+ lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
+ wsi->hdr_parsing_completed = 0;
+
+ if (lwsi_state(w) == LRS_IDLING) {
+ lwsi_set_state(w, LRS_WAITING_SERVER_REPLY);
+ w->hdr_parsing_completed = 0;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ w->http.ah->parser_state = WSI_TOKEN_NAME_PART;
+ w->http.ah->lextable_pos = 0;
+ /* If we're (re)starting on headers, need other implied init */
+ wsi->http.ah->ues = URIES_IDLE;
+#endif
+ }
+
+ lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
+ wsi->context->timeout_secs);
+
+ lws_callback_on_writable(w);
+
+ goto client_http_body_sent;
+
+ case LRS_ISSUE_HTTP_BODY:
+ if (wsi->client_http_body_pending) {
+ //lws_set_timeout(wsi,
+ // PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
+ // context->timeout_secs);
+ /* user code must ask for writable callback */
+ break;
+ }
+client_http_body_sent:
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ /* prepare ourselves to do the parsing */
+ wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
+ wsi->http.ah->lextable_pos = 0;
+#endif
+ lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
+ lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
+ context->timeout_secs);
+ break;
+
+ case LRS_WAITING_SERVER_REPLY:
+ /*
+ * handle server hanging up on us...
+ * but if there is POLLIN waiting, handle that first
+ */
+ if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) ==
+ LWS_POLLHUP) {
+
+ lwsl_debug("Server connection %p (fd=%d) dead\n",
+ (void *)wsi, pollfd->fd);
+ cce = "Peer hung up";
+ goto bail3;
+ }
+
+ if (!(pollfd->revents & LWS_POLLIN))
+ break;
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ /* interpret the server response
+ *
+ * HTTP/1.1 101 Switching Protocols
+ * Upgrade: websocket
+ * Connection: Upgrade
+ * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
+ * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
+ * Sec-WebSocket-Protocol: chat
+ *
+ * we have to take some care here to only take from the
+ * socket bytewise. The browser may (and has been seen to
+ * in the case that onopen() performs websocket traffic)
+ * coalesce both handshake response and websocket traffic
+ * in one packet, since at that point the connection is
+ * definitively ready from browser pov.
+ */
+ len = 1;
+ while (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE &&
+ len > 0) {
+ int plen = 1;
+
+ n = lws_ssl_capable_read(wsi, &c, 1);
+ lws_latency(context, wsi, "send lws_issue_raw", n,
+ n == 1);
+ switch (n) {
+ case 0:
+ case LWS_SSL_CAPABLE_ERROR:
+ cce = "read failed";
+ goto bail3;
+ case LWS_SSL_CAPABLE_MORE_SERVICE:
+ return 0;
+ }
+
+ if (lws_parse(wsi, &c, &plen)) {
+ lwsl_warn("problems parsing header\n");
+ goto bail3;
+ }
+ }
+
+ /*
+ * hs may also be coming in multiple packets, there is a 5-sec
+ * libwebsocket timeout still active here too, so if parsing did
+ * not complete just wait for next packet coming in this state
+ */
+ if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
+ break;
+
+#endif
+
+ /*
+ * otherwise deal with the handshake. If there's any
+ * packet traffic already arrived we'll trigger poll() again
+ * right away and deal with it that way
+ */
+ return lws_client_interpret_server_handshake(wsi);
+
+bail3:
+ lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n");
+ if (cce)
+ lwsl_info("reason: %s\n", cce);
+ wsi->protocol->callback(wsi,
+ LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+ wsi->user_space, (void *)cce, cce ? strlen(cce) : 0);
+ wsi->already_did_cce = 1;
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cbail3");
+ return -1;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+
+int LWS_WARN_UNUSED_RESULT
+lws_http_transaction_completed_client(struct lws *wsi)
+{
+ struct lws *wsi_eff = lws_client_wsi_effective(wsi);
+
+ lwsl_info("%s: wsi: %p, wsi_eff: %p\n", __func__, wsi, wsi_eff);
+
+ if (user_callback_handle_rxflow(wsi_eff->protocol->callback,
+ wsi_eff, LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
+ wsi_eff->user_space, NULL, 0)) {
+ lwsl_debug("%s: Completed call returned nonzero (role 0x%x)\n",
+ __func__, lwsi_role(wsi_eff));
+ return -1;
+ }
+
+ /*
+ * Are we constitutionally capable of having a queue, ie, we are on
+ * the "active client connections" list?
+ *
+ * If not, that's it for us.
+ */
+
+ if (lws_dll_is_null(&wsi->dll_active_client_conns))
+ return -1;
+
+ /* if this was a queued guy, close him and remove from queue */
+
+ if (wsi->transaction_from_pipeline_queue) {
+ lwsl_debug("closing queued wsi %p\n", wsi_eff);
+ /* so the close doesn't trigger a CCE */
+ wsi_eff->already_did_cce = 1;
+ __lws_close_free_wsi(wsi_eff,
+ LWS_CLOSE_STATUS_CLIENT_TRANSACTION_DONE,
+ "queued client done");
+ }
+
+ /* after the first one, they can only be coming from the queue */
+ wsi->transaction_from_pipeline_queue = 1;
+
+ wsi->http.rx_content_length = 0;
+ wsi->hdr_parsing_completed = 0;
+
+ /* is there a new tail after removing that one? */
+ wsi_eff = lws_client_wsi_effective(wsi);
+
+ /*
+ * Do we have something pipelined waiting?
+ * it's OK if he hasn't managed to send his headers yet... he's next
+ * in line to do that...
+ */
+ if (wsi_eff == wsi) {
+ /*
+ * Nothing pipelined... we should hang around a bit
+ * in case something turns up...
+ */
+ lwsl_info("%s: nothing pipelined waiting\n", __func__);
+ lwsi_set_state(wsi, LRS_IDLING);
+
+ lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_CONN_IDLE, 5);
+
+ return 0;
+ }
+
+ /*
+ * H1: we can serialize the queued guys into the same ah
+ * H2: everybody needs their own ah until their own STREAM_END
+ */
+
+ /* otherwise set ourselves up ready to go again */
+ lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
+
+ wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
+ wsi->http.ah->lextable_pos = 0;
+
+ lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
+ wsi->context->timeout_secs);
+
+ /* If we're (re)starting on headers, need other implied init */
+ wsi->http.ah->ues = URIES_IDLE;
+
+ lwsl_info("%s: %p: new queued transaction as %p\n", __func__, wsi, wsi_eff);
+ lws_callback_on_writable(wsi);
+
+ return 0;
+}
+
+LWS_VISIBLE LWS_EXTERN unsigned int
+lws_http_client_http_response(struct lws *wsi)
+{
+ if (!wsi->http.ah)
+ return 0;
+
+ return wsi->http.ah->http_response;
+}
+#endif
+#if defined(LWS_PLAT_OPTEE)
+char *
+strrchr(const char *s, int c)
+{
+ char *hit = NULL;
+
+ while (*s)
+ if (*(s++) == (char)c)
+ hit = (char *)s - 1;
+
+ return hit;
+}
+
+#define atoll atoi
+#endif
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+int
+lws_client_interpret_server_handshake(struct lws *wsi)
+{
+ int n, port = 0, ssl = 0;
+ int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
+ const char *prot, *ads = NULL, *path, *cce = NULL;
+ struct allocated_headers *ah = NULL;
+ struct lws *w = lws_client_wsi_effective(wsi);
+ char *p, *q;
+ char new_path[300];
+
+ lws_client_stash_destroy(wsi);
+
+ ah = wsi->http.ah;
+ if (!wsi->do_ws) {
+ /* we are being an http client...
+ */
+#if defined(LWS_ROLE_H2)
+ if (wsi->client_h2_alpn || wsi->client_h2_substream) {
+ lwsl_debug("%s: %p: transitioning to h2 client\n", __func__, wsi);
+ lws_role_transition(wsi, LWSIFR_CLIENT,
+ LRS_ESTABLISHED, &role_ops_h2);
+ } else
+#endif
+ {
+#if defined(LWS_ROLE_H1)
+ {
+ lwsl_debug("%s: %p: transitioning to h1 client\n", __func__, wsi);
+ lws_role_transition(wsi, LWSIFR_CLIENT,
+ LRS_ESTABLISHED, &role_ops_h1);
+ }
+#else
+ return -1;
+#endif
+ }
+
+ wsi->http.ah = ah;
+ ah->http_response = 0;
+ }
+
+ /*
+ * well, what the server sent looked reasonable for syntax.
+ * Now let's confirm it sent all the necessary headers
+ *
+ * http (non-ws) client will expect something like this
+ *
+ * HTTP/1.0.200
+ * server:.libwebsockets
+ * content-type:.text/html
+ * content-length:.17703
+ * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000
+ */
+
+ wsi->http.connection_type = HTTP_CONNECTION_KEEP_ALIVE;
+ if (!wsi->client_h2_substream) {
+ p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
+ if (wsi->do_ws && !p) {
+ lwsl_info("no URI\n");
+ cce = "HS: URI missing";
+ goto bail3;
+ }
+ if (!p) {
+ p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0);
+ wsi->http.connection_type = HTTP_CONNECTION_CLOSE;
+ }
+ if (!p) {
+ cce = "HS: URI missing";
+ lwsl_info("no URI\n");
+ goto bail3;
+ }
+ } else {
+ p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_STATUS);
+ if (!p) {
+ cce = "HS: :status missing";
+ lwsl_info("no status\n");
+ goto bail3;
+ }
+ }
+ n = atoi(p);
+ if (ah)
+ ah->http_response = n;
+
+ if (n == 301 || n == 302 || n == 303 || n == 307 || n == 308) {
+ p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION);
+ if (!p) {
+ cce = "HS: Redirect code but no Location";
+ goto bail3;
+ }
+
+ /* Relative reference absolute path */
+ if (p[0] == '/') {
+#if defined(LWS_WITH_TLS)
+ ssl = wsi->tls.use_ssl & LCCSCF_USE_SSL;
+#endif
+ ads = lws_hdr_simple_ptr(wsi,
+ _WSI_TOKEN_CLIENT_PEER_ADDRESS);
+ port = wsi->c_port;
+ /* +1 as lws_client_reset expects leading / omitted */
+ path = p + 1;
+ }
+ /* Absolute (Full) URI */
+ else if (strchr(p, ':')) {
+ if (lws_parse_uri(p, &prot, &ads, &port, &path)) {
+ cce = "HS: URI did not parse";
+ goto bail3;
+ }
+
+ if (!strcmp(prot, "wss") || !strcmp(prot, "https"))
+ ssl = 1;
+ }
+ /* Relative reference relative path */
+ else {
+ /* This doesn't try to calculate an absolute path,
+ * that will be left to the server */
+#if defined(LWS_WITH_TLS)
+ ssl = wsi->tls.use_ssl & LCCSCF_USE_SSL;
+#endif
+ ads = lws_hdr_simple_ptr(wsi,
+ _WSI_TOKEN_CLIENT_PEER_ADDRESS);
+ port = wsi->c_port;
+ /* +1 as lws_client_reset expects leading / omitted */
+ path = new_path + 1;
+ lws_strncpy(new_path, lws_hdr_simple_ptr(wsi,
+ _WSI_TOKEN_CLIENT_URI), sizeof(new_path));
+ q = strrchr(new_path, '/');
+ if (q)
+ lws_strncpy(q + 1, p, sizeof(new_path) -
+ (q - new_path));
+ else
+ path = p;
+ }
+
+#if defined(LWS_WITH_TLS)
+ if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && !ssl) {
+ cce = "HS: Redirect attempted SSL downgrade";
+ goto bail3;
+ }
+#endif
+
+ if (!lws_client_reset(&wsi, ssl, ads, port, path, ads)) {
+ /* there are two ways to fail out with NULL return...
+ * simple, early problem where the wsi is intact, or
+ * we went through with the reconnect attempt and the
+ * wsi is already closed. In the latter case, the wsi
+ * has beet set to NULL additionally.
+ */
+ lwsl_err("Redirect failed\n");
+ cce = "HS: Redirect failed";
+ if (wsi)
+ goto bail3;
+
+ return 1;
+ }
+ return 0;
+ }
+
+ if (!wsi->do_ws) {
+
+ /* if h1 KA is allowed, enable the queued pipeline guys */
+
+ if (!wsi->client_h2_alpn && !wsi->client_h2_substream && w == wsi) { /* ie, coming to this for the first time */
+ if (wsi->http.connection_type == HTTP_CONNECTION_KEEP_ALIVE)
+ wsi->keepalive_active = 1;
+ else {
+ /*
+ * Ugh... now the main http connection has seen
+ * both sides, we learn the server doesn't
+ * support keepalive.
+ *
+ * That means any guys queued on us are going
+ * to have to be restarted from connect2 with
+ * their own connections.
+ */
+
+ /*
+ * stick around telling any new guys they can't
+ * pipeline to this server
+ */
+ wsi->keepalive_rejected = 1;
+
+ lws_vhost_lock(wsi->vhost);
+ lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+ wsi->dll_client_transaction_queue_head.next) {
+ struct lws *ww = lws_container_of(d, struct lws,
+ dll_client_transaction_queue);
+
+ /* remove him from our queue */
+ lws_dll_lws_remove(&ww->dll_client_transaction_queue);
+ /* give up on pipelining */
+ ww->client_pipeline = 0;
+
+ /* go back to "trying to connect" state */
+ lws_role_transition(ww, LWSIFR_CLIENT,
+ LRS_UNCONNECTED,
+#if defined(LWS_ROLE_H1)
+ &role_ops_h1);
+#else
+#if defined (LWS_ROLE_H2)
+ &role_ops_h2);
+#else
+ &role_ops_raw);
+#endif
+#endif
+ ww->user_space = NULL;
+ } lws_end_foreach_dll_safe(d, d1);
+ lws_vhost_unlock(wsi->vhost);
+ }
+ }
+
+#ifdef LWS_WITH_HTTP_PROXY
+ wsi->http.perform_rewrite = 0;
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
+ if (!strncmp(lws_hdr_simple_ptr(wsi,
+ WSI_TOKEN_HTTP_CONTENT_TYPE),
+ "text/html", 9))
+ wsi->http.perform_rewrite = 1;
+ }
+#endif
+
+ /* allocate the per-connection user memory (if any) */
+ if (lws_ensure_user_space(wsi)) {
+ lwsl_err("Problem allocating wsi user mem\n");
+ cce = "HS: OOM";
+ goto bail2;
+ }
+
+ /* he may choose to send us stuff in chunked transfer-coding */
+ wsi->chunked = 0;
+ wsi->chunk_remaining = 0; /* ie, next thing is chunk size */
+ if (lws_hdr_total_length(wsi,
+ WSI_TOKEN_HTTP_TRANSFER_ENCODING)) {
+ wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi,
+ WSI_TOKEN_HTTP_TRANSFER_ENCODING),
+ "chunked");
+ /* first thing is hex, after payload there is crlf */
+ wsi->chunk_parser = ELCP_HEX;
+ }
+
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
+ wsi->http.rx_content_length =
+ atoll(lws_hdr_simple_ptr(wsi,
+ WSI_TOKEN_HTTP_CONTENT_LENGTH));
+ lwsl_info("%s: incoming content length %llu\n",
+ __func__, (unsigned long long)
+ wsi->http.rx_content_length);
+ wsi->http.rx_content_remain =
+ wsi->http.rx_content_length;
+ } else /* can't do 1.1 without a content length or chunked */
+ if (!wsi->chunked)
+ wsi->http.connection_type =
+ HTTP_CONNECTION_CLOSE;
+
+ /*
+ * we seem to be good to go, give client last chance to check
+ * headers and OK it
+ */
+ if (wsi->protocol->callback(wsi,
+ LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
+ wsi->user_space, NULL, 0)) {
+
+ cce = "HS: disallowed by client filter";
+ goto bail2;
+ }
+
+ /* clear his proxy connection timeout */
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+ wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
+
+ /* call him back to inform him he is up */
+ if (wsi->protocol->callback(wsi,
+ LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
+ wsi->user_space, NULL, 0)) {
+ cce = "HS: disallowed at ESTABLISHED";
+ goto bail3;
+ }
+
+ /*
+ * for pipelining, master needs to keep his ah... guys who
+ * queued on him can drop it now though.
+ */
+
+ if (w != wsi)
+ /* free up parsing allocations for queued guy */
+ lws_header_table_detach(w, 0);
+
+ lwsl_info("%s: client connection up\n", __func__);
+
+ return 0;
+ }
+
+#if defined(LWS_ROLE_WS)
+ switch (lws_client_ws_upgrade(wsi, &cce)) {
+ case 2:
+ goto bail2;
+ case 3:
+ goto bail3;
+ }
+
+ return 0;
+#endif
+
+bail3:
+ close_reason = LWS_CLOSE_STATUS_NOSTATUS;
+
+bail2:
+ if (wsi->protocol) {
+ n = 0;
+ if (cce)
+ n = (int)strlen(cce);
+ wsi->protocol->callback(wsi,
+ LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+ wsi->user_space, (void *)cce,
+ (unsigned int)n);
+ }
+ wsi->already_did_cce = 1;
+
+ lwsl_info("closing connection due to bail2 connection error\n");
+
+ /* closing will free up his parsing allocations */
+ lws_close_free_wsi(wsi, close_reason, "c hs interp");
+
+ return 1;
+}
+#endif
+
+char *
+lws_generate_client_handshake(struct lws *wsi, char *pkt)
+{
+ char *p = pkt;
+ const char *meth;
+ const char *pp = lws_hdr_simple_ptr(wsi,
+ _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
+
+ meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
+ if (!meth) {
+ meth = "GET";
+ wsi->do_ws = 1;
+ } else {
+ wsi->do_ws = 0;
+ }
+
+ if (!strcmp(meth, "RAW")) {
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+ lwsl_notice("client transition to raw\n");
+
+ if (pp) {
+ const struct lws_protocols *pr;
+
+ pr = lws_vhost_name_to_protocol(wsi->vhost, pp);
+
+ if (!pr) {
+ lwsl_err("protocol %s not enabled on vhost\n",
+ pp);
+ return NULL;
+ }
+
+ lws_bind_protocol(wsi, pr);
+ }
+
+ if ((wsi->protocol->callback)(wsi, LWS_CALLBACK_RAW_ADOPT,
+ wsi->user_space, NULL, 0))
+ return NULL;
+
+ lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_raw_skt);
+ lws_header_table_detach(wsi, 1);
+
+ return NULL;
+ }
+
+ /*
+ * 04 example client handshake
+ *
+ * GET /chat HTTP/1.1
+ * Host: server.example.com
+ * Upgrade: websocket
+ * Connection: Upgrade
+ * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+ * Sec-WebSocket-Origin: http://example.com
+ * Sec-WebSocket-Protocol: chat, superchat
+ * Sec-WebSocket-Version: 4
+ */
+
+ p += sprintf(p, "%s %s HTTP/1.1\x0d\x0a", meth,
+ lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));
+
+ p += sprintf(p, "Pragma: no-cache\x0d\x0a"
+ "Cache-Control: no-cache\x0d\x0a");
+
+ p += sprintf(p, "Host: %s\x0d\x0a",
+ lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
+
+ if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) {
+ if (lws_check_opt(wsi->context->options,
+ LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN))
+ p += sprintf(p, "Origin: %s\x0d\x0a",
+ lws_hdr_simple_ptr(wsi,
+ _WSI_TOKEN_CLIENT_ORIGIN));
+ else
+ p += sprintf(p, "Origin: http://%s\x0d\x0a",
+ lws_hdr_simple_ptr(wsi,
+ _WSI_TOKEN_CLIENT_ORIGIN));
+ }
+#if defined(LWS_ROLE_WS)
+ if (wsi->do_ws)
+ p = lws_generate_client_ws_handshake(wsi, p);
+#endif
+
+ /* give userland a chance to append, eg, cookies */
+
+ if (wsi->protocol->callback(wsi,
+ LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
+ wsi->user_space, &p,
+ (pkt + wsi->context->pt_serv_buf_size) - p - 12))
+ return NULL;
+
+ p += sprintf(p, "\x0d\x0a");
+
+ return p;
+}
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+
+LWS_VISIBLE int
+lws_http_client_read(struct lws *wsi, char **buf, int *len)
+{
+ int rlen, n;
+
+ rlen = lws_ssl_capable_read(wsi, (unsigned char *)*buf, *len);
+ *len = 0;
+
+ // lwsl_notice("%s: rlen %d\n", __func__, rlen);
+
+ /* allow the source to signal he has data again next time */
+ lws_change_pollfd(wsi, 0, LWS_POLLIN);
+
+ if (rlen == LWS_SSL_CAPABLE_ERROR) {
+ lwsl_notice("%s: SSL capable error\n", __func__);
+ return -1;
+ }
+
+ if (rlen == 0)
+ return -1;
+
+ if (rlen < 0)
+ return 0;
+
+ *len = rlen;
+ wsi->client_rx_avail = 0;
+
+ /*
+ * server may insist on transfer-encoding: chunked,
+ * so http client must deal with it
+ */
+spin_chunks:
+ while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) {
+ switch (wsi->chunk_parser) {
+ case ELCP_HEX:
+ if ((*buf)[0] == '\x0d') {
+ wsi->chunk_parser = ELCP_CR;
+ break;
+ }
+ n = char_to_hex((*buf)[0]);
+ if (n < 0) {
+ lwsl_debug("chunking failure\n");
+ return -1;
+ }
+ wsi->chunk_remaining <<= 4;
+ wsi->chunk_remaining |= n;
+ break;
+ case ELCP_CR:
+ if ((*buf)[0] != '\x0a') {
+ lwsl_debug("chunking failure\n");
+ return -1;
+ }
+ wsi->chunk_parser = ELCP_CONTENT;
+ lwsl_info("chunk %d\n", wsi->chunk_remaining);
+ if (wsi->chunk_remaining)
+ break;
+ lwsl_info("final chunk\n");
+ goto completed;
+
+ case ELCP_CONTENT:
+ break;
+
+ case ELCP_POST_CR:
+ if ((*buf)[0] != '\x0d') {
+ lwsl_debug("chunking failure\n");
+
+ return -1;
+ }
+
+ wsi->chunk_parser = ELCP_POST_LF;
+ break;
+
+ case ELCP_POST_LF:
+ if ((*buf)[0] != '\x0a')
+ return -1;
+
+ wsi->chunk_parser = ELCP_HEX;
+ wsi->chunk_remaining = 0;
+ break;
+ }
+ (*buf)++;
+ (*len)--;
+ }
+
+ if (wsi->chunked && !wsi->chunk_remaining)
+ return 0;
+
+ if (wsi->http.rx_content_remain &&
+ wsi->http.rx_content_remain < (unsigned int)*len)
+ n = (int)wsi->http.rx_content_remain;
+ else
+ n = *len;
+
+ if (wsi->chunked && wsi->chunk_remaining &&
+ wsi->chunk_remaining < n)
+ n = wsi->chunk_remaining;
+
+#ifdef LWS_WITH_HTTP_PROXY
+ /* hubbub */
+ if (wsi->http.perform_rewrite)
+ lws_rewrite_parse(wsi->http.rw, (unsigned char *)*buf, n);
+ else
+#endif
+ {
+ struct lws *wsi_eff = lws_client_wsi_effective(wsi);
+
+ if (user_callback_handle_rxflow(wsi_eff->protocol->callback,
+ wsi_eff, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
+ wsi_eff->user_space, *buf, n)) {
+ lwsl_debug("%s: RECEIVE_CLIENT_HTTP_READ returned -1\n",
+ __func__);
+
+ return -1;
+ }
+ }
+
+ if (wsi->chunked && wsi->chunk_remaining) {
+ (*buf) += n;
+ wsi->chunk_remaining -= n;
+ *len -= n;
+ }
+
+ if (wsi->chunked && !wsi->chunk_remaining)
+ wsi->chunk_parser = ELCP_POST_CR;
+
+ if (wsi->chunked && *len)
+ goto spin_chunks;
+
+ if (wsi->chunked)
+ return 0;
+
+ /* if we know the content length, decrement the content remaining */
+ if (wsi->http.rx_content_length > 0)
+ wsi->http.rx_content_remain -= n;
+
+ // lwsl_notice("rx_content_remain %lld, rx_content_length %lld\n",
+ // wsi->http.rx_content_remain, wsi->http.rx_content_length);
+
+ if (wsi->http.rx_content_remain || !wsi->http.rx_content_length)
+ return 0;
+
+completed:
+
+ if (lws_http_transaction_completed_client(wsi)) {
+ lwsl_notice("%s: transaction completed says -1\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif \ No newline at end of file
diff --git a/thirdparty/lws/header.c b/thirdparty/libwebsockets/roles/http/header.c
index e2562cd6ea..99e56f7564 100644
--- a/thirdparty/lws/header.c
+++ b/thirdparty/libwebsockets/roles/http/header.c
@@ -19,12 +19,12 @@
* MA 02110-1301 USA
*/
-#include "private-libwebsockets.h"
-
+#include "core/private.h"
#include "lextable-strings.h"
-const unsigned char *lws_token_to_string(enum lws_token_indexes token)
+const unsigned char *
+lws_token_to_string(enum lws_token_indexes token)
{
if ((unsigned int)token >= ARRAY_SIZE(set))
return NULL;
@@ -38,7 +38,7 @@ lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
unsigned char **p, unsigned char *end)
{
#ifdef LWS_WITH_HTTP2
- if (wsi->mode == LWSCM_HTTP2_SERVING)
+ if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
return lws_add_http2_header_by_name(wsi, name,
value, length, p, end);
#else
@@ -66,7 +66,7 @@ int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
unsigned char *end)
{
#ifdef LWS_WITH_HTTP2
- if (wsi->mode == LWSCM_HTTP2_SERVING)
+ if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
return 0;
#else
(void)wsi;
@@ -80,19 +80,39 @@ int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
}
int
+lws_finalize_write_http_header(struct lws *wsi, unsigned char *start,
+ unsigned char **pp, unsigned char *end)
+{
+ unsigned char *p;
+ int len;
+
+ if (lws_finalize_http_header(wsi, pp, end))
+ return 1;
+
+ p = *pp;
+ len = lws_ptr_diff(p, start);
+
+ if (lws_write(wsi, start, len, LWS_WRITE_HTTP_HEADERS) != len)
+ return 1;
+
+ return 0;
+}
+
+int
lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end)
{
const unsigned char *name;
#ifdef LWS_WITH_HTTP2
- if (wsi->mode == LWSCM_HTTP2_SERVING)
+ if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
return lws_add_http2_header_by_token(wsi, token, value,
length, p, end);
#endif
name = lws_token_to_string(token);
if (!name)
return 1;
+
return lws_add_http_header_by_name(wsi, name, value, length, p, end);
}
@@ -107,8 +127,31 @@ int lws_add_http_header_content_length(struct lws *wsi,
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)b, n, p, end))
return 1;
- wsi->u.http.tx_content_length = content_length;
- wsi->u.http.tx_content_remain = content_length;
+ wsi->http.tx_content_length = content_length;
+ wsi->http.tx_content_remain = content_length;
+
+ lwsl_info("%s: wsi %p: tx_content_length/remain %llu\n", __func__,
+ wsi, (unsigned long long)content_length);
+
+ return 0;
+}
+
+int
+lws_add_http_common_headers(struct lws *wsi, unsigned int code,
+ const char *content_type, lws_filepos_t content_len,
+ unsigned char **p, unsigned char *end)
+{
+ if (lws_add_http_header_status(wsi, code, p, end))
+ return 1;
+
+ if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
+ (unsigned char *)content_type,
+ (int)strlen(content_type), p, end))
+ return 1;
+
+ if (content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN &&
+ lws_add_http_header_content_length(wsi, content_len, p, end))
+ return 1;
return 0;
}
@@ -157,11 +200,11 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
int n;
#ifdef LWS_WITH_ACCESS_LOG
- wsi->access_log.response = code;
+ wsi->http.access_log.response = code;
#endif
#ifdef LWS_WITH_HTTP2
- if (wsi->mode == LWSCM_HTTP2_SERVING)
+ if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
return lws_add_http2_header_status(wsi, code, p, end);
#endif
if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
@@ -171,18 +214,16 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
if (code == 100)
description = "Continue";
-
if (code == 200)
description = "OK";
-
if (code == 304)
description = "Not Modified";
else
if (code >= 300 && code < 400)
description = "Redirect";
- if (wsi->u.http.request_version < ARRAY_SIZE(hver))
- p1 = hver[wsi->u.http.request_version];
+ if (wsi->http.request_version < ARRAY_SIZE(hver))
+ p1 = hver[wsi->http.request_version];
else
p1 = hver[0];
@@ -196,7 +237,7 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
if (lws_add_http_header_by_name(wsi,
(const unsigned char *)headers->name,
(unsigned char *)headers->value,
- strlen(headers->value), p, end))
+ (int)strlen(headers->value), p, end))
return 1;
headers = headers->next;
@@ -231,6 +272,26 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
int n = 0, m = 0, len;
char slen[20];
+ if (!wsi->vhost) {
+ lwsl_err("%s: wsi not bound to vhost\n", __func__);
+
+ return 1;
+ }
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ if (!wsi->handling_404 &&
+ wsi->vhost->http.error_document_404 &&
+ code == HTTP_STATUS_NOT_FOUND)
+ /* we should do a redirect, and do the 404 there */
+ if (lws_http_redirect(wsi, HTTP_STATUS_FOUND,
+ (uint8_t *)wsi->vhost->http.error_document_404,
+ (int)strlen(wsi->vhost->http.error_document_404),
+ &p, end) > 0)
+ return 0;
+#endif
+
+ /* if the redirect failed, just do a simple status */
+ p = start;
+
if (!html_body)
html_body = "";
@@ -242,12 +303,11 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
&p, end))
return 1;
- len = 35 + strlen(html_body) + sprintf(slen, "%d", code);
+ len = 35 + (int)strlen(html_body) + sprintf(slen, "%d", code);
n = sprintf(slen, "%d", len);
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
- (unsigned char *)slen, n,
- &p, end))
+ (unsigned char *)slen, n, &p, end))
return 1;
if (lws_finalize_http_header(wsi, &p, end))
@@ -270,7 +330,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
* Solve it by writing the headers now...
*/
m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
- if (m != (int)(p - start))
+ if (m != lws_ptr_diff(p, start))
return 1;
/*
@@ -281,15 +341,15 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
len = sprintf((char *)body,
"<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
- wsi->u.http.tx_content_length = len;
- wsi->u.http.tx_content_remain = len;
+ wsi->http.tx_content_length = len;
+ wsi->http.tx_content_remain = len;
- wsi->u.h2.pending_status_body = lws_malloc(len + LWS_PRE + 1,
+ wsi->h2.pending_status_body = lws_malloc(len + LWS_PRE + 1,
"pending status body");
- if (!wsi->u.h2.pending_status_body)
+ if (!wsi->h2.pending_status_body)
return -1;
- strcpy(wsi->u.h2.pending_status_body + LWS_PRE,
+ strcpy(wsi->h2.pending_status_body + LWS_PRE,
(const char *)body);
lws_callback_on_writable(wsi);
@@ -305,15 +365,12 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
"<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
- n = (int)(p - start);
-
+ n = lws_ptr_diff(p, start);
m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
if (m != n)
return 1;
}
- lwsl_notice("%s: return\n", __func__);
-
return m != n;
}
@@ -322,34 +379,29 @@ lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
unsigned char **p, unsigned char *end)
{
unsigned char *start = *p;
- int n;
if (lws_add_http_header_status(wsi, code, p, end))
return -1;
- if (lws_add_http_header_by_token(wsi,
- WSI_TOKEN_HTTP_LOCATION,
- loc, len, p, end))
+ if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION, loc, len,
+ p, end))
return -1;
/*
* if we're going with http/1.1 and keepalive, we have to give fake
* content metadata so the client knows we completed the transaction and
* it can do the redirect...
*/
- if (lws_add_http_header_by_token(wsi,
- WSI_TOKEN_HTTP_CONTENT_TYPE,
- (unsigned char *)"text/html", 9,
- p, end))
+ if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
+ (unsigned char *)"text/html", 9, p,
+ end))
return -1;
- if (lws_add_http_header_by_token(wsi,
- WSI_TOKEN_HTTP_CONTENT_LENGTH,
- (unsigned char *)"0", 1, p, end))
+ if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
+ (unsigned char *)"0", 1, p, end))
return -1;
if (lws_finalize_http_header(wsi, p, end))
return -1;
- n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
-
- return n;
+ return lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS |
+ LWS_WRITE_H2_STREAM_END);
}
diff --git a/thirdparty/lws/lextable-strings.h b/thirdparty/libwebsockets/roles/http/lextable-strings.h
index ab42c3e476..631f5cb600 100644
--- a/thirdparty/lws/lextable-strings.h
+++ b/thirdparty/libwebsockets/roles/http/lextable-strings.h
@@ -98,6 +98,10 @@ STORE_IN_ROM static const char * const set[] = {
"connect ",
"head ",
"te:", /* http/2 wants it to reject it */
+ "replay-nonce:", /* ACME */
+ ":protocol", /* defined in mcmanus-httpbis-h2-ws-02 */
+
+ "x-auth-token:",
"", /* not matchable */
diff --git a/thirdparty/libwebsockets/roles/http/lextable.h b/thirdparty/libwebsockets/roles/http/lextable.h
new file mode 100644
index 0000000000..9a8063b157
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/http/lextable.h
@@ -0,0 +1,838 @@
+/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */,
+ 0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */,
+ 0x6F /* 'o' */, 0x51, 0x00 /* (to 0x0057 state 10) */,
+ 0x68 /* 'h' */, 0x5D, 0x00 /* (to 0x0066 state 18) */,
+ 0x63 /* 'c' */, 0x69, 0x00 /* (to 0x0075 state 23) */,
+ 0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x0099 state 34) */,
+ 0x73 /* 's' */, 0xA0, 0x00 /* (to 0x00B2 state 48) */,
+ 0x0D /* '.' */, 0xD9, 0x00 /* (to 0x00EE state 68) */,
+ 0x61 /* 'a' */, 0x31, 0x01 /* (to 0x0149 state 129) */,
+ 0x69 /* 'i' */, 0x70, 0x01 /* (to 0x018B state 163) */,
+ 0x64 /* 'd' */, 0x19, 0x02 /* (to 0x0237 state 265) */,
+ 0x72 /* 'r' */, 0x22, 0x02 /* (to 0x0243 state 270) */,
+ 0x3A /* ':' */, 0x56, 0x02 /* (to 0x027A state 299) */,
+ 0x65 /* 'e' */, 0xE8, 0x02 /* (to 0x030F state 409) */,
+ 0x66 /* 'f' */, 0x04, 0x03 /* (to 0x032E state 425) */,
+ 0x6C /* 'l' */, 0x26, 0x03 /* (to 0x0353 state 458) */,
+ 0x6D /* 'm' */, 0x49, 0x03 /* (to 0x0379 state 484) */,
+ 0x74 /* 't' */, 0xB8, 0x03 /* (to 0x03EB state 578) */,
+ 0x76 /* 'v' */, 0xD9, 0x03 /* (to 0x040F state 606) */,
+ 0x77 /* 'w' */, 0xE6, 0x03 /* (to 0x041F state 614) */,
+ 0x78 /* 'x' */, 0x0D, 0x04 /* (to 0x0449 state 650) */,
+ 0x08, /* fail */
+/* pos 0040: 1 */ 0xE5 /* 'e' -> */,
+/* pos 0041: 2 */ 0xF4 /* 't' -> */,
+/* pos 0042: 3 */ 0xA0 /* ' ' -> */,
+/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */,
+/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */,
+ 0x72 /* 'r' */, 0x95, 0x01 /* (to 0x01DD state 211) */,
+ 0x61 /* 'a' */, 0xE6, 0x03 /* (to 0x0431 state 631) */,
+ 0x75 /* 'u' */, 0xE8, 0x03 /* (to 0x0436 state 635) */,
+ 0x08, /* fail */
+/* pos 0052: 6 */ 0xF3 /* 's' -> */,
+/* pos 0053: 7 */ 0xF4 /* 't' -> */,
+/* pos 0054: 8 */ 0xA0 /* ' ' -> */,
+/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */,
+/* pos 0057: 10 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x005E state 11) */,
+ 0x72 /* 'r' */, 0x51, 0x00 /* (to 0x00AB state 42) */,
+ 0x08, /* fail */
+/* pos 005e: 11 */ 0xF4 /* 't' -> */,
+/* pos 005f: 12 */ 0xE9 /* 'i' -> */,
+/* pos 0060: 13 */ 0xEF /* 'o' -> */,
+/* pos 0061: 14 */ 0xEE /* 'n' -> */,
+/* pos 0062: 15 */ 0xF3 /* 's' -> */,
+/* pos 0063: 16 */ 0xA0 /* ' ' -> */,
+/* pos 0064: 17 */ 0x00, 0x02 /* - terminal marker 2 - */,
+/* pos 0066: 18 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x0070 state 19) */,
+ 0x74 /* 't' */, 0xBF, 0x00 /* (to 0x0128 state 110) */,
+ 0x65 /* 'e' */, 0x04, 0x04 /* (to 0x0470 state 676) */,
+ 0x08, /* fail */
+/* pos 0070: 19 */ 0xF3 /* 's' -> */,
+/* pos 0071: 20 */ 0xF4 /* 't' -> */,
+/* pos 0072: 21 */ 0xBA /* ':' -> */,
+/* pos 0073: 22 */ 0x00, 0x03 /* - terminal marker 3 - */,
+/* pos 0075: 23 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x007C state 24) */,
+ 0x61 /* 'a' */, 0x72, 0x01 /* (to 0x01EA state 217) */,
+ 0x08, /* fail */
+/* pos 007c: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0083 state 25) */,
+ 0x6F /* 'o' */, 0x87, 0x01 /* (to 0x0206 state 243) */,
+ 0x08, /* fail */
+/* pos 0083: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x008A state 26) */,
+ 0x74 /* 't' */, 0x86, 0x01 /* (to 0x020C state 248) */,
+ 0x08, /* fail */
+/* pos 008a: 26 */ 0xE5 /* 'e' -> */,
+/* pos 008b: 27 */ 0xE3 /* 'c' -> */,
+/* pos 008c: 28 */ 0xF4 /* 't' -> */,
+/* pos 008d: 29 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0094 state 30) */,
+ 0x20 /* ' ' */, 0xDE, 0x03 /* (to 0x046E state 675) */,
+ 0x08, /* fail */
+/* pos 0094: 30 */ 0xEF /* 'o' -> */,
+/* pos 0095: 31 */ 0xEE /* 'n' -> */,
+/* pos 0096: 32 */ 0xBA /* ':' -> */,
+/* pos 0097: 33 */ 0x00, 0x04 /* - terminal marker 4 - */,
+/* pos 0099: 34 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x00A3 state 35) */,
+ 0x73 /* 's' */, 0x68, 0x03 /* (to 0x0404 state 596) */,
+ 0x72 /* 'r' */, 0xA0, 0x03 /* (to 0x043F state 642) */,
+ 0x08, /* fail */
+/* pos 00a3: 35 */ 0xE7 /* 'g' -> */,
+/* pos 00a4: 36 */ 0xF2 /* 'r' -> */,
+/* pos 00a5: 37 */ 0xE1 /* 'a' -> */,
+/* pos 00a6: 38 */ 0xE4 /* 'd' -> */,
+/* pos 00a7: 39 */ 0xE5 /* 'e' -> */,
+/* pos 00a8: 40 */ 0xBA /* ':' -> */,
+/* pos 00a9: 41 */ 0x00, 0x05 /* - terminal marker 5 - */,
+/* pos 00ab: 42 */ 0xE9 /* 'i' -> */,
+/* pos 00ac: 43 */ 0xE7 /* 'g' -> */,
+/* pos 00ad: 44 */ 0xE9 /* 'i' -> */,
+/* pos 00ae: 45 */ 0xEE /* 'n' -> */,
+/* pos 00af: 46 */ 0xBA /* ':' -> */,
+/* pos 00b0: 47 */ 0x00, 0x06 /* - terminal marker 6 - */,
+/* pos 00b2: 48 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x00B9 state 49) */,
+ 0x74 /* 't' */, 0x1C, 0x03 /* (to 0x03D1 state 553) */,
+ 0x08, /* fail */
+/* pos 00b9: 49 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x00C3 state 50) */,
+ 0x72 /* 'r' */, 0x05, 0x03 /* (to 0x03C1 state 539) */,
+ 0x74 /* 't' */, 0x08, 0x03 /* (to 0x03C7 state 544) */,
+ 0x08, /* fail */
+/* pos 00c3: 50 */ 0xAD /* '-' -> */,
+/* pos 00c4: 51 */ 0xF7 /* 'w' -> */,
+/* pos 00c5: 52 */ 0xE5 /* 'e' -> */,
+/* pos 00c6: 53 */ 0xE2 /* 'b' -> */,
+/* pos 00c7: 54 */ 0xF3 /* 's' -> */,
+/* pos 00c8: 55 */ 0xEF /* 'o' -> */,
+/* pos 00c9: 56 */ 0xE3 /* 'c' -> */,
+/* pos 00ca: 57 */ 0xEB /* 'k' -> */,
+/* pos 00cb: 58 */ 0xE5 /* 'e' -> */,
+/* pos 00cc: 59 */ 0xF4 /* 't' -> */,
+/* pos 00cd: 60 */ 0xAD /* '-' -> */,
+/* pos 00ce: 61 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x00E7 state 62) */,
+ 0x65 /* 'e' */, 0x20, 0x00 /* (to 0x00F1 state 70) */,
+ 0x6B /* 'k' */, 0x29, 0x00 /* (to 0x00FD state 81) */,
+ 0x70 /* 'p' */, 0x38, 0x00 /* (to 0x010F state 88) */,
+ 0x61 /* 'a' */, 0x3F, 0x00 /* (to 0x0119 state 97) */,
+ 0x6E /* 'n' */, 0x44, 0x00 /* (to 0x0121 state 104) */,
+ 0x76 /* 'v' */, 0x89, 0x01 /* (to 0x0269 state 284) */,
+ 0x6F /* 'o' */, 0x8F, 0x01 /* (to 0x0272 state 292) */,
+ 0x08, /* fail */
+/* pos 00e7: 62 */ 0xF2 /* 'r' -> */,
+/* pos 00e8: 63 */ 0xE1 /* 'a' -> */,
+/* pos 00e9: 64 */ 0xE6 /* 'f' -> */,
+/* pos 00ea: 65 */ 0xF4 /* 't' -> */,
+/* pos 00eb: 66 */ 0xBA /* ':' -> */,
+/* pos 00ec: 67 */ 0x00, 0x07 /* - terminal marker 7 - */,
+/* pos 00ee: 68 */ 0x8A /* '.' -> */,
+/* pos 00ef: 69 */ 0x00, 0x08 /* - terminal marker 8 - */,
+/* pos 00f1: 70 */ 0xF8 /* 'x' -> */,
+/* pos 00f2: 71 */ 0xF4 /* 't' -> */,
+/* pos 00f3: 72 */ 0xE5 /* 'e' -> */,
+/* pos 00f4: 73 */ 0xEE /* 'n' -> */,
+/* pos 00f5: 74 */ 0xF3 /* 's' -> */,
+/* pos 00f6: 75 */ 0xE9 /* 'i' -> */,
+/* pos 00f7: 76 */ 0xEF /* 'o' -> */,
+/* pos 00f8: 77 */ 0xEE /* 'n' -> */,
+/* pos 00f9: 78 */ 0xF3 /* 's' -> */,
+/* pos 00fa: 79 */ 0xBA /* ':' -> */,
+/* pos 00fb: 80 */ 0x00, 0x09 /* - terminal marker 9 - */,
+/* pos 00fd: 81 */ 0xE5 /* 'e' -> */,
+/* pos 00fe: 82 */ 0xF9 /* 'y' -> */,
+/* pos 00ff: 83 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0109 state 84) */,
+ 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x010C state 86) */,
+ 0x3A /* ':' */, 0x62, 0x01 /* (to 0x0267 state 283) */,
+ 0x08, /* fail */
+/* pos 0109: 84 */ 0xBA /* ':' -> */,
+/* pos 010a: 85 */ 0x00, 0x0A /* - terminal marker 10 - */,
+/* pos 010c: 86 */ 0xBA /* ':' -> */,
+/* pos 010d: 87 */ 0x00, 0x0B /* - terminal marker 11 - */,
+/* pos 010f: 88 */ 0xF2 /* 'r' -> */,
+/* pos 0110: 89 */ 0xEF /* 'o' -> */,
+/* pos 0111: 90 */ 0xF4 /* 't' -> */,
+/* pos 0112: 91 */ 0xEF /* 'o' -> */,
+/* pos 0113: 92 */ 0xE3 /* 'c' -> */,
+/* pos 0114: 93 */ 0xEF /* 'o' -> */,
+/* pos 0115: 94 */ 0xEC /* 'l' -> */,
+/* pos 0116: 95 */ 0xBA /* ':' -> */,
+/* pos 0117: 96 */ 0x00, 0x0C /* - terminal marker 12 - */,
+/* pos 0119: 97 */ 0xE3 /* 'c' -> */,
+/* pos 011a: 98 */ 0xE3 /* 'c' -> */,
+/* pos 011b: 99 */ 0xE5 /* 'e' -> */,
+/* pos 011c: 100 */ 0xF0 /* 'p' -> */,
+/* pos 011d: 101 */ 0xF4 /* 't' -> */,
+/* pos 011e: 102 */ 0xBA /* ':' -> */,
+/* pos 011f: 103 */ 0x00, 0x0D /* - terminal marker 13 - */,
+/* pos 0121: 104 */ 0xEF /* 'o' -> */,
+/* pos 0122: 105 */ 0xEE /* 'n' -> */,
+/* pos 0123: 106 */ 0xE3 /* 'c' -> */,
+/* pos 0124: 107 */ 0xE5 /* 'e' -> */,
+/* pos 0125: 108 */ 0xBA /* ':' -> */,
+/* pos 0126: 109 */ 0x00, 0x0E /* - terminal marker 14 - */,
+/* pos 0128: 110 */ 0xF4 /* 't' -> */,
+/* pos 0129: 111 */ 0xF0 /* 'p' -> */,
+/* pos 012a: 112 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x0131 state 113) */,
+ 0x32 /* '2' */, 0x10, 0x00 /* (to 0x013D state 118) */,
+ 0x08, /* fail */
+/* pos 0131: 113 */ 0xB1 /* '1' -> */,
+/* pos 0132: 114 */ 0xAE /* '.' -> */,
+/* pos 0133: 115 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x013A state 116) */,
+ 0x30 /* '0' */, 0x27, 0x03 /* (to 0x045D state 660) */,
+ 0x08, /* fail */
+/* pos 013a: 116 */ 0xA0 /* ' ' -> */,
+/* pos 013b: 117 */ 0x00, 0x0F /* - terminal marker 15 - */,
+/* pos 013d: 118 */ 0xAD /* '-' -> */,
+/* pos 013e: 119 */ 0xF3 /* 's' -> */,
+/* pos 013f: 120 */ 0xE5 /* 'e' -> */,
+/* pos 0140: 121 */ 0xF4 /* 't' -> */,
+/* pos 0141: 122 */ 0xF4 /* 't' -> */,
+/* pos 0142: 123 */ 0xE9 /* 'i' -> */,
+/* pos 0143: 124 */ 0xEE /* 'n' -> */,
+/* pos 0144: 125 */ 0xE7 /* 'g' -> */,
+/* pos 0145: 126 */ 0xF3 /* 's' -> */,
+/* pos 0146: 127 */ 0xBA /* ':' -> */,
+/* pos 0147: 128 */ 0x00, 0x10 /* - terminal marker 16 - */,
+/* pos 0149: 129 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x0156 state 130) */,
+ 0x75 /* 'u' */, 0xAC, 0x00 /* (to 0x01F8 state 230) */,
+ 0x67 /* 'g' */, 0x86, 0x01 /* (to 0x02D5 state 358) */,
+ 0x6C /* 'l' */, 0x87, 0x01 /* (to 0x02D9 state 361) */,
+ 0x08, /* fail */
+/* pos 0156: 130 */ 0xE3 /* 'c' -> */,
+/* pos 0157: 131 */ 0xE5 /* 'e' -> */,
+/* pos 0158: 132 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x015F state 133) */,
+ 0x73 /* 's' */, 0x0E, 0x00 /* (to 0x0169 state 136) */,
+ 0x08, /* fail */
+/* pos 015f: 133 */ 0xF4 /* 't' -> */,
+/* pos 0160: 134 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0167 state 135) */,
+ 0x2D /* '-' */, 0x59, 0x00 /* (to 0x01BC state 192) */,
+ 0x08, /* fail */
+/* pos 0167: 135 */ 0x00, 0x11 /* - terminal marker 17 - */,
+/* pos 0169: 136 */ 0xF3 /* 's' -> */,
+/* pos 016a: 137 */ 0xAD /* '-' -> */,
+/* pos 016b: 138 */ 0xE3 /* 'c' -> */,
+/* pos 016c: 139 */ 0xEF /* 'o' -> */,
+/* pos 016d: 140 */ 0xEE /* 'n' -> */,
+/* pos 016e: 141 */ 0xF4 /* 't' -> */,
+/* pos 016f: 142 */ 0xF2 /* 'r' -> */,
+/* pos 0170: 143 */ 0xEF /* 'o' -> */,
+/* pos 0171: 144 */ 0xEC /* 'l' -> */,
+/* pos 0172: 145 */ 0xAD /* '-' -> */,
+/* pos 0173: 146 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x017A state 147) */,
+ 0x61 /* 'a' */, 0x51, 0x01 /* (to 0x02C7 state 345) */,
+ 0x08, /* fail */
+/* pos 017a: 147 */ 0xE5 /* 'e' -> */,
+/* pos 017b: 148 */ 0xF1 /* 'q' -> */,
+/* pos 017c: 149 */ 0xF5 /* 'u' -> */,
+/* pos 017d: 150 */ 0xE5 /* 'e' -> */,
+/* pos 017e: 151 */ 0xF3 /* 's' -> */,
+/* pos 017f: 152 */ 0xF4 /* 't' -> */,
+/* pos 0180: 153 */ 0xAD /* '-' -> */,
+/* pos 0181: 154 */ 0xE8 /* 'h' -> */,
+/* pos 0182: 155 */ 0xE5 /* 'e' -> */,
+/* pos 0183: 156 */ 0xE1 /* 'a' -> */,
+/* pos 0184: 157 */ 0xE4 /* 'd' -> */,
+/* pos 0185: 158 */ 0xE5 /* 'e' -> */,
+/* pos 0186: 159 */ 0xF2 /* 'r' -> */,
+/* pos 0187: 160 */ 0xF3 /* 's' -> */,
+/* pos 0188: 161 */ 0xBA /* ':' -> */,
+/* pos 0189: 162 */ 0x00, 0x12 /* - terminal marker 18 - */,
+/* pos 018b: 163 */ 0xE6 /* 'f' -> */,
+/* pos 018c: 164 */ 0xAD /* '-' -> */,
+/* pos 018d: 165 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x019A state 166) */,
+ 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x01B0 state 181) */,
+ 0x72 /* 'r' */, 0xA7, 0x01 /* (to 0x033A state 435) */,
+ 0x75 /* 'u' */, 0xAB, 0x01 /* (to 0x0341 state 441) */,
+ 0x08, /* fail */
+/* pos 019a: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x01A1 state 167) */,
+ 0x61 /* 'a' */, 0x97, 0x01 /* (to 0x0334 state 430) */,
+ 0x08, /* fail */
+/* pos 01a1: 167 */ 0xE4 /* 'd' -> */,
+/* pos 01a2: 168 */ 0xE9 /* 'i' -> */,
+/* pos 01a3: 169 */ 0xE6 /* 'f' -> */,
+/* pos 01a4: 170 */ 0xE9 /* 'i' -> */,
+/* pos 01a5: 171 */ 0xE5 /* 'e' -> */,
+/* pos 01a6: 172 */ 0xE4 /* 'd' -> */,
+/* pos 01a7: 173 */ 0xAD /* '-' -> */,
+/* pos 01a8: 174 */ 0xF3 /* 's' -> */,
+/* pos 01a9: 175 */ 0xE9 /* 'i' -> */,
+/* pos 01aa: 176 */ 0xEE /* 'n' -> */,
+/* pos 01ab: 177 */ 0xE3 /* 'c' -> */,
+/* pos 01ac: 178 */ 0xE5 /* 'e' -> */,
+/* pos 01ad: 179 */ 0xBA /* ':' -> */,
+/* pos 01ae: 180 */ 0x00, 0x13 /* - terminal marker 19 - */,
+/* pos 01b0: 181 */ 0xEF /* 'o' -> */,
+/* pos 01b1: 182 */ 0xEE /* 'n' -> */,
+/* pos 01b2: 183 */ 0xE5 /* 'e' -> */,
+/* pos 01b3: 184 */ 0xAD /* '-' -> */,
+/* pos 01b4: 185 */ 0xED /* 'm' -> */,
+/* pos 01b5: 186 */ 0xE1 /* 'a' -> */,
+/* pos 01b6: 187 */ 0xF4 /* 't' -> */,
+/* pos 01b7: 188 */ 0xE3 /* 'c' -> */,
+/* pos 01b8: 189 */ 0xE8 /* 'h' -> */,
+/* pos 01b9: 190 */ 0xBA /* ':' -> */,
+/* pos 01ba: 191 */ 0x00, 0x14 /* - terminal marker 20 - */,
+/* pos 01bc: 192 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x01C9 state 193) */,
+ 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x01D3 state 202) */,
+ 0x63 /* 'c' */, 0xF4, 0x00 /* (to 0x02B6 state 330) */,
+ 0x72 /* 'r' */, 0xFA, 0x00 /* (to 0x02BF state 338) */,
+ 0x08, /* fail */
+/* pos 01c9: 193 */ 0xEE /* 'n' -> */,
+/* pos 01ca: 194 */ 0xE3 /* 'c' -> */,
+/* pos 01cb: 195 */ 0xEF /* 'o' -> */,
+/* pos 01cc: 196 */ 0xE4 /* 'd' -> */,
+/* pos 01cd: 197 */ 0xE9 /* 'i' -> */,
+/* pos 01ce: 198 */ 0xEE /* 'n' -> */,
+/* pos 01cf: 199 */ 0xE7 /* 'g' -> */,
+/* pos 01d0: 200 */ 0xBA /* ':' -> */,
+/* pos 01d1: 201 */ 0x00, 0x15 /* - terminal marker 21 - */,
+/* pos 01d3: 202 */ 0xE1 /* 'a' -> */,
+/* pos 01d4: 203 */ 0xEE /* 'n' -> */,
+/* pos 01d5: 204 */ 0xE7 /* 'g' -> */,
+/* pos 01d6: 205 */ 0xF5 /* 'u' -> */,
+/* pos 01d7: 206 */ 0xE1 /* 'a' -> */,
+/* pos 01d8: 207 */ 0xE7 /* 'g' -> */,
+/* pos 01d9: 208 */ 0xE5 /* 'e' -> */,
+/* pos 01da: 209 */ 0xBA /* ':' -> */,
+/* pos 01db: 210 */ 0x00, 0x16 /* - terminal marker 22 - */,
+/* pos 01dd: 211 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01E4 state 212) */,
+ 0x6F /* 'o' */, 0xA7, 0x01 /* (to 0x0387 state 497) */,
+ 0x08, /* fail */
+/* pos 01e4: 212 */ 0xE7 /* 'g' -> */,
+/* pos 01e5: 213 */ 0xED /* 'm' -> */,
+/* pos 01e6: 214 */ 0xE1 /* 'a' -> */,
+/* pos 01e7: 215 */ 0xBA /* ':' -> */,
+/* pos 01e8: 216 */ 0x00, 0x17 /* - terminal marker 23 - */,
+/* pos 01ea: 217 */ 0xE3 /* 'c' -> */,
+/* pos 01eb: 218 */ 0xE8 /* 'h' -> */,
+/* pos 01ec: 219 */ 0xE5 /* 'e' -> */,
+/* pos 01ed: 220 */ 0xAD /* '-' -> */,
+/* pos 01ee: 221 */ 0xE3 /* 'c' -> */,
+/* pos 01ef: 222 */ 0xEF /* 'o' -> */,
+/* pos 01f0: 223 */ 0xEE /* 'n' -> */,
+/* pos 01f1: 224 */ 0xF4 /* 't' -> */,
+/* pos 01f2: 225 */ 0xF2 /* 'r' -> */,
+/* pos 01f3: 226 */ 0xEF /* 'o' -> */,
+/* pos 01f4: 227 */ 0xEC /* 'l' -> */,
+/* pos 01f5: 228 */ 0xBA /* ':' -> */,
+/* pos 01f6: 229 */ 0x00, 0x18 /* - terminal marker 24 - */,
+/* pos 01f8: 230 */ 0xF4 /* 't' -> */,
+/* pos 01f9: 231 */ 0xE8 /* 'h' -> */,
+/* pos 01fa: 232 */ 0xEF /* 'o' -> */,
+/* pos 01fb: 233 */ 0xF2 /* 'r' -> */,
+/* pos 01fc: 234 */ 0xE9 /* 'i' -> */,
+/* pos 01fd: 235 */ 0xFA /* 'z' -> */,
+/* pos 01fe: 236 */ 0xE1 /* 'a' -> */,
+/* pos 01ff: 237 */ 0xF4 /* 't' -> */,
+/* pos 0200: 238 */ 0xE9 /* 'i' -> */,
+/* pos 0201: 239 */ 0xEF /* 'o' -> */,
+/* pos 0202: 240 */ 0xEE /* 'n' -> */,
+/* pos 0203: 241 */ 0xBA /* ':' -> */,
+/* pos 0204: 242 */ 0x00, 0x19 /* - terminal marker 25 - */,
+/* pos 0206: 243 */ 0xEB /* 'k' -> */,
+/* pos 0207: 244 */ 0xE9 /* 'i' -> */,
+/* pos 0208: 245 */ 0xE5 /* 'e' -> */,
+/* pos 0209: 246 */ 0xBA /* ':' -> */,
+/* pos 020a: 247 */ 0x00, 0x1A /* - terminal marker 26 - */,
+/* pos 020c: 248 */ 0xE5 /* 'e' -> */,
+/* pos 020d: 249 */ 0xEE /* 'n' -> */,
+/* pos 020e: 250 */ 0xF4 /* 't' -> */,
+/* pos 020f: 251 */ 0xAD /* '-' -> */,
+/* pos 0210: 252 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0220 state 253) */,
+ 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0231 state 260) */,
+ 0x64 /* 'd' */, 0xC9, 0x00 /* (to 0x02DF state 366) */,
+ 0x65 /* 'e' */, 0xD3, 0x00 /* (to 0x02EC state 378) */,
+ 0x72 /* 'r' */, 0xEC, 0x00 /* (to 0x0308 state 403) */,
+ 0x08, /* fail */
+/* pos 0220: 253 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x022A state 254) */,
+ 0x61 /* 'a' */, 0xD3, 0x00 /* (to 0x02F6 state 387) */,
+ 0x6F /* 'o' */, 0xD9, 0x00 /* (to 0x02FF state 395) */,
+ 0x08, /* fail */
+/* pos 022a: 254 */ 0xEE /* 'n' -> */,
+/* pos 022b: 255 */ 0xE7 /* 'g' -> */,
+/* pos 022c: 256 */ 0xF4 /* 't' -> */,
+/* pos 022d: 257 */ 0xE8 /* 'h' -> */,
+/* pos 022e: 258 */ 0xBA /* ':' -> */,
+/* pos 022f: 259 */ 0x00, 0x1B /* - terminal marker 27 - */,
+/* pos 0231: 260 */ 0xF9 /* 'y' -> */,
+/* pos 0232: 261 */ 0xF0 /* 'p' -> */,
+/* pos 0233: 262 */ 0xE5 /* 'e' -> */,
+/* pos 0234: 263 */ 0xBA /* ':' -> */,
+/* pos 0235: 264 */ 0x00, 0x1C /* - terminal marker 28 - */,
+/* pos 0237: 265 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x023E state 266) */,
+ 0x65 /* 'e' */, 0xFF, 0x01 /* (to 0x0439 state 637) */,
+ 0x08, /* fail */
+/* pos 023e: 266 */ 0xF4 /* 't' -> */,
+/* pos 023f: 267 */ 0xE5 /* 'e' -> */,
+/* pos 0240: 268 */ 0xBA /* ':' -> */,
+/* pos 0241: 269 */ 0x00, 0x1D /* - terminal marker 29 - */,
+/* pos 0243: 270 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x024A state 271) */,
+ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0250 state 276) */,
+ 0x08, /* fail */
+/* pos 024a: 271 */ 0xEE /* 'n' -> */,
+/* pos 024b: 272 */ 0xE7 /* 'g' -> */,
+/* pos 024c: 273 */ 0xE5 /* 'e' -> */,
+/* pos 024d: 274 */ 0xBA /* ':' -> */,
+/* pos 024e: 275 */ 0x00, 0x1E /* - terminal marker 30 - */,
+/* pos 0250: 276 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x025A state 277) */,
+ 0x74 /* 't' */, 0x63, 0x01 /* (to 0x03B6 state 529) */,
+ 0x70 /* 'p' */, 0x22, 0x02 /* (to 0x0478 state 682) */,
+ 0x08, /* fail */
+/* pos 025a: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0261 state 278) */,
+ 0x72 /* 'r' */, 0x53, 0x01 /* (to 0x03B0 state 524) */,
+ 0x08, /* fail */
+/* pos 0261: 278 */ 0xF2 /* 'r' -> */,
+/* pos 0262: 279 */ 0xE5 /* 'e' -> */,
+/* pos 0263: 280 */ 0xF2 /* 'r' -> */,
+/* pos 0264: 281 */ 0xBA /* ':' -> */,
+/* pos 0265: 282 */ 0x00, 0x1F /* - terminal marker 31 - */,
+/* pos 0267: 283 */ 0x00, 0x20 /* - terminal marker 32 - */,
+/* pos 0269: 284 */ 0xE5 /* 'e' -> */,
+/* pos 026a: 285 */ 0xF2 /* 'r' -> */,
+/* pos 026b: 286 */ 0xF3 /* 's' -> */,
+/* pos 026c: 287 */ 0xE9 /* 'i' -> */,
+/* pos 026d: 288 */ 0xEF /* 'o' -> */,
+/* pos 026e: 289 */ 0xEE /* 'n' -> */,
+/* pos 026f: 290 */ 0xBA /* ':' -> */,
+/* pos 0270: 291 */ 0x00, 0x21 /* - terminal marker 33 - */,
+/* pos 0272: 292 */ 0xF2 /* 'r' -> */,
+/* pos 0273: 293 */ 0xE9 /* 'i' -> */,
+/* pos 0274: 294 */ 0xE7 /* 'g' -> */,
+/* pos 0275: 295 */ 0xE9 /* 'i' -> */,
+/* pos 0276: 296 */ 0xEE /* 'n' -> */,
+/* pos 0277: 297 */ 0xBA /* ':' -> */,
+/* pos 0278: 298 */ 0x00, 0x22 /* - terminal marker 34 - */,
+/* pos 027a: 299 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0287 state 300) */,
+ 0x6D /* 'm' */, 0x14, 0x00 /* (to 0x0291 state 309) */,
+ 0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0298 state 315) */,
+ 0x73 /* 's' */, 0x20, 0x00 /* (to 0x02A3 state 319) */,
+ 0x08, /* fail */
+/* pos 0287: 300 */ 0xF5 /* 'u' -> */,
+/* pos 0288: 301 */ 0xF4 /* 't' -> */,
+/* pos 0289: 302 */ 0xE8 /* 'h' -> */,
+/* pos 028a: 303 */ 0xEF /* 'o' -> */,
+/* pos 028b: 304 */ 0xF2 /* 'r' -> */,
+/* pos 028c: 305 */ 0xE9 /* 'i' -> */,
+/* pos 028d: 306 */ 0xF4 /* 't' -> */,
+/* pos 028e: 307 */ 0xF9 /* 'y' -> */,
+/* pos 028f: 308 */ 0x00, 0x23 /* - terminal marker 35 - */,
+/* pos 0291: 309 */ 0xE5 /* 'e' -> */,
+/* pos 0292: 310 */ 0xF4 /* 't' -> */,
+/* pos 0293: 311 */ 0xE8 /* 'h' -> */,
+/* pos 0294: 312 */ 0xEF /* 'o' -> */,
+/* pos 0295: 313 */ 0xE4 /* 'd' -> */,
+/* pos 0296: 314 */ 0x00, 0x24 /* - terminal marker 36 - */,
+/* pos 0298: 315 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x029F state 316) */,
+ 0x72 /* 'r' */, 0xE9, 0x01 /* (to 0x0484 state 693) */,
+ 0x08, /* fail */
+/* pos 029f: 316 */ 0xF4 /* 't' -> */,
+/* pos 02a0: 317 */ 0xE8 /* 'h' -> */,
+/* pos 02a1: 318 */ 0x00, 0x25 /* - terminal marker 37 - */,
+/* pos 02a3: 319 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x02AA state 320) */,
+ 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x02B0 state 325) */,
+ 0x08, /* fail */
+/* pos 02aa: 320 */ 0xE8 /* 'h' -> */,
+/* pos 02ab: 321 */ 0xE5 /* 'e' -> */,
+/* pos 02ac: 322 */ 0xED /* 'm' -> */,
+/* pos 02ad: 323 */ 0xE5 /* 'e' -> */,
+/* pos 02ae: 324 */ 0x00, 0x26 /* - terminal marker 38 - */,
+/* pos 02b0: 325 */ 0xE1 /* 'a' -> */,
+/* pos 02b1: 326 */ 0xF4 /* 't' -> */,
+/* pos 02b2: 327 */ 0xF5 /* 'u' -> */,
+/* pos 02b3: 328 */ 0xF3 /* 's' -> */,
+/* pos 02b4: 329 */ 0x00, 0x27 /* - terminal marker 39 - */,
+/* pos 02b6: 330 */ 0xE8 /* 'h' -> */,
+/* pos 02b7: 331 */ 0xE1 /* 'a' -> */,
+/* pos 02b8: 332 */ 0xF2 /* 'r' -> */,
+/* pos 02b9: 333 */ 0xF3 /* 's' -> */,
+/* pos 02ba: 334 */ 0xE5 /* 'e' -> */,
+/* pos 02bb: 335 */ 0xF4 /* 't' -> */,
+/* pos 02bc: 336 */ 0xBA /* ':' -> */,
+/* pos 02bd: 337 */ 0x00, 0x28 /* - terminal marker 40 - */,
+/* pos 02bf: 338 */ 0xE1 /* 'a' -> */,
+/* pos 02c0: 339 */ 0xEE /* 'n' -> */,
+/* pos 02c1: 340 */ 0xE7 /* 'g' -> */,
+/* pos 02c2: 341 */ 0xE5 /* 'e' -> */,
+/* pos 02c3: 342 */ 0xF3 /* 's' -> */,
+/* pos 02c4: 343 */ 0xBA /* ':' -> */,
+/* pos 02c5: 344 */ 0x00, 0x29 /* - terminal marker 41 - */,
+/* pos 02c7: 345 */ 0xEC /* 'l' -> */,
+/* pos 02c8: 346 */ 0xEC /* 'l' -> */,
+/* pos 02c9: 347 */ 0xEF /* 'o' -> */,
+/* pos 02ca: 348 */ 0xF7 /* 'w' -> */,
+/* pos 02cb: 349 */ 0xAD /* '-' -> */,
+/* pos 02cc: 350 */ 0xEF /* 'o' -> */,
+/* pos 02cd: 351 */ 0xF2 /* 'r' -> */,
+/* pos 02ce: 352 */ 0xE9 /* 'i' -> */,
+/* pos 02cf: 353 */ 0xE7 /* 'g' -> */,
+/* pos 02d0: 354 */ 0xE9 /* 'i' -> */,
+/* pos 02d1: 355 */ 0xEE /* 'n' -> */,
+/* pos 02d2: 356 */ 0xBA /* ':' -> */,
+/* pos 02d3: 357 */ 0x00, 0x2A /* - terminal marker 42 - */,
+/* pos 02d5: 358 */ 0xE5 /* 'e' -> */,
+/* pos 02d6: 359 */ 0xBA /* ':' -> */,
+/* pos 02d7: 360 */ 0x00, 0x2B /* - terminal marker 43 - */,
+/* pos 02d9: 361 */ 0xEC /* 'l' -> */,
+/* pos 02da: 362 */ 0xEF /* 'o' -> */,
+/* pos 02db: 363 */ 0xF7 /* 'w' -> */,
+/* pos 02dc: 364 */ 0xBA /* ':' -> */,
+/* pos 02dd: 365 */ 0x00, 0x2C /* - terminal marker 44 - */,
+/* pos 02df: 366 */ 0xE9 /* 'i' -> */,
+/* pos 02e0: 367 */ 0xF3 /* 's' -> */,
+/* pos 02e1: 368 */ 0xF0 /* 'p' -> */,
+/* pos 02e2: 369 */ 0xEF /* 'o' -> */,
+/* pos 02e3: 370 */ 0xF3 /* 's' -> */,
+/* pos 02e4: 371 */ 0xE9 /* 'i' -> */,
+/* pos 02e5: 372 */ 0xF4 /* 't' -> */,
+/* pos 02e6: 373 */ 0xE9 /* 'i' -> */,
+/* pos 02e7: 374 */ 0xEF /* 'o' -> */,
+/* pos 02e8: 375 */ 0xEE /* 'n' -> */,
+/* pos 02e9: 376 */ 0xBA /* ':' -> */,
+/* pos 02ea: 377 */ 0x00, 0x2D /* - terminal marker 45 - */,
+/* pos 02ec: 378 */ 0xEE /* 'n' -> */,
+/* pos 02ed: 379 */ 0xE3 /* 'c' -> */,
+/* pos 02ee: 380 */ 0xEF /* 'o' -> */,
+/* pos 02ef: 381 */ 0xE4 /* 'd' -> */,
+/* pos 02f0: 382 */ 0xE9 /* 'i' -> */,
+/* pos 02f1: 383 */ 0xEE /* 'n' -> */,
+/* pos 02f2: 384 */ 0xE7 /* 'g' -> */,
+/* pos 02f3: 385 */ 0xBA /* ':' -> */,
+/* pos 02f4: 386 */ 0x00, 0x2E /* - terminal marker 46 - */,
+/* pos 02f6: 387 */ 0xEE /* 'n' -> */,
+/* pos 02f7: 388 */ 0xE7 /* 'g' -> */,
+/* pos 02f8: 389 */ 0xF5 /* 'u' -> */,
+/* pos 02f9: 390 */ 0xE1 /* 'a' -> */,
+/* pos 02fa: 391 */ 0xE7 /* 'g' -> */,
+/* pos 02fb: 392 */ 0xE5 /* 'e' -> */,
+/* pos 02fc: 393 */ 0xBA /* ':' -> */,
+/* pos 02fd: 394 */ 0x00, 0x2F /* - terminal marker 47 - */,
+/* pos 02ff: 395 */ 0xE3 /* 'c' -> */,
+/* pos 0300: 396 */ 0xE1 /* 'a' -> */,
+/* pos 0301: 397 */ 0xF4 /* 't' -> */,
+/* pos 0302: 398 */ 0xE9 /* 'i' -> */,
+/* pos 0303: 399 */ 0xEF /* 'o' -> */,
+/* pos 0304: 400 */ 0xEE /* 'n' -> */,
+/* pos 0305: 401 */ 0xBA /* ':' -> */,
+/* pos 0306: 402 */ 0x00, 0x30 /* - terminal marker 48 - */,
+/* pos 0308: 403 */ 0xE1 /* 'a' -> */,
+/* pos 0309: 404 */ 0xEE /* 'n' -> */,
+/* pos 030a: 405 */ 0xE7 /* 'g' -> */,
+/* pos 030b: 406 */ 0xE5 /* 'e' -> */,
+/* pos 030c: 407 */ 0xBA /* ':' -> */,
+/* pos 030d: 408 */ 0x00, 0x31 /* - terminal marker 49 - */,
+/* pos 030f: 409 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x0316 state 410) */,
+ 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x031B state 414) */,
+ 0x08, /* fail */
+/* pos 0316: 410 */ 0xE1 /* 'a' -> */,
+/* pos 0317: 411 */ 0xE7 /* 'g' -> */,
+/* pos 0318: 412 */ 0xBA /* ':' -> */,
+/* pos 0319: 413 */ 0x00, 0x32 /* - terminal marker 50 - */,
+/* pos 031b: 414 */ 0xF0 /* 'p' -> */,
+/* pos 031c: 415 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0323 state 416) */,
+ 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0328 state 420) */,
+ 0x08, /* fail */
+/* pos 0323: 416 */ 0xE3 /* 'c' -> */,
+/* pos 0324: 417 */ 0xF4 /* 't' -> */,
+/* pos 0325: 418 */ 0xBA /* ':' -> */,
+/* pos 0326: 419 */ 0x00, 0x33 /* - terminal marker 51 - */,
+/* pos 0328: 420 */ 0xF2 /* 'r' -> */,
+/* pos 0329: 421 */ 0xE5 /* 'e' -> */,
+/* pos 032a: 422 */ 0xF3 /* 's' -> */,
+/* pos 032b: 423 */ 0xBA /* ':' -> */,
+/* pos 032c: 424 */ 0x00, 0x34 /* - terminal marker 52 - */,
+/* pos 032e: 425 */ 0xF2 /* 'r' -> */,
+/* pos 032f: 426 */ 0xEF /* 'o' -> */,
+/* pos 0330: 427 */ 0xED /* 'm' -> */,
+/* pos 0331: 428 */ 0xBA /* ':' -> */,
+/* pos 0332: 429 */ 0x00, 0x35 /* - terminal marker 53 - */,
+/* pos 0334: 430 */ 0xF4 /* 't' -> */,
+/* pos 0335: 431 */ 0xE3 /* 'c' -> */,
+/* pos 0336: 432 */ 0xE8 /* 'h' -> */,
+/* pos 0337: 433 */ 0xBA /* ':' -> */,
+/* pos 0338: 434 */ 0x00, 0x36 /* - terminal marker 54 - */,
+/* pos 033a: 435 */ 0xE1 /* 'a' -> */,
+/* pos 033b: 436 */ 0xEE /* 'n' -> */,
+/* pos 033c: 437 */ 0xE7 /* 'g' -> */,
+/* pos 033d: 438 */ 0xE5 /* 'e' -> */,
+/* pos 033e: 439 */ 0xBA /* ':' -> */,
+/* pos 033f: 440 */ 0x00, 0x37 /* - terminal marker 55 - */,
+/* pos 0341: 441 */ 0xEE /* 'n' -> */,
+/* pos 0342: 442 */ 0xED /* 'm' -> */,
+/* pos 0343: 443 */ 0xEF /* 'o' -> */,
+/* pos 0344: 444 */ 0xE4 /* 'd' -> */,
+/* pos 0345: 445 */ 0xE9 /* 'i' -> */,
+/* pos 0346: 446 */ 0xE6 /* 'f' -> */,
+/* pos 0347: 447 */ 0xE9 /* 'i' -> */,
+/* pos 0348: 448 */ 0xE5 /* 'e' -> */,
+/* pos 0349: 449 */ 0xE4 /* 'd' -> */,
+/* pos 034a: 450 */ 0xAD /* '-' -> */,
+/* pos 034b: 451 */ 0xF3 /* 's' -> */,
+/* pos 034c: 452 */ 0xE9 /* 'i' -> */,
+/* pos 034d: 453 */ 0xEE /* 'n' -> */,
+/* pos 034e: 454 */ 0xE3 /* 'c' -> */,
+/* pos 034f: 455 */ 0xE5 /* 'e' -> */,
+/* pos 0350: 456 */ 0xBA /* ':' -> */,
+/* pos 0351: 457 */ 0x00, 0x38 /* - terminal marker 56 - */,
+/* pos 0353: 458 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x035D state 459) */,
+ 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x036B state 472) */,
+ 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0370 state 476) */,
+ 0x08, /* fail */
+/* pos 035d: 459 */ 0xF3 /* 's' -> */,
+/* pos 035e: 460 */ 0xF4 /* 't' -> */,
+/* pos 035f: 461 */ 0xAD /* '-' -> */,
+/* pos 0360: 462 */ 0xED /* 'm' -> */,
+/* pos 0361: 463 */ 0xEF /* 'o' -> */,
+/* pos 0362: 464 */ 0xE4 /* 'd' -> */,
+/* pos 0363: 465 */ 0xE9 /* 'i' -> */,
+/* pos 0364: 466 */ 0xE6 /* 'f' -> */,
+/* pos 0365: 467 */ 0xE9 /* 'i' -> */,
+/* pos 0366: 468 */ 0xE5 /* 'e' -> */,
+/* pos 0367: 469 */ 0xE4 /* 'd' -> */,
+/* pos 0368: 470 */ 0xBA /* ':' -> */,
+/* pos 0369: 471 */ 0x00, 0x39 /* - terminal marker 57 - */,
+/* pos 036b: 472 */ 0xEE /* 'n' -> */,
+/* pos 036c: 473 */ 0xEB /* 'k' -> */,
+/* pos 036d: 474 */ 0xBA /* ':' -> */,
+/* pos 036e: 475 */ 0x00, 0x3A /* - terminal marker 58 - */,
+/* pos 0370: 476 */ 0xE3 /* 'c' -> */,
+/* pos 0371: 477 */ 0xE1 /* 'a' -> */,
+/* pos 0372: 478 */ 0xF4 /* 't' -> */,
+/* pos 0373: 479 */ 0xE9 /* 'i' -> */,
+/* pos 0374: 480 */ 0xEF /* 'o' -> */,
+/* pos 0375: 481 */ 0xEE /* 'n' -> */,
+/* pos 0376: 482 */ 0xBA /* ':' -> */,
+/* pos 0377: 483 */ 0x00, 0x3B /* - terminal marker 59 - */,
+/* pos 0379: 484 */ 0xE1 /* 'a' -> */,
+/* pos 037a: 485 */ 0xF8 /* 'x' -> */,
+/* pos 037b: 486 */ 0xAD /* '-' -> */,
+/* pos 037c: 487 */ 0xE6 /* 'f' -> */,
+/* pos 037d: 488 */ 0xEF /* 'o' -> */,
+/* pos 037e: 489 */ 0xF2 /* 'r' -> */,
+/* pos 037f: 490 */ 0xF7 /* 'w' -> */,
+/* pos 0380: 491 */ 0xE1 /* 'a' -> */,
+/* pos 0381: 492 */ 0xF2 /* 'r' -> */,
+/* pos 0382: 493 */ 0xE4 /* 'd' -> */,
+/* pos 0383: 494 */ 0xF3 /* 's' -> */,
+/* pos 0384: 495 */ 0xBA /* ':' -> */,
+/* pos 0385: 496 */ 0x00, 0x3C /* - terminal marker 60 - */,
+/* pos 0387: 497 */ 0xF8 /* 'x' -> */,
+/* pos 0388: 498 */ 0xF9 /* 'y' -> */,
+/* pos 0389: 499 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0390 state 500) */,
+ 0x20 /* ' ' */, 0xBB, 0x00 /* (to 0x0447 state 649) */,
+ 0x08, /* fail */
+/* pos 0390: 500 */ 0xE1 /* 'a' -> */,
+/* pos 0391: 501 */ 0xF5 /* 'u' -> */,
+/* pos 0392: 502 */ 0xF4 /* 't' -> */,
+/* pos 0393: 503 */ 0xE8 /* 'h' -> */,
+/* pos 0394: 504 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x039B state 505) */,
+ 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x03A5 state 514) */,
+ 0x08, /* fail */
+/* pos 039b: 505 */ 0xEE /* 'n' -> */,
+/* pos 039c: 506 */ 0xF4 /* 't' -> */,
+/* pos 039d: 507 */ 0xE9 /* 'i' -> */,
+/* pos 039e: 508 */ 0xE3 /* 'c' -> */,
+/* pos 039f: 509 */ 0xE1 /* 'a' -> */,
+/* pos 03a0: 510 */ 0xF4 /* 't' -> */,
+/* pos 03a1: 511 */ 0xE5 /* 'e' -> */,
+/* pos 03a2: 512 */ 0xBA /* ':' -> */,
+/* pos 03a3: 513 */ 0x00, 0x3D /* - terminal marker 61 - */,
+/* pos 03a5: 514 */ 0xF2 /* 'r' -> */,
+/* pos 03a6: 515 */ 0xE9 /* 'i' -> */,
+/* pos 03a7: 516 */ 0xFA /* 'z' -> */,
+/* pos 03a8: 517 */ 0xE1 /* 'a' -> */,
+/* pos 03a9: 518 */ 0xF4 /* 't' -> */,
+/* pos 03aa: 519 */ 0xE9 /* 'i' -> */,
+/* pos 03ab: 520 */ 0xEF /* 'o' -> */,
+/* pos 03ac: 521 */ 0xEE /* 'n' -> */,
+/* pos 03ad: 522 */ 0xBA /* ':' -> */,
+/* pos 03ae: 523 */ 0x00, 0x3E /* - terminal marker 62 - */,
+/* pos 03b0: 524 */ 0xE5 /* 'e' -> */,
+/* pos 03b1: 525 */ 0xF3 /* 's' -> */,
+/* pos 03b2: 526 */ 0xE8 /* 'h' -> */,
+/* pos 03b3: 527 */ 0xBA /* ':' -> */,
+/* pos 03b4: 528 */ 0x00, 0x3F /* - terminal marker 63 - */,
+/* pos 03b6: 529 */ 0xF2 /* 'r' -> */,
+/* pos 03b7: 530 */ 0xF9 /* 'y' -> */,
+/* pos 03b8: 531 */ 0xAD /* '-' -> */,
+/* pos 03b9: 532 */ 0xE1 /* 'a' -> */,
+/* pos 03ba: 533 */ 0xE6 /* 'f' -> */,
+/* pos 03bb: 534 */ 0xF4 /* 't' -> */,
+/* pos 03bc: 535 */ 0xE5 /* 'e' -> */,
+/* pos 03bd: 536 */ 0xF2 /* 'r' -> */,
+/* pos 03be: 537 */ 0xBA /* ':' -> */,
+/* pos 03bf: 538 */ 0x00, 0x40 /* - terminal marker 64 - */,
+/* pos 03c1: 539 */ 0xF6 /* 'v' -> */,
+/* pos 03c2: 540 */ 0xE5 /* 'e' -> */,
+/* pos 03c3: 541 */ 0xF2 /* 'r' -> */,
+/* pos 03c4: 542 */ 0xBA /* ':' -> */,
+/* pos 03c5: 543 */ 0x00, 0x41 /* - terminal marker 65 - */,
+/* pos 03c7: 544 */ 0xAD /* '-' -> */,
+/* pos 03c8: 545 */ 0xE3 /* 'c' -> */,
+/* pos 03c9: 546 */ 0xEF /* 'o' -> */,
+/* pos 03ca: 547 */ 0xEF /* 'o' -> */,
+/* pos 03cb: 548 */ 0xEB /* 'k' -> */,
+/* pos 03cc: 549 */ 0xE9 /* 'i' -> */,
+/* pos 03cd: 550 */ 0xE5 /* 'e' -> */,
+/* pos 03ce: 551 */ 0xBA /* ':' -> */,
+/* pos 03cf: 552 */ 0x00, 0x42 /* - terminal marker 66 - */,
+/* pos 03d1: 553 */ 0xF2 /* 'r' -> */,
+/* pos 03d2: 554 */ 0xE9 /* 'i' -> */,
+/* pos 03d3: 555 */ 0xE3 /* 'c' -> */,
+/* pos 03d4: 556 */ 0xF4 /* 't' -> */,
+/* pos 03d5: 557 */ 0xAD /* '-' -> */,
+/* pos 03d6: 558 */ 0xF4 /* 't' -> */,
+/* pos 03d7: 559 */ 0xF2 /* 'r' -> */,
+/* pos 03d8: 560 */ 0xE1 /* 'a' -> */,
+/* pos 03d9: 561 */ 0xEE /* 'n' -> */,
+/* pos 03da: 562 */ 0xF3 /* 's' -> */,
+/* pos 03db: 563 */ 0xF0 /* 'p' -> */,
+/* pos 03dc: 564 */ 0xEF /* 'o' -> */,
+/* pos 03dd: 565 */ 0xF2 /* 'r' -> */,
+/* pos 03de: 566 */ 0xF4 /* 't' -> */,
+/* pos 03df: 567 */ 0xAD /* '-' -> */,
+/* pos 03e0: 568 */ 0xF3 /* 's' -> */,
+/* pos 03e1: 569 */ 0xE5 /* 'e' -> */,
+/* pos 03e2: 570 */ 0xE3 /* 'c' -> */,
+/* pos 03e3: 571 */ 0xF5 /* 'u' -> */,
+/* pos 03e4: 572 */ 0xF2 /* 'r' -> */,
+/* pos 03e5: 573 */ 0xE9 /* 'i' -> */,
+/* pos 03e6: 574 */ 0xF4 /* 't' -> */,
+/* pos 03e7: 575 */ 0xF9 /* 'y' -> */,
+/* pos 03e8: 576 */ 0xBA /* ':' -> */,
+/* pos 03e9: 577 */ 0x00, 0x43 /* - terminal marker 67 - */,
+/* pos 03eb: 578 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x03F2 state 579) */,
+ 0x65 /* 'e' */, 0x87, 0x00 /* (to 0x0475 state 680) */,
+ 0x08, /* fail */
+/* pos 03f2: 579 */ 0xE1 /* 'a' -> */,
+/* pos 03f3: 580 */ 0xEE /* 'n' -> */,
+/* pos 03f4: 581 */ 0xF3 /* 's' -> */,
+/* pos 03f5: 582 */ 0xE6 /* 'f' -> */,
+/* pos 03f6: 583 */ 0xE5 /* 'e' -> */,
+/* pos 03f7: 584 */ 0xF2 /* 'r' -> */,
+/* pos 03f8: 585 */ 0xAD /* '-' -> */,
+/* pos 03f9: 586 */ 0xE5 /* 'e' -> */,
+/* pos 03fa: 587 */ 0xEE /* 'n' -> */,
+/* pos 03fb: 588 */ 0xE3 /* 'c' -> */,
+/* pos 03fc: 589 */ 0xEF /* 'o' -> */,
+/* pos 03fd: 590 */ 0xE4 /* 'd' -> */,
+/* pos 03fe: 591 */ 0xE9 /* 'i' -> */,
+/* pos 03ff: 592 */ 0xEE /* 'n' -> */,
+/* pos 0400: 593 */ 0xE7 /* 'g' -> */,
+/* pos 0401: 594 */ 0xBA /* ':' -> */,
+/* pos 0402: 595 */ 0x00, 0x44 /* - terminal marker 68 - */,
+/* pos 0404: 596 */ 0xE5 /* 'e' -> */,
+/* pos 0405: 597 */ 0xF2 /* 'r' -> */,
+/* pos 0406: 598 */ 0xAD /* '-' -> */,
+/* pos 0407: 599 */ 0xE1 /* 'a' -> */,
+/* pos 0408: 600 */ 0xE7 /* 'g' -> */,
+/* pos 0409: 601 */ 0xE5 /* 'e' -> */,
+/* pos 040a: 602 */ 0xEE /* 'n' -> */,
+/* pos 040b: 603 */ 0xF4 /* 't' -> */,
+/* pos 040c: 604 */ 0xBA /* ':' -> */,
+/* pos 040d: 605 */ 0x00, 0x45 /* - terminal marker 69 - */,
+/* pos 040f: 606 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0416 state 607) */,
+ 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x041B state 611) */,
+ 0x08, /* fail */
+/* pos 0416: 607 */ 0xF2 /* 'r' -> */,
+/* pos 0417: 608 */ 0xF9 /* 'y' -> */,
+/* pos 0418: 609 */ 0xBA /* ':' -> */,
+/* pos 0419: 610 */ 0x00, 0x46 /* - terminal marker 70 - */,
+/* pos 041b: 611 */ 0xE1 /* 'a' -> */,
+/* pos 041c: 612 */ 0xBA /* ':' -> */,
+/* pos 041d: 613 */ 0x00, 0x47 /* - terminal marker 71 - */,
+/* pos 041f: 614 */ 0xF7 /* 'w' -> */,
+/* pos 0420: 615 */ 0xF7 /* 'w' -> */,
+/* pos 0421: 616 */ 0xAD /* '-' -> */,
+/* pos 0422: 617 */ 0xE1 /* 'a' -> */,
+/* pos 0423: 618 */ 0xF5 /* 'u' -> */,
+/* pos 0424: 619 */ 0xF4 /* 't' -> */,
+/* pos 0425: 620 */ 0xE8 /* 'h' -> */,
+/* pos 0426: 621 */ 0xE5 /* 'e' -> */,
+/* pos 0427: 622 */ 0xEE /* 'n' -> */,
+/* pos 0428: 623 */ 0xF4 /* 't' -> */,
+/* pos 0429: 624 */ 0xE9 /* 'i' -> */,
+/* pos 042a: 625 */ 0xE3 /* 'c' -> */,
+/* pos 042b: 626 */ 0xE1 /* 'a' -> */,
+/* pos 042c: 627 */ 0xF4 /* 't' -> */,
+/* pos 042d: 628 */ 0xE5 /* 'e' -> */,
+/* pos 042e: 629 */ 0xBA /* ':' -> */,
+/* pos 042f: 630 */ 0x00, 0x48 /* - terminal marker 72 - */,
+/* pos 0431: 631 */ 0xF4 /* 't' -> */,
+/* pos 0432: 632 */ 0xE3 /* 'c' -> */,
+/* pos 0433: 633 */ 0xE8 /* 'h' -> */,
+/* pos 0434: 634 */ 0x00, 0x49 /* - terminal marker 73 - */,
+/* pos 0436: 635 */ 0xF4 /* 't' -> */,
+/* pos 0437: 636 */ 0x00, 0x4A /* - terminal marker 74 - */,
+/* pos 0439: 637 */ 0xEC /* 'l' -> */,
+/* pos 043a: 638 */ 0xE5 /* 'e' -> */,
+/* pos 043b: 639 */ 0xF4 /* 't' -> */,
+/* pos 043c: 640 */ 0xE5 /* 'e' -> */,
+/* pos 043d: 641 */ 0x00, 0x4B /* - terminal marker 75 - */,
+/* pos 043f: 642 */ 0xE9 /* 'i' -> */,
+/* pos 0440: 643 */ 0xAD /* '-' -> */,
+/* pos 0441: 644 */ 0xE1 /* 'a' -> */,
+/* pos 0442: 645 */ 0xF2 /* 'r' -> */,
+/* pos 0443: 646 */ 0xE7 /* 'g' -> */,
+/* pos 0444: 647 */ 0xF3 /* 's' -> */,
+/* pos 0445: 648 */ 0x00, 0x4C /* - terminal marker 76 - */,
+/* pos 0447: 649 */ 0x00, 0x4D /* - terminal marker 77 - */,
+/* pos 0449: 650 */ 0xAD /* '-' -> */,
+/* pos 044a: 651 */ 0x72 /* 'r' */, 0x0A, 0x00 /* (to 0x0454 state 652) */,
+ 0x66 /* 'f' */, 0x13, 0x00 /* (to 0x0460 state 662) */,
+ 0x61 /* 'a' */, 0x3C, 0x00 /* (to 0x048C state 700) */,
+ 0x08, /* fail */
+/* pos 0454: 652 */ 0xE5 /* 'e' -> */,
+/* pos 0455: 653 */ 0xE1 /* 'a' -> */,
+/* pos 0456: 654 */ 0xEC /* 'l' -> */,
+/* pos 0457: 655 */ 0xAD /* '-' -> */,
+/* pos 0458: 656 */ 0xE9 /* 'i' -> */,
+/* pos 0459: 657 */ 0xF0 /* 'p' -> */,
+/* pos 045a: 658 */ 0xBA /* ':' -> */,
+/* pos 045b: 659 */ 0x00, 0x4E /* - terminal marker 78 - */,
+/* pos 045d: 660 */ 0xA0 /* ' ' -> */,
+/* pos 045e: 661 */ 0x00, 0x4F /* - terminal marker 79 - */,
+/* pos 0460: 662 */ 0xEF /* 'o' -> */,
+/* pos 0461: 663 */ 0xF2 /* 'r' -> */,
+/* pos 0462: 664 */ 0xF7 /* 'w' -> */,
+/* pos 0463: 665 */ 0xE1 /* 'a' -> */,
+/* pos 0464: 666 */ 0xF2 /* 'r' -> */,
+/* pos 0465: 667 */ 0xE4 /* 'd' -> */,
+/* pos 0466: 668 */ 0xE5 /* 'e' -> */,
+/* pos 0467: 669 */ 0xE4 /* 'd' -> */,
+/* pos 0468: 670 */ 0xAD /* '-' -> */,
+/* pos 0469: 671 */ 0xE6 /* 'f' -> */,
+/* pos 046a: 672 */ 0xEF /* 'o' -> */,
+/* pos 046b: 673 */ 0xF2 /* 'r' -> */,
+/* pos 046c: 674 */ 0x00, 0x50 /* - terminal marker 80 - */,
+/* pos 046e: 675 */ 0x00, 0x51 /* - terminal marker 81 - */,
+/* pos 0470: 676 */ 0xE1 /* 'a' -> */,
+/* pos 0471: 677 */ 0xE4 /* 'd' -> */,
+/* pos 0472: 678 */ 0xA0 /* ' ' -> */,
+/* pos 0473: 679 */ 0x00, 0x52 /* - terminal marker 82 - */,
+/* pos 0475: 680 */ 0xBA /* ':' -> */,
+/* pos 0476: 681 */ 0x00, 0x53 /* - terminal marker 83 - */,
+/* pos 0478: 682 */ 0xEC /* 'l' -> */,
+/* pos 0479: 683 */ 0xE1 /* 'a' -> */,
+/* pos 047a: 684 */ 0xF9 /* 'y' -> */,
+/* pos 047b: 685 */ 0xAD /* '-' -> */,
+/* pos 047c: 686 */ 0xEE /* 'n' -> */,
+/* pos 047d: 687 */ 0xEF /* 'o' -> */,
+/* pos 047e: 688 */ 0xEE /* 'n' -> */,
+/* pos 047f: 689 */ 0xE3 /* 'c' -> */,
+/* pos 0480: 690 */ 0xE5 /* 'e' -> */,
+/* pos 0481: 691 */ 0xBA /* ':' -> */,
+/* pos 0482: 692 */ 0x00, 0x54 /* - terminal marker 84 - */,
+/* pos 0484: 693 */ 0xEF /* 'o' -> */,
+/* pos 0485: 694 */ 0xF4 /* 't' -> */,
+/* pos 0486: 695 */ 0xEF /* 'o' -> */,
+/* pos 0487: 696 */ 0xE3 /* 'c' -> */,
+/* pos 0488: 697 */ 0xEF /* 'o' -> */,
+/* pos 0489: 698 */ 0xEC /* 'l' -> */,
+/* pos 048a: 699 */ 0x00, 0x55 /* - terminal marker 85 - */,
+/* pos 048c: 700 */ 0xF5 /* 'u' -> */,
+/* pos 048d: 701 */ 0xF4 /* 't' -> */,
+/* pos 048e: 702 */ 0xE8 /* 'h' -> */,
+/* pos 048f: 703 */ 0xAD /* '-' -> */,
+/* pos 0490: 704 */ 0xF4 /* 't' -> */,
+/* pos 0491: 705 */ 0xEF /* 'o' -> */,
+/* pos 0492: 706 */ 0xEB /* 'k' -> */,
+/* pos 0493: 707 */ 0xE5 /* 'e' -> */,
+/* pos 0494: 708 */ 0xEE /* 'n' -> */,
+/* pos 0495: 709 */ 0xBA /* ':' -> */,
+/* pos 0496: 710 */ 0x00, 0x56 /* - terminal marker 86 - */,
+/* total size 1176 bytes */
diff --git a/thirdparty/libwebsockets/roles/http/private.h b/thirdparty/libwebsockets/roles/http/private.h
new file mode 100644
index 0000000000..2aa7a92f75
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/http/private.h
@@ -0,0 +1,257 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * This is included from core/private.h if either H1 or H2 roles are
+ * enabled
+ */
+
+#if defined(LWS_WITH_HTTP_PROXY)
+ #include <hubbub/hubbub.h>
+ #include <hubbub/parser.h>
+ #endif
+
+#define lwsi_role_http(wsi) (lwsi_role_h1(wsi) || lwsi_role_h2(wsi))
+
+enum http_version {
+ HTTP_VERSION_1_0,
+ HTTP_VERSION_1_1,
+ HTTP_VERSION_2
+};
+
+enum http_connection_type {
+ HTTP_CONNECTION_CLOSE,
+ HTTP_CONNECTION_KEEP_ALIVE
+};
+
+/*
+ * This is totally opaque to code using the library. It's exported as a
+ * forward-reference pointer-only declaration; the user can use the pointer with
+ * other APIs to get information out of it.
+ */
+
+#if defined(LWS_WITH_ESP32)
+typedef uint16_t ah_data_idx_t;
+#else
+typedef uint32_t ah_data_idx_t;
+#endif
+
+struct lws_fragments {
+ ah_data_idx_t offset;
+ uint16_t len;
+ uint8_t nfrag; /* which ah->frag[] continues this content, or 0 */
+ uint8_t flags; /* only http2 cares */
+};
+
+#if defined(LWS_WITH_RANGES)
+enum range_states {
+ LWSRS_NO_ACTIVE_RANGE,
+ LWSRS_BYTES_EQ,
+ LWSRS_FIRST,
+ LWSRS_STARTING,
+ LWSRS_ENDING,
+ LWSRS_COMPLETED,
+ LWSRS_SYNTAX,
+};
+
+struct lws_range_parsing {
+ unsigned long long start, end, extent, agg, budget;
+ const char buf[128];
+ int pos;
+ enum range_states state;
+ char start_valid, end_valid, ctr, count_ranges, did_try, inside, send_ctr;
+};
+
+int
+lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp,
+ unsigned long long extent);
+int
+lws_ranges_next(struct lws_range_parsing *rp);
+void
+lws_ranges_reset(struct lws_range_parsing *rp);
+#endif
+
+/*
+ * these are assigned from a pool held in the context.
+ * Both client and server mode uses them for http header analysis
+ */
+
+struct allocated_headers {
+ struct allocated_headers *next; /* linked list */
+ struct lws *wsi; /* owner */
+ char *data; /* prepared by context init to point to dedicated storage */
+ ah_data_idx_t data_length;
+ /*
+ * the randomly ordered fragments, indexed by frag_index and
+ * lws_fragments->nfrag for continuation.
+ */
+ struct lws_fragments frags[WSI_TOKEN_COUNT];
+ time_t assigned;
+ /*
+ * for each recognized token, frag_index says which frag[] his data
+ * starts in (0 means the token did not appear)
+ * the actual header data gets dumped as it comes in, into data[]
+ */
+ uint8_t frag_index[WSI_TOKEN_COUNT];
+
+#ifndef LWS_NO_CLIENT
+ char initial_handshake_hash_base64[30];
+#endif
+
+ uint32_t pos;
+ uint32_t http_response;
+ uint32_t current_token_limit;
+ int hdr_token_idx;
+
+ int16_t lextable_pos;
+
+ uint8_t in_use;
+ uint8_t nfrag;
+ char /*enum uri_path_states */ ups;
+ char /*enum uri_esc_states */ ues;
+
+ char esc_stash;
+ char post_literal_equal;
+ uint8_t /* enum lws_token_indexes */ parser_state;
+};
+
+
+
+#if defined(LWS_WITH_HTTP_PROXY)
+struct lws_rewrite {
+ hubbub_parser *parser;
+ hubbub_parser_optparams params;
+ const char *from, *to;
+ int from_len, to_len;
+ unsigned char *p, *end;
+ struct lws *wsi;
+};
+static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len)
+{
+ if ((int)s->len != len)
+ return 1;
+
+ return strncmp((const char *)s->ptr, p, len);
+}
+typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw);
+LWS_EXTERN struct lws_rewrite *
+lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to);
+LWS_EXTERN void
+lws_rewrite_destroy(struct lws_rewrite *r);
+LWS_EXTERN int
+lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len);
+#endif
+
+struct lws_pt_role_http {
+ struct allocated_headers *ah_list;
+ struct lws *ah_wait_list;
+#ifdef LWS_WITH_CGI
+ struct lws_cgi *cgi_list;
+#endif
+ int ah_wait_list_length;
+ uint32_t ah_pool_length;
+
+ int ah_count_in_use;
+};
+
+struct lws_peer_role_http {
+ uint32_t count_ah;
+ uint32_t total_ah;
+};
+
+struct lws_vhost_role_http {
+ char http_proxy_address[128];
+ const struct lws_http_mount *mount_list;
+ const char *error_document_404;
+ unsigned int http_proxy_port;
+};
+
+#ifdef LWS_WITH_ACCESS_LOG
+struct lws_access_log {
+ char *header_log;
+ char *user_agent;
+ char *referrer;
+ unsigned long sent;
+ int response;
+};
+#endif
+
+struct _lws_http_mode_related {
+ struct lws *new_wsi_list;
+
+#if defined(LWS_WITH_HTTP_PROXY)
+ struct lws_rewrite *rw;
+#endif
+ struct allocated_headers *ah;
+ struct lws *ah_wait_list;
+
+ lws_filepos_t filepos;
+ lws_filepos_t filelen;
+ lws_fop_fd_t fop_fd;
+
+#if defined(LWS_WITH_RANGES)
+ struct lws_range_parsing range;
+ char multipart_content_type[64];
+#endif
+
+#ifdef LWS_WITH_ACCESS_LOG
+ struct lws_access_log access_log;
+#endif
+#ifdef LWS_WITH_CGI
+ struct lws_cgi *cgi; /* wsi being cgi master have one of these */
+#endif
+
+ enum http_version request_version;
+ enum http_connection_type connection_type;
+ lws_filepos_t tx_content_length;
+ lws_filepos_t tx_content_remain;
+ lws_filepos_t rx_content_length;
+ lws_filepos_t rx_content_remain;
+
+#if defined(LWS_WITH_HTTP_PROXY)
+ unsigned int perform_rewrite:1;
+#endif
+};
+
+
+#ifndef LWS_NO_CLIENT
+enum lws_chunk_parser {
+ ELCP_HEX,
+ ELCP_CR,
+ ELCP_CONTENT,
+ ELCP_POST_CR,
+ ELCP_POST_LF,
+};
+#endif
+
+enum lws_parse_urldecode_results {
+ LPUR_CONTINUE,
+ LPUR_SWALLOW,
+ LPUR_FORBID,
+ LPUR_EXCESSIVE,
+};
+
+int
+lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
+
+void
+_lws_header_table_reset(struct allocated_headers *ah);
+
+LWS_EXTERN int
+_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah);
diff --git a/thirdparty/libwebsockets/roles/http/server/access-log.c b/thirdparty/libwebsockets/roles/http/server/access-log.c
new file mode 100644
index 0000000000..0e75309d7a
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/http/server/access-log.c
@@ -0,0 +1,182 @@
+/*
+ * libwebsockets - server access log handling
+ *
+ * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+
+/*
+ * Produce Apache-compatible log string for wsi, like this:
+ *
+ * 2.31.234.19 - - [27/Mar/2016:03:22:44 +0800]
+ * "GET /aep-screen.png HTTP/1.1"
+ * 200 152987 "https://libwebsockets.org/index.html"
+ * "Mozilla/5.0 (Macint... Chrome/49.0.2623.87 Safari/537.36"
+ *
+ */
+
+extern const char * const method_names[];
+
+static const char * const hver[] = {
+ "HTTP/1.0", "HTTP/1.1", "HTTP/2"
+};
+
+void
+lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth)
+{
+#ifdef LWS_WITH_IPV6
+ char ads[INET6_ADDRSTRLEN];
+#else
+ char ads[INET_ADDRSTRLEN];
+#endif
+ char da[64];
+ const char *pa, *me;
+ struct tm *tmp;
+ time_t t = time(NULL);
+ int l = 256, m;
+
+ if (!wsi->vhost)
+ return;
+
+ /* only worry about preparing it if we store it */
+ if (wsi->vhost->log_fd == (int)LWS_INVALID_FILE)
+ return;
+
+ if (wsi->access_log_pending)
+ lws_access_log(wsi);
+
+ wsi->http.access_log.header_log = lws_malloc(l, "access log");
+ if (wsi->http.access_log.header_log) {
+
+ tmp = localtime(&t);
+ if (tmp)
+ strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", tmp);
+ else
+ strcpy(da, "01/Jan/1970:00:00:00 +0000");
+
+ pa = lws_get_peer_simple(wsi, ads, sizeof(ads));
+ if (!pa)
+ pa = "(unknown)";
+
+ if (wsi->http2_substream)
+ me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
+ else
+ me = method_names[meth];
+ if (!me)
+ me = "(null)";
+
+ lws_snprintf(wsi->http.access_log.header_log, l,
+ "%s - - [%s] \"%s %s %s\"",
+ pa, da, me, uri_ptr,
+ hver[wsi->http.request_version]);
+
+ l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT);
+ if (l) {
+ wsi->http.access_log.user_agent = lws_malloc(l + 2, "access log");
+ if (!wsi->http.access_log.user_agent) {
+ lwsl_err("OOM getting user agent\n");
+ lws_free_set_NULL(wsi->http.access_log.header_log);
+ return;
+ }
+
+ lws_hdr_copy(wsi, wsi->http.access_log.user_agent,
+ l + 1, WSI_TOKEN_HTTP_USER_AGENT);
+
+ for (m = 0; m < l; m++)
+ if (wsi->http.access_log.user_agent[m] == '\"')
+ wsi->http.access_log.user_agent[m] = '\'';
+ }
+ l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER);
+ if (l) {
+ wsi->http.access_log.referrer = lws_malloc(l + 2, "referrer");
+ if (!wsi->http.access_log.referrer) {
+ lwsl_err("OOM getting user agent\n");
+ lws_free_set_NULL(wsi->http.access_log.user_agent);
+ lws_free_set_NULL(wsi->http.access_log.header_log);
+ return;
+ }
+ lws_hdr_copy(wsi, wsi->http.access_log.referrer,
+ l + 1, WSI_TOKEN_HTTP_REFERER);
+
+ for (m = 0; m < l; m++)
+ if (wsi->http.access_log.referrer[m] == '\"')
+ wsi->http.access_log.referrer[m] = '\'';
+ }
+ wsi->access_log_pending = 1;
+ }
+}
+
+
+int
+lws_access_log(struct lws *wsi)
+{
+ char *p = wsi->http.access_log.user_agent, ass[512],
+ *p1 = wsi->http.access_log.referrer;
+ int l;
+
+ if (!wsi->vhost)
+ return 0;
+
+ if (wsi->vhost->log_fd == (int)LWS_INVALID_FILE)
+ return 0;
+
+ if (!wsi->access_log_pending)
+ return 0;
+
+ if (!wsi->http.access_log.header_log)
+ return 0;
+
+ if (!p)
+ p = "";
+
+ if (!p1)
+ p1 = "";
+
+ /*
+ * We do this in two parts to restrict an oversize referrer such that
+ * we will always have space left to append an empty useragent, while
+ * maintaining the structure of the log text
+ */
+ l = lws_snprintf(ass, sizeof(ass) - 7, "%s %d %lu \"%s",
+ wsi->http.access_log.header_log,
+ wsi->http.access_log.response, wsi->http.access_log.sent, p1);
+ if (strlen(p) > sizeof(ass) - 6 - l)
+ p[sizeof(ass) - 6 - l] = '\0';
+ l += lws_snprintf(ass + l, sizeof(ass) - 1 - l, "\" \"%s\"\n", p);
+
+ if (write(wsi->vhost->log_fd, ass, l) != l)
+ lwsl_err("Failed to write log\n");
+
+ if (wsi->http.access_log.header_log) {
+ lws_free(wsi->http.access_log.header_log);
+ wsi->http.access_log.header_log = NULL;
+ }
+ if (wsi->http.access_log.user_agent) {
+ lws_free(wsi->http.access_log.user_agent);
+ wsi->http.access_log.user_agent = NULL;
+ }
+ if (wsi->http.access_log.referrer) {
+ lws_free(wsi->http.access_log.referrer);
+ wsi->http.access_log.referrer = NULL;
+ }
+ wsi->access_log_pending = 0;
+
+ return 0;
+}
+
diff --git a/thirdparty/lws/server/fops-zip.c b/thirdparty/libwebsockets/roles/http/server/fops-zip.c
index 2b254f67af..4db83ce621 100644
--- a/thirdparty/lws/server/fops-zip.c
+++ b/thirdparty/libwebsockets/roles/http/server/fops-zip.c
@@ -51,7 +51,7 @@
* MA 02110-1301 USA
*/
-#include "private-libwebsockets.h"
+#include "core/private.h"
#include <zlib.h>
@@ -241,13 +241,13 @@ lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
if (priv->hdr.filename_len != len)
goto next;
- if (len >= sizeof(buf) - 1)
+ if (len >= (int)sizeof(buf) - 1)
return LWS_FZ_ERR_NAME_TOO_LONG;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
&amount, buf, len))
return LWS_FZ_ERR_NAME_READ;
- if (amount != len)
+ if ((int)amount != len)
return LWS_FZ_ERR_NAME_READ;
buf[len] = '\0';
@@ -348,9 +348,8 @@ lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
m = sizeof(rp) - 1;
if ((vpath - vfs_path - 1) < m)
- m = vpath - vfs_path - 1;
- strncpy(rp, vfs_path, m);
- rp[m] = '\0';
+ m = lws_ptr_diff(vpath, vfs_path) - 1;
+ lws_strncpy(rp, vfs_path, m + 1);
/* open the zip file itself using the incoming fops, not fops_zip */
@@ -363,7 +362,7 @@ lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
if (*vpath == '/')
vpath++;
- m = lws_fops_zip_scan(priv, vpath, strlen(vpath));
+ m = lws_fops_zip_scan(priv, vpath, (int)strlen(vpath));
if (m) {
lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
goto bail2;
@@ -565,7 +564,7 @@ spin:
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
- /* and fall through */
+ /* fallthru */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
diff --git a/thirdparty/lws/server/lejp-conf.c b/thirdparty/libwebsockets/roles/http/server/lejp-conf.c
index c2b684c278..e9ce854cfc 100644
--- a/thirdparty/lws/server/lejp-conf.c
+++ b/thirdparty/libwebsockets/roles/http/server/lejp-conf.c
@@ -19,8 +19,7 @@
* MA 02110-1301 USA
*/
-#include "private-libwebsockets.h"
-#include "../misc/lejp.h"
+#include "core/private.h"
#ifndef _WIN32
/* this is needed for Travis CI */
@@ -40,6 +39,7 @@ static const char * const paths_global[] = {
"global.timeout-secs",
"global.reject-service-keywords[].*",
"global.reject-service-keywords[]",
+ "global.default-alpn",
};
enum lejp_global_paths {
@@ -52,7 +52,8 @@ enum lejp_global_paths {
LWJPGP_PINGPONG_SECS,
LWJPGP_TIMEOUT_SECS,
LWJPGP_REJECT_SERVICE_KEYWORDS_NAME,
- LWJPGP_REJECT_SERVICE_KEYWORDS
+ LWJPGP_REJECT_SERVICE_KEYWORDS,
+ LWJPGP_DEFAULT_ALPN,
};
static const char * const paths_vhosts[] = {
@@ -100,6 +101,10 @@ static const char * const paths_vhosts[] = {
"vhosts[].client-ssl-ca",
"vhosts[].client-ssl-ciphers",
"vhosts[].onlyraw",
+ "vhosts[].client-cert-required",
+ "vhosts[].ignore-missing-cert",
+ "vhosts[].error-document-404",
+ "vhosts[].alpn",
};
enum lejp_vhost_paths {
@@ -147,6 +152,10 @@ enum lejp_vhost_paths {
LEJPVP_CLIENT_SSL_CA,
LEJPVP_CLIENT_CIPHERS,
LEJPVP_FLAG_ONLYRAW,
+ LEJPVP_FLAG_CLIENT_CERT_REQUIRED,
+ LEJPVP_IGNORE_MISSING_CERT,
+ LEJPVP_ERROR_DOCUMENT_404,
+ LEJPVP_ALPN,
};
static const char * const parser_errs[] = {
@@ -216,7 +225,7 @@ arg_to_bool(const char *s)
if (n)
return 1;
- for (n = 0; n < ARRAY_SIZE(on); n++)
+ for (n = 0; n < (int)ARRAY_SIZE(on); n++)
if (!strcasecmp(s, on[n]))
return 1;
@@ -285,6 +294,10 @@ lejp_globals_cb(struct lejp_ctx *ctx, char reason)
a->info->timeout_secs = atoi(ctx->buf);
return 0;
+ case LWJPGP_DEFAULT_ALPN:
+ a->info->alpn = a->p;
+ break;
+
default:
return 0;
}
@@ -312,20 +325,39 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
#endif
if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
+ uint32_t i[4];
+ const char *ss;
+
/* set the defaults for this vhost */
a->valid = 1;
a->head = NULL;
a->last = NULL;
- a->info->port = 0;
- a->info->iface = NULL;
+
+ i[0] = a->info->count_threads;
+ i[1] = a->info->options & (
+ LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME |
+ LWS_SERVER_OPTION_LIBUV |
+ LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
+ LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
+ LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN |
+ LWS_SERVER_OPTION_LIBEVENT |
+ LWS_SERVER_OPTION_LIBEV
+ );
+ ss = a->info->server_string;
+ i[2] = a->info->ws_ping_pong_interval;
+ i[3] = a->info->timeout_secs;
+
+ memset(a->info, 0, sizeof(*a->info));
+
+ a->info->count_threads = i[0];
+ a->info->options = i[1];
+ a->info->server_string = ss;
+ a->info->ws_ping_pong_interval = i[2];
+ a->info->timeout_secs = i[3];
+
a->info->protocols = a->protocols;
a->info->extensions = a->extensions;
- a->info->ssl_cert_filepath = NULL;
- a->info->ssl_private_key_filepath = NULL;
- a->info->ssl_ca_filepath = NULL;
- a->info->client_ssl_cert_filepath = NULL;
- a->info->client_ssl_private_key_filepath = NULL;
- a->info->client_ssl_ca_filepath = NULL;
+#if defined(LWS_WITH_TLS)
a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384:"
"DHE-RSA-AES256-GCM-SHA384:"
@@ -339,7 +371,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
"!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:"
"!AES256-SHA256";
- a->info->timeout_secs = 5;
+#endif
a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384:"
"DHE-RSA-AES256-GCM-SHA384:"
@@ -353,13 +385,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
"!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:"
"!AES256-SHA256";
- a->info->pvo = NULL;
- a->info->headers = NULL;
a->info->keepalive_timeout = 5;
- a->info->log_filepath = NULL;
- a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK |
- LWS_SERVER_OPTION_STS | LWS_SERVER_OPTION_ONLY_RAW);
- a->enable_client_ssl = 0;
}
if (reason == LEJPCB_OBJECT_START &&
@@ -379,7 +405,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->pvo->next = a->info->pvo;
a->info->pvo = a->pvo;
a->pvo->name = a->p;
- lwsl_notice(" adding protocol %s\n", a->p);
+ lwsl_info(" adding protocol %s\n", a->p);
a->p += n;
a->pvo->value = a->p;
a->pvo->options = NULL;
@@ -431,6 +457,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
}
a->any_vhosts = 1;
+#if defined(LWS_WITH_TLS)
if (a->enable_client_ssl) {
const char *cert_filepath = a->info->client_ssl_cert_filepath;
const char *private_key_filepath = a->info->client_ssl_private_key_filepath;
@@ -444,6 +471,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
lws_init_vhost_client_ssl(a->info, vhost);
}
+#endif
return 0;
}
@@ -474,7 +502,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
if (a->last)
a->last->mount_next = m;
- for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
+ for (n = 0; n < (int)ARRAY_SIZE(mount_protocols); n++)
if (!strncmp(a->m.origin, mount_protocols[n],
strlen(mount_protocols[n]))) {
lwsl_info("----%s\n", a->m.origin);
@@ -484,7 +512,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
break;
}
- if (n == ARRAY_SIZE(mount_protocols)) {
+ if (n == (int)ARRAY_SIZE(mount_protocols)) {
lwsl_err("unsupported protocol:// %s\n", a->m.origin);
return 1;
}
@@ -573,9 +601,11 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
case LEJPVP_KEEPALIVE_TIMEOUT:
a->info->keepalive_timeout = atoi(ctx->buf);
return 0;
+#if defined(LWS_WITH_TLS)
case LEJPVP_CLIENT_CIPHERS:
a->info->client_ssl_cipher_list = a->p;
break;
+#endif
case LEJPVP_CIPHERS:
a->info->ssl_cipher_list = a->p;
break;
@@ -651,6 +681,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
case LEJPVP_ENABLE_CLIENT_SSL:
a->enable_client_ssl = arg_to_bool(ctx->buf);
return 0;
+#if defined(LWS_WITH_TLS)
case LEJPVP_CLIENT_SSL_KEY:
a->info->client_ssl_private_key_filepath = a->p;
break;
@@ -660,6 +691,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
case LEJPVP_CLIENT_SSL_CA:
a->info->client_ssl_ca_filepath = a->p;
break;
+#endif
case LEJPVP_NOIPV6:
if (arg_to_bool(ctx->buf))
@@ -683,6 +715,24 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
return 0;
+ case LEJPVP_FLAG_CLIENT_CERT_REQUIRED:
+ if (arg_to_bool(ctx->buf))
+ a->info->options |=
+ LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
+ return 0;
+
+ case LEJPVP_IGNORE_MISSING_CERT:
+ if (arg_to_bool(ctx->buf))
+ a->info->options |= LWS_SERVER_OPTION_IGNORE_MISSING_CERT;
+ else
+ a->info->options &= ~(LWS_SERVER_OPTION_IGNORE_MISSING_CERT);
+
+ return 0;
+
+ case LEJPVP_ERROR_DOCUMENT_404:
+ a->info->error_document_404 = a->p;
+ break;
+
case LEJPVP_SSL_OPTION_SET:
a->info->ssl_options_set |= atol(ctx->buf);
return 0;
@@ -690,6 +740,10 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->info->ssl_options_clear |= atol(ctx->buf);
return 0;
+ case LEJPVP_ALPN:
+ a->info->alpn = a->p;
+ break;
+
default:
return 0;
}
@@ -701,7 +755,7 @@ dostring:
n = p1 - p;
if (n > a->end - a->p)
n = a->end - a->p;
- strncpy(a->p, p, n);
+ lws_strncpy(a->p, p, n + 1);
a->p += n;
a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
p += n + strlen(ESC_INSTALL_DATADIR);
diff --git a/thirdparty/libwebsockets/roles/http/server/parsers.c b/thirdparty/libwebsockets/roles/http/server/parsers.c
new file mode 100644
index 0000000000..cb022e362b
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/http/server/parsers.c
@@ -0,0 +1,1139 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+
+static const unsigned char lextable[] = {
+ #include "../lextable.h"
+};
+
+#define FAIL_CHAR 0x08
+
+static struct allocated_headers *
+_lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size)
+{
+ struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct");
+
+ if (!ah)
+ return NULL;
+
+ ah->data = lws_malloc(data_size, "ah data");
+ if (!ah->data) {
+ lws_free(ah);
+
+ return NULL;
+ }
+ ah->next = pt->http.ah_list;
+ pt->http.ah_list = ah;
+ ah->data_length = data_size;
+ pt->http.ah_pool_length++;
+
+ lwsl_info("%s: created ah %p (size %d): pool length %d\n", __func__,
+ ah, (int)data_size, pt->http.ah_pool_length);
+
+ return ah;
+}
+
+int
+_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah)
+{
+ lws_start_foreach_llp(struct allocated_headers **, a, pt->http.ah_list) {
+ if ((*a) == ah) {
+ *a = ah->next;
+ pt->http.ah_pool_length--;
+ lwsl_info("%s: freed ah %p : pool length %d\n",
+ __func__, ah, pt->http.ah_pool_length);
+ if (ah->data)
+ lws_free(ah->data);
+ lws_free(ah);
+
+ return 0;
+ }
+ } lws_end_foreach_llp(a, next);
+
+ return 1;
+}
+
+void
+_lws_header_table_reset(struct allocated_headers *ah)
+{
+ /* init the ah to reflect no headers or data have appeared yet */
+ memset(ah->frag_index, 0, sizeof(ah->frag_index));
+ memset(ah->frags, 0, sizeof(ah->frags));
+ ah->nfrag = 0;
+ ah->pos = 0;
+ ah->http_response = 0;
+ ah->parser_state = WSI_TOKEN_NAME_PART;
+ ah->lextable_pos = 0;
+}
+
+// doesn't scrub the ah rxbuffer by default, parent must do if needed
+
+void
+__lws_header_table_reset(struct lws *wsi, int autoservice)
+{
+ struct allocated_headers *ah = wsi->http.ah;
+ struct lws_context_per_thread *pt;
+ struct lws_pollfd *pfd;
+
+ /* if we have the idea we're resetting 'our' ah, must be bound to one */
+ assert(ah);
+ /* ah also concurs with ownership */
+ assert(ah->wsi == wsi);
+
+ _lws_header_table_reset(ah);
+
+ /* since we will restart the ah, our new headers are not completed */
+ wsi->hdr_parsing_completed = 0;
+
+ /* while we hold the ah, keep a timeout on the wsi */
+ __lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
+ wsi->vhost->timeout_secs_ah_idle);
+
+ time(&ah->assigned);
+
+ if (wsi->position_in_fds_table != LWS_NO_FDS_POS &&
+ lws_buflist_next_segment_len(&wsi->buflist, NULL) &&
+ autoservice) {
+ lwsl_debug("%s: service on readbuf ah\n", __func__);
+
+ pt = &wsi->context->pt[(int)wsi->tsi];
+ /*
+ * Unlike a normal connect, we have the headers already
+ * (or the first part of them anyway)
+ */
+ pfd = &pt->fds[wsi->position_in_fds_table];
+ pfd->revents |= LWS_POLLIN;
+ lwsl_err("%s: calling service\n", __func__);
+ lws_service_fd_tsi(wsi->context, pfd, wsi->tsi);
+ }
+}
+
+void
+lws_header_table_reset(struct lws *wsi, int autoservice)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+ lws_pt_lock(pt, __func__);
+
+ __lws_header_table_reset(wsi, autoservice);
+
+ lws_pt_unlock(pt);
+}
+
+static void
+_lws_header_ensure_we_are_on_waiting_list(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ struct lws_pollargs pa;
+ struct lws **pwsi = &pt->http.ah_wait_list;
+
+ while (*pwsi) {
+ if (*pwsi == wsi)
+ return;
+ pwsi = &(*pwsi)->http.ah_wait_list;
+ }
+
+ lwsl_info("%s: wsi: %p\n", __func__, wsi);
+ wsi->http.ah_wait_list = pt->http.ah_wait_list;
+ pt->http.ah_wait_list = wsi;
+ pt->http.ah_wait_list_length++;
+
+ /* we cannot accept input then */
+
+ _lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa);
+}
+
+static int
+__lws_remove_from_ah_waiting_list(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ struct lws **pwsi =&pt->http.ah_wait_list;
+
+ while (*pwsi) {
+ if (*pwsi == wsi) {
+ lwsl_info("%s: wsi %p\n", __func__, wsi);
+ /* point prev guy to our next */
+ *pwsi = wsi->http.ah_wait_list;
+ /* we shouldn't point anywhere now */
+ wsi->http.ah_wait_list = NULL;
+ pt->http.ah_wait_list_length--;
+
+ return 1;
+ }
+ pwsi = &(*pwsi)->http.ah_wait_list;
+ }
+
+ return 0;
+}
+
+int LWS_WARN_UNUSED_RESULT
+lws_header_table_attach(struct lws *wsi, int autoservice)
+{
+ struct lws_context *context = wsi->context;
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ struct lws_pollargs pa;
+ int n;
+
+ lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__,
+ (void *)wsi, (void *)wsi->http.ah, wsi->tsi,
+ pt->http.ah_count_in_use);
+
+ lws_pt_lock(pt, __func__);
+
+ /* if we are already bound to one, just clear it down */
+ if (wsi->http.ah) {
+ lwsl_info("%s: cleardown\n", __func__);
+ goto reset;
+ }
+
+ n = pt->http.ah_count_in_use == context->max_http_header_pool;
+#if defined(LWS_WITH_PEER_LIMITS)
+ if (!n) {
+ n = lws_peer_confirm_ah_attach_ok(context, wsi->peer);
+ if (n)
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
+ }
+#endif
+ if (n) {
+ /*
+ * Pool is either all busy, or we don't want to give this
+ * particular guy an ah right now...
+ *
+ * Make sure we are on the waiting list, and return that we
+ * weren't able to provide the ah
+ */
+ _lws_header_ensure_we_are_on_waiting_list(wsi);
+
+ goto bail;
+ }
+
+ __lws_remove_from_ah_waiting_list(wsi);
+
+ wsi->http.ah = _lws_create_ah(pt, context->max_http_header_data);
+ if (!wsi->http.ah) { /* we could not create an ah */
+ _lws_header_ensure_we_are_on_waiting_list(wsi);
+
+ goto bail;
+ }
+
+ wsi->http.ah->in_use = 1;
+ wsi->http.ah->wsi = wsi; /* mark our owner */
+ pt->http.ah_count_in_use++;
+
+#if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
+ lws_context_lock(context); /* <====================================== */
+ if (wsi->peer)
+ wsi->peer->http.count_ah++;
+ lws_context_unlock(context); /* ====================================> */
+#endif
+
+ _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
+
+ lwsl_info("%s: did attach wsi %p: ah %p: count %d (on exit)\n", __func__,
+ (void *)wsi, (void *)wsi->http.ah, pt->http.ah_count_in_use);
+
+reset:
+ __lws_header_table_reset(wsi, autoservice);
+
+ lws_pt_unlock(pt);
+
+#ifndef LWS_NO_CLIENT
+ if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED)
+ if (!lws_client_connect_via_info2(wsi))
+ /* our client connect has failed, the wsi
+ * has been closed
+ */
+ return -1;
+#endif
+
+ return 0;
+
+bail:
+ lws_pt_unlock(pt);
+
+ return 1;
+}
+
+int __lws_header_table_detach(struct lws *wsi, int autoservice)
+{
+ struct lws_context *context = wsi->context;
+ struct allocated_headers *ah = wsi->http.ah;
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ struct lws_pollargs pa;
+ struct lws **pwsi, **pwsi_eligible;
+ time_t now;
+
+ __lws_remove_from_ah_waiting_list(wsi);
+
+ if (!ah)
+ return 0;
+
+ lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
+ (void *)wsi, (void *)ah, wsi->tsi,
+ pt->http.ah_count_in_use);
+
+ /* we did have an ah attached */
+ time(&now);
+ if (ah->assigned && now - ah->assigned > 3) {
+ /*
+ * we're detaching the ah, but it was held an
+ * unreasonably long time
+ */
+ lwsl_debug("%s: wsi %p: ah held %ds, role/state 0x%x 0x%x,"
+ "\n", __func__, wsi, (int)(now - ah->assigned),
+ lwsi_role(wsi), lwsi_state(wsi));
+ }
+
+ ah->assigned = 0;
+
+ /* if we think we're detaching one, there should be one in use */
+ assert(pt->http.ah_count_in_use > 0);
+ /* and this specific one should have been in use */
+ assert(ah->in_use);
+ memset(&wsi->http.ah, 0, sizeof(wsi->http.ah));
+
+#if defined(LWS_WITH_PEER_LIMITS)
+ if (ah->wsi)
+ lws_peer_track_ah_detach(context, wsi->peer);
+#endif
+ ah->wsi = NULL; /* no owner */
+
+ pwsi = &pt->http.ah_wait_list;
+
+ /* oh there is nobody on the waiting list... leave the ah unattached */
+ if (!*pwsi)
+ goto nobody_usable_waiting;
+
+ /*
+ * at least one wsi on the same tsi is waiting, give it to oldest guy
+ * who is allowed to take it (if any)
+ */
+ lwsl_info("pt wait list %p\n", *pwsi);
+ wsi = NULL;
+ pwsi_eligible = NULL;
+
+ while (*pwsi) {
+#if defined(LWS_WITH_PEER_LIMITS)
+ /* are we willing to give this guy an ah? */
+ if (!lws_peer_confirm_ah_attach_ok(context, (*pwsi)->peer))
+#endif
+ {
+ wsi = *pwsi;
+ pwsi_eligible = pwsi;
+ }
+#if defined(LWS_WITH_PEER_LIMITS)
+ else
+ if (!(*pwsi)->http.ah_wait_list)
+ lws_stats_atomic_bump(context, pt,
+ LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
+#endif
+ pwsi = &(*pwsi)->http.ah_wait_list;
+ }
+
+ if (!wsi) /* everybody waiting already has too many ah... */
+ goto nobody_usable_waiting;
+
+ lwsl_info("%s: transferring ah to last eligible wsi in wait list %p (wsistate 0x%x)\n", __func__, wsi, wsi->wsistate);
+
+ wsi->http.ah = ah;
+ ah->wsi = wsi; /* new owner */
+
+ __lws_header_table_reset(wsi, autoservice);
+#if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
+ lws_context_lock(context); /* <====================================== */
+ if (wsi->peer)
+ wsi->peer->http.count_ah++;
+ lws_context_unlock(context); /* ====================================> */
+#endif
+
+ /* clients acquire the ah and then insert themselves in fds table... */
+ if (wsi->position_in_fds_table != LWS_NO_FDS_POS) {
+ lwsl_info("%s: Enabling %p POLLIN\n", __func__, wsi);
+
+ /* he has been stuck waiting for an ah, but now his wait is
+ * over, let him progress */
+
+ _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
+ }
+
+ /* point prev guy to next guy in list instead */
+ *pwsi_eligible = wsi->http.ah_wait_list;
+ /* the guy who got one is out of the list */
+ wsi->http.ah_wait_list = NULL;
+ pt->http.ah_wait_list_length--;
+
+#ifndef LWS_NO_CLIENT
+ if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) {
+ lws_pt_unlock(pt);
+
+ if (!lws_client_connect_via_info2(wsi)) {
+ /* our client connect has failed, the wsi
+ * has been closed
+ */
+
+ return -1;
+ }
+ return 0;
+ }
+#endif
+
+ assert(!!pt->http.ah_wait_list_length == !!(lws_intptr_t)pt->http.ah_wait_list);
+bail:
+ lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
+ (void *)wsi, (void *)ah, pt->tid, pt->http.ah_count_in_use);
+
+ return 0;
+
+nobody_usable_waiting:
+ lwsl_info("%s: nobody usable waiting\n", __func__);
+ _lws_destroy_ah(pt, ah);
+ pt->http.ah_count_in_use--;
+
+ goto bail;
+}
+
+int lws_header_table_detach(struct lws *wsi, int autoservice)
+{
+ struct lws_context *context = wsi->context;
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ int n;
+
+ lws_pt_lock(pt, __func__);
+ n = __lws_header_table_detach(wsi, autoservice);
+ lws_pt_unlock(pt);
+
+ return n;
+}
+
+LWS_VISIBLE int
+lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx)
+{
+ int n;
+
+ if (!wsi->http.ah)
+ return 0;
+
+ n = wsi->http.ah->frag_index[h];
+ if (!n)
+ return 0;
+ do {
+ if (!frag_idx)
+ return wsi->http.ah->frags[n].len;
+ n = wsi->http.ah->frags[n].nfrag;
+ } while (frag_idx-- && n);
+
+ return 0;
+}
+
+LWS_VISIBLE int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h)
+{
+ int n;
+ int len = 0;
+
+ if (!wsi->http.ah)
+ return 0;
+
+ n = wsi->http.ah->frag_index[h];
+ if (!n)
+ return 0;
+ do {
+ len += wsi->http.ah->frags[n].len;
+ n = wsi->http.ah->frags[n].nfrag;
+ } while (n);
+
+ return len;
+}
+
+LWS_VISIBLE int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len,
+ enum lws_token_indexes h, int frag_idx)
+{
+ int n = 0;
+ int f;
+
+ if (!wsi->http.ah)
+ return -1;
+
+ f = wsi->http.ah->frag_index[h];
+
+ if (!f)
+ return -1;
+
+ while (n < frag_idx) {
+ f = wsi->http.ah->frags[f].nfrag;
+ if (!f)
+ return -1;
+ n++;
+ }
+
+ if (wsi->http.ah->frags[f].len >= len)
+ return -1;
+
+ memcpy(dst, wsi->http.ah->data + wsi->http.ah->frags[f].offset,
+ wsi->http.ah->frags[f].len);
+ dst[wsi->http.ah->frags[f].len] = '\0';
+
+ return wsi->http.ah->frags[f].len;
+}
+
+LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len,
+ enum lws_token_indexes h)
+{
+ int toklen = lws_hdr_total_length(wsi, h);
+ int n;
+
+ if (toklen >= len)
+ return -1;
+
+ if (!wsi->http.ah)
+ return -1;
+
+ n = wsi->http.ah->frag_index[h];
+ if (!n)
+ return 0;
+
+ do {
+ if (wsi->http.ah->frags[n].len >= len)
+ return -1;
+ strncpy(dst, &wsi->http.ah->data[wsi->http.ah->frags[n].offset],
+ wsi->http.ah->frags[n].len);
+ dst += wsi->http.ah->frags[n].len;
+ len -= wsi->http.ah->frags[n].len;
+ n = wsi->http.ah->frags[n].nfrag;
+ } while (n);
+ *dst = '\0';
+
+ return toklen;
+}
+
+char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)
+{
+ int n;
+
+ n = wsi->http.ah->frag_index[h];
+ if (!n)
+ return NULL;
+
+ return wsi->http.ah->data + wsi->http.ah->frags[n].offset;
+}
+
+static int LWS_WARN_UNUSED_RESULT
+lws_pos_in_bounds(struct lws *wsi)
+{
+ if (wsi->http.ah->pos <
+ (unsigned int)wsi->context->max_http_header_data)
+ return 0;
+
+ if ((int)wsi->http.ah->pos == wsi->context->max_http_header_data) {
+ lwsl_err("Ran out of header data space\n");
+ return 1;
+ }
+
+ /*
+ * with these tests everywhere, it should never be able to exceed
+ * the limit, only meet it
+ */
+ lwsl_err("%s: pos %d, limit %d\n", __func__, wsi->http.ah->pos,
+ wsi->context->max_http_header_data);
+ assert(0);
+
+ return 1;
+}
+
+int LWS_WARN_UNUSED_RESULT
+lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
+{
+ wsi->http.ah->nfrag++;
+ if (wsi->http.ah->nfrag == ARRAY_SIZE(wsi->http.ah->frags)) {
+ lwsl_warn("More hdr frags than we can deal with, dropping\n");
+ return -1;
+ }
+
+ wsi->http.ah->frag_index[h] = wsi->http.ah->nfrag;
+
+ wsi->http.ah->frags[wsi->http.ah->nfrag].offset = wsi->http.ah->pos;
+ wsi->http.ah->frags[wsi->http.ah->nfrag].len = 0;
+ wsi->http.ah->frags[wsi->http.ah->nfrag].nfrag = 0;
+
+ do {
+ if (lws_pos_in_bounds(wsi))
+ return -1;
+
+ wsi->http.ah->data[wsi->http.ah->pos++] = *s;
+ if (*s)
+ wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
+ } while (*s++);
+
+ return 0;
+}
+
+static int LWS_WARN_UNUSED_RESULT
+issue_char(struct lws *wsi, unsigned char c)
+{
+ unsigned short frag_len;
+
+ if (lws_pos_in_bounds(wsi))
+ return -1;
+
+ frag_len = wsi->http.ah->frags[wsi->http.ah->nfrag].len;
+ /*
+ * If we haven't hit the token limit, just copy the character into
+ * the header
+ */
+ if (frag_len < wsi->http.ah->current_token_limit) {
+ wsi->http.ah->data[wsi->http.ah->pos++] = c;
+ if (c)
+ wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
+ return 0;
+ }
+
+ /* Insert a null character when we *hit* the limit: */
+ if (frag_len == wsi->http.ah->current_token_limit) {
+ if (lws_pos_in_bounds(wsi))
+ return -1;
+
+ wsi->http.ah->data[wsi->http.ah->pos++] = '\0';
+ lwsl_warn("header %i exceeds limit %d\n",
+ wsi->http.ah->parser_state,
+ wsi->http.ah->current_token_limit);
+ }
+
+ return 1;
+}
+
+int
+lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
+{
+ struct allocated_headers *ah = wsi->http.ah;
+ unsigned int enc = 0;
+ uint8_t c = *_c;
+
+ // lwsl_notice("ah->ups %d\n", ah->ups);
+
+ /*
+ * PRIORITY 1
+ * special URI processing... convert %xx
+ */
+ switch (ah->ues) {
+ case URIES_IDLE:
+ if (c == '%') {
+ ah->ues = URIES_SEEN_PERCENT;
+ goto swallow;
+ }
+ break;
+ case URIES_SEEN_PERCENT:
+ if (char_to_hex(c) < 0)
+ /* illegal post-% char */
+ goto forbid;
+
+ ah->esc_stash = c;
+ ah->ues = URIES_SEEN_PERCENT_H1;
+ goto swallow;
+
+ case URIES_SEEN_PERCENT_H1:
+ if (char_to_hex(c) < 0)
+ /* illegal post-% char */
+ goto forbid;
+
+ *_c = (char_to_hex(ah->esc_stash) << 4) |
+ char_to_hex(c);
+ c = *_c;
+ enc = 1;
+ ah->ues = URIES_IDLE;
+ break;
+ }
+
+ /*
+ * PRIORITY 2
+ * special URI processing...
+ * convert /.. or /... or /../ etc to /
+ * convert /./ to /
+ * convert // or /// etc to /
+ * leave /.dir or whatever alone
+ */
+
+ switch (ah->ups) {
+ case URIPS_IDLE:
+ if (!c)
+ return -1;
+ /* genuine delimiter */
+ if ((c == '&' || c == ';') && !enc) {
+ if (issue_char(wsi, c) < 0)
+ return -1;
+ /* swallow the terminator */
+ ah->frags[ah->nfrag].len--;
+ /* link to next fragment */
+ ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
+ ah->nfrag++;
+ if (ah->nfrag >= ARRAY_SIZE(ah->frags))
+ goto excessive;
+ /* start next fragment after the & */
+ ah->post_literal_equal = 0;
+ ah->frags[ah->nfrag].offset = ah->pos;
+ ah->frags[ah->nfrag].len = 0;
+ ah->frags[ah->nfrag].nfrag = 0;
+ goto swallow;
+ }
+ /* uriencoded = in the name part, disallow */
+ if (c == '=' && enc &&
+ ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
+ !ah->post_literal_equal) {
+ c = '_';
+ *_c =c;
+ }
+
+ /* after the real =, we don't care how many = */
+ if (c == '=' && !enc)
+ ah->post_literal_equal = 1;
+
+ /* + to space */
+ if (c == '+' && !enc) {
+ c = ' ';
+ *_c = c;
+ }
+ /* issue the first / always */
+ if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
+ ah->ups = URIPS_SEEN_SLASH;
+ break;
+ case URIPS_SEEN_SLASH:
+ /* swallow subsequent slashes */
+ if (c == '/')
+ goto swallow;
+ /* track and swallow the first . after / */
+ if (c == '.') {
+ ah->ups = URIPS_SEEN_SLASH_DOT;
+ goto swallow;
+ }
+ ah->ups = URIPS_IDLE;
+ break;
+ case URIPS_SEEN_SLASH_DOT:
+ /* swallow second . */
+ if (c == '.') {
+ ah->ups = URIPS_SEEN_SLASH_DOT_DOT;
+ goto swallow;
+ }
+ /* change /./ to / */
+ if (c == '/') {
+ ah->ups = URIPS_SEEN_SLASH;
+ goto swallow;
+ }
+ /* it was like /.dir ... regurgitate the . */
+ ah->ups = URIPS_IDLE;
+ if (issue_char(wsi, '.') < 0)
+ return -1;
+ break;
+
+ case URIPS_SEEN_SLASH_DOT_DOT:
+
+ /* /../ or /..[End of URI] --> backup to last / */
+ if (c == '/' || c == '?') {
+ /*
+ * back up one dir level if possible
+ * safe against header fragmentation because
+ * the method URI can only be in 1 fragment
+ */
+ if (ah->frags[ah->nfrag].len > 2) {
+ ah->pos--;
+ ah->frags[ah->nfrag].len--;
+ do {
+ ah->pos--;
+ ah->frags[ah->nfrag].len--;
+ } while (ah->frags[ah->nfrag].len > 1 &&
+ ah->data[ah->pos] != '/');
+ }
+ ah->ups = URIPS_SEEN_SLASH;
+ if (ah->frags[ah->nfrag].len > 1)
+ break;
+ goto swallow;
+ }
+
+ /* /..[^/] ... regurgitate and allow */
+
+ if (issue_char(wsi, '.') < 0)
+ return -1;
+ if (issue_char(wsi, '.') < 0)
+ return -1;
+ ah->ups = URIPS_IDLE;
+ break;
+ }
+
+ if (c == '?' && !enc &&
+ !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI args */
+ if (ah->ues != URIES_IDLE)
+ goto forbid;
+
+ /* seal off uri header */
+ if (issue_char(wsi, '\0') < 0)
+ return -1;
+
+ /* move to using WSI_TOKEN_HTTP_URI_ARGS */
+ ah->nfrag++;
+ if (ah->nfrag >= ARRAY_SIZE(ah->frags))
+ goto excessive;
+ ah->frags[ah->nfrag].offset = ah->pos;
+ ah->frags[ah->nfrag].len = 0;
+ ah->frags[ah->nfrag].nfrag = 0;
+
+ ah->post_literal_equal = 0;
+ ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
+ ah->ups = URIPS_IDLE;
+ goto swallow;
+ }
+
+ return LPUR_CONTINUE;
+
+swallow:
+ return LPUR_SWALLOW;
+
+forbid:
+ return LPUR_FORBID;
+
+excessive:
+ return LPUR_EXCESSIVE;
+}
+
+static const unsigned char methods[] = {
+ WSI_TOKEN_GET_URI,
+ WSI_TOKEN_POST_URI,
+ WSI_TOKEN_OPTIONS_URI,
+ WSI_TOKEN_PUT_URI,
+ WSI_TOKEN_PATCH_URI,
+ WSI_TOKEN_DELETE_URI,
+ WSI_TOKEN_CONNECT,
+ WSI_TOKEN_HEAD_URI,
+};
+
+/*
+ * possible returns:, -1 fail, 0 ok or 2, transition to raw
+ */
+
+int LWS_WARN_UNUSED_RESULT
+lws_parse(struct lws *wsi, unsigned char *buf, int *len)
+{
+ struct allocated_headers *ah = wsi->http.ah;
+ struct lws_context *context = wsi->context;
+ unsigned int n, m;
+ unsigned char c;
+ int r, pos;
+
+ assert(wsi->http.ah);
+
+ do {
+ (*len)--;
+ c = *buf++;
+
+ switch (ah->parser_state) {
+ default:
+
+ lwsl_parser("WSI_TOK_(%d) '%c'\n", ah->parser_state, c);
+
+ /* collect into malloc'd buffers */
+ /* optional initial space swallow */
+ if (!ah->frags[ah->frag_index[ah->parser_state]].len &&
+ c == ' ')
+ break;
+
+ for (m = 0; m < ARRAY_SIZE(methods); m++)
+ if (ah->parser_state == methods[m])
+ break;
+ if (m == ARRAY_SIZE(methods))
+ /* it was not any of the methods */
+ goto check_eol;
+
+ /* special URI processing... end at space */
+
+ if (c == ' ') {
+ /* enforce starting with / */
+ if (!ah->frags[ah->nfrag].len)
+ if (issue_char(wsi, '/') < 0)
+ return -1;
+
+ if (ah->ups == URIPS_SEEN_SLASH_DOT_DOT) {
+ /*
+ * back up one dir level if possible
+ * safe against header fragmentation because
+ * the method URI can only be in 1 fragment
+ */
+ if (ah->frags[ah->nfrag].len > 2) {
+ ah->pos--;
+ ah->frags[ah->nfrag].len--;
+ do {
+ ah->pos--;
+ ah->frags[ah->nfrag].len--;
+ } while (ah->frags[ah->nfrag].len > 1 &&
+ ah->data[ah->pos] != '/');
+ }
+ }
+
+ /* begin parsing HTTP version: */
+ if (issue_char(wsi, '\0') < 0)
+ return -1;
+ ah->parser_state = WSI_TOKEN_HTTP;
+ goto start_fragment;
+ }
+
+ r = lws_parse_urldecode(wsi, &c);
+ switch (r) {
+ case LPUR_CONTINUE:
+ break;
+ case LPUR_SWALLOW:
+ goto swallow;
+ case LPUR_FORBID:
+ goto forbid;
+ case LPUR_EXCESSIVE:
+ goto excessive;
+ default:
+ return -1;
+ }
+check_eol:
+ /* bail at EOL */
+ if (ah->parser_state != WSI_TOKEN_CHALLENGE &&
+ c == '\x0d') {
+ if (ah->ues != URIES_IDLE)
+ goto forbid;
+
+ c = '\0';
+ ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
+ lwsl_parser("*\n");
+ }
+
+ n = issue_char(wsi, c);
+ if ((int)n < 0)
+ return -1;
+ if (n > 0)
+ ah->parser_state = WSI_TOKEN_SKIPPING;
+
+swallow:
+ /* per-protocol end of headers management */
+
+ if (ah->parser_state == WSI_TOKEN_CHALLENGE)
+ goto set_parsing_complete;
+ break;
+
+ /* collecting and checking a name part */
+ case WSI_TOKEN_NAME_PART:
+ lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X (role=0x%x) "
+ "wsi->lextable_pos=%d\n", c, c, lwsi_role(wsi),
+ ah->lextable_pos);
+
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+
+ pos = ah->lextable_pos;
+
+ while (1) {
+ if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */
+ if ((lextable[pos] & 0x7f) != c) {
+nope:
+ ah->lextable_pos = -1;
+ break;
+ }
+ /* fall thru */
+ pos++;
+ if (lextable[pos] == FAIL_CHAR)
+ goto nope;
+
+ ah->lextable_pos = pos;
+ break;
+ }
+
+ if (lextable[pos] == FAIL_CHAR)
+ goto nope;
+
+ /* b7 = 0, end or 3-byte */
+ if (lextable[pos] < FAIL_CHAR) { /* terminal marker */
+ ah->lextable_pos = pos;
+ break;
+ }
+
+ if (lextable[pos] == c) { /* goto */
+ ah->lextable_pos = pos + (lextable[pos + 1]) +
+ (lextable[pos + 2] << 8);
+ break;
+ }
+
+ /* fall thru goto */
+ pos += 3;
+ /* continue */
+ }
+
+ /*
+ * If it's h1, server needs to look out for unknown
+ * methods...
+ */
+ if (ah->lextable_pos < 0 && lwsi_role_h1(wsi) &&
+ lwsi_role_server(wsi)) {
+ /* this is not a header we know about */
+ for (m = 0; m < ARRAY_SIZE(methods); m++)
+ if (ah->frag_index[methods[m]]) {
+ /*
+ * already had the method, no idea what
+ * this crap from the client is, ignore
+ */
+ ah->parser_state = WSI_TOKEN_SKIPPING;
+ break;
+ }
+ /*
+ * hm it's an unknown http method from a client in fact,
+ * it cannot be valid http
+ */
+ if (m == ARRAY_SIZE(methods)) {
+ /*
+ * are we set up to accept raw in these cases?
+ */
+ if (lws_check_opt(wsi->vhost->options,
+ LWS_SERVER_OPTION_FALLBACK_TO_RAW))
+ return 2; /* transition to raw */
+
+ lwsl_info("Unknown method - dropping\n");
+ goto forbid;
+ }
+ break;
+ }
+ /*
+ * ...otherwise for a client, let him ignore unknown headers
+ * coming from the server
+ */
+ if (ah->lextable_pos < 0) {
+ ah->parser_state = WSI_TOKEN_SKIPPING;
+ break;
+ }
+
+ if (lextable[ah->lextable_pos] < FAIL_CHAR) {
+ /* terminal state */
+
+ n = ((unsigned int)lextable[ah->lextable_pos] << 8) |
+ lextable[ah->lextable_pos + 1];
+
+ lwsl_parser("known hdr %d\n", n);
+ for (m = 0; m < ARRAY_SIZE(methods); m++)
+ if (n == methods[m] &&
+ ah->frag_index[methods[m]]) {
+ lwsl_warn("Duplicated method\n");
+ return -1;
+ }
+
+ /*
+ * WSORIGIN is protocol equiv to ORIGIN,
+ * JWebSocket likes to send it, map to ORIGIN
+ */
+ if (n == WSI_TOKEN_SWORIGIN)
+ n = WSI_TOKEN_ORIGIN;
+
+ ah->parser_state = (enum lws_token_indexes)
+ (WSI_TOKEN_GET_URI + n);
+ ah->ups = URIPS_IDLE;
+
+ if (context->token_limits)
+ ah->current_token_limit = context->
+ token_limits->token_limit[
+ ah->parser_state];
+ else
+ ah->current_token_limit =
+ wsi->context->max_http_header_data;
+
+ if (ah->parser_state == WSI_TOKEN_CHALLENGE)
+ goto set_parsing_complete;
+
+ goto start_fragment;
+ }
+ break;
+
+start_fragment:
+ ah->nfrag++;
+excessive:
+ if (ah->nfrag == ARRAY_SIZE(ah->frags)) {
+ lwsl_warn("More hdr frags than we can deal with\n");
+ return -1;
+ }
+
+ ah->frags[ah->nfrag].offset = ah->pos;
+ ah->frags[ah->nfrag].len = 0;
+ ah->frags[ah->nfrag].nfrag = 0;
+ ah->frags[ah->nfrag].flags = 2;
+
+ n = ah->frag_index[ah->parser_state];
+ if (!n) { /* first fragment */
+ ah->frag_index[ah->parser_state] = ah->nfrag;
+ ah->hdr_token_idx = ah->parser_state;
+ break;
+ }
+ /* continuation */
+ while (ah->frags[n].nfrag)
+ n = ah->frags[n].nfrag;
+ ah->frags[n].nfrag = ah->nfrag;
+
+ if (issue_char(wsi, ' ') < 0)
+ return -1;
+ break;
+
+ /* skipping arg part of a name we didn't recognize */
+ case WSI_TOKEN_SKIPPING:
+ lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c);
+
+ if (c == '\x0d')
+ ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
+ break;
+
+ case WSI_TOKEN_SKIPPING_SAW_CR:
+ lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
+ if (ah->ues != URIES_IDLE)
+ goto forbid;
+ if (c == '\x0a') {
+ ah->parser_state = WSI_TOKEN_NAME_PART;
+ ah->lextable_pos = 0;
+ } else
+ ah->parser_state = WSI_TOKEN_SKIPPING;
+ break;
+ /* we're done, ignore anything else */
+
+ case WSI_PARSING_COMPLETE:
+ lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c);
+ break;
+ }
+
+ } while (*len);
+
+ return 0;
+
+set_parsing_complete:
+ if (ah->ues != URIES_IDLE)
+ goto forbid;
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
+ wsi->rx_frame_type = /* temp for ws version index */
+ atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
+
+ lwsl_parser("v%02d hdrs done\n", wsi->rx_frame_type);
+ }
+ ah->parser_state = WSI_PARSING_COMPLETE;
+ wsi->hdr_parsing_completed = 1;
+
+ return 0;
+
+forbid:
+ lwsl_notice(" forbidding on uri sanitation\n");
+ lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
+
+ return -1;
+}
+
diff --git a/thirdparty/lws/server/server.c b/thirdparty/libwebsockets/roles/http/server/server.c
index db05954257..350af3cd7e 100644
--- a/thirdparty/lws/server/server.c
+++ b/thirdparty/libwebsockets/roles/http/server/server.c
@@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -19,7 +19,7 @@
* MA 02110-1301 USA
*/
-#include "private-libwebsockets.h"
+#include "core/private.h"
const char * const method_names[] = {
"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT", "HEAD",
@@ -28,42 +28,46 @@ const char * const method_names[] = {
#endif
};
-#if defined (LWS_WITH_ESP8266)
-#undef memcpy
-void *memcpy(void *dest, const void *src, size_t n)
-{
- return ets_memcpy(dest, src, n);
-}
-#endif
+/*
+ * return 0: all done
+ * 1: nonfatal error
+ * <0: fatal error
+ *
+ * REQUIRES CONTEXT LOCK HELD
+ */
int
-lws_context_init_server(struct lws_context_creation_info *info,
- struct lws_vhost *vhost)
+_lws_vhost_init_server(const struct lws_context_creation_info *info,
+ struct lws_vhost *vhost)
{
-#if LWS_POSIX
int n, opt = 1, limit = 1;
-#endif
lws_sockfd_type sockfd;
struct lws_vhost *vh;
struct lws *wsi;
- int m = 0;
+ int m = 0, is;
(void)method_names;
(void)opt;
+
+ if (info) {
+ vhost->iface = info->iface;
+ vhost->listen_port = info->port;
+ }
+
/* set up our external listening socket we serve on */
- if (info->port == CONTEXT_PORT_NO_LISTEN ||
- info->port == CONTEXT_PORT_NO_LISTEN_SERVER)
+ if (vhost->listen_port == CONTEXT_PORT_NO_LISTEN ||
+ vhost->listen_port == CONTEXT_PORT_NO_LISTEN_SERVER)
return 0;
vh = vhost->context->vhost_list;
while (vh) {
- if (vh->listen_port == info->port) {
- if ((!info->iface && !vh->iface) ||
- (info->iface && vh->iface &&
- !strcmp(info->iface, vh->iface))) {
- vhost->listen_port = info->port;
- vhost->iface = info->iface;
+ if (vh->listen_port == vhost->listen_port) {
+ if (((!vhost->iface && !vh->iface) ||
+ (vhost->iface && vh->iface &&
+ !strcmp(vhost->iface, vh->iface))) &&
+ vh->lserv_wsi
+ ) {
lwsl_notice(" using listen skt from vhost %s\n",
vh->name);
return 0;
@@ -72,7 +76,59 @@ lws_context_init_server(struct lws_context_creation_info *info,
vh = vh->vhost_next;
}
-#if LWS_POSIX
+ if (vhost->iface) {
+ /*
+ * let's check before we do anything else about the disposition
+ * of the interface he wants to bind to...
+ */
+ is = lws_socket_bind(vhost, LWS_SOCK_INVALID, vhost->listen_port, vhost->iface);
+ lwsl_debug("initial if check says %d\n", is);
+deal:
+
+ lws_start_foreach_llp(struct lws_vhost **, pv,
+ vhost->context->no_listener_vhost_list) {
+ if (is >= LWS_ITOSA_USABLE && *pv == vhost) {
+ /* on the list and shouldn't be: remove it */
+ lwsl_debug("deferred iface: removing vh %s\n", (*pv)->name);
+ *pv = vhost->no_listener_vhost_list;
+ vhost->no_listener_vhost_list = NULL;
+ goto done_list;
+ }
+ if (is < LWS_ITOSA_USABLE && *pv == vhost)
+ goto done_list;
+ } lws_end_foreach_llp(pv, no_listener_vhost_list);
+
+ /* not on the list... */
+
+ if (is < LWS_ITOSA_USABLE) {
+
+ /* ... but needs to be: so add it */
+
+ lwsl_debug("deferred iface: adding vh %s\n", vhost->name);
+ vhost->no_listener_vhost_list = vhost->context->no_listener_vhost_list;
+ vhost->context->no_listener_vhost_list = vhost;
+ }
+
+done_list:
+
+ switch (is) {
+ default:
+ break;
+ case LWS_ITOSA_NOT_EXIST:
+ /* can't add it */
+ if (info) /* first time */
+ lwsl_err("VH %s: iface %s port %d DOESN'T EXIST\n",
+ vhost->name, vhost->iface, vhost->listen_port);
+ return 1;
+ case LWS_ITOSA_NOT_USABLE:
+ /* can't add it */
+ if (info) /* first time */
+ lwsl_err("VH %s: iface %s port %d NOT USABLE\n",
+ vhost->name, vhost->iface, vhost->listen_port);
+ return 1;
+ }
+ }
+
(void)n;
#if defined(__linux__)
limit = vhost->context->count_threads;
@@ -80,159 +136,150 @@ lws_context_init_server(struct lws_context_creation_info *info,
for (m = 0; m < limit; m++) {
#ifdef LWS_WITH_UNIX_SOCK
- if (LWS_UNIX_SOCK_ENABLED(vhost))
- sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
- else
+ if (LWS_UNIX_SOCK_ENABLED(vhost))
+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ else
#endif
#ifdef LWS_WITH_IPV6
- if (LWS_IPV6_ENABLED(vhost))
- sockfd = socket(AF_INET6, SOCK_STREAM, 0);
- else
+ if (LWS_IPV6_ENABLED(vhost))
+ sockfd = socket(AF_INET6, SOCK_STREAM, 0);
+ else
#endif
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd == -1) {
-#else
-#if defined(LWS_WITH_ESP8266)
- sockfd = esp8266_create_tcp_listen_socket(vhost);
- if (!lws_sockfd_valid(sockfd)) {
-#endif
+ if (sockfd == LWS_SOCK_INVALID) {
+ lwsl_err("ERROR opening socket\n");
+ return 1;
+ }
+#if !defined(LWS_WITH_ESP32)
+#if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE)
+ /*
+ * only accept that we are the only listener on the port
+ * https://msdn.microsoft.com/zh-tw/library/
+ * windows/desktop/ms740621(v=vs.85).aspx
+ *
+ * for lws, to match Linux, we default to exclusive listen
+ */
+ if (!lws_check_opt(vhost->options,
+ LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) {
+ if (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+ (const void *)&opt, sizeof(opt)) < 0) {
+ lwsl_err("reuseaddr failed\n");
+ compatible_close(sockfd);
+ return -1;
+ }
+ } else
#endif
- lwsl_err("ERROR opening socket\n");
- return 1;
- }
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
-#if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE)
- /*
- * only accept that we are the only listener on the port
- * https://msdn.microsoft.com/zh-tw/library/
- * windows/desktop/ms740621(v=vs.85).aspx
- *
- * for lws, to match Linux, we default to exclusive listen
- */
- if (!lws_check_opt(vhost->options,
- LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) {
- if (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+ /*
+ * allow us to restart even if old sockets in TIME_WAIT
+ */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&opt, sizeof(opt)) < 0) {
lwsl_err("reuseaddr failed\n");
compatible_close(sockfd);
- return 1;
+ return -1;
}
- } else
-#endif
-
- /*
- * allow us to restart even if old sockets in TIME_WAIT
- */
- if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
- (const void *)&opt, sizeof(opt)) < 0) {
- lwsl_err("reuseaddr failed\n");
- compatible_close(sockfd);
- return 1;
- }
#if defined(LWS_WITH_IPV6) && defined(IPV6_V6ONLY)
- if (LWS_IPV6_ENABLED(vhost)) {
- if (vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) {
+ if (LWS_IPV6_ENABLED(vhost) &&
+ vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) {
int value = (vhost->options &
LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE) ? 1 : 0;
if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
(const void*)&value, sizeof(value)) < 0) {
compatible_close(sockfd);
- return 1;
+ return -1;
}
}
- }
#endif
#if defined(__linux__) && defined(SO_REUSEPORT)
- n = lws_check_opt(vhost->options, LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE);
+ /* keep coverity happy */
#if LWS_MAX_SMP > 1
- n = 1;
+ n = 1;
+#else
+ n = lws_check_opt(vhost->options,
+ LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE);
#endif
-
- if (n)
- if (vhost->context->count_threads > 1)
+ if (n && vhost->context->count_threads > 1)
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
(const void *)&opt, sizeof(opt)) < 0) {
compatible_close(sockfd);
- return 1;
+ return -1;
}
#endif
#endif
- lws_plat_set_socket_options(vhost, sockfd);
+ lws_plat_set_socket_options(vhost, sockfd);
-#if LWS_POSIX
- n = lws_socket_bind(vhost, sockfd, info->port, info->iface);
- if (n < 0)
- goto bail;
- info->port = n;
-#endif
- vhost->listen_port = info->port;
- vhost->iface = info->iface;
+ is = lws_socket_bind(vhost, sockfd, vhost->listen_port, vhost->iface);
+ /*
+ * There is a race where the network device may come up and then
+ * go away and fail here. So correctly handle unexpected failure
+ * here despite we earlier confirmed it.
+ */
+ if (is < 0) {
+ lwsl_info("%s: lws_socket_bind says %d\n", __func__, is);
+ compatible_close(sockfd);
+ goto deal;
+ }
+ vhost->listen_port = is;
- wsi = lws_zalloc(sizeof(struct lws), "listen wsi");
- if (wsi == NULL) {
- lwsl_err("Out of mem\n");
- goto bail;
- }
- wsi->context = vhost->context;
- wsi->desc.sockfd = sockfd;
- wsi->mode = LWSCM_SERVER_LISTENER;
- wsi->protocol = vhost->protocols;
- wsi->tsi = m;
- wsi->vhost = vhost;
- wsi->listener = 1;
-
-#ifdef LWS_WITH_LIBUV
- if (LWS_LIBUV_ENABLED(vhost->context))
- lws_uv_initvhost(vhost, wsi);
-#endif
+ lwsl_debug("%s: lws_socket_bind says %d\n", __func__, is);
- if (insert_wsi_socket_into_fds(vhost->context, wsi))
- goto bail;
+ wsi = lws_zalloc(sizeof(struct lws), "listen wsi");
+ if (wsi == NULL) {
+ lwsl_err("Out of mem\n");
+ goto bail;
+ }
+ wsi->context = vhost->context;
+ wsi->desc.sockfd = sockfd;
+ lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_listen);
+ wsi->protocol = vhost->protocols;
+ wsi->tsi = m;
+ wsi->vhost = vhost;
+ wsi->listener = 1;
- vhost->context->count_wsi_allocated++;
- vhost->lserv_wsi = wsi;
+ if (wsi->context->event_loop_ops->init_vhost_listen_wsi)
+ wsi->context->event_loop_ops->init_vhost_listen_wsi(wsi);
-#if LWS_POSIX
- n = listen(wsi->desc.sockfd, LWS_SOMAXCONN);
- if (n < 0) {
- lwsl_err("listen failed with error %d\n", LWS_ERRNO);
- vhost->lserv_wsi = NULL;
- vhost->context->count_wsi_allocated--;
- remove_wsi_socket_from_fds(wsi);
- goto bail;
- }
+ if (__insert_wsi_socket_into_fds(vhost->context, wsi)) {
+ lwsl_notice("inserting wsi socket into fds failed\n");
+ goto bail;
+ }
+
+ vhost->context->count_wsi_allocated++;
+ vhost->lserv_wsi = wsi;
+
+ n = listen(wsi->desc.sockfd, LWS_SOMAXCONN);
+ if (n < 0) {
+ lwsl_err("listen failed with error %d\n", LWS_ERRNO);
+ vhost->lserv_wsi = NULL;
+ vhost->context->count_wsi_allocated--;
+ __remove_wsi_socket_from_fds(wsi);
+ goto bail;
+ }
} /* for each thread able to independently listen */
-#else
-#if defined(LWS_WITH_ESP8266)
- esp8266_tcp_stream_bind(wsi->desc.sockfd, info->port, wsi);
-#endif
-#endif
- if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {
+
+ if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {
#ifdef LWS_WITH_UNIX_SOCK
if (LWS_UNIX_SOCK_ENABLED(vhost))
- lwsl_info(" Listening on \"%s\"\n", info->iface);
+ lwsl_info(" Listening on \"%s\"\n", vhost->iface);
else
#endif
- lwsl_info(" Listening on port %d\n", info->port);
+ lwsl_info(" Listening on port %d\n", vhost->listen_port);
}
+ // info->port = vhost->listen_port;
+
return 0;
bail:
compatible_close(sockfd);
- return 1;
+ return -1;
}
-#if defined(LWS_WITH_ESP8266)
-#undef strchr
-#define strchr ets_strchr
-#endif
-
struct lws_vhost *
lws_select_vhost(struct lws_context *context, int port, const char *servername)
{
@@ -240,11 +287,11 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername)
const char *p;
int n, m, colon;
- n = strlen(servername);
+ n = (int)strlen(servername);
colon = n;
p = strchr(servername, ':');
if (p)
- colon = p - servername;
+ colon = lws_ptr_diff(p, servername);
/* Priotity 1: first try exact matches */
@@ -266,7 +313,7 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername)
*/
vhost = context->vhost_list;
while (vhost) {
- m = strlen(vhost->name);
+ m = (int)strlen(vhost->name);
if (port == vhost->listen_port &&
m <= (colon - 2) &&
servername[colon - m - 1] == '.' &&
@@ -283,8 +330,8 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername)
vhost = context->vhost_list;
while (vhost) {
if (port == vhost->listen_port) {
- lwsl_info("vhost match to %s based on port %d\n",
- vhost->name, port);
+ lwsl_info("%s: vhost match to %s based on port %d\n",
+ __func__, vhost->name, port);
return vhost;
}
vhost = vhost->vhost_next;
@@ -298,7 +345,7 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername)
LWS_VISIBLE LWS_EXTERN const char *
lws_get_mimetype(const char *file, const struct lws_http_mount *m)
{
- int n = strlen(file);
+ int n = (int)strlen(file);
const struct lws_protocol_vhost_options *pvo = NULL;
if (m)
@@ -388,7 +435,7 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
const struct lws_protocol_vhost_options *pvo = m->interpret;
struct lws_process_html_args args;
const char *mimetype;
-#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266)
+#if !defined(_WIN32_WCE)
const struct lws_plat_file_ops *fops;
const char *vpath;
lws_fop_flags_t fflags = LWS_O_RDONLY;
@@ -402,14 +449,24 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
char path[256], sym[512];
unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p;
unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE;
-#if !defined(WIN32) && LWS_POSIX && !defined(LWS_WITH_ESP32)
+#if !defined(WIN32) && !defined(LWS_WITH_ESP32)
size_t len;
#endif
int n;
+ wsi->handling_404 = 0;
+ if (!wsi->vhost)
+ return -1;
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ if (wsi->vhost->http.error_document_404 &&
+ !strcmp(uri, wsi->vhost->http.error_document_404))
+ wsi->handling_404 = 1;
+#endif
+
lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
-#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266)
+#if !defined(_WIN32_WCE)
fflags |= lws_vfs_prepare_flags(wsi);
@@ -417,13 +474,14 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
spin++;
fops = lws_vfs_select_fops(wsi->context->fops, path, &vpath);
- if (wsi->u.http.fop_fd)
- lws_vfs_file_close(&wsi->u.http.fop_fd);
+ if (wsi->http.fop_fd)
+ lws_vfs_file_close(&wsi->http.fop_fd);
- wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
+ wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
path, vpath, &fflags);
- if (!wsi->u.http.fop_fd) {
- lwsl_err("Unable to open '%s'\n", path);
+ if (!wsi->http.fop_fd) {
+ lwsl_info("%s: Unable to open '%s': errno %d\n",
+ __func__, path, errno);
return -1;
}
@@ -435,7 +493,7 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
break;
#endif
#if !defined(WIN32)
- if (fstat(wsi->u.http.fop_fd->fd, &st)) {
+ if (fstat(wsi->http.fop_fd->fd, &st)) {
lwsl_info("unable to stat %s\n", path);
goto bail;
}
@@ -453,10 +511,10 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
#endif
#endif
- wsi->u.http.fop_fd->mod_time = (uint32_t)st.st_mtime;
+ wsi->http.fop_fd->mod_time = (uint32_t)st.st_mtime;
fflags |= LWS_FOP_FLAG_MOD_TIME_VALID;
-#if !defined(WIN32) && LWS_POSIX && !defined(LWS_WITH_ESP32)
+#if !defined(WIN32) && !defined(LWS_WITH_ESP32)
if ((S_IFMT & st.st_mode) == S_IFLNK) {
len = readlink(path, sym, sizeof(sym) - 1);
if (len) {
@@ -480,15 +538,15 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
lwsl_err("symlink loop %s \n", path);
n = sprintf(sym, "%08llX%08lX",
- (unsigned long long)lws_vfs_get_length(wsi->u.http.fop_fd),
- (unsigned long)lws_vfs_get_mod_time(wsi->u.http.fop_fd));
+ (unsigned long long)lws_vfs_get_length(wsi->http.fop_fd),
+ (unsigned long)lws_vfs_get_mod_time(wsi->http.fop_fd));
/* disable ranges if IF_RANGE token invalid */
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_RANGE))
if (strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_RANGE)))
/* differs - defeat Range: */
- wsi->u.http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0;
+ wsi->http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0;
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) {
/*
@@ -523,7 +581,7 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
return -1;
}
- lws_vfs_file_close(&wsi->u.http.fop_fd);
+ lws_vfs_file_close(&wsi->http.fop_fd);
return lws_http_transaction_completed(wsi);
}
@@ -549,10 +607,12 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
* a protocol
*/
while (pvo) {
- n = strlen(path);
+ n = (int)strlen(path);
if (n > (int)strlen(pvo->name) &&
!strcmp(&path[n - strlen(pvo->name)], pvo->name)) {
- wsi->sending_chunked = 1;
+ wsi->interpreting = 1;
+ if (!wsi->http2_substream)
+ wsi->sending_chunked = 1;
wsi->protocol_interpret_idx =
(char)(lws_intptr_t)pvo->value;
lwsl_info("want %s interpreted by %s\n", path,
@@ -574,14 +634,15 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
if (lws_bind_protocol(wsi, pp))
return 1;
args.p = (char *)p;
- args.max_len = end - p;
+ args.max_len = lws_ptr_diff(end, p);
if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS,
wsi->user_space, &args, 0))
return -1;
p = (unsigned char *)args.p;
}
- n = lws_serve_http_file(wsi, path, mimetype, (char *)start, p - start);
+ n = lws_serve_http_file(wsi, path, mimetype, (char *)start,
+ lws_ptr_diff(p, start));
if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
return -1; /* error or can't reuse connection: close the socket */
@@ -592,13 +653,14 @@ bail:
return -1;
}
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
const struct lws_http_mount *
lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)
{
const struct lws_http_mount *hm, *hit = NULL;
int best = 0;
- hm = wsi->vhost->mount_list;
+ hm = wsi->vhost->http.mount_list;
while (hm) {
if (uri_len >= hm->mountpoint_len &&
!strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) &&
@@ -623,9 +685,9 @@ lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)
return hit;
}
+#endif
-#if LWS_POSIX
-
+#if !defined(LWS_WITH_ESP32)
static int
lws_find_string_in_file(const char *filename, const char *string, int stringlen)
{
@@ -635,7 +697,7 @@ lws_find_string_in_file(const char *filename, const char *string, int stringlen)
fd = open(filename, O_RDONLY);
if (fd < 0) {
lwsl_err("can't open auth file: %s\n", filename);
- return 1;
+ return 0;
}
while (1) {
@@ -669,6 +731,7 @@ lws_find_string_in_file(const char *filename, const char *string, int stringlen)
return hit;
}
+#endif
static int
lws_unauthorised_basic_auth(struct lws *wsi)
@@ -702,8 +765,6 @@ lws_unauthorised_basic_auth(struct lws *wsi)
}
-#endif
-
int lws_clean_url(char *p)
{
if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') {
@@ -732,7 +793,6 @@ int lws_clean_url(char *p)
return 0;
}
-
static const unsigned char methods[] = {
WSI_TOKEN_GET_URI,
WSI_TOKEN_POST_URI,
@@ -752,7 +812,7 @@ lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
{
int n, count = 0;
- for (n = 0; n < ARRAY_SIZE(methods); n++)
+ for (n = 0; n < (int)ARRAY_SIZE(methods); n++)
if (lws_hdr_total_length(wsi, methods[n]))
count++;
if (!count) {
@@ -767,7 +827,7 @@ lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
return -1;
}
- for (n = 0; n < ARRAY_SIZE(methods); n++)
+ for (n = 0; n < (int)ARRAY_SIZE(methods); n++)
if (lws_hdr_total_length(wsi, methods[n])) {
*puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
*puri_len = lws_hdr_total_length(wsi, methods[n]);
@@ -797,7 +857,7 @@ lws_http_action(struct lws *wsi)
};
meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
- if (meth < 0 || meth >= ARRAY_SIZE(method_names))
+ if (meth < 0 || meth >= (int)ARRAY_SIZE(method_names))
goto bail_nuke_ah;
/* we insist on absolute paths */
@@ -811,26 +871,36 @@ lws_http_action(struct lws *wsi)
lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth],
meth, uri_ptr);
+ if (wsi->role_ops && wsi->role_ops->check_upgrades)
+ switch (wsi->role_ops->check_upgrades(wsi)) {
+ case LWS_UPG_RET_DONE:
+ return 0;
+ case LWS_UPG_RET_CONTINUE:
+ break;
+ case LWS_UPG_RET_BAIL:
+ goto bail_nuke_ah;
+ }
+
if (lws_ensure_user_space(wsi))
goto bail_nuke_ah;
/* HTTP header had a content length? */
- wsi->u.http.rx_content_length = 0;
+ wsi->http.rx_content_length = 0;
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI))
- wsi->u.http.rx_content_length = 100 * 1024 * 1024;
+ wsi->http.rx_content_length = 100 * 1024 * 1024;
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
lws_hdr_copy(wsi, content_length_str,
sizeof(content_length_str) - 1,
WSI_TOKEN_HTTP_CONTENT_LENGTH);
- wsi->u.http.rx_content_length = atoll(content_length_str);
+ wsi->http.rx_content_length = atoll(content_length_str);
}
if (wsi->http2_substream) {
- wsi->u.http.request_version = HTTP_VERSION_2;
+ wsi->http.request_version = HTTP_VERSION_2;
} else {
/* http_version? Default to 1.0, override with token: */
request_version = HTTP_VERSION_1_0;
@@ -845,7 +915,7 @@ lws_http_action(struct lws *wsi)
http_version_str[7] == '1')
request_version = HTTP_VERSION_1_1;
}
- wsi->u.http.request_version = request_version;
+ wsi->http.request_version = request_version;
/* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
if (request_version == HTTP_VERSION_1_1)
@@ -865,7 +935,7 @@ lws_http_action(struct lws *wsi)
if (!strcasecmp(http_conn_str, "close"))
connection_type = HTTP_CONNECTION_CLOSE;
}
- wsi->u.http.connection_type = connection_type;
+ wsi->http.connection_type = connection_type;
}
n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
@@ -881,8 +951,8 @@ lws_http_action(struct lws *wsi)
*/
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
wsi->context->timeout_secs);
-#ifdef LWS_OPENSSL_SUPPORT
- if (wsi->redirect_to_https) {
+#ifdef LWS_WITH_TLS
+ if (wsi->tls.redirect_to_https) {
/*
* we accepted http:// only so we could redirect to
* https://, so issue the redirect. Create the redirection
@@ -921,6 +991,8 @@ lws_http_action(struct lws *wsi)
if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0]))
return 1;
+ lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
+
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
wsi->user_space, uri_ptr, uri_len);
@@ -989,7 +1061,6 @@ lws_http_action(struct lws *wsi)
return lws_http_transaction_completed(wsi);
}
-#if LWS_POSIX
/* basic auth? */
if (hit->basic_auth_login_file) {
@@ -1029,11 +1100,10 @@ lws_http_action(struct lws *wsi)
return lws_unauthorised_basic_auth(wsi);
}
- lwsl_notice("basic auth accepted\n");
+ lwsl_info("basic auth accepted\n");
/* accept the auth */
}
-#endif
#if defined(LWS_WITH_HTTP_PROXY)
/*
@@ -1064,7 +1134,7 @@ lws_http_action(struct lws *wsi)
else
n = pslash - hit->origin;
- if (n >= sizeof(ads) - 2)
+ if (n >= (int)sizeof(ads) - 2)
n = sizeof(ads) - 2;
memcpy(ads, hit->origin, n);
@@ -1104,9 +1174,10 @@ lws_http_action(struct lws *wsi)
i.uri_replace_from = hit->origin;
i.uri_replace_to = hit->mountpoint;
- lwsl_notice("proxying to %s port %d url %s, ssl %d, from %s, to %s\n",
- i.address, i.port, i.path, i.ssl_connection,
- i.uri_replace_from, i.uri_replace_to);
+ lwsl_notice("proxying to %s port %d url %s, ssl %d, "
+ "from %s, to %s\n",
+ i.address, i.port, i.path, i.ssl_connection,
+ i.uri_replace_from, i.uri_replace_to);
if (!lws_client_connect_via_info(&i)) {
lwsl_err("proxy connect fail\n");
@@ -1144,8 +1215,10 @@ lws_http_action(struct lws *wsi)
args.len = uri_len;
args.max_len = hit->auth_mask;
args.final = 0; /* used to signal callback dealt with it */
+ args.chunked = 0;
- n = wsi->protocol->callback(wsi, LWS_CALLBACK_CHECK_ACCESS_RIGHTS,
+ n = wsi->protocol->callback(wsi,
+ LWS_CALLBACK_CHECK_ACCESS_RIGHTS,
wsi->user_space, &args, 0);
if (n) {
lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED,
@@ -1195,7 +1268,7 @@ lws_http_action(struct lws *wsi)
}
#endif
- n = strlen(s);
+ n = (int)strlen(s);
if (s[0] == '\0' || (n == 1 && s[n - 1] == '/'))
s = (char *)hit->def;
if (!s)
@@ -1206,14 +1279,17 @@ lws_http_action(struct lws *wsi)
wsi->cache_revalidate = hit->cache_revalidate;
wsi->cache_intermediaries = hit->cache_intermediaries;
- n = lws_http_serve(wsi, s, hit->origin, hit);
+ n = 1;
+ if (hit->origin_protocol == LWSMPRO_FILE)
+ n = lws_http_serve(wsi, s, hit->origin, hit);
if (n) {
/*
- * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
+ * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
*/
if (hit->protocol) {
- const struct lws_protocols *pp = lws_vhost_name_to_protocol(
- wsi->vhost, hit->protocol);
+ const struct lws_protocols *pp =
+ lws_vhost_name_to_protocol(
+ wsi->vhost, hit->protocol);
if (lws_bind_protocol(wsi, pp))
return 1;
@@ -1245,125 +1321,90 @@ deal_body:
* In any case, return 0 and let lws_read decide how to
* proceed based on state
*/
- if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
+ if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
/* Prepare to read body if we have a content length: */
- lwsl_debug("wsi->u.http.rx_content_length %lld %d %d\n",
- (long long)wsi->u.http.rx_content_length,
+ lwsl_debug("wsi->http.rx_content_length %lld %d %d\n",
+ (long long)wsi->http.rx_content_length,
wsi->upgraded_to_http2, wsi->http2_substream);
- if (wsi->u.http.rx_content_length > 0) {
- lwsl_notice("%s: %p: LWSS_HTTP_BODY state set\n",
- __func__, wsi);
- wsi->state = LWSS_HTTP_BODY;
- wsi->u.http.rx_content_remain =
- wsi->u.http.rx_content_length;
+ if (wsi->http.rx_content_length > 0) {
+ struct lws_tokens ebuf;
+ int m;
+
+ lwsi_set_state(wsi, LRS_BODY);
+ lwsl_info("%s: %p: LRS_BODY state set (0x%x)\n",
+ __func__, wsi, wsi->wsistate);
+ wsi->http.rx_content_remain =
+ wsi->http.rx_content_length;
+
+ /*
+ * At this point we have transitioned from deferred
+ * action to expecting BODY on the stream wsi, if it's
+ * in a bundle like h2. So if the stream wsi has its
+ * own buflist, we need to deal with that first.
+ */
+
+ while (1) {
+ ebuf.len = (int)lws_buflist_next_segment_len(
+ &wsi->buflist, (uint8_t **)&ebuf.token);
+ if (!ebuf.len)
+ break;
+ lwsl_notice("%s: consuming %d\n", __func__, (int)ebuf.len);
+ m = lws_read_h1(wsi, (uint8_t *)ebuf.token, ebuf.len);
+ if (m < 0)
+ return -1;
+
+ if (lws_buflist_aware_consume(wsi, &ebuf, m, 1))
+ return -1;
+ }
}
}
return 0;
bail_nuke_ah:
- /* we're closing, losing some rx is OK */
- lws_header_table_force_to_detachable_state(wsi);
lws_header_table_detach(wsi, 1);
return 1;
-#if LWS_POSIX
transaction_result_n:
lws_return_http_status(wsi, n, NULL);
return lws_http_transaction_completed(wsi);
-#endif
-}
-
-static int
-lws_server_init_wsi_for_ws(struct lws *wsi)
-{
- int n;
-
- wsi->state = LWSS_ESTABLISHED;
- lws_restart_ws_ping_pong_timer(wsi);
-
- /*
- * create the frame buffer for this connection according to the
- * size mentioned in the protocol definition. If 0 there, use
- * a big default for compatibility
- */
-
- n = wsi->protocol->rx_buffer_size;
- if (!n)
- n = wsi->context->pt_serv_buf_size;
- n += LWS_PRE;
- wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "rx_ubuf");
- if (!wsi->u.ws.rx_ubuf) {
- lwsl_err("Out of Mem allocating rx buffer %d\n", n);
- return 1;
- }
- wsi->u.ws.rx_ubuf_alloc = n;
- lwsl_debug("Allocating RX buffer %d\n", n);
-
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
- if (!wsi->parent_carries_io)
- if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
- (const char *)&n, sizeof n)) {
- lwsl_warn("Failed to set SNDBUF to %d", n);
- return 1;
- }
-#endif
-
- /* notify user code that we're ready to roll */
-
- if (wsi->protocol->callback)
- if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,
- wsi->user_space,
-#ifdef LWS_OPENSSL_SUPPORT
- wsi->ssl,
-#else
- NULL,
-#endif
- 0))
- return 1;
-
- return 0;
}
int
lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
{
- int protocol_len, n = 0, hit, non_space_char_found = 0, m;
struct lws_context *context = lws_get_context(wsi);
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- struct _lws_header_related hdr;
- struct allocated_headers *ah;
unsigned char *obuf = *buf;
- char protocol_list[128];
- char protocol_name[64];
+#if defined(LWS_WITH_HTTP2)
+ char tbuf[128], *p;
+#endif
size_t olen = len;
- char *p;
+ int n = 0, m, i;
if (len >= 10000000) {
lwsl_err("%s: assert: len %ld\n", __func__, (long)len);
assert(0);
}
- if (!wsi->u.hdr.ah) {
+ if (!wsi->http.ah) {
lwsl_err("%s: assert: NULL ah\n", __func__);
assert(0);
}
- lwsl_hexdump(*buf, len);
-
- while (len--) {
- wsi->more_rx_waiting = !!len;
-
- if (wsi->mode != LWSCM_HTTP_SERVING &&
- wsi->mode != LWSCM_HTTP2_SERVING &&
- wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) {
- lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode);
+ while (len) {
+ if (!lwsi_role_server(wsi) || !lwsi_role_http(wsi)) {
+ lwsl_err("%s: bad wsi role 0x%x\n", __func__,
+ lwsi_role(wsi));
goto bail_nuke_ah;
}
- m = lws_parse(wsi, *(*buf)++);
+ i = (int)len;
+ m = lws_parse(wsi, *buf, &i);
+ lwsl_info("%s: parsed count %d\n", __func__, (int)len - i);
+ (*buf) += (int)len - i;
+ len = i;
if (m) {
if (m == 2) {
/*
@@ -1384,8 +1425,8 @@ raw_transition:
wsi->user_space, NULL, 0))
goto bail_nuke_ah;
- lws_header_table_force_to_detachable_state(wsi);
- lws_union_transition(wsi, LWSCM_RAW);
+ lws_role_transition(wsi, 0, LRS_ESTABLISHED,
+ &role_ops_raw_skt);
lws_header_table_detach(wsi, 1);
if (m == 2 && (wsi->protocol->callback)(wsi,
@@ -1399,12 +1440,10 @@ raw_transition:
goto bail_nuke_ah;
}
- if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
+ if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
continue;
lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
- lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
- wsi->more_rx_waiting);
/* select vhost */
@@ -1418,7 +1457,7 @@ raw_transition:
} else
lwsl_info("no host\n");
- if (wsi->mode != LWSCM_HTTP2_SERVING) {
+ if (!lwsi_role_h2(wsi) || !lwsi_role_server(wsi)) {
wsi->vhost->conn_stats.h1_trans++;
if (!wsi->conn_stat_done) {
wsi->vhost->conn_stats.h1_conn++;
@@ -1435,38 +1474,39 @@ raw_transition:
if (lws_hdr_copy(wsi, ua, sizeof(ua) - 1,
WSI_TOKEN_HTTP_USER_AGENT) > 0) {
- ua[sizeof(ua) - 1] = '\0';
- while (rej) {
- if (strstr(ua, rej->name)) {
#ifdef LWS_WITH_ACCESS_LOG
- char *uri_ptr = NULL;
- int meth, uri_len;
+ char *uri_ptr = NULL;
+ int meth, uri_len;
#endif
+ ua[sizeof(ua) - 1] = '\0';
+ while (rej) {
+ if (!strstr(ua, rej->name)) {
+ rej = rej->next;
+ continue;
+ }
- msg = strchr(rej->value, ' ');
- if (msg)
- msg++;
- lws_return_http_status(wsi,
- atoi(rej->value), msg);
+ msg = strchr(rej->value, ' ');
+ if (msg)
+ msg++;
+ lws_return_http_status(wsi,
+ atoi(rej->value), msg);
#ifdef LWS_WITH_ACCESS_LOG
- meth = lws_http_get_uri_and_method(wsi,
- &uri_ptr, &uri_len);
- if (meth >= 0)
- lws_prepare_access_log_info(wsi,
+ meth = lws_http_get_uri_and_method(wsi,
+ &uri_ptr, &uri_len);
+ if (meth >= 0)
+ lws_prepare_access_log_info(wsi,
uri_ptr, meth);
- /* wsi close will do the log */
+ /* wsi close will do the log */
#endif
- wsi->vhost->conn_stats.rejected++;
- /*
- * We don't want anything from
- * this rejected guy. Follow
- * the close flow, not the
- * transaction complete flow.
- */
- goto bail_nuke_ah;
- }
- rej = rej->next;
+ wsi->vhost->conn_stats.rejected++;
+ /*
+ * We don't want anything from
+ * this rejected guy. Follow
+ * the close flow, not the
+ * transaction complete flow.
+ */
+ goto bail_nuke_ah;
}
}
}
@@ -1478,20 +1518,24 @@ raw_transition:
goto raw_transition;
}
- wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT;
+ lwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT);
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
/* is this websocket protocol or normal http 1.0? */
if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
- if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
+ if (!strcasecmp(lws_hdr_simple_ptr(wsi,
+ WSI_TOKEN_UPGRADE),
"websocket")) {
+#if defined(LWS_ROLE_WS)
wsi->vhost->conn_stats.ws_upg++;
lwsl_info("Upgrade to ws\n");
goto upgrade_ws;
+#endif
}
-#ifdef LWS_WITH_HTTP2
- if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
+#if defined(LWS_WITH_HTTP2)
+ if (!strcasecmp(lws_hdr_simple_ptr(wsi,
+ WSI_TOKEN_UPGRADE),
"h2c")) {
wsi->vhost->conn_stats.h2_upg++;
lwsl_info("Upgrade to h2c\n");
@@ -1505,23 +1549,19 @@ raw_transition:
/* no upgrade ack... he remained as HTTP */
- lwsl_info("No upgrade\n");
- ah = wsi->u.hdr.ah;
+ lwsl_info("%s: %p: No upgrade\n", __func__, wsi);
- lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED);
- wsi->state = LWSS_HTTP;
- wsi->u.http.fop_fd = NULL;
+ lwsi_set_state(wsi, LRS_ESTABLISHED);
+ wsi->http.fop_fd = NULL;
- /* expose it at the same offset as u.hdr */
- wsi->u.http.ah = ah;
lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
- (void *)wsi->u.hdr.ah);
+ (void *)wsi->http.ah);
n = lws_http_action(wsi);
return n;
-#ifdef LWS_WITH_HTTP2
+#if defined(LWS_WITH_HTTP2)
upgrade_h2c:
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
lwsl_info("missing http2_settings\n");
@@ -1532,8 +1572,7 @@ upgrade_h2c:
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
/* convert the peer's HTTP-Settings */
- n = lws_b64_decode_string(p, protocol_list,
- sizeof(protocol_list));
+ n = lws_b64_decode_string(p, tbuf, sizeof(tbuf));
if (n < 0) {
lwsl_parser("HTTP2_SETTINGS too long\n");
return 1;
@@ -1541,16 +1580,10 @@ upgrade_h2c:
/* adopt the header info */
- ah = wsi->u.hdr.ah;
-
- lws_union_transition(wsi, LWSCM_HTTP2_SERVING);
-
- /* http2 union member has http union struct at start */
- wsi->u.http.ah = ah;
-
- if (!wsi->u.h2.h2n) {
- wsi->u.h2.h2n = lws_zalloc(sizeof(*wsi->u.h2.h2n), "h2n");
- if (!wsi->u.h2.h2n)
+ if (!wsi->h2.h2n) {
+ wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n),
+ "h2n");
+ if (!wsi->h2.h2n)
return 1;
}
@@ -1558,195 +1591,39 @@ upgrade_h2c:
/* HTTP2 union */
- lws_h2_settings(wsi, &wsi->u.h2.h2n->set,
- (unsigned char *)protocol_list, n);
+ lws_h2_settings(wsi, &wsi->h2.h2n->set, (unsigned char *)tbuf, n);
- lws_hpack_dynamic_size(wsi, wsi->u.h2.h2n->set.s[
+ lws_hpack_dynamic_size(wsi, wsi->h2.h2n->set.s[
H2SET_HEADER_TABLE_SIZE]);
- strcpy(protocol_list, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
- "Connection: Upgrade\x0d\x0a"
- "Upgrade: h2c\x0d\x0a\x0d\x0a");
- n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
- strlen(protocol_list));
- if (n != strlen(protocol_list)) {
+ strcpy(tbuf, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
+ "Connection: Upgrade\x0d\x0a"
+ "Upgrade: h2c\x0d\x0a\x0d\x0a");
+ m = (int)strlen(tbuf);
+ n = lws_issue_raw(wsi, (unsigned char *)tbuf, m);
+ if (n != m) {
lwsl_debug("http2 switch: ERROR writing to socket\n");
return 1;
}
- wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE;
+ lwsi_set_state(wsi, LRS_H2_AWAIT_PREFACE);
+ wsi->upgraded_to_http2 = 1;
return 0;
#endif
-
+#if defined(LWS_ROLE_WS)
upgrade_ws:
- if (!wsi->protocol)
- lwsl_err("NULL protocol at lws_read\n");
-
- /*
- * It's websocket
- *
- * Select the first protocol we support from the list
- * the client sent us.
- *
- * Copy it to remove header fragmentation
- */
-
- if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
- WSI_TOKEN_PROTOCOL) < 0) {
- lwsl_err("protocol list too long");
- goto bail_nuke_ah;
- }
-
- protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
- protocol_list[protocol_len] = '\0';
- p = protocol_list;
- hit = 0;
-
- while (*p && !hit) {
- n = 0;
- non_space_char_found = 0;
- while (n < sizeof(protocol_name) - 1 &&
- *p && *p != ',') {
- /* ignore leading spaces */
- if (!non_space_char_found && *p == ' ') {
- n++;
- continue;
- }
- non_space_char_found = 1;
- protocol_name[n++] = *p++;
- }
- protocol_name[n] = '\0';
- if (*p)
- p++;
-
- lwsl_info("checking %s\n", protocol_name);
-
- n = 0;
- while (wsi->vhost->protocols[n].callback) {
- lwsl_info("try %s\n",
- wsi->vhost->protocols[n].name);
-
- if (wsi->vhost->protocols[n].name &&
- !strcmp(wsi->vhost->protocols[n].name,
- protocol_name)) {
- wsi->protocol = &wsi->vhost->protocols[n];
- hit = 1;
- break;
- }
-
- n++;
- }
- }
-
- /* we didn't find a protocol he wanted? */
-
- if (!hit) {
- if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
- lwsl_info("No protocol from \"%s\" supported\n",
- protocol_list);
- goto bail_nuke_ah;
- }
- /*
- * some clients only have one protocol and
- * do not send the protocol list header...
- * allow it and match to the vhost's default
- * protocol (which itself defaults to zero)
- */
- lwsl_info("defaulting to prot handler %d\n",
- wsi->vhost->default_protocol_index);
- n = wsi->vhost->default_protocol_index;
- wsi->protocol = &wsi->vhost->protocols[
- (int)wsi->vhost->default_protocol_index];
- }
-
- /* allocate wsi->user storage */
- if (lws_ensure_user_space(wsi))
- goto bail_nuke_ah;
-
- /*
- * Give the user code a chance to study the request and
- * have the opportunity to deny it
- */
- if ((wsi->protocol->callback)(wsi,
- LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
- wsi->user_space,
- lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
- lwsl_warn("User code denied connection\n");
- goto bail_nuke_ah;
- }
-
- /*
- * Perform the handshake according to the protocol version the
- * client announced
- */
-
- switch (wsi->ietf_spec_revision) {
- case 13:
- lwsl_parser("lws_parse calling handshake_04\n");
- if (handshake_0405(context, wsi)) {
- lwsl_info("hs0405 has failed the connection\n");
- goto bail_nuke_ah;
- }
- break;
-
- default:
- lwsl_info("Unknown client spec version %d\n",
- wsi->ietf_spec_revision);
+ if (lws_process_ws_upgrade(wsi))
goto bail_nuke_ah;
- }
-
- lws_same_vh_protocol_insert(wsi, n);
-
- /* we are upgrading to ws, so http/1.1 and keepalive +
- * pipelined header considerations about keeping the ah around
- * no longer apply. However it's common for the first ws
- * protocol data to have been coalesced with the browser
- * upgrade request and to already be in the ah rx buffer.
- */
-
- lwsl_info("%s: %p: inheriting ws ah (rxpos:%d, rxlen:%d)\n",
- __func__, wsi, wsi->u.hdr.ah->rxpos,
- wsi->u.hdr.ah->rxlen);
- lws_pt_lock(pt);
- hdr = wsi->u.hdr;
-
- lws_union_transition(wsi, LWSCM_WS_SERVING);
- /*
- * first service is WS mode will notice this, use the RX and
- * then detach the ah (caution: we are not in u.hdr union
- * mode any more then... ah_temp member is at start the same
- * though)
- *
- * Because rxpos/rxlen shows something in the ah, we will get
- * service guaranteed next time around the event loop
- *
- * All union members begin with hdr, so we can use it even
- * though we transitioned to ws union mode (the ah detach
- * code uses it anyway).
- */
- wsi->u.hdr = hdr;
- lws_pt_unlock(pt);
-
- lws_server_init_wsi_for_ws(wsi);
- lwsl_parser("accepted v%02d connection\n",
- wsi->ietf_spec_revision);
-
- /* !!! drop ah unreservedly after ESTABLISHED */
- if (!wsi->more_rx_waiting) {
- lws_header_table_force_to_detachable_state(wsi);
- lws_header_table_detach(wsi, 1);
- }
return 0;
+#endif
} /* while all chars are handled */
return 0;
bail_nuke_ah:
/* drop the header info */
- /* we're closing, losing some rx is OK */
- lws_header_table_force_to_detachable_state(wsi);
lws_header_table_detach(wsi, 1);
return 1;
@@ -1772,10 +1649,13 @@ lws_get_idlest_tsi(struct lws_context *context)
}
struct lws *
-lws_create_new_server_wsi(struct lws_vhost *vhost)
+lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi)
{
struct lws *new_wsi;
- int n = lws_get_idlest_tsi(vhost->context);
+ int n = fixed_tsi;
+
+ if (n < 0)
+ n = lws_get_idlest_tsi(vhost->context);
if (n < 0) {
lwsl_err("no space for new conn\n");
@@ -1799,12 +1679,11 @@ lws_create_new_server_wsi(struct lws_vhost *vhost)
/* initialize the instance struct */
- new_wsi->state = LWSS_HTTP;
- new_wsi->mode = LWSCM_HTTP_SERVING;
+ lwsi_set_state(new_wsi, LRS_UNCONNECTED);
new_wsi->hdr_parsing_completed = 0;
-#ifdef LWS_OPENSSL_SUPPORT
- new_wsi->use_ssl = LWS_SSL_ENABLED(vhost);
+#ifdef LWS_WITH_TLS
+ new_wsi->tls.use_ssl = LWS_SSL_ENABLED(vhost);
#endif
/*
@@ -1815,9 +1694,8 @@ lws_create_new_server_wsi(struct lws_vhost *vhost)
*/
new_wsi->protocol = vhost->protocols;
new_wsi->user_space = NULL;
- new_wsi->ietf_spec_revision = 0;
new_wsi->desc.sockfd = LWS_SOCK_INVALID;
- new_wsi->position_in_fds_table = -1;
+ new_wsi->position_in_fds_table = LWS_NO_FDS_POS;
vhost->context->count_wsi_allocated++;
@@ -1845,7 +1723,6 @@ lws_http_transaction_completed(struct lws *wsi)
return 0;
}
- lwsl_debug("%s: wsi %p\n", __func__, wsi);
/* if we can't go back to accept new headers, drop the connection */
if (wsi->http2_substream)
return 0;
@@ -1853,22 +1730,28 @@ lws_http_transaction_completed(struct lws *wsi)
if (wsi->seen_zero_length_recv)
return 1;
- if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
- lwsl_info("%s: %p: close connection\n", __func__, wsi);
+ if (wsi->http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
+ lwsl_notice("%s: %p: close connection\n", __func__, wsi);
return 1;
}
if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0]))
return 1;
- /* otherwise set ourselves up ready to go again */
- wsi->state = LWSS_HTTP;
- wsi->mode = LWSCM_HTTP_SERVING;
- wsi->u.http.tx_content_length = 0;
- wsi->u.http.tx_content_remain = 0;
+ /*
+ * otherwise set ourselves up ready to go again, but because we have no
+ * idea about the wsi writability, we make put it in a holding state
+ * until we can verify POLLOUT. The part of this that confirms POLLOUT
+ * with no partials is in lws_server_socket_service() below.
+ */
+ lwsl_debug("%s: %p: setting DEF_ACT from 0x%x\n", __func__,
+ wsi, wsi->wsistate);
+ lwsi_set_state(wsi, LRS_DEFERRING_ACTION);
+ wsi->http.tx_content_length = 0;
+ wsi->http.tx_content_remain = 0;
wsi->hdr_parsing_completed = 0;
#ifdef LWS_WITH_ACCESS_LOG
- wsi->access_log.sent = 0;
+ wsi->http.access_log.sent = 0;
#endif
if (wsi->vhost->keepalive_timeout)
@@ -1887,21 +1770,20 @@ lws_http_transaction_completed(struct lws *wsi)
* that is already at least the start of another header set, simply
* reset the existing header table and keep it.
*/
- if (wsi->u.hdr.ah) {
- lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
- wsi->more_rx_waiting);
-
- if (!wsi->more_rx_waiting) {
- lws_header_table_force_to_detachable_state(wsi);
+ if (wsi->http.ah) {
+ // lws_buflist_describe(&wsi->buflist, wsi);
+ if (!lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
+ lwsl_info("%s: %p: nothing in buflist so detaching ah\n",
+ __func__, wsi);
lws_header_table_detach(wsi, 1);
-#ifdef LWS_OPENSSL_SUPPORT
+#ifdef LWS_WITH_TLS
/*
* additionally... if we are hogging an SSL instance
* with no pending pipelined headers (or ah now), and
* SSL is scarce, drop this connection without waiting
*/
- if (wsi->vhost->use_ssl &&
+ if (wsi->vhost->tls.use_ssl &&
wsi->context->simultaneous_ssl_restriction &&
wsi->context->simultaneous_ssl ==
wsi->context->simultaneous_ssl_restriction) {
@@ -1911,7 +1793,9 @@ lws_http_transaction_completed(struct lws *wsi)
}
#endif
} else {
- lws_header_table_reset(wsi, 1);
+ lwsl_info("%s: %p: resetting and keeping ah as pipeline\n",
+ __func__, wsi);
+ lws_header_table_reset(wsi, 0);
/*
* If we kept the ah, we should restrict the amount
* of time we are willing to keep it. Otherwise it
@@ -1921,12 +1805,18 @@ lws_http_transaction_completed(struct lws *wsi)
lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
wsi->vhost->keepalive_timeout);
}
- }
+ /* If we're (re)starting on headers, need other implied init */
+ if (wsi->http.ah)
+ wsi->http.ah->ues = URIES_IDLE;
- /* If we're (re)starting on headers, need other implied init */
- wsi->u.hdr.ues = URIES_IDLE;
+ //lwsi_set_state(wsi, LRS_ESTABLISHED);
+ } else
+ if (lws_buflist_next_segment_len(&wsi->buflist, NULL))
+ if (lws_header_table_attach(wsi, 0))
+ lwsl_debug("acquired ah\n");
lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi);
+ lws_callback_on_writable(wsi);
return 0;
}
@@ -1949,11 +1839,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) {
peer = lws_get_or_create_peer(vh, fd.sockfd);
- if (!peer) {
- lwsl_err("OOM creating peer\n");
- return NULL;
- }
- if (context->ip_limit_wsi &&
+ if (peer && context->ip_limit_wsi &&
peer->count_wsi >= context->ip_limit_wsi) {
lwsl_notice("Peer reached wsi limit %d\n",
context->ip_limit_wsi);
@@ -1964,7 +1850,10 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
}
#endif
- new_wsi = lws_create_new_server_wsi(vh);
+ n = -1;
+ if (parent)
+ n = parent->tsi;
+ new_wsi = lws_create_new_server_wsi(vh, n);
if (!new_wsi) {
if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO))
compatible_close(fd.sockfd);
@@ -2000,28 +1889,51 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
lwsl_notice("OOM trying to get user_space\n");
goto bail;
}
+#if defined(LWS_ROLE_WS)
if (type & LWS_ADOPT_WS_PARENTIO) {
new_wsi->desc.sockfd = LWS_SOCK_INVALID;
lwsl_debug("binding to %s\n", new_wsi->protocol->name);
lws_bind_protocol(new_wsi, new_wsi->protocol);
- lws_union_transition(new_wsi, LWSCM_WS_SERVING);
+ lws_role_transition(new_wsi, LWSIFR_SERVER,
+ LRS_ESTABLISHED, &role_ops_ws);
+ /* allocate the ws struct for the wsi */
+ new_wsi->ws = lws_zalloc(sizeof(*new_wsi->ws), "ws struct");
+ if (!new_wsi->ws) {
+ lwsl_notice("OOM\n");
+ goto bail;
+ }
lws_server_init_wsi_for_ws(new_wsi);
return new_wsi;
}
+#endif
} else
- if (type & LWS_ADOPT_HTTP) /* he will transition later */
+#if defined(LWS_ROLE_H1)
+ if (type & LWS_ADOPT_HTTP) {/* he will transition later */
new_wsi->protocol =
&vh->protocols[vh->default_protocol_index];
- else { /* this is the only time he will transition */
+ new_wsi->role_ops = &role_ops_h1;
+ }
+ else
+#endif
+ { /* this is the only time he will transition */
lws_bind_protocol(new_wsi,
&vh->protocols[vh->raw_protocol_index]);
- lws_union_transition(new_wsi, LWSCM_RAW);
+ lws_role_transition(new_wsi, 0, LRS_ESTABLISHED,
+ &role_ops_raw_skt);
}
if (type & LWS_ADOPT_SOCKET) { /* socket desc */
lwsl_debug("%s: new wsi %p, sockfd %d\n", __func__, new_wsi,
(int)(lws_intptr_t)fd.sockfd);
+#if !defined(LWS_WITH_ESP32)
+ if (type & LWS_ADOPT_FLAG_UDP)
+ /*
+ * these can be >128 bytes, so just alloc for UDP
+ */
+ new_wsi->udp = lws_malloc(sizeof(*new_wsi->udp),
+ "udp struct");
+#endif
if (type & LWS_ADOPT_HTTP)
/* the transport is accepted...
@@ -2030,11 +1942,6 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
context->timeout_secs);
-#if LWS_POSIX == 0
-#if defined(LWS_WITH_ESP8266)
- esp8266_tcp_stream_accept(accept_fd, new_wsi);
-#endif
-#endif
} else /* file desc */
lwsl_debug("%s: new wsi %p, filefd %d\n", __func__, new_wsi,
(int)(lws_intptr_t)fd.filefd);
@@ -2058,29 +1965,43 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
/* non-SSL */
if (!(type & LWS_ADOPT_HTTP)) {
if (!(type & LWS_ADOPT_SOCKET))
- new_wsi->mode = LWSCM_RAW_FILEDESC;
+ lws_role_transition(new_wsi, 0, LRS_ESTABLISHED,
+ &role_ops_raw_file);
else
- new_wsi->mode = LWSCM_RAW;
+ lws_role_transition(new_wsi, 0, LRS_ESTABLISHED,
+ &role_ops_raw_skt);
}
+#if defined(LWS_ROLE_H1)
+ else
+ lws_role_transition(new_wsi, LWSIFR_SERVER,
+ LRS_HEADERS, &role_ops_h1);
+#endif
} else {
/* SSL */
if (!(type & LWS_ADOPT_HTTP))
- new_wsi->mode = LWSCM_SSL_INIT_RAW;
+ lws_role_transition(new_wsi, 0, LRS_SSL_INIT,
+ &role_ops_raw_skt);
+#if defined(LWS_ROLE_H1)
else
- new_wsi->mode = LWSCM_SSL_INIT;
-
+ lws_role_transition(new_wsi, LWSIFR_SERVER,
+ LRS_SSL_INIT, &role_ops_h1);
+#endif
ssl = 1;
}
- lws_libev_accept(new_wsi, new_wsi->desc);
- lws_libuv_accept(new_wsi, new_wsi->desc);
- lws_libevent_accept(new_wsi, new_wsi->desc);
+ lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate);
+
+ if (context->event_loop_ops->accept)
+ context->event_loop_ops->accept(new_wsi);
if (!ssl) {
- if (insert_wsi_socket_into_fds(context, new_wsi)) {
+ lws_pt_lock(pt, __func__);
+ if (__insert_wsi_socket_into_fds(context, new_wsi)) {
+ lws_pt_unlock(pt);
lwsl_err("%s: fail inserting socket\n", __func__);
goto fail;
}
+ lws_pt_unlock(pt);
} else
if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) {
lwsl_info("%s: fail ssl negotiation\n", __func__);
@@ -2102,11 +2023,13 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
lwsl_info("%s: waiting for ah\n", __func__);
}
+ lws_cancel_service_pt(new_wsi);
+
return new_wsi;
fail:
if (type & LWS_ADOPT_SOCKET)
- lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS);
+ lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt skt fail");
return NULL;
@@ -2143,8 +2066,8 @@ static struct lws*
adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
{
struct lws_context_per_thread *pt;
- struct allocated_headers *ah;
struct lws_pollfd *pfd;
+ int n;
if (!wsi)
return NULL;
@@ -2152,10 +2075,16 @@ adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
if (!readbuf || len == 0)
return wsi;
- if (len > sizeof(ah->rx)) {
- lwsl_err("%s: rx in too big\n", __func__);
+ if (wsi->position_in_fds_table == LWS_NO_FDS_POS)
+ return wsi;
+
+ pt = &wsi->context->pt[(int)wsi->tsi];
+
+ n = lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf, len);
+ if (n < 0)
goto bail;
- }
+ if (n)
+ lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
/*
* we can't process the initial read data until we can attach an ah.
@@ -2167,14 +2096,9 @@ adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
* readbuf data to wsi or ah yet, and we will do it next if we get
* the ah.
*/
- if (wsi->u.hdr.ah || !lws_header_table_attach(wsi, 0)) {
- ah = wsi->u.hdr.ah;
- memcpy(ah->rx, readbuf, len);
- ah->rxpos = 0;
- ah->rxlen = (int16_t)len;
+ if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) {
lwsl_notice("%s: calling service on readbuf ah\n", __func__);
- pt = &wsi->context->pt[(int)wsi->tsi];
/* unlike a normal connect, we have the headers already
* (or the first part of them anyway).
@@ -2191,25 +2115,11 @@ adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
return wsi;
}
lwsl_err("%s: deferring handling ah\n", __func__);
- /*
- * hum if no ah came, we are on the wait list and must defer
- * dealing with this until the ah arrives.
- *
- * later successful lws_header_table_attach() will apply the
- * below to the rx buffer (via lws_header_table_reset()).
- */
- wsi->u.hdr.preamble_rx = lws_malloc(len, "preamble_rx");
- if (!wsi->u.hdr.preamble_rx) {
- lwsl_err("OOM\n");
- goto bail;
- }
- memcpy(wsi->u.hdr.preamble_rx, readbuf, len);
- wsi->u.hdr.preamble_rx_len = len;
return wsi;
bail:
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt skt readbuf fail");
return NULL;
}
@@ -2232,397 +2142,6 @@ lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,
}
LWS_VISIBLE int
-lws_server_socket_service(struct lws_context *context, struct lws *wsi,
- struct lws_pollfd *pollfd)
-{
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- lws_sockfd_type accept_fd = LWS_SOCK_INVALID;
- struct allocated_headers *ah;
- lws_sock_file_fd_type fd;
- int opts = LWS_ADOPT_SOCKET | LWS_ADOPT_ALLOW_SSL;
-#if LWS_POSIX
- struct sockaddr_storage cli_addr;
- socklen_t clilen;
-#endif
- int n, len;
-
- switch (wsi->mode) {
-
- case LWSCM_HTTP_SERVING:
- case LWSCM_HTTP_SERVING_ACCEPTED:
- case LWSCM_HTTP2_SERVING:
- case LWSCM_RAW:
-
- /* handle http headers coming in */
-
- /* pending truncated sends have uber priority */
-
- if (wsi->trunc_len) {
- if (!(pollfd->revents & LWS_POLLOUT))
- break;
-
- if (lws_issue_raw(wsi, wsi->trunc_alloc +
- wsi->trunc_offset,
- wsi->trunc_len) < 0)
- goto fail;
- /*
- * we can't afford to allow input processing to send
- * something new, so spin around he event loop until
- * he doesn't have any partials
- */
- break;
- }
-
- /* any incoming data ready? */
-
- if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
- goto try_pollout;
-
- /*
- * If we previously just did POLLIN when IN and OUT were
- * signalled (because POLLIN processing may have used up
- * the POLLOUT), don't let that happen twice in a row...
- * next time we see the situation favour POLLOUT
- */
-#if !defined(LWS_WITH_ESP8266)
- if (wsi->favoured_pollin &&
- (pollfd->revents & pollfd->events & LWS_POLLOUT)) {
- lwsl_notice("favouring pollout\n");
- wsi->favoured_pollin = 0;
- goto try_pollout;
- }
-#endif
-
- /* these states imply we MUST have an ah attached */
-
- if (wsi->mode != LWSCM_RAW && (wsi->state == LWSS_HTTP ||
- wsi->state == LWSS_HTTP_ISSUING_FILE ||
- wsi->state == LWSS_HTTP_HEADERS)) {
- if (!wsi->u.hdr.ah) {
- /* no autoservice beacuse we will do it next */
- if (lws_header_table_attach(wsi, 0)) {
- lwsl_info("wsi %p: ah get fail\n", wsi);
- goto try_pollout;
- }
- }
- ah = wsi->u.hdr.ah;
-
- /* if nothing in ah rx buffer, get some fresh rx */
- if (ah->rxpos == ah->rxlen) {
- ah->rxlen = lws_ssl_capable_read(wsi, ah->rx,
- sizeof(ah->rx));
- ah->rxpos = 0;
- switch (ah->rxlen) {
- case 0:
- lwsl_info("%s: read 0 len a\n", __func__);
- wsi->seen_zero_length_recv = 1;
- lws_change_pollfd(wsi, LWS_POLLIN, 0);
- goto try_pollout;
- /* fallthru */
- case LWS_SSL_CAPABLE_ERROR:
- goto fail;
- case LWS_SSL_CAPABLE_MORE_SERVICE:
- ah->rxlen = ah->rxpos = 0;
- goto try_pollout;
- }
-
- /*
- * make sure ah does not get detached if we
- * have live data in the rx
- */
- if (ah->rxlen)
- wsi->more_rx_waiting = 1;
- }
-
- if (!(ah->rxpos != ah->rxlen && ah->rxlen)) {
- lwsl_err("%s: assert: rxpos %d, rxlen %d\n",
- __func__, ah->rxpos, ah->rxlen);
-
- assert(0);
- }
-
- /* just ignore incoming if waiting for close */
- if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
- wsi->state != LWSS_HTTP_ISSUING_FILE) {
- /*
- * otherwise give it to whoever wants it
- * according to the connection state
- */
-
- n = lws_read(wsi, ah->rx + ah->rxpos,
- ah->rxlen - ah->rxpos);
- if (n < 0) /* we closed wsi */
- return 1;
-
- if (!wsi->u.hdr.ah)
- break;
- if ( wsi->u.hdr.ah->rxlen)
- wsi->u.hdr.ah->rxpos += n;
-
- lwsl_debug("%s: wsi %p: ah read rxpos %d, rxlen %d\n",
- __func__, wsi, wsi->u.hdr.ah->rxpos,
- wsi->u.hdr.ah->rxlen);
-
- if (lws_header_table_is_in_detachable_state(wsi) &&
- (wsi->mode != LWSCM_HTTP_SERVING &&
- wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED &&
- wsi->mode != LWSCM_HTTP2_SERVING))
- lws_header_table_detach(wsi, 1);
-
- break;
- }
-
- goto try_pollout;
- }
-
- len = lws_ssl_capable_read(wsi, pt->serv_buf,
- context->pt_serv_buf_size);
- lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len);
- switch (len) {
- case 0:
- lwsl_info("%s: read 0 len b\n", __func__);
-
- /* fallthru */
- case LWS_SSL_CAPABLE_ERROR:
- goto fail;
- case LWS_SSL_CAPABLE_MORE_SERVICE:
- goto try_pollout;
- }
-
- if (len < 0) /* coverity */
- goto fail;
-
- if (wsi->mode == LWSCM_RAW) {
- n = user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_RAW_RX,
- wsi->user_space, pt->serv_buf, len);
- if (n < 0) {
- lwsl_info("LWS_CALLBACK_RAW_RX_fail\n");
- goto fail;
- }
- goto try_pollout;
- }
-
- /* just ignore incoming if waiting for close */
- if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
- wsi->state != LWSS_HTTP_ISSUING_FILE) {
- /*
- * this may want to send
- * (via HTTP callback for example)
- */
- n = lws_read(wsi, pt->serv_buf, len);
- if (n < 0) /* we closed wsi */
- return 1;
- /*
- * he may have used up the
- * writability above, if we will defer POLLOUT
- * processing in favour of POLLIN, note it
- */
- if (pollfd->revents & LWS_POLLOUT)
- wsi->favoured_pollin = 1;
- break;
- }
- /*
- * he may have used up the
- * writability above, if we will defer POLLOUT
- * processing in favour of POLLIN, note it
- */
- if (pollfd->revents & LWS_POLLOUT)
- wsi->favoured_pollin = 1;
-
-try_pollout:
-
- /* this handles POLLOUT for http serving fragments */
-
- if (!(pollfd->revents & LWS_POLLOUT))
- break;
-
- /* one shot */
- if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
- lwsl_notice("%s a\n", __func__);
- goto fail;
- }
-
- if (wsi->mode == LWSCM_RAW) {
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_C_WRITEABLE_CB, 1);
-#if defined(LWS_WITH_STATS)
- if (wsi->active_writable_req_us) {
- uint64_t ul = time_in_microseconds() -
- wsi->active_writable_req_us;
-
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_MS_WRITABLE_DELAY, ul);
- lws_stats_atomic_max(wsi->context, pt,
- LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
- wsi->active_writable_req_us = 0;
- }
-#endif
- n = user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_RAW_WRITEABLE,
- wsi->user_space, NULL, 0);
- if (n < 0) {
- lwsl_info("writeable_fail\n");
- goto fail;
- }
- break;
- }
-
- if (!wsi->hdr_parsing_completed)
- break;
-
- if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
-
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_C_WRITEABLE_CB, 1);
-#if defined(LWS_WITH_STATS)
- if (wsi->active_writable_req_us) {
- uint64_t ul = time_in_microseconds() -
- wsi->active_writable_req_us;
-
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_MS_WRITABLE_DELAY, ul);
- lws_stats_atomic_max(wsi->context, pt,
- LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
- wsi->active_writable_req_us = 0;
- }
-#endif
-
- n = user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_HTTP_WRITEABLE,
- wsi->user_space, NULL, 0);
- if (n < 0) {
- lwsl_info("writeable_fail\n");
- goto fail;
- }
- break;
- }
-
- /* >0 == completion, <0 == error
- *
- * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
- * it's done. That's the case even if we just completed the
- * send, so wait for that.
- */
- n = lws_serve_http_file_fragment(wsi);
- if (n < 0)
- goto fail;
-
- break;
-
- case LWSCM_SERVER_LISTENER:
-
-#if LWS_POSIX
- /* pollin means a client has connected to us then */
-
- do {
- if (!(pollfd->revents & LWS_POLLIN) ||
- !(pollfd->events & LWS_POLLIN))
- break;
-
-#ifdef LWS_OPENSSL_SUPPORT
- /*
- * can we really accept it, with regards to SSL limit?
- * another vhost may also have had POLLIN on his listener this
- * round and used it up already
- */
-
- if (wsi->vhost->use_ssl &&
- context->simultaneous_ssl_restriction &&
- context->simultaneous_ssl ==
- context->simultaneous_ssl_restriction)
- /* no... ignore it, he won't come again until we are
- * below the simultaneous_ssl_restriction limit and
- * POLLIN is enabled on him again
- */
- break;
-#endif
- /* listen socket got an unencrypted connection... */
-
- clilen = sizeof(cli_addr);
- lws_latency_pre(context, wsi);
-
- /*
- * We cannot identify the peer who is in the listen
- * socket connect queue before we accept it; even if
- * we could, not accepting it due to PEER_LIMITS would
- * block the connect queue for other legit peers.
- */
- accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr,
- &clilen);
- lws_latency(context, wsi, "listener accept", accept_fd,
- accept_fd >= 0);
- if (accept_fd < 0) {
- if (LWS_ERRNO == LWS_EAGAIN ||
- LWS_ERRNO == LWS_EWOULDBLOCK) {
- break;
- }
- lwsl_err("ERROR on accept: %s\n", strerror(LWS_ERRNO));
- break;
- }
-
- lws_plat_set_socket_options(wsi->vhost, accept_fd);
-
-#if defined(LWS_WITH_IPV6)
- lwsl_debug("accepted new conn port %u on fd=%d\n",
- ((cli_addr.ss_family == AF_INET6) ?
- ntohs(((struct sockaddr_in6 *) &cli_addr)->sin6_port) :
- ntohs(((struct sockaddr_in *) &cli_addr)->sin_port)),
- accept_fd);
-#else
- lwsl_debug("accepted new conn port %u on fd=%d\n",
- ntohs(((struct sockaddr_in *) &cli_addr)->sin_port),
- accept_fd);
-#endif
-
-#else
- /* not very beautiful... */
- accept_fd = (lws_sockfd_type)pollfd;
-#endif
- /*
- * look at who we connected to and give user code a chance
- * to reject based on client IP. There's no protocol selected
- * yet so we issue this to protocols[0]
- */
- if ((wsi->vhost->protocols[0].callback)(wsi,
- LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
- NULL, (void *)(lws_intptr_t)accept_fd, 0)) {
- lwsl_debug("Callback denied network connection\n");
- compatible_close(accept_fd);
- break;
- }
-
- if (!(wsi->vhost->options & LWS_SERVER_OPTION_ONLY_RAW))
- opts |= LWS_ADOPT_HTTP;
- else
- opts = LWS_ADOPT_SOCKET;
-
- fd.sockfd = accept_fd;
- if (!lws_adopt_descriptor_vhost(wsi->vhost, opts, fd,
- NULL, NULL))
- /* already closed cleanly as necessary */
- return 1;
-
-#if LWS_POSIX
- } while (pt->fds_count < context->fd_limit_per_thread - 1 &&
- lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0);
-#endif
- return 0;
-
- default:
- break;
- }
-
- if (!lws_server_socket_service_ssl(wsi, accept_fd))
- return 0;
-
-fail:
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-
- return 1;
-}
-
-LWS_VISIBLE int
lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
const char *other_headers, int other_headers_len)
{
@@ -2630,13 +2149,13 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
#if defined(LWS_WITH_RANGES)
- struct lws_range_parsing *rp = &wsi->u.http.range;
+ struct lws_range_parsing *rp = &wsi->http.range;
#endif
char cache_control[50], *cc = "no-store";
unsigned char *response = pt->serv_buf + LWS_PRE;
unsigned char *p = response;
unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
- lws_filepos_t computed_total_content_length;
+ lws_filepos_t total_content_length;
int ret = 0, cclen = 8, n = HTTP_STATUS_OK;
lws_fop_flags_t fflags = LWS_O_RDONLY;
#if defined(LWS_WITH_RANGES)
@@ -2645,29 +2164,34 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
const struct lws_plat_file_ops *fops;
const char *vpath;
+ if (wsi->handling_404)
+ n = HTTP_STATUS_NOT_FOUND;
+
/*
* We either call the platform fops .open with first arg platform fops,
* or we call fops_zip .open with first arg platform fops, and fops_zip
* open will decide whether to switch to fops_zip or stay with fops_def.
*
- * If wsi->u.http.fop_fd is already set, the caller already opened it
+ * If wsi->http.fop_fd is already set, the caller already opened it
*/
- if (!wsi->u.http.fop_fd) {
+ if (!wsi->http.fop_fd) {
fops = lws_vfs_select_fops(wsi->context->fops, file, &vpath);
fflags |= lws_vfs_prepare_flags(wsi);
- wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
+ wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
file, vpath, &fflags);
- if (!wsi->u.http.fop_fd) {
- lwsl_err("Unable to open '%s'\n", file);
-
- return -1;
+ if (!wsi->http.fop_fd) {
+ lwsl_info("%s: Unable to open: '%s': errno %d\n",
+ __func__, file, errno);
+ if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
+ return -1;
+ return !wsi->http2_substream;
}
}
- wsi->u.http.filelen = lws_vfs_get_length(wsi->u.http.fop_fd);
- computed_total_content_length = wsi->u.http.filelen;
+ wsi->http.filelen = lws_vfs_get_length(wsi->http.fop_fd);
+ total_content_length = wsi->http.filelen;
#if defined(LWS_WITH_RANGES)
- ranges = lws_ranges_init(wsi, rp, wsi->u.http.filelen);
+ ranges = lws_ranges_init(wsi, rp, wsi->http.filelen);
lwsl_debug("Range count %d\n", ranges);
/*
@@ -2684,7 +2208,7 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
if (lws_http_transaction_completed(wsi))
return -1; /* <0 means just hang up */
- lws_vfs_file_close(&wsi->u.http.fop_fd);
+ lws_vfs_file_close(&wsi->http.fop_fd);
return 0; /* == 0 means we dealt with the transaction complete */
}
@@ -2695,7 +2219,7 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
if (lws_add_http_header_status(wsi, n, &p, end))
return -1;
- if ((wsi->u.http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP |
+ if ((wsi->http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP |
LWS_FOP_FLAG_COMPR_IS_GZIP)) ==
(LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) {
if (lws_add_http_header_by_token(wsi,
@@ -2710,20 +2234,24 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
ranges < 2 &&
#endif
content_type && content_type[0])
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
+ if (lws_add_http_header_by_token(wsi,
+ WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)content_type,
- strlen(content_type), &p, end))
+ (int)strlen(content_type),
+ &p, end))
return -1;
#if defined(LWS_WITH_RANGES)
if (ranges >= 2) { /* multipart byteranges */
- strncpy(wsi->u.http.multipart_content_type, content_type,
- sizeof(wsi->u.http.multipart_content_type) - 1);
- wsi->u.http.multipart_content_type[
- sizeof(wsi->u.http.multipart_content_type) - 1] = '\0';
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
- (unsigned char *)"multipart/byteranges; boundary=_lws",
- 20, &p, end))
+ lws_strncpy(wsi->http.multipart_content_type, content_type,
+ sizeof(wsi->http.multipart_content_type));
+
+ if (lws_add_http_header_by_token(wsi,
+ WSI_TOKEN_HTTP_CONTENT_TYPE,
+ (unsigned char *)
+ "multipart/byteranges; "
+ "boundary=_lws",
+ 20, &p, end))
return -1;
/*
@@ -2738,8 +2266,8 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
* Precompute it for the main response header
*/
- computed_total_content_length = (lws_filepos_t)rp->agg +
- 6 /* final _lws\r\n */;
+ total_content_length = (lws_filepos_t)rp->agg +
+ 6 /* final _lws\r\n */;
lws_ranges_reset(rp);
while (lws_ranges_next(rp)) {
@@ -2747,7 +2275,7 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
"bytes %llu-%llu/%llu",
rp->start, rp->end, rp->extent);
- computed_total_content_length +=
+ total_content_length +=
6 /* header _lws\r\n */ +
/* Content-Type: xxx/xxx\r\n */
14 + strlen(content_type) + 2 +
@@ -2761,35 +2289,38 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
}
if (ranges == 1) {
- computed_total_content_length = (lws_filepos_t)rp->agg;
+ total_content_length = (lws_filepos_t)rp->agg;
n = lws_snprintf(cache_control, sizeof(cache_control),
"bytes %llu-%llu/%llu",
rp->start, rp->end, rp->extent);
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_RANGE,
+ if (lws_add_http_header_by_token(wsi,
+ WSI_TOKEN_HTTP_CONTENT_RANGE,
(unsigned char *)cache_control,
n, &p, end))
return -1;
}
- wsi->u.http.range.inside = 0;
+ wsi->http.range.inside = 0;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES,
(unsigned char *)"bytes", 5, &p, end))
return -1;
#endif
- if (!wsi->sending_chunked) {
- if (lws_add_http_header_content_length(wsi,
- computed_total_content_length,
- &p, end))
- return -1;
- } else {
- if (lws_add_http_header_by_token(wsi,
+ if (!wsi->http2_substream) {
+ if (!wsi->sending_chunked) {
+ if (lws_add_http_header_content_length(wsi,
+ total_content_length,
+ &p, end))
+ return -1;
+ } else {
+ if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
(unsigned char *)"chunked",
7, &p, end))
- return -1;
+ return -1;
+ }
}
if (wsi->cache_secs && wsi->cache_reuse) {
@@ -2808,7 +2339,7 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
(unsigned char *)cc, cclen, &p, end))
return -1;
- if (wsi->u.http.connection_type == HTTP_CONNECTION_KEEP_ALIVE)
+ if (wsi->http.connection_type == HTTP_CONNECTION_KEEP_ALIVE)
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
(unsigned char *)"keep-alive", 10, &p, end))
return -1;
@@ -2830,91 +2361,268 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
return -1;
}
- wsi->u.http.filepos = 0;
- wsi->state = LWSS_HTTP_ISSUING_FILE;
+ wsi->http.filepos = 0;
+ lwsi_set_state(wsi, LRS_ISSUING_FILE);
lws_callback_on_writable(wsi);
return 0;
}
-int
-lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
+LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
{
- int m;
+ struct lws_context *context = wsi->context;
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ struct lws_process_html_args args;
+ lws_filepos_t amount, poss;
+ unsigned char *p, *pstart;
+#if defined(LWS_WITH_RANGES)
+ unsigned char finished = 0;
+#endif
+ int n, m;
+
+ lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
+
+ do {
+
+ if (wsi->trunc_len) {
+ if (lws_issue_raw(wsi, wsi->trunc_alloc +
+ wsi->trunc_offset,
+ wsi->trunc_len) < 0) {
+ lwsl_info("%s: closing\n", __func__);
+ goto file_had_it;
+ }
+ break;
+ }
+
+ if (wsi->http.filepos == wsi->http.filelen)
+ goto all_sent;
+
+ n = 0;
+
+ pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
+
+ p = pstart;
+
+#if defined(LWS_WITH_RANGES)
+ if (wsi->http.range.count_ranges && !wsi->http.range.inside) {
+
+ lwsl_notice("%s: doing range start %llu\n", __func__,
+ wsi->http.range.start);
+
+ if ((long long)lws_vfs_file_seek_cur(wsi->http.fop_fd,
+ wsi->http.range.start -
+ wsi->http.filepos) < 0)
+ goto file_had_it;
+
+ wsi->http.filepos = wsi->http.range.start;
+
+ if (wsi->http.range.count_ranges > 1) {
+ n = lws_snprintf((char *)p,
+ context->pt_serv_buf_size -
+ LWS_H2_FRAME_HEADER_LENGTH,
+ "_lws\x0d\x0a"
+ "Content-Type: %s\x0d\x0a"
+ "Content-Range: bytes %llu-%llu/%llu\x0d\x0a"
+ "\x0d\x0a",
+ wsi->http.multipart_content_type,
+ wsi->http.range.start,
+ wsi->http.range.end,
+ wsi->http.range.extent);
+ p += n;
+ }
- lwsl_parser("%s: received %d byte packet\n", __func__, (int)len);
-#if 0
- lwsl_hexdump(*buf, len);
+ wsi->http.range.budget = wsi->http.range.end -
+ wsi->http.range.start + 1;
+ wsi->http.range.inside = 1;
+ }
#endif
- /* let the rx protocol state machine have as much as it needs */
+ poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH;
+
+ if (wsi->http.tx_content_length)
+ if (poss > wsi->http.tx_content_remain)
+ poss = wsi->http.tx_content_remain;
- while (len) {
/*
- * we were accepting input but now we stopped doing so
+ * if there is a hint about how much we will do well to send at
+ * one time, restrict ourselves to only trying to send that.
*/
- if (wsi->rxflow_bitmap) {
- lws_rxflow_cache(wsi, *buf, 0, len);
- lwsl_parser("%s: cached %ld\n", __func__, (long)len);
- return 1;
- }
+ if (wsi->protocol->tx_packet_size &&
+ poss > wsi->protocol->tx_packet_size)
+ poss = wsi->protocol->tx_packet_size;
- if (wsi->u.ws.rx_draining_ext) {
- m = lws_rx_sm(wsi, 0);
- if (m < 0)
- return -1;
- continue;
- }
+ if (wsi->role_ops->tx_credit) {
+ lws_filepos_t txc = wsi->role_ops->tx_credit(wsi);
- /* account for what we're using in rxflow buffer */
- if (wsi->rxflow_buffer) {
- wsi->rxflow_pos++;
- if (wsi->rxflow_pos > wsi->rxflow_len) {
- lwsl_err("bumped rxflow buffer too far (%d / %d)", wsi->rxflow_pos, wsi->rxflow_len);
- assert(0);
+ if (!txc) {
+ lwsl_info("%s: came here with no tx credit\n",
+ __func__);
+ return 0;
}
+ if (txc < poss)
+ poss = txc;
+
+ /*
+ * consumption of the actual payload amount sent will be
+ * handled when the role data frame is sent
+ */
}
- /* consume payload bytes efficiently */
- if (wsi->lws_rx_parse_state ==
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED) {
- m = lws_payload_until_length_exhausted(wsi, buf, &len);
- if (wsi->rxflow_buffer)
- wsi->rxflow_pos += m;
+#if defined(LWS_WITH_RANGES)
+ if (wsi->http.range.count_ranges) {
+ if (wsi->http.range.count_ranges > 1)
+ poss -= 7; /* allow for final boundary */
+ if (poss > wsi->http.range.budget)
+ poss = wsi->http.range.budget;
+ }
+#endif
+ if (wsi->sending_chunked) {
+ /* we need to drop the chunk size in here */
+ p += 10;
+ /* allow for the chunk to grow by 128 in translation */
+ poss -= 10 + 128;
}
- if (wsi->rxflow_buffer && wsi->rxflow_pos == wsi->rxflow_len) {
- lwsl_debug("%s: %p flow buf: drained\n", __func__, wsi);
- lws_free_set_NULL(wsi->rxflow_buffer);
- /* having drained the rxflow buffer, can rearm POLLIN */
-#ifdef LWS_NO_SERVER
- m =
+ if (lws_vfs_file_read(wsi->http.fop_fd, &amount, p, poss) < 0)
+ goto file_had_it; /* caller will close */
+
+ if (wsi->sending_chunked)
+ n = (int)amount;
+ else
+ n = lws_ptr_diff(p, pstart) + (int)amount;
+
+ lwsl_debug("%s: sending %d\n", __func__, n);
+
+ if (n) {
+ lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
+ context->timeout_secs);
+
+ if (wsi->interpreting) {
+ args.p = (char *)p;
+ args.len = n;
+ args.max_len = (unsigned int)poss + 128;
+ args.final = wsi->http.filepos + n ==
+ wsi->http.filelen;
+ args.chunked = wsi->sending_chunked;
+ if (user_callback_handle_rxflow(
+ wsi->vhost->protocols[
+ (int)wsi->protocol_interpret_idx].callback,
+ wsi, LWS_CALLBACK_PROCESS_HTML,
+ wsi->user_space, &args, 0) < 0)
+ goto file_had_it;
+ n = args.len;
+ p = (unsigned char *)args.p;
+ } else
+ p = pstart;
+
+#if defined(LWS_WITH_RANGES)
+ if (wsi->http.range.send_ctr + 1 ==
+ wsi->http.range.count_ranges && // last range
+ wsi->http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
+ wsi->http.range.budget - amount == 0) {// final part
+ n += lws_snprintf((char *)pstart + n, 6,
+ "_lws\x0d\x0a"); // append trailing boundary
+ lwsl_debug("added trailing boundary\n");
+ }
+#endif
+ m = lws_write(wsi, p, n,
+ wsi->http.filepos + amount == wsi->http.filelen ?
+ LWS_WRITE_HTTP_FINAL :
+ LWS_WRITE_HTTP
+ );
+ if (m < 0)
+ goto file_had_it;
+
+ wsi->http.filepos += amount;
+
+#if defined(LWS_WITH_RANGES)
+ if (wsi->http.range.count_ranges >= 1) {
+ wsi->http.range.budget -= amount;
+ if (wsi->http.range.budget == 0) {
+ lwsl_notice("range budget exhausted\n");
+ wsi->http.range.inside = 0;
+ wsi->http.range.send_ctr++;
+
+ if (lws_ranges_next(&wsi->http.range) < 1) {
+ finished = 1;
+ goto all_sent;
+ }
+ }
+ }
#endif
- _lws_rx_flow_control(wsi);
- /* m ignored, needed for NO_SERVER case */
+
+ if (m != n) {
+ /* adjust for what was not sent */
+ if (lws_vfs_file_seek_cur(wsi->http.fop_fd,
+ m - n) ==
+ (lws_fileofs_t)-1)
+ goto file_had_it;
+ }
}
- /* process the byte */
- m = lws_rx_sm(wsi, *(*buf)++);
- if (m < 0)
- return -1;
- len--;
- }
+all_sent:
+ if ((!wsi->trunc_len && wsi->http.filepos >= wsi->http.filelen)
+#if defined(LWS_WITH_RANGES)
+ || finished)
+#else
+ )
+#endif
+ {
+ lwsi_set_state(wsi, LRS_ESTABLISHED);
+ /* we might be in keepalive, so close it off here */
+ lws_vfs_file_close(&wsi->http.fop_fd);
+
+ lwsl_debug("file completed\n");
+
+ if (wsi->protocol->callback &&
+ user_callback_handle_rxflow(wsi->protocol->callback,
+ wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
+ wsi->user_space, NULL,
+ 0) < 0) {
+ /*
+ * For http/1.x, the choices from
+ * transaction_completed are either
+ * 0 to use the connection for pipelined
+ * or nonzero to hang it up.
+ *
+ * However for http/2. while we are
+ * still interested in hanging up the
+ * nwsi if there was a network-level
+ * fatal error, simply completing the
+ * transaction is a matter of the stream
+ * state, not the root connection at the
+ * network level
+ */
+ if (wsi->http2_substream)
+ return 1;
+ else
+ return -1;
+ }
+
+ return 1; /* >0 indicates completed */
+ }
+ } while (0); // while (!lws_send_pipe_choked(wsi))
+
+ lws_callback_on_writable(wsi);
- lwsl_parser("%s: exit with %d unused\n", __func__, (int)len);
+ return 0; /* indicates further processing must be done */
- return 0;
+file_had_it:
+ lws_vfs_file_close(&wsi->http.fop_fd);
+
+ return -1;
}
+
LWS_VISIBLE void
lws_server_get_canonical_hostname(struct lws_context *context,
- struct lws_context_creation_info *info)
+ const struct lws_context_creation_info *info)
{
if (lws_check_opt(info->options,
LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME))
return;
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
+#if !defined(LWS_WITH_ESP32)
/* find canonical hostname */
gethostname((char *)context->canonical_hostname,
sizeof(context->canonical_hostname) - 1);
@@ -2968,11 +2676,11 @@ skip:
sp = s->start + 1;
continue;
}
- if (hits == 1 && s->pos == strlen(s->vars[hit])) {
+ if (hits == 1 && s->pos == (int)strlen(s->vars[hit])) {
pc = s->replace(s->data, hit);
if (!pc)
pc = "NULL";
- n = strlen(pc);
+ n = (int)strlen(pc);
s->swallow[s->pos] = '\0';
if (n != s->pos) {
memmove(s->start + n,
@@ -2994,31 +2702,33 @@ skip:
sp++;
}
- /* no space left for final chunk trailer */
- if (args->final && args->len + 7 >= args->max_len)
- return -1;
+ if (args->chunked) {
+ /* no space left for final chunk trailer */
+ if (args->final && args->len + 7 >= args->max_len)
+ return -1;
- n = sprintf(buffer, "%X\x0d\x0a", args->len);
-
- args->p -= n;
- memcpy(args->p, buffer, n);
- args->len += n;
-
- if (args->final) {
- sp = args->p + args->len;
- *sp++ = '\x0d';
- *sp++ = '\x0a';
- *sp++ = '0';
- *sp++ = '\x0d';
- *sp++ = '\x0a';
- *sp++ = '\x0d';
- *sp++ = '\x0a';
- args->len += 7;
- } else {
- sp = args->p + args->len;
- *sp++ = '\x0d';
- *sp++ = '\x0a';
- args->len += 2;
+ n = sprintf(buffer, "%X\x0d\x0a", args->len);
+
+ args->p -= n;
+ memcpy(args->p, buffer, n);
+ args->len += n;
+
+ if (args->final) {
+ sp = args->p + args->len;
+ *sp++ = '\x0d';
+ *sp++ = '\x0a';
+ *sp++ = '0';
+ *sp++ = '\x0d';
+ *sp++ = '\x0a';
+ *sp++ = '\x0d';
+ *sp++ = '\x0a';
+ args->len += 7;
+ } else {
+ sp = args->p + args->len;
+ *sp++ = '\x0d';
+ *sp++ = '\x0a';
+ args->len += 2;
+ }
}
return 0;
diff --git a/thirdparty/libwebsockets/roles/listen/ops-listen.c b/thirdparty/libwebsockets/roles/listen/ops-listen.c
new file mode 100644
index 0000000000..dbeb9753a2
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/listen/ops-listen.c
@@ -0,0 +1,177 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <core/private.h>
+
+static int
+rops_handle_POLLIN_listen(struct lws_context_per_thread *pt, struct lws *wsi,
+ struct lws_pollfd *pollfd)
+{
+ struct lws_context *context = wsi->context;
+ lws_sockfd_type accept_fd = LWS_SOCK_INVALID;
+ lws_sock_file_fd_type fd;
+ int opts = LWS_ADOPT_SOCKET | LWS_ADOPT_ALLOW_SSL;
+ struct sockaddr_storage cli_addr;
+ socklen_t clilen;
+
+ /* pollin means a client has connected to us then
+ *
+ * pollout is a hack on esp32 for background accepts signalling
+ * they completed
+ */
+
+ do {
+ struct lws *cwsi;
+
+ if (!(pollfd->revents & (LWS_POLLIN | LWS_POLLOUT)) ||
+ !(pollfd->events & LWS_POLLIN))
+ break;
+
+#if defined(LWS_WITH_TLS)
+ /*
+ * can we really accept it, with regards to SSL limit?
+ * another vhost may also have had POLLIN on his
+ * listener this round and used it up already
+ */
+ if (wsi->vhost->tls.use_ssl &&
+ context->simultaneous_ssl_restriction &&
+ context->simultaneous_ssl ==
+ context->simultaneous_ssl_restriction)
+ /*
+ * no... ignore it, he won't come again until
+ * we are below the simultaneous_ssl_restriction
+ * limit and POLLIN is enabled on him again
+ */
+ break;
+#endif
+ /* listen socket got an unencrypted connection... */
+
+ clilen = sizeof(cli_addr);
+ lws_latency_pre(context, wsi);
+
+ /*
+ * We cannot identify the peer who is in the listen
+ * socket connect queue before we accept it; even if
+ * we could, not accepting it due to PEER_LIMITS would
+ * block the connect queue for other legit peers.
+ */
+
+ accept_fd = accept((int)pollfd->fd,
+ (struct sockaddr *)&cli_addr, &clilen);
+ lws_latency(context, wsi, "listener accept",
+ (int)accept_fd, accept_fd != LWS_SOCK_INVALID);
+ if (accept_fd == LWS_SOCK_INVALID) {
+ if (LWS_ERRNO == LWS_EAGAIN ||
+ LWS_ERRNO == LWS_EWOULDBLOCK) {
+ break;
+ }
+ lwsl_err("ERROR on accept: %s\n",
+ strerror(LWS_ERRNO));
+ break;
+ }
+
+ lws_plat_set_socket_options(wsi->vhost, accept_fd);
+
+#if defined(LWS_WITH_IPV6)
+ lwsl_debug("accepted new conn port %u on fd=%d\n",
+ ((cli_addr.ss_family == AF_INET6) ?
+ ntohs(((struct sockaddr_in6 *) &cli_addr)->sin6_port) :
+ ntohs(((struct sockaddr_in *) &cli_addr)->sin_port)),
+ accept_fd);
+#else
+ lwsl_debug("accepted new conn port %u on fd=%d\n",
+ ntohs(((struct sockaddr_in *) &cli_addr)->sin_port),
+ accept_fd);
+#endif
+
+ /*
+ * look at who we connected to and give user code a
+ * chance to reject based on client IP. There's no
+ * protocol selected yet so we issue this to
+ * protocols[0]
+ */
+ if ((wsi->vhost->protocols[0].callback)(wsi,
+ LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
+ NULL,
+ (void *)(lws_intptr_t)accept_fd, 0)) {
+ lwsl_debug("Callback denied net connection\n");
+ compatible_close(accept_fd);
+ break;
+ }
+
+ if (!(wsi->vhost->options & LWS_SERVER_OPTION_ONLY_RAW))
+ opts |= LWS_ADOPT_HTTP;
+ else
+ opts = LWS_ADOPT_SOCKET;
+
+ fd.sockfd = accept_fd;
+ cwsi = lws_adopt_descriptor_vhost(wsi->vhost, opts, fd,
+ NULL, NULL);
+ if (!cwsi)
+ /* already closed cleanly as necessary */
+ return LWS_HPI_RET_WSI_ALREADY_DIED;
+
+ if (lws_server_socket_service_ssl(cwsi, accept_fd)) {
+ lws_close_free_wsi(cwsi, LWS_CLOSE_STATUS_NOSTATUS,
+ "listen svc fail");
+ return LWS_HPI_RET_WSI_ALREADY_DIED;
+ }
+
+ lwsl_info("%s: new wsi %p: wsistate 0x%x, role_ops %s\n",
+ __func__, cwsi, cwsi->wsistate, cwsi->role_ops->name);
+
+ } while (pt->fds_count < context->fd_limit_per_thread - 1 &&
+ wsi->position_in_fds_table != LWS_NO_FDS_POS &&
+ lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0);
+
+ return LWS_HPI_RET_HANDLED;
+}
+
+int rops_handle_POLLOUT_listen(struct lws *wsi)
+{
+ return LWS_HP_RET_USER_SERVICE;
+}
+
+struct lws_role_ops role_ops_listen = {
+ /* role name */ "listen",
+ /* alpn id */ NULL,
+ /* check_upgrades */ NULL,
+ /* init_context */ NULL,
+ /* init_vhost */ NULL,
+ /* destroy_vhost */ NULL,
+ /* periodic_checks */ NULL,
+ /* service_flag_pending */ NULL,
+ /* handle_POLLIN */ rops_handle_POLLIN_listen,
+ /* handle_POLLOUT */ rops_handle_POLLOUT_listen,
+ /* perform_user_POLLOUT */ NULL,
+ /* callback_on_writable */ NULL,
+ /* tx_credit */ NULL,
+ /* write_role_protocol */ NULL,
+ /* encapsulation_parent */ NULL,
+ /* alpn_negotiated */ NULL,
+ /* close_via_role_protocol */ NULL,
+ /* close_role */ NULL,
+ /* close_kill_connection */ NULL,
+ /* destroy_role */ NULL,
+ /* writeable cb clnt, srv */ { 0, 0 },
+ /* close cb clnt, srv */ { 0, 0 },
+ /* file_handle */ 0,
+};
diff --git a/thirdparty/libwebsockets/roles/pipe/ops-pipe.c b/thirdparty/libwebsockets/roles/pipe/ops-pipe.c
new file mode 100644
index 0000000000..b9348d58d7
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/pipe/ops-pipe.c
@@ -0,0 +1,81 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <core/private.h>
+
+static int
+rops_handle_POLLIN_pipe(struct lws_context_per_thread *pt, struct lws *wsi,
+ struct lws_pollfd *pollfd)
+{
+#if !defined(WIN32) && !defined(_WIN32)
+ char s[100];
+ int n;
+
+ /*
+ * discard the byte(s) that signaled us
+ * We really don't care about the number of bytes, but coverity
+ * thinks we should.
+ */
+ n = read(wsi->desc.sockfd, s, sizeof(s));
+ (void)n;
+ if (n < 0)
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+#endif
+ /*
+ * the poll() wait, or the event loop for libuv etc is a
+ * process-wide resource that we interrupted. So let every
+ * protocol that may be interested in the pipe event know that
+ * it happened.
+ */
+ if (lws_broadcast(wsi->context, LWS_CALLBACK_EVENT_WAIT_CANCELLED,
+ NULL, 0)) {
+ lwsl_info("closed in event cancel\n");
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ }
+
+ return LWS_HPI_RET_HANDLED;
+}
+
+struct lws_role_ops role_ops_pipe = {
+ /* role name */ "pipe",
+ /* alpn id */ NULL,
+ /* check_upgrades */ NULL,
+ /* init_context */ NULL,
+ /* init_vhost */ NULL,
+ /* destroy_vhost */ NULL,
+ /* periodic_checks */ NULL,
+ /* service_flag_pending */ NULL,
+ /* handle_POLLIN */ rops_handle_POLLIN_pipe,
+ /* handle_POLLOUT */ NULL,
+ /* perform_user_POLLOUT */ NULL,
+ /* callback_on_writable */ NULL,
+ /* tx_credit */ NULL,
+ /* write_role_protocol */ NULL,
+ /* encapsulation_parent */ NULL,
+ /* alpn_negotiated */ NULL,
+ /* close_via_role_protocol */ NULL,
+ /* close_role */ NULL,
+ /* close_kill_connection */ NULL,
+ /* destroy_role */ NULL,
+ /* writeable cb clnt, srv */ { 0, 0 },
+ /* close cb clnt, srv */ { 0, 0 },
+ /* file_handle */ 1,
+};
diff --git a/thirdparty/libwebsockets/roles/private.h b/thirdparty/libwebsockets/roles/private.h
new file mode 100644
index 0000000000..ae4278b5d3
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/private.h
@@ -0,0 +1,282 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * This is included from core/private.h
+ */
+
+typedef uint32_t lws_wsi_state_t;
+
+/*
+ * The wsi->role_ops pointer decides almost everything about what role the wsi
+ * will play, h2, raw, ws, etc.
+ *
+ * However there are a few additional flags needed that vary, such as if the
+ * role is a client or server side, if it has that concept. And the connection
+ * fulfilling the role, has a separate dynamic state.
+ *
+ * 31 16 15 0
+ * [ role flags ] [ state ]
+ *
+ * The role flags part is generally invariant for the lifetime of the wsi,
+ * although it can change if the connection role itself does, eg, if the
+ * connection upgrades from H1 -> WS1 the role flags may be changed at that
+ * point.
+ *
+ * The state part reflects the dynamic connection state, and the states are
+ * reused between roles.
+ *
+ * None of the internal role or state representations are made available outside
+ * of lws internals. Even for lws internals, if you add stuff here, please keep
+ * the constants inside this header only by adding necessary helpers here and
+ * use the helpers in the actual code. This is to ease any future refactors.
+ *
+ * Notice LWSIFR_ENCAP means we have a parent wsi that actually carries our
+ * data as a stream inside a different protocol.
+ */
+
+#define _RS 16
+
+#define LWSIFR_CLIENT (0x1000 << _RS) /* client side */
+#define LWSIFR_SERVER (0x2000 << _RS) /* server side */
+
+#define LWSIFR_P_ENCAP_H2 (0x0100 << _RS) /* we are encapsulated by h2 */
+
+enum lwsi_role {
+ LWSI_ROLE_MASK = (0xffff << _RS),
+ LWSI_ROLE_ENCAP_MASK = (0x0f00 << _RS),
+};
+
+#define lwsi_role(wsi) (wsi->wsistate & LWSI_ROLE_MASK)
+#if !defined (_DEBUG)
+#define lwsi_set_role(wsi, role) wsi->wsistate = \
+ (wsi->wsistate & (~LWSI_ROLE_MASK)) | role
+#else
+void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role);
+#endif
+
+#define lwsi_role_client(wsi) (!!(wsi->wsistate & LWSIFR_CLIENT))
+#define lwsi_role_server(wsi) (!!(wsi->wsistate & LWSIFR_SERVER))
+#define lwsi_role_h2_ENCAPSULATION(wsi) \
+ ((wsi->wsistate & LWSI_ROLE_ENCAP_MASK) == LWSIFR_P_ENCAP_H2)
+
+/* Pollout wants a callback in this state */
+#define LWSIFS_POCB (0x100)
+/* Before any protocol connection was established */
+#define LWSIFS_NOT_EST (0x200)
+
+enum lwsi_state {
+
+ /* Phase 1: pre-transport */
+
+ LRS_UNCONNECTED = LWSIFS_NOT_EST | 0,
+ LRS_WAITING_CONNECT = LWSIFS_NOT_EST | 1,
+
+ /* Phase 2: establishing intermediaries on top of transport */
+
+ LRS_WAITING_PROXY_REPLY = LWSIFS_NOT_EST | 2,
+ LRS_WAITING_SSL = LWSIFS_NOT_EST | 3,
+ LRS_WAITING_SOCKS_GREETING_REPLY = LWSIFS_NOT_EST | 4,
+ LRS_WAITING_SOCKS_CONNECT_REPLY = LWSIFS_NOT_EST | 5,
+ LRS_WAITING_SOCKS_AUTH_REPLY = LWSIFS_NOT_EST | 6,
+
+ /* Phase 3: establishing tls tunnel */
+
+ LRS_SSL_INIT = LWSIFS_NOT_EST | 7,
+ LRS_SSL_ACK_PENDING = LWSIFS_NOT_EST | 8,
+ LRS_PRE_WS_SERVING_ACCEPT = LWSIFS_NOT_EST | 9,
+
+ /* Phase 4: connected */
+
+ LRS_WAITING_SERVER_REPLY = LWSIFS_NOT_EST | 10,
+ LRS_H2_AWAIT_PREFACE = LWSIFS_NOT_EST | 11,
+ LRS_H2_AWAIT_SETTINGS = LWSIFS_NOT_EST |
+ LWSIFS_POCB | 12,
+
+ /* Phase 5: protocol logically established */
+
+ LRS_H2_CLIENT_SEND_SETTINGS = LWSIFS_POCB | 13,
+ LRS_H2_WAITING_TO_SEND_HEADERS = LWSIFS_POCB | 14,
+ LRS_DEFERRING_ACTION = LWSIFS_POCB | 15,
+ LRS_IDLING = 16,
+ LRS_H1C_ISSUE_HANDSHAKE = 17,
+ LRS_H1C_ISSUE_HANDSHAKE2 = 18,
+ LRS_ISSUE_HTTP_BODY = 19,
+ LRS_ISSUING_FILE = 20,
+ LRS_HEADERS = 21,
+ LRS_BODY = 22,
+ LRS_ESTABLISHED = LWSIFS_POCB | 23,
+ /* we are established, but we have embarked on serving a single
+ * transaction. Other transaction input may be pending, but we will
+ * not service it while we are busy dealing with the current
+ * transaction.
+ *
+ * When we complete the current transaction, we would reset our state
+ * back to ESTABLISHED and start to process the next transaction.
+ */
+ LRS_DOING_TRANSACTION = LWSIFS_POCB | 24,
+
+ /* Phase 6: finishing */
+
+ LRS_WAITING_TO_SEND_CLOSE = LWSIFS_POCB | 25,
+ LRS_RETURNED_CLOSE = LWSIFS_POCB | 26,
+ LRS_AWAITING_CLOSE_ACK = LWSIFS_POCB | 27,
+ LRS_FLUSHING_BEFORE_CLOSE = LWSIFS_POCB | 28,
+ LRS_SHUTDOWN = 29,
+
+ /* Phase 7: dead */
+
+ LRS_DEAD_SOCKET = 30,
+
+ LRS_MASK = 0xffff
+};
+
+#define lwsi_state(wsi) ((enum lwsi_state)(wsi->wsistate & LRS_MASK))
+#define lwsi_state_PRE_CLOSE(wsi) ((enum lwsi_state)(wsi->wsistate_pre_close & LRS_MASK))
+#define lwsi_state_est(wsi) (!(wsi->wsistate & LWSIFS_NOT_EST))
+#define lwsi_state_est_PRE_CLOSE(wsi) (!(wsi->wsistate_pre_close & LWSIFS_NOT_EST))
+#define lwsi_state_can_handle_POLLOUT(wsi) (wsi->wsistate & LWSIFS_POCB)
+#if !defined (_DEBUG)
+#define lwsi_set_state(wsi, lrs) wsi->wsistate = \
+ (wsi->wsistate & (~LRS_MASK)) | lrs
+#else
+void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs);
+#endif
+
+/*
+ * internal role-specific ops
+ */
+struct lws_context_per_thread;
+struct lws_role_ops {
+ const char *name;
+ const char *alpn;
+ /*
+ * After http headers have parsed, this is the last chance for a role
+ * to upgrade the connection to something else using the headers.
+ * ws-over-h2 is upgraded from h2 like this.
+ */
+ int (*check_upgrades)(struct lws *wsi);
+ /* role-specific context init during context creation */
+ int (*init_context)(struct lws_context *context,
+ const struct lws_context_creation_info *info);
+ /* role-specific per-vhost init during vhost creation */
+ int (*init_vhost)(struct lws_vhost *vh,
+ const struct lws_context_creation_info *info);
+ /* role-specific per-vhost destructor during vhost destroy */
+ int (*destroy_vhost)(struct lws_vhost *vh);
+ /* generic 1Hz callback for the role itself */
+ int (*periodic_checks)(struct lws_context *context, int tsi,
+ time_t now);
+ /* chance for the role to force POLLIN without network activity */
+ int (*service_flag_pending)(struct lws_context *context, int tsi);
+ /* an fd using this role has POLLIN signalled */
+ int (*handle_POLLIN)(struct lws_context_per_thread *pt, struct lws *wsi,
+ struct lws_pollfd *pollfd);
+ /* an fd using the role wanted a POLLOUT callback and now has it */
+ int (*handle_POLLOUT)(struct lws *wsi);
+ /* perform user pollout */
+ int (*perform_user_POLLOUT)(struct lws *wsi);
+ /* do effective callback on writeable */
+ int (*callback_on_writable)(struct lws *wsi);
+ /* connection-specific tx credit in bytes */
+ lws_fileofs_t (*tx_credit)(struct lws *wsi);
+ /* role-specific write formatting */
+ int (*write_role_protocol)(struct lws *wsi, unsigned char *buf,
+ size_t len, enum lws_write_protocol *wp);
+
+ /* get encapsulation parent */
+ struct lws * (*encapsulation_parent)(struct lws *wsi);
+
+ /* role-specific destructor */
+ int (*alpn_negotiated)(struct lws *wsi, const char *alpn);
+
+ /* chance for the role to handle close in the protocol */
+ int (*close_via_role_protocol)(struct lws *wsi,
+ enum lws_close_status reason);
+ /* role-specific close processing */
+ int (*close_role)(struct lws_context_per_thread *pt, struct lws *wsi);
+ /* role-specific connection close processing */
+ int (*close_kill_connection)(struct lws *wsi,
+ enum lws_close_status reason);
+ /* role-specific destructor */
+ int (*destroy_role)(struct lws *wsi);
+
+ /*
+ * the callback reasons for WRITEABLE for client, server
+ * (just client applies if no concept of client or server)
+ */
+ uint16_t writeable_cb[2];
+ /*
+ * the callback reasons for CLOSE for client, server
+ * (just client applies if no concept of client or server)
+ */
+ uint16_t close_cb[2];
+
+ unsigned int file_handle:1; /* role operates on files not sockets */
+};
+
+/* core roles */
+extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen,
+ role_ops_pipe;
+
+/* bring in role private declarations */
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ #include "roles/http/private.h"
+#else
+ #define lwsi_role_http(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_H1)
+ #include "roles/h1/private.h"
+#else
+ #define lwsi_role_h1(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_H2)
+ #include "roles/h2/private.h"
+#else
+ #define lwsi_role_h2(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_WS)
+ #include "roles/ws/private.h"
+#else
+ #define lwsi_role_ws(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_CGI)
+ #include "roles/cgi/private.h"
+#else
+ #define lwsi_role_cgi(wsi) (0)
+#endif
+
+enum {
+ LWS_HP_RET_BAIL_OK,
+ LWS_HP_RET_BAIL_DIE,
+ LWS_HP_RET_USER_SERVICE,
+
+ LWS_HPI_RET_WSI_ALREADY_DIED, /* we closed it */
+ LWS_HPI_RET_HANDLED, /* no probs */
+ LWS_HPI_RET_PLEASE_CLOSE_ME, /* close it for us */
+
+ LWS_UPG_RET_DONE,
+ LWS_UPG_RET_CONTINUE,
+ LWS_UPG_RET_BAIL
+};
diff --git a/thirdparty/libwebsockets/roles/raw/ops-raw.c b/thirdparty/libwebsockets/roles/raw/ops-raw.c
new file mode 100644
index 0000000000..68b52bded6
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/raw/ops-raw.c
@@ -0,0 +1,223 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <core/private.h>
+
+static int
+rops_handle_POLLIN_raw_skt(struct lws_context_per_thread *pt, struct lws *wsi,
+ struct lws_pollfd *pollfd)
+{
+ struct lws_tokens ebuf;
+ int n, buffered;
+
+ /* pending truncated sends have uber priority */
+
+ if (wsi->trunc_len) {
+ if (!(pollfd->revents & LWS_POLLOUT))
+ return LWS_HPI_RET_HANDLED;
+
+ if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
+ wsi->trunc_len) < 0)
+ goto fail;
+ /*
+ * we can't afford to allow input processing to send
+ * something new, so spin around he event loop until
+ * he doesn't have any partials
+ */
+ return LWS_HPI_RET_HANDLED;
+ }
+
+ if ((pollfd->revents & pollfd->events & LWS_POLLIN) &&
+ /* any tunnel has to have been established... */
+ lwsi_state(wsi) != LRS_SSL_ACK_PENDING &&
+ !(wsi->favoured_pollin &&
+ (pollfd->revents & pollfd->events & LWS_POLLOUT))) {
+
+ buffered = lws_buflist_aware_read(pt, wsi, &ebuf);
+ switch (ebuf.len) {
+ case 0:
+ lwsl_info("%s: read 0 len\n", __func__);
+ wsi->seen_zero_length_recv = 1;
+ lws_change_pollfd(wsi, LWS_POLLIN, 0);
+
+ /*
+ * we need to go to fail here, since it's the only
+ * chance we get to understand that the socket has
+ * closed
+ */
+ // goto try_pollout;
+ goto fail;
+
+ case LWS_SSL_CAPABLE_ERROR:
+ goto fail;
+ case LWS_SSL_CAPABLE_MORE_SERVICE:
+ goto try_pollout;
+ }
+
+ n = user_callback_handle_rxflow(wsi->protocol->callback,
+ wsi, LWS_CALLBACK_RAW_RX,
+ wsi->user_space, ebuf.token,
+ ebuf.len);
+ if (n < 0) {
+ lwsl_info("LWS_CALLBACK_RAW_RX_fail\n");
+ goto fail;
+ }
+
+ if (lws_buflist_aware_consume(wsi, &ebuf, ebuf.len, buffered))
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ } else
+ if (wsi->favoured_pollin &&
+ (pollfd->revents & pollfd->events & LWS_POLLOUT))
+ /* we balanced the last favouring of pollin */
+ wsi->favoured_pollin = 0;
+
+try_pollout:
+
+ /* this handles POLLOUT for http serving fragments */
+
+ if (!(pollfd->revents & LWS_POLLOUT))
+ return LWS_HPI_RET_HANDLED;
+
+ /* one shot */
+ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+ lwsl_notice("%s a\n", __func__);
+ goto fail;
+ }
+
+ /* clear back-to-back write detection */
+ wsi->could_have_pending = 0;
+
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_C_WRITEABLE_CB, 1);
+#if defined(LWS_WITH_STATS)
+ if (wsi->active_writable_req_us) {
+ uint64_t ul = time_in_microseconds() -
+ wsi->active_writable_req_us;
+
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_MS_WRITABLE_DELAY, ul);
+ lws_stats_atomic_max(wsi->context, pt,
+ LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
+ wsi->active_writable_req_us = 0;
+ }
+#endif
+ n = user_callback_handle_rxflow(wsi->protocol->callback,
+ wsi, LWS_CALLBACK_RAW_WRITEABLE,
+ wsi->user_space, NULL, 0);
+ if (n < 0) {
+ lwsl_info("writeable_fail\n");
+ goto fail;
+ }
+
+ return LWS_HPI_RET_HANDLED;
+
+fail:
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "raw svc fail");
+
+ return LWS_HPI_RET_WSI_ALREADY_DIED;
+}
+
+
+static int
+rops_handle_POLLIN_raw_file(struct lws_context_per_thread *pt, struct lws *wsi,
+ struct lws_pollfd *pollfd)
+{
+ int n;
+
+ if (pollfd->revents & LWS_POLLOUT) {
+ n = lws_callback_as_writeable(wsi);
+ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+ lwsl_info("failed at set pollfd\n");
+ return LWS_HPI_RET_WSI_ALREADY_DIED;
+ }
+ if (n)
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ }
+
+ if (pollfd->revents & LWS_POLLIN) {
+ if (user_callback_handle_rxflow(wsi->protocol->callback,
+ wsi, LWS_CALLBACK_RAW_RX_FILE,
+ wsi->user_space, NULL, 0)) {
+ lwsl_debug("raw rx callback closed it\n");
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ }
+ }
+
+ if (pollfd->revents & LWS_POLLHUP)
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+
+ return LWS_HPI_RET_HANDLED;
+}
+
+
+struct lws_role_ops role_ops_raw_skt = {
+ /* role name */ "raw-skt",
+ /* alpn id */ NULL,
+ /* check_upgrades */ NULL,
+ /* init_context */ NULL,
+ /* init_vhost */ NULL,
+ /* destroy_vhost */ NULL,
+ /* periodic_checks */ NULL,
+ /* service_flag_pending */ NULL,
+ /* handle_POLLIN */ rops_handle_POLLIN_raw_skt,
+ /* handle_POLLOUT */ NULL,
+ /* perform_user_POLLOUT */ NULL,
+ /* callback_on_writable */ NULL,
+ /* tx_credit */ NULL,
+ /* write_role_protocol */ NULL,
+ /* encapsulation_parent */ NULL,
+ /* alpn_negotiated */ NULL,
+ /* close_via_role_protocol */ NULL,
+ /* close_role */ NULL,
+ /* close_kill_connection */ NULL,
+ /* destroy_role */ NULL,
+ /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE, 0 },
+ /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE, 0 },
+ /* file_handle */ 0,
+};
+
+
+
+struct lws_role_ops role_ops_raw_file = {
+ /* role name */ "raw-file",
+ /* alpn id */ NULL,
+ /* check_upgrades */ NULL,
+ /* init_context */ NULL,
+ /* init_vhost */ NULL,
+ /* destroy_vhost */ NULL,
+ /* periodic_checks */ NULL,
+ /* service_flag_pending */ NULL,
+ /* handle_POLLIN */ rops_handle_POLLIN_raw_file,
+ /* handle_POLLOUT */ NULL,
+ /* perform_user_POLLOUT */ NULL,
+ /* callback_on_writable */ NULL,
+ /* tx_credit */ NULL,
+ /* write_role_protocol */ NULL,
+ /* encapsulation_parent */ NULL,
+ /* alpn_negotiated */ NULL,
+ /* close_via_role_protocol */ NULL,
+ /* close_role */ NULL,
+ /* close_kill_connection */ NULL,
+ /* destroy_role */ NULL,
+ /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 },
+ /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE, 0 },
+ /* file_handle */ 1,
+};
diff --git a/thirdparty/lws/client/client-parser.c b/thirdparty/libwebsockets/roles/ws/client-parser-ws.c
index 0e42dac362..aa561ce034 100644
--- a/thirdparty/lws/client/client-parser.c
+++ b/thirdparty/libwebsockets/roles/ws/client-parser-ws.c
@@ -19,31 +19,38 @@
* MA 02110-1301 USA
*/
-#include "private-libwebsockets.h"
+#include "core/private.h"
/*
- * parsers.c: lws_rx_sm() needs to be roughly kept in
+ * parsers.c: lws_ws_rx_sm() needs to be roughly kept in
* sync with changes here, esp related to ext draining
*/
-int lws_client_rx_sm(struct lws *wsi, unsigned char c)
+int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
{
int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
- int handled, n, m, rx_draining_ext = 0;
+ int handled, m;
unsigned short close_code;
- struct lws_tokens eff_buf;
+ struct lws_tokens ebuf;
unsigned char *pp;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ int rx_draining_ext = 0, n;
+#endif
+
+ ebuf.token = NULL;
+ ebuf.len = 0;
- if (wsi->u.ws.rx_draining_ext) {
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (wsi->ws->rx_draining_ext) {
assert(!c);
- eff_buf.token = NULL;
- eff_buf.token_len = 0;
+
lws_remove_wsi_from_draining_ext_list(wsi);
rx_draining_ext = 1;
lwsl_debug("%s: doing draining flow\n", __func__);
goto drain_extension;
}
+#endif
if (wsi->socket_is_permanently_unusable)
return -1;
@@ -51,35 +58,38 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW:
/* control frames (PING) may interrupt checkable sequences */
- wsi->u.ws.defeat_check_utf8 = 0;
+ wsi->ws->defeat_check_utf8 = 0;
- switch (wsi->ietf_spec_revision) {
+ switch (wsi->ws->ietf_spec_revision) {
case 13:
- wsi->u.ws.opcode = c & 0xf;
+ wsi->ws->opcode = c & 0xf;
/* revisit if an extension wants them... */
- switch (wsi->u.ws.opcode) {
+ switch (wsi->ws->opcode) {
case LWSWSOPC_TEXT_FRAME:
- wsi->u.ws.rsv_first_msg = (c & 0x70);
- wsi->u.ws.continuation_possible = 1;
- wsi->u.ws.check_utf8 = lws_check_opt(
+ wsi->ws->rsv_first_msg = (c & 0x70);
+ wsi->ws->continuation_possible = 1;
+ wsi->ws->check_utf8 = lws_check_opt(
wsi->context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8);
- wsi->u.ws.utf8 = 0;
+ wsi->ws->utf8 = 0;
+ wsi->ws->first_fragment = 1;
break;
case LWSWSOPC_BINARY_FRAME:
- wsi->u.ws.rsv_first_msg = (c & 0x70);
- wsi->u.ws.check_utf8 = 0;
- wsi->u.ws.continuation_possible = 1;
+ wsi->ws->rsv_first_msg = (c & 0x70);
+ wsi->ws->check_utf8 = 0;
+ wsi->ws->continuation_possible = 1;
+ wsi->ws->first_fragment = 1;
break;
case LWSWSOPC_CONTINUATION:
- if (!wsi->u.ws.continuation_possible) {
+ if (!wsi->ws->continuation_possible) {
lwsl_info("disordered continuation\n");
return -1;
}
+ wsi->ws->first_fragment = 0;
break;
case LWSWSOPC_CLOSE:
- wsi->u.ws.check_utf8 = 0;
- wsi->u.ws.utf8 = 0;
+ wsi->ws->check_utf8 = 0;
+ wsi->ws->utf8 = 0;
break;
case 3:
case 4:
@@ -94,45 +104,45 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
lwsl_info("illegal opcode\n");
return -1;
default:
- wsi->u.ws.defeat_check_utf8 = 1;
+ wsi->ws->defeat_check_utf8 = 1;
break;
}
- wsi->u.ws.rsv = (c & 0x70);
+ wsi->ws->rsv = (c & 0x70);
/* revisit if an extension wants them... */
if (
-#ifndef LWS_NO_EXTENSIONS
- !wsi->count_act_ext &&
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ !wsi->ws->count_act_ext &&
#endif
- wsi->u.ws.rsv) {
+ wsi->ws->rsv) {
lwsl_info("illegal rsv bits set\n");
return -1;
}
- wsi->u.ws.final = !!((c >> 7) & 1);
+ wsi->ws->final = !!((c >> 7) & 1);
lwsl_ext("%s: This RX frame Final %d\n", __func__,
- wsi->u.ws.final);
+ wsi->ws->final);
- if (wsi->u.ws.owed_a_fin &&
- (wsi->u.ws.opcode == LWSWSOPC_TEXT_FRAME ||
- wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME)) {
+ if (wsi->ws->owed_a_fin &&
+ (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
+ wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
lwsl_info("hey you owed us a FIN\n");
return -1;
}
- if ((!(wsi->u.ws.opcode & 8)) && wsi->u.ws.final) {
- wsi->u.ws.continuation_possible = 0;
- wsi->u.ws.owed_a_fin = 0;
+ if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) {
+ wsi->ws->continuation_possible = 0;
+ wsi->ws->owed_a_fin = 0;
}
- if ((wsi->u.ws.opcode & 8) && !wsi->u.ws.final) {
+ if ((wsi->ws->opcode & 8) && !wsi->ws->final) {
lwsl_info("control msg can't be fragmented\n");
return -1;
}
- if (!wsi->u.ws.final)
- wsi->u.ws.owed_a_fin = 1;
+ if (!wsi->ws->final)
+ wsi->ws->owed_a_fin = 1;
- switch (wsi->u.ws.opcode) {
+ switch (wsi->ws->opcode) {
case LWSWSOPC_TEXT_FRAME:
case LWSWSOPC_BINARY_FRAME:
- wsi->u.ws.frame_is_binary = wsi->u.ws.opcode ==
+ wsi->ws->frame_is_binary = wsi->ws->opcode ==
LWSWSOPC_BINARY_FRAME;
break;
}
@@ -141,38 +151,38 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
default:
lwsl_err("unknown spec version %02d\n",
- wsi->ietf_spec_revision);
+ wsi->ws->ietf_spec_revision);
break;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN:
- wsi->u.ws.this_frame_masked = !!(c & 0x80);
+ wsi->ws->this_frame_masked = !!(c & 0x80);
switch (c & 0x7f) {
case 126:
/* control frames are not allowed to have big lengths */
- if (wsi->u.ws.opcode & 8)
+ if (wsi->ws->opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
break;
case 127:
/* control frames are not allowed to have big lengths */
- if (wsi->u.ws.opcode & 8)
+ if (wsi->ws->opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
break;
default:
- wsi->u.ws.rx_packet_length = c;
- if (wsi->u.ws.this_frame_masked)
+ wsi->ws->rx_packet_length = c & 0x7f;
+ if (wsi->ws->this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
- if (c)
+ if (wsi->ws->rx_packet_length) {
wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
- else {
+ LWS_RXPS_WS_FRAME_PAYLOAD;
+ } else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
@@ -182,18 +192,18 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_2:
- wsi->u.ws.rx_packet_length = c << 8;
+ wsi->ws->rx_packet_length = c << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_1:
- wsi->u.ws.rx_packet_length |= c;
- if (wsi->u.ws.this_frame_masked)
+ wsi->ws->rx_packet_length |= c;
+ if (wsi->ws->this_frame_masked)
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
- if (wsi->u.ws.rx_packet_length)
+ if (wsi->ws->rx_packet_length)
wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
+ LWS_RXPS_WS_FRAME_PAYLOAD;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
@@ -208,58 +218,58 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
return -1;
}
#if defined __LP64__
- wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
+ wsi->ws->rx_packet_length = ((size_t)c) << 56;
#else
- wsi->u.ws.rx_packet_length = 0;
+ wsi->ws->rx_packet_length = 0;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_7:
#if defined __LP64__
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
+ wsi->ws->rx_packet_length |= ((size_t)c) << 48;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_6:
#if defined __LP64__
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
+ wsi->ws->rx_packet_length |= ((size_t)c) << 40;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_5:
#if defined __LP64__
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
+ wsi->ws->rx_packet_length |= ((size_t)c) << 32;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_4:
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
+ wsi->ws->rx_packet_length |= ((size_t)c) << 24;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_3:
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
+ wsi->ws->rx_packet_length |= ((size_t)c) << 16;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_2:
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
+ wsi->ws->rx_packet_length |= ((size_t)c) << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_1:
- wsi->u.ws.rx_packet_length |= (size_t)c;
- if (wsi->u.ws.this_frame_masked)
+ wsi->ws->rx_packet_length |= (size_t)c;
+ if (wsi->ws->this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
- if (wsi->u.ws.rx_packet_length)
+ if (wsi->ws->rx_packet_length)
wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
+ LWS_RXPS_WS_FRAME_PAYLOAD;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
@@ -268,53 +278,53 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
- wsi->u.ws.mask[0] = c;
+ wsi->ws->mask[0] = c;
if (c)
- wsi->u.ws.all_zero_nonce = 0;
+ wsi->ws->all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
- wsi->u.ws.mask[1] = c;
+ wsi->ws->mask[1] = c;
if (c)
- wsi->u.ws.all_zero_nonce = 0;
+ wsi->ws->all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
- wsi->u.ws.mask[2] = c;
+ wsi->ws->mask[2] = c;
if (c)
- wsi->u.ws.all_zero_nonce = 0;
+ wsi->ws->all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
- wsi->u.ws.mask[3] = c;
+ wsi->ws->mask[3] = c;
if (c)
- wsi->u.ws.all_zero_nonce = 0;
+ wsi->ws->all_zero_nonce = 0;
- if (wsi->u.ws.rx_packet_length)
+ if (wsi->ws->rx_packet_length)
wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
+ LWS_RXPS_WS_FRAME_PAYLOAD;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
break;
- case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
-
- assert(wsi->u.ws.rx_ubuf);
+ case LWS_RXPS_WS_FRAME_PAYLOAD:
- if (wsi->u.ws.rx_draining_ext)
+ assert(wsi->ws->rx_ubuf);
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (wsi->ws->rx_draining_ext)
goto drain_extension;
+#endif
+ if (wsi->ws->this_frame_masked && !wsi->ws->all_zero_nonce)
+ c ^= wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
- if (wsi->u.ws.this_frame_masked && !wsi->u.ws.all_zero_nonce)
- c ^= wsi->u.ws.mask[(wsi->u.ws.mask_idx++) & 3];
-
- wsi->u.ws.rx_ubuf[LWS_PRE + (wsi->u.ws.rx_ubuf_head++)] = c;
+ wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] = c;
- if (--wsi->u.ws.rx_packet_length == 0) {
+ if (--wsi->ws->rx_packet_length == 0) {
/* spill because we have the whole frame */
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
@@ -325,11 +335,11 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
* supposed to default to context->pt_serv_buf_size
*/
if (!wsi->protocol->rx_buffer_size &&
- wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size)
+ wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size)
break;
if (wsi->protocol->rx_buffer_size &&
- wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size)
+ wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size)
break;
/* spill because we filled our rx buffer */
@@ -342,18 +352,18 @@ spill:
* layer? If so service it and hide it from the user callback
*/
- switch (wsi->u.ws.opcode) {
+ switch (wsi->ws->opcode) {
case LWSWSOPC_CLOSE:
- pp = (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE];
+ pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE];
if (lws_check_opt(wsi->context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8) &&
- wsi->u.ws.rx_ubuf_head > 2 &&
- lws_check_utf8(&wsi->u.ws.utf8, pp + 2,
- wsi->u.ws.rx_ubuf_head - 2))
+ wsi->ws->rx_ubuf_head > 2 &&
+ lws_check_utf8(&wsi->ws->utf8, pp + 2,
+ wsi->ws->rx_ubuf_head - 2))
goto utf8_fail;
- /* is this an acknowledgement of our close? */
- if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
+ /* is this an acknowledgment of our close? */
+ if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
/*
* fine he has told us he is closing too, let's
* finish our close
@@ -363,8 +373,8 @@ spill:
}
lwsl_parser("client sees server close len = %d\n",
- wsi->u.ws.rx_ubuf_head);
- if (wsi->u.ws.rx_ubuf_head >= 2) {
+ wsi->ws->rx_ubuf_head);
+ if (wsi->ws->rx_ubuf_head >= 2) {
close_code = (pp[0] << 8) | pp[1];
if (close_code < 1000 ||
close_code == 1004 ||
@@ -384,39 +394,32 @@ spill:
wsi->protocol->callback, wsi,
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
wsi->user_space, pp,
- wsi->u.ws.rx_ubuf_head))
+ wsi->ws->rx_ubuf_head))
return -1;
- if (lws_partial_buffered(wsi))
- /*
- * if we're in the middle of something,
- * we can't do a normal close response and
- * have to just close our end.
- */
- wsi->socket_is_permanently_unusable = 1;
- else
- /*
- * parrot the close packet payload back
- * we do not care about how it went, we are closing
- * immediately afterwards
- */
- lws_write(wsi, (unsigned char *)
- &wsi->u.ws.rx_ubuf[LWS_PRE],
- wsi->u.ws.rx_ubuf_head,
- LWS_WRITE_CLOSE);
- wsi->state = LWSS_RETURNED_CLOSE_ALREADY;
- /* close the connection */
- return -1;
+ memcpy(wsi->ws->ping_payload_buf + LWS_PRE, pp,
+ wsi->ws->rx_ubuf_head);
+ wsi->ws->close_in_ping_buffer_len = wsi->ws->rx_ubuf_head;
+
+ lwsl_info("%s: scheduling return close as ack\n", __func__);
+ __lws_change_pollfd(wsi, LWS_POLLIN, 0);
+ lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 3);
+ wsi->waiting_to_send_close_frame = 1;
+ wsi->close_needs_ack = 0;
+ lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
+ lws_callback_on_writable(wsi);
+ handled = 1;
+ break;
case LWSWSOPC_PING:
lwsl_info("received %d byte ping, sending pong\n",
- wsi->u.ws.rx_ubuf_head);
+ wsi->ws->rx_ubuf_head);
/* he set a close reason on this guy, ignore PING */
- if (wsi->u.ws.close_in_ping_buffer_len)
+ if (wsi->ws->close_in_ping_buffer_len)
goto ping_drop;
- if (wsi->u.ws.ping_pending_flag) {
+ if (wsi->ws->ping_pending_flag) {
/*
* there is already a pending ping payload
* we should just log and drop
@@ -426,30 +429,30 @@ spill:
}
/* control packets can only be < 128 bytes long */
- if (wsi->u.ws.rx_ubuf_head > 128 - 3) {
+ if (wsi->ws->rx_ubuf_head > 128 - 3) {
lwsl_parser("DROP PING payload too large\n");
goto ping_drop;
}
/* stash the pong payload */
- memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE,
- &wsi->u.ws.rx_ubuf[LWS_PRE],
- wsi->u.ws.rx_ubuf_head);
+ memcpy(wsi->ws->ping_payload_buf + LWS_PRE,
+ &wsi->ws->rx_ubuf[LWS_PRE],
+ wsi->ws->rx_ubuf_head);
- wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head;
- wsi->u.ws.ping_pending_flag = 1;
+ wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head;
+ wsi->ws->ping_pending_flag = 1;
/* get it sent as soon as possible */
lws_callback_on_writable(wsi);
ping_drop:
- wsi->u.ws.rx_ubuf_head = 0;
+ wsi->ws->rx_ubuf_head = 0;
handled = 1;
break;
case LWSWSOPC_PONG:
lwsl_info("client receied pong\n");
- lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE],
- wsi->u.ws.rx_ubuf_head);
+ lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
+ wsi->ws->rx_ubuf_head);
if (wsi->pending_timeout ==
PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
@@ -467,30 +470,11 @@ ping_drop:
break;
default:
+ /* not handled or failed */
+ lwsl_ext("Unhandled ext opc 0x%x\n", wsi->ws->opcode);
+ wsi->ws->rx_ubuf_head = 0;
- lwsl_parser("Reserved opc 0x%2X\n", wsi->u.ws.opcode);
-
- /*
- * It's something special we can't understand here.
- * Pass the payload up to the extension's parsing
- * state machine.
- */
-
- eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
- eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
-
- if (lws_ext_cb_active(wsi,
- LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
- &eff_buf, 0) <= 0) {
- /* not handled or failed */
- lwsl_ext("Unhandled ext opc 0x%x\n",
- wsi->u.ws.opcode);
- wsi->u.ws.rx_ubuf_head = 0;
-
- return 0;
- }
- handled = 1;
- break;
+ return -1;
}
/*
@@ -501,53 +485,71 @@ ping_drop:
if (handled)
goto already_done;
- eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
- eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
+ ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
+ ebuf.len = wsi->ws->rx_ubuf_head;
- if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len)
+ if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len)
goto already_done;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
drain_extension:
- lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len);
+ lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len);
- n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
+ n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
lwsl_ext("Ext RX returned %d\n", n);
if (n < 0) {
wsi->socket_is_permanently_unusable = 1;
return -1;
}
+#endif
+ lwsl_debug("post inflate ebuf len %d\n", ebuf.len);
- lwsl_ext("post inflate eff_buf len %d\n", eff_buf.token_len);
-
- if (rx_draining_ext && !eff_buf.token_len) {
+ if (
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ rx_draining_ext &&
+#endif
+ !ebuf.len) {
lwsl_debug(" --- ending drain on 0 read result\n");
goto already_done;
}
- if (wsi->u.ws.check_utf8 && !wsi->u.ws.defeat_check_utf8) {
- if (lws_check_utf8(&wsi->u.ws.utf8,
- (unsigned char *)eff_buf.token,
- eff_buf.token_len))
+ if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
+ if (lws_check_utf8(&wsi->ws->utf8,
+ (unsigned char *)ebuf.token,
+ ebuf.len)) {
+ lws_close_reason(wsi,
+ LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+ (uint8_t *)"bad utf8", 8);
goto utf8_fail;
+ }
/* we are ending partway through utf-8 character? */
- if (!wsi->u.ws.rx_packet_length && wsi->u.ws.final &&
- wsi->u.ws.utf8 && !n) {
+ if (!wsi->ws->rx_packet_length && wsi->ws->final &&
+ wsi->ws->utf8
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ && !n
+#endif
+ ) {
lwsl_info("FINAL utf8 error\n");
+ lws_close_reason(wsi,
+ LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+ (uint8_t *)"partial utf8", 12);
utf8_fail:
lwsl_info("utf8 error\n");
+ lwsl_hexdump_info(ebuf.token, ebuf.len);
+
return -1;
}
}
- if (eff_buf.token_len < 0 &&
+ if (ebuf.len < 0 &&
callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
goto already_done;
- if (!eff_buf.token)
+ if (!ebuf.token)
goto already_done;
- eff_buf.token[eff_buf.token_len] = '\0';
+ ebuf.token[ebuf.len] = '\0';
if (!wsi->protocol->callback)
goto already_done;
@@ -555,7 +557,12 @@ utf8_fail:
if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
lwsl_info("Client doing pong callback\n");
- if (n && eff_buf.token_len)
+ if (
+ /* coverity says dead code otherwise */
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ n &&
+#endif
+ ebuf.len)
/* extension had more... main loop will come back
* we want callback to be done with this set, if so,
* because lws_is_final() hides it was final until the
@@ -565,21 +572,26 @@ utf8_fail:
else
lws_remove_wsi_from_draining_ext_list(wsi);
- if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
- wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
- wsi->state == LWSS_AWAITING_CLOSE_ACK)
+ if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
+ lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
+ lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
goto already_done;
m = wsi->protocol->callback(wsi,
(enum lws_callback_reasons)callback_action,
- wsi->user_space, eff_buf.token, eff_buf.token_len);
+ wsi->user_space, ebuf.token, ebuf.len);
+
+ wsi->ws->first_fragment = 0;
+
+ // lwsl_notice("%s: bulk ws rx: input used %d, output %d\n",
+ // __func__, wsi->ws->rx_ubuf_head, ebuf.len);
/* if user code wants to close, let caller know */
if (m)
return 1;
already_done:
- wsi->u.ws.rx_ubuf_head = 0;
+ wsi->ws->rx_ubuf_head = 0;
break;
default:
lwsl_err("client rx illegal state\n");
diff --git a/thirdparty/libwebsockets/roles/ws/client-ws.c b/thirdparty/libwebsockets/roles/ws/client-ws.c
new file mode 100644
index 0000000000..fd6cf42551
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/ws/client-ws.c
@@ -0,0 +1,629 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <core/private.h>
+
+/*
+ * In-place str to lower case
+ */
+
+static void
+strtolower(char *s)
+{
+ while (*s) {
+#ifdef LWS_PLAT_OPTEE
+ int tolower_optee(int c);
+ *s = tolower_optee((int)*s);
+#else
+ *s = tolower((int)*s);
+#endif
+ s++;
+ }
+}
+
+int
+lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi)
+{
+ int v = SPEC_LATEST_SUPPORTED;
+
+ /* allocate the ws struct for the wsi */
+ wsi->ws = lws_zalloc(sizeof(*wsi->ws), "client ws struct");
+ if (!wsi->ws) {
+ lwsl_notice("OOM\n");
+ return 1;
+ }
+
+ /* -1 means just use latest supported */
+ if (i->ietf_version_or_minus_one != -1 &&
+ i->ietf_version_or_minus_one)
+ v = i->ietf_version_or_minus_one;
+
+ wsi->ws->ietf_spec_revision = v;
+
+ return 0;
+}
+
+#if !defined(LWS_NO_CLIENT)
+int
+lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
+{
+ if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
+ (lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
+ (lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
+ !lwsi_role_client(wsi))
+ return 0;
+
+ // lwsl_notice("%s: hs client gets %d in\n", __func__, (int)len);
+
+ while (len) {
+ /*
+ * we were accepting input but now we stopped doing so
+ */
+ if (lws_is_flowcontrolled(wsi)) {
+ //lwsl_notice("%s: caching %ld\n", __func__, (long)len);
+ lws_rxflow_cache(wsi, *buf, 0, (int)len);
+ *buf += len;
+ return 0;
+ }
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (wsi->ws->rx_draining_ext) {
+ int m;
+
+ //lwsl_notice("%s: draining ext\n", __func__);
+ if (lwsi_role_client(wsi))
+ m = lws_ws_client_rx_sm(wsi, 0);
+ else
+ m = lws_ws_rx_sm(wsi, 0, 0);
+ if (m < 0)
+ return -1;
+ continue;
+ }
+#endif
+ /* caller will account for buflist usage */
+
+ if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
+ lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n",
+ __func__, (int)len);
+ return -1;
+ }
+ len--;
+ }
+ // lwsl_notice("%s: finished with %ld\n", __func__, (long)len);
+
+ return 0;
+}
+#endif
+
+char *
+lws_generate_client_ws_handshake(struct lws *wsi, char *p)
+{
+ char buf[128], hash[20], key_b64[40];
+ int n;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ const struct lws_extension *ext;
+ int ext_count = 0;
+#endif
+
+ /*
+ * create the random key
+ */
+ n = lws_get_random(wsi->context, hash, 16);
+ if (n != 16) {
+ lwsl_err("Unable to read from random dev %s\n",
+ SYSTEM_RANDOM_FILEPATH);
+ return NULL;
+ }
+
+ lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
+
+ p += sprintf(p, "Upgrade: websocket\x0d\x0a"
+ "Connection: Upgrade\x0d\x0a"
+ "Sec-WebSocket-Key: ");
+ strcpy(p, key_b64);
+ p += strlen(key_b64);
+ p += sprintf(p, "\x0d\x0a");
+ if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
+ p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
+ lws_hdr_simple_ptr(wsi,
+ _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));
+
+ /* tell the server what extensions we could support */
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ ext = wsi->vhost->ws.extensions;
+ while (ext && ext->callback) {
+
+ n = wsi->vhost->protocols[0].callback(wsi,
+ LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
+ wsi->user_space, (char *)ext->name, 0);
+
+ /*
+ * zero return from callback means go ahead and allow
+ * the extension, it's what we get if the callback is
+ * unhandled
+ */
+
+ if (n) {
+ ext++;
+ continue;
+ }
+
+ /* apply it */
+
+ if (ext_count)
+ *p++ = ',';
+ else
+ p += sprintf(p, "Sec-WebSocket-Extensions: ");
+ p += sprintf(p, "%s", ext->client_offer);
+ ext_count++;
+
+ ext++;
+ }
+ if (ext_count)
+ p += sprintf(p, "\x0d\x0a");
+#endif
+
+ if (wsi->ws->ietf_spec_revision)
+ p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
+ wsi->ws->ietf_spec_revision);
+
+ /* prepare the expected server accept response */
+
+ key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
+ n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
+ key_b64);
+
+ lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);
+
+ lws_b64_encode_string(hash, 20,
+ wsi->http.ah->initial_handshake_hash_base64,
+ sizeof(wsi->http.ah->initial_handshake_hash_base64));
+
+ return p;
+}
+
+int
+lws_client_ws_upgrade(struct lws *wsi, const char **cce)
+{
+ int n, len, okay = 0;
+ struct lws_context *context = wsi->context;
+ const char *pc;
+ char *p;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ char *sb = (char *)&pt->serv_buf[0];
+ const struct lws_ext_options *opts;
+ const struct lws_extension *ext;
+ char ext_name[128];
+ const char *c, *a;
+ char ignore;
+ int more = 1;
+#endif
+
+ if (wsi->client_h2_substream) {/* !!! client ws-over-h2 not there yet */
+ lwsl_warn("%s: client ws-over-h2 upgrade not supported yet\n",
+ __func__);
+ *cce = "HS: h2 / ws upgrade unsupported";
+ goto bail3;
+ }
+
+ if (wsi->http.ah->http_response == 401) {
+ lwsl_warn(
+ "lws_client_handshake: got bad HTTP response '%d'\n",
+ wsi->http.ah->http_response);
+ *cce = "HS: ws upgrade unauthorized";
+ goto bail3;
+ }
+
+ if (wsi->http.ah->http_response != 101) {
+ lwsl_warn(
+ "lws_client_handshake: got bad HTTP response '%d'\n",
+ wsi->http.ah->http_response);
+ *cce = "HS: ws upgrade response not 101";
+ goto bail3;
+ }
+
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
+ lwsl_info("no ACCEPT\n");
+ *cce = "HS: ACCEPT missing";
+ goto bail3;
+ }
+
+ p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
+ if (!p) {
+ lwsl_info("no UPGRADE\n");
+ *cce = "HS: UPGRADE missing";
+ goto bail3;
+ }
+ strtolower(p);
+ if (strcmp(p, "websocket")) {
+ lwsl_warn(
+ "lws_client_handshake: got bad Upgrade header '%s'\n", p);
+ *cce = "HS: Upgrade to something other than websocket";
+ goto bail3;
+ }
+
+ p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION);
+ if (!p) {
+ lwsl_info("no Connection hdr\n");
+ *cce = "HS: CONNECTION missing";
+ goto bail3;
+ }
+ strtolower(p);
+ if (strcmp(p, "upgrade")) {
+ lwsl_warn("lws_client_int_s_hs: bad header %s\n", p);
+ *cce = "HS: UPGRADE malformed";
+ goto bail3;
+ }
+
+ pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
+ if (!pc) {
+ lwsl_parser("lws_client_int_s_hs: no protocol list\n");
+ } else
+ lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc);
+
+ /*
+ * confirm the protocol the server wants to talk was in the list
+ * of protocols we offered
+ */
+
+ len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
+ if (!len) {
+ lwsl_info("%s: WSI_TOKEN_PROTOCOL is null\n", __func__);
+ /*
+ * no protocol name to work from,
+ * default to first protocol
+ */
+ n = 0;
+ wsi->protocol = &wsi->vhost->protocols[0];
+ goto check_extensions;
+ }
+
+ p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
+ len = (int)strlen(p);
+
+ while (pc && *pc && !okay) {
+ if (!strncmp(pc, p, len) &&
+ (pc[len] == ',' || pc[len] == '\0')) {
+ okay = 1;
+ continue;
+ }
+ while (*pc && *pc++ != ',')
+ ;
+ while (*pc && *pc == ' ')
+ pc++;
+ }
+
+ if (!okay) {
+ lwsl_info("%s: got bad protocol %s\n", __func__, p);
+ *cce = "HS: PROTOCOL malformed";
+ goto bail2;
+ }
+
+ /*
+ * identify the selected protocol struct and set it
+ */
+ n = 0;
+ /* keep client connection pre-bound protocol */
+ if (!lwsi_role_client(wsi))
+ wsi->protocol = NULL;
+
+ while (wsi->vhost->protocols[n].callback) {
+ if (!wsi->protocol &&
+ strcmp(p, wsi->vhost->protocols[n].name) == 0) {
+ wsi->protocol = &wsi->vhost->protocols[n];
+ break;
+ }
+ n++;
+ }
+
+ if (!wsi->vhost->protocols[n].callback) { /* no match */
+ /* if server, that's already fatal */
+ if (!lwsi_role_client(wsi)) {
+ lwsl_info("%s: fail protocol %s\n", __func__, p);
+ *cce = "HS: Cannot match protocol";
+ goto bail2;
+ }
+
+ /* for client, find the index of our pre-bound protocol */
+
+ n = 0;
+ while (wsi->vhost->protocols[n].callback) {
+ if (wsi->protocol && strcmp(wsi->protocol->name,
+ wsi->vhost->protocols[n].name) == 0) {
+ wsi->protocol = &wsi->vhost->protocols[n];
+ break;
+ }
+ n++;
+ }
+
+ if (!wsi->vhost->protocols[n].callback) {
+ if (wsi->protocol)
+ lwsl_err("Failed to match protocol %s\n",
+ wsi->protocol->name);
+ else
+ lwsl_err("No protocol on client\n");
+ goto bail2;
+ }
+ }
+
+ lwsl_debug("Selected protocol %s\n", wsi->protocol->name);
+
+check_extensions:
+ /*
+ * stitch protocol choice into the vh protocol linked list
+ * We always insert ourselves at the start of the list
+ *
+ * X <-> B
+ * X <-> pAn <-> pB
+ */
+
+ lws_vhost_lock(wsi->vhost);
+
+ wsi->same_vh_protocol_prev = /* guy who points to us */
+ &wsi->vhost->same_vh_protocol_list[n];
+ wsi->same_vh_protocol_next = /* old first guy is our next */
+ wsi->vhost->same_vh_protocol_list[n];
+ /* we become the new first guy */
+ wsi->vhost->same_vh_protocol_list[n] = wsi;
+
+ if (wsi->same_vh_protocol_next)
+ /* old first guy points back to us now */
+ wsi->same_vh_protocol_next->same_vh_protocol_prev =
+ &wsi->same_vh_protocol_next;
+ wsi->on_same_vh_list = 1;
+
+ lws_vhost_unlock(wsi->vhost);
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ /* instantiate the accepted extensions */
+
+ if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
+ lwsl_ext("no client extensions allowed by server\n");
+ goto check_accept;
+ }
+
+ /*
+ * break down the list of server accepted extensions
+ * and go through matching them or identifying bogons
+ */
+
+ if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size,
+ WSI_TOKEN_EXTENSIONS) < 0) {
+ lwsl_warn("ext list from server failed to copy\n");
+ *cce = "HS: EXT: list too big";
+ goto bail2;
+ }
+
+ c = sb;
+ n = 0;
+ ignore = 0;
+ a = NULL;
+ while (more) {
+
+ if (*c && (*c != ',' && *c != '\t')) {
+ if (*c == ';') {
+ ignore = 1;
+ if (!a)
+ a = c + 1;
+ }
+ if (ignore || *c == ' ') {
+ c++;
+ continue;
+ }
+
+ ext_name[n] = *c++;
+ if (n < (int)sizeof(ext_name) - 1)
+ n++;
+ continue;
+ }
+ ext_name[n] = '\0';
+ ignore = 0;
+ if (!*c)
+ more = 0;
+ else {
+ c++;
+ if (!n)
+ continue;
+ }
+
+ /* check we actually support it */
+
+ lwsl_notice("checking client ext %s\n", ext_name);
+
+ n = 0;
+ ext = wsi->vhost->ws.extensions;
+ while (ext && ext->callback) {
+ if (strcmp(ext_name, ext->name)) {
+ ext++;
+ continue;
+ }
+
+ n = 1;
+ lwsl_notice("instantiating client ext %s\n", ext_name);
+
+ /* instantiate the extension on this conn */
+
+ wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext;
+
+ /* allow him to construct his ext instance */
+
+ if (ext->callback(lws_get_context(wsi), ext, wsi,
+ LWS_EXT_CB_CLIENT_CONSTRUCT,
+ (void *)&wsi->ws->act_ext_user[wsi->ws->count_act_ext],
+ (void *)&opts, 0)) {
+ lwsl_info(" ext %s failed construction\n",
+ ext_name);
+ ext++;
+ continue;
+ }
+
+ /*
+ * allow the user code to override ext defaults if it
+ * wants to
+ */
+ ext_name[0] = '\0';
+ if (user_callback_handle_rxflow(wsi->protocol->callback,
+ wsi, LWS_CALLBACK_WS_EXT_DEFAULTS,
+ (char *)ext->name, ext_name,
+ sizeof(ext_name))) {
+ *cce = "HS: EXT: failed setting defaults";
+ goto bail2;
+ }
+
+ if (ext_name[0] &&
+ lws_ext_parse_options(ext, wsi, wsi->ws->act_ext_user[
+ wsi->ws->count_act_ext], opts, ext_name,
+ (int)strlen(ext_name))) {
+ lwsl_err("%s: unable to parse user defaults '%s'",
+ __func__, ext_name);
+ *cce = "HS: EXT: failed parsing defaults";
+ goto bail2;
+ }
+
+ /*
+ * give the extension the server options
+ */
+ if (a && lws_ext_parse_options(ext, wsi,
+ wsi->ws->act_ext_user[wsi->ws->count_act_ext],
+ opts, a, lws_ptr_diff(c, a))) {
+ lwsl_err("%s: unable to parse remote def '%s'",
+ __func__, a);
+ *cce = "HS: EXT: failed parsing options";
+ goto bail2;
+ }
+
+ if (ext->callback(lws_get_context(wsi), ext, wsi,
+ LWS_EXT_CB_OPTION_CONFIRM,
+ wsi->ws->act_ext_user[wsi->ws->count_act_ext],
+ NULL, 0)) {
+ lwsl_err("%s: ext %s rejects server options %s",
+ __func__, ext->name, a);
+ *cce = "HS: EXT: Rejects server options";
+ goto bail2;
+ }
+
+ wsi->ws->count_act_ext++;
+
+ ext++;
+ }
+
+ if (n == 0) {
+ lwsl_warn("Unknown ext '%s'!\n", ext_name);
+ *cce = "HS: EXT: unknown ext";
+ goto bail2;
+ }
+
+ a = NULL;
+ n = 0;
+ }
+
+check_accept:
+#endif
+
+ /*
+ * Confirm his accept token is the one we precomputed
+ */
+
+ p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT);
+ if (strcmp(p, wsi->http.ah->initial_handshake_hash_base64)) {
+ lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p,
+ wsi->http.ah->initial_handshake_hash_base64);
+ *cce = "HS: Accept hash wrong";
+ goto bail2;
+ }
+
+ /* allocate the per-connection user memory (if any) */
+ if (lws_ensure_user_space(wsi)) {
+ lwsl_err("Problem allocating wsi user mem\n");
+ *cce = "HS: OOM";
+ goto bail2;
+ }
+
+ /*
+ * we seem to be good to go, give client last chance to check
+ * headers and OK it
+ */
+ if (wsi->protocol->callback(wsi,
+ LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
+ wsi->user_space, NULL, 0)) {
+ *cce = "HS: Rejected by filter cb";
+ goto bail2;
+ }
+
+ /* clear his proxy connection timeout */
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+ /* free up his parsing allocations */
+ lws_header_table_detach(wsi, 0);
+
+ lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED,
+ &role_ops_ws);
+ lws_restart_ws_ping_pong_timer(wsi);
+
+ wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
+
+ /*
+ * create the frame buffer for this connection according to the
+ * size mentioned in the protocol definition. If 0 there, then
+ * use a big default for compatibility
+ */
+ n = (int)wsi->protocol->rx_buffer_size;
+ if (!n)
+ n = context->pt_serv_buf_size;
+ n += LWS_PRE;
+ wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */,
+ "client frame buffer");
+ if (!wsi->ws->rx_ubuf) {
+ lwsl_err("Out of Mem allocating rx buffer %d\n", n);
+ *cce = "HS: OOM";
+ goto bail2;
+ }
+ wsi->ws->rx_ubuf_alloc = n;
+ lwsl_info("Allocating client RX buffer %d\n", n);
+
+#if !defined(LWS_WITH_ESP32)
+ if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
+ (const char *)&n, sizeof n)) {
+ lwsl_warn("Failed to set SNDBUF to %d", n);
+ *cce = "HS: SO_SNDBUF failed";
+ goto bail3;
+ }
+#endif
+
+ lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);
+
+ /* call him back to inform him he is up */
+
+ if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED,
+ wsi->user_space, NULL, 0)) {
+ *cce = "HS: Rejected at CLIENT_ESTABLISHED";
+ goto bail3;
+ }
+
+ return 0;
+
+bail3:
+ return 3;
+
+bail2:
+ return 2;
+}
diff --git a/thirdparty/libwebsockets/roles/ws/ops-ws.c b/thirdparty/libwebsockets/roles/ws/ops-ws.c
new file mode 100644
index 0000000000..5ddaba9e18
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/ws/ops-ws.c
@@ -0,0 +1,1992 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <core/private.h>
+
+#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
+
+/*
+ * client-parser.c: lws_ws_client_rx_sm() needs to be roughly kept in
+ * sync with changes here, esp related to ext draining
+ */
+
+int
+lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)
+{
+ int callback_action = LWS_CALLBACK_RECEIVE;
+ int ret = 0;
+ unsigned short close_code;
+ struct lws_tokens ebuf;
+ unsigned char *pp;
+ int n = 0;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ int rx_draining_ext = 0;
+ int lin;
+#endif
+
+ ebuf.token = NULL;
+ ebuf.len = 0;
+ if (wsi->socket_is_permanently_unusable)
+ return -1;
+
+ switch (wsi->lws_rx_parse_state) {
+ case LWS_RXPS_NEW:
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (wsi->ws->rx_draining_ext) {
+ ebuf.token = NULL;
+ ebuf.len = 0;
+ lws_remove_wsi_from_draining_ext_list(wsi);
+ rx_draining_ext = 1;
+ lwsl_debug("%s: doing draining flow\n", __func__);
+
+ goto drain_extension;
+ }
+#endif
+ switch (wsi->ws->ietf_spec_revision) {
+ case 13:
+ /*
+ * no prepended frame key any more
+ */
+ wsi->ws->all_zero_nonce = 1;
+ goto handle_first;
+
+ default:
+ lwsl_warn("lws_ws_rx_sm: unknown spec version %d\n",
+ wsi->ws->ietf_spec_revision);
+ break;
+ }
+ break;
+ case LWS_RXPS_04_mask_1:
+ wsi->ws->mask[1] = c;
+ if (c)
+ wsi->ws->all_zero_nonce = 0;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2;
+ break;
+ case LWS_RXPS_04_mask_2:
+ wsi->ws->mask[2] = c;
+ if (c)
+ wsi->ws->all_zero_nonce = 0;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3;
+ break;
+ case LWS_RXPS_04_mask_3:
+ wsi->ws->mask[3] = c;
+ if (c)
+ wsi->ws->all_zero_nonce = 0;
+
+ /*
+ * start from the zero'th byte in the XOR key buffer since
+ * this is the start of a frame with a new key
+ */
+
+ wsi->ws->mask_idx = 0;
+
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1;
+ break;
+
+ /*
+ * 04 logical framing from the spec (all this is masked when incoming
+ * and has to be unmasked)
+ *
+ * We ignore the possibility of extension data because we don't
+ * negotiate any extensions at the moment.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-------+-+-------------+-------------------------------+
+ * |F|R|R|R| opcode|R| Payload len | Extended payload length |
+ * |I|S|S|S| (4) |S| (7) | (16/63) |
+ * |N|V|V|V| |V| | (if payload len==126/127) |
+ * | |1|2|3| |4| | |
+ * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+ * | Extended payload length continued, if payload len == 127 |
+ * + - - - - - - - - - - - - - - - +-------------------------------+
+ * | | Extension data |
+ * +-------------------------------+ - - - - - - - - - - - - - - - +
+ * : :
+ * +---------------------------------------------------------------+
+ * : Application data :
+ * +---------------------------------------------------------------+
+ *
+ * We pass payload through to userland as soon as we get it, ignoring
+ * FIN. It's up to userland to buffer it up if it wants to see a
+ * whole unfragmented block of the original size (which may be up to
+ * 2^63 long!)
+ */
+
+ case LWS_RXPS_04_FRAME_HDR_1:
+handle_first:
+
+ wsi->ws->opcode = c & 0xf;
+ wsi->ws->rsv = c & 0x70;
+ wsi->ws->final = !!((c >> 7) & 1);
+ wsi->ws->defeat_check_utf8 = 0;
+
+ if (((wsi->ws->opcode) & 8) && !wsi->ws->final) {
+ lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
+ (uint8_t *)"frag ctl", 8);
+ return -1;
+ }
+
+ switch (wsi->ws->opcode) {
+ case LWSWSOPC_TEXT_FRAME:
+ wsi->ws->check_utf8 = lws_check_opt(
+ wsi->context->options,
+ LWS_SERVER_OPTION_VALIDATE_UTF8);
+ /* fallthru */
+ case LWSWSOPC_BINARY_FRAME:
+ if (wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)
+ wsi->ws->check_utf8 = 0;
+ if (wsi->ws->continuation_possible) {
+ lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad cont", 8);
+ return -1;
+ }
+ wsi->ws->rsv_first_msg = (c & 0x70);
+ wsi->ws->frame_is_binary =
+ wsi->ws->opcode == LWSWSOPC_BINARY_FRAME;
+ wsi->ws->first_fragment = 1;
+ wsi->ws->continuation_possible = !wsi->ws->final;
+ break;
+ case LWSWSOPC_CONTINUATION:
+ if (!wsi->ws->continuation_possible) {
+ lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad cont", 8);
+ return -1;
+ }
+ break;
+ case LWSWSOPC_CLOSE:
+ wsi->ws->check_utf8 = 0;
+ wsi->ws->utf8 = 0;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 0xb:
+ case 0xc:
+ case 0xd:
+ case 0xe:
+ case 0xf:
+ lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad opc", 7);
+ lwsl_info("illegal opcode\n");
+ return -1;
+ }
+
+ if (wsi->ws->owed_a_fin &&
+ (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
+ wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
+ lwsl_info("hey you owed us a FIN\n");
+ lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad fin", 7);
+ return -1;
+ }
+ if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) {
+ wsi->ws->continuation_possible = 0;
+ wsi->ws->owed_a_fin = 0;
+ }
+
+ if (!wsi->ws->final)
+ wsi->ws->owed_a_fin = 1;
+
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
+ if (wsi->ws->rsv &&
+ (
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ !wsi->ws->count_act_ext ||
+#endif
+ (wsi->ws->rsv & ~0x40))) {
+ lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
+ (uint8_t *)"rsv bits", 8);
+ return -1;
+ }
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN:
+
+ wsi->ws->this_frame_masked = !!(c & 0x80);
+
+ switch (c & 0x7f) {
+ case 126:
+ /* control frames are not allowed to have big lengths */
+ if (wsi->ws->opcode & 8)
+ goto illegal_ctl_length;
+
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
+ break;
+ case 127:
+ /* control frames are not allowed to have big lengths */
+ if (wsi->ws->opcode & 8)
+ goto illegal_ctl_length;
+
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
+ break;
+ default:
+ wsi->ws->rx_packet_length = c & 0x7f;
+
+
+ if (wsi->ws->this_frame_masked)
+ wsi->lws_rx_parse_state =
+ LWS_RXPS_07_COLLECT_FRAME_KEY_1;
+ else
+ if (wsi->ws->rx_packet_length) {
+ wsi->lws_rx_parse_state =
+ LWS_RXPS_WS_FRAME_PAYLOAD;
+ } else {
+ wsi->lws_rx_parse_state = LWS_RXPS_NEW;
+ goto spill;
+ }
+ break;
+ }
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN16_2:
+ wsi->ws->rx_packet_length = c << 8;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN16_1:
+ wsi->ws->rx_packet_length |= c;
+ if (wsi->ws->this_frame_masked)
+ wsi->lws_rx_parse_state =
+ LWS_RXPS_07_COLLECT_FRAME_KEY_1;
+ else {
+ wsi->lws_rx_parse_state =
+ LWS_RXPS_WS_FRAME_PAYLOAD;
+ }
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_8:
+ if (c & 0x80) {
+ lwsl_warn("b63 of length must be zero\n");
+ /* kill the connection */
+ return -1;
+ }
+#if defined __LP64__
+ wsi->ws->rx_packet_length = ((size_t)c) << 56;
+#else
+ wsi->ws->rx_packet_length = 0;
+#endif
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_7:
+#if defined __LP64__
+ wsi->ws->rx_packet_length |= ((size_t)c) << 48;
+#endif
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_6:
+#if defined __LP64__
+ wsi->ws->rx_packet_length |= ((size_t)c) << 40;
+#endif
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_5:
+#if defined __LP64__
+ wsi->ws->rx_packet_length |= ((size_t)c) << 32;
+#endif
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_4:
+ wsi->ws->rx_packet_length |= ((size_t)c) << 24;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_3:
+ wsi->ws->rx_packet_length |= ((size_t)c) << 16;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_2:
+ wsi->ws->rx_packet_length |= ((size_t)c) << 8;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_1:
+ wsi->ws->rx_packet_length |= ((size_t)c);
+ if (wsi->ws->this_frame_masked)
+ wsi->lws_rx_parse_state =
+ LWS_RXPS_07_COLLECT_FRAME_KEY_1;
+ else
+ wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD;
+ break;
+
+ case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
+ wsi->ws->mask[0] = c;
+ if (c)
+ wsi->ws->all_zero_nonce = 0;
+ wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
+ break;
+
+ case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
+ wsi->ws->mask[1] = c;
+ if (c)
+ wsi->ws->all_zero_nonce = 0;
+ wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
+ break;
+
+ case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
+ wsi->ws->mask[2] = c;
+ if (c)
+ wsi->ws->all_zero_nonce = 0;
+ wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
+ break;
+
+ case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
+ wsi->ws->mask[3] = c;
+ if (c)
+ wsi->ws->all_zero_nonce = 0;
+ wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD;
+ wsi->ws->mask_idx = 0;
+ if (wsi->ws->rx_packet_length == 0) {
+ wsi->lws_rx_parse_state = LWS_RXPS_NEW;
+ goto spill;
+ }
+ break;
+
+
+ case LWS_RXPS_WS_FRAME_PAYLOAD:
+ assert(wsi->ws->rx_ubuf);
+
+ if (wsi->ws->rx_ubuf_head + LWS_PRE >= wsi->ws->rx_ubuf_alloc) {
+ lwsl_err("Attempted overflow \n");
+ return -1;
+ }
+ if (!(already_processed & ALREADY_PROCESSED_IGNORE_CHAR)) {
+ if (wsi->ws->all_zero_nonce)
+ wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] =
+ c;
+ else
+ wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] =
+ c ^ wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
+
+ --wsi->ws->rx_packet_length;
+ }
+
+ if (!wsi->ws->rx_packet_length) {
+ lwsl_debug("%s: ws fragment length exhausted\n", __func__);
+ /* spill because we have the whole frame */
+ wsi->lws_rx_parse_state = LWS_RXPS_NEW;
+ goto spill;
+ }
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (wsi->ws->rx_draining_ext) {
+ lwsl_debug("%s: UNTIL_EXHAUSTED draining\n", __func__);
+ goto drain_extension;
+ }
+#endif
+ /*
+ * if there's no protocol max frame size given, we are
+ * supposed to default to context->pt_serv_buf_size
+ */
+ if (!wsi->protocol->rx_buffer_size &&
+ wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size)
+ break;
+
+ if (wsi->protocol->rx_buffer_size &&
+ wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size)
+ break;
+
+ /* spill because we filled our rx buffer */
+spill:
+ /*
+ * is this frame a control packet we should take care of at this
+ * layer? If so service it and hide it from the user callback
+ */
+
+ lwsl_parser("spill on %s\n", wsi->protocol->name);
+
+ switch (wsi->ws->opcode) {
+ case LWSWSOPC_CLOSE:
+
+ if (wsi->ws->peer_has_sent_close)
+ break;
+
+ wsi->ws->peer_has_sent_close = 1;
+
+ pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE];
+ if (lws_check_opt(wsi->context->options,
+ LWS_SERVER_OPTION_VALIDATE_UTF8) &&
+ wsi->ws->rx_ubuf_head > 2 &&
+ lws_check_utf8(&wsi->ws->utf8, pp + 2,
+ wsi->ws->rx_ubuf_head - 2))
+ goto utf8_fail;
+
+ /* is this an acknowledgment of our close? */
+ if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
+ /*
+ * fine he has told us he is closing too, let's
+ * finish our close
+ */
+ lwsl_parser("seen client close ack\n");
+ return -1;
+ }
+ if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
+ /* if he sends us 2 CLOSE, kill him */
+ return -1;
+
+ if (lws_partial_buffered(wsi)) {
+ /*
+ * if we're in the middle of something,
+ * we can't do a normal close response and
+ * have to just close our end.
+ */
+ wsi->socket_is_permanently_unusable = 1;
+ lwsl_parser("Closing on peer close due to Pending tx\n");
+ return -1;
+ }
+
+ if (wsi->ws->rx_ubuf_head >= 2) {
+ close_code = (pp[0] << 8) | pp[1];
+ if (close_code < 1000 ||
+ close_code == 1004 ||
+ close_code == 1005 ||
+ close_code == 1006 ||
+ close_code == 1012 ||
+ close_code == 1013 ||
+ close_code == 1014 ||
+ close_code == 1015 ||
+ (close_code >= 1016 && close_code < 3000)
+ ) {
+ pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff;
+ pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff;
+ }
+ }
+
+ if (user_callback_handle_rxflow(
+ wsi->protocol->callback, wsi,
+ LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
+ wsi->user_space,
+ &wsi->ws->rx_ubuf[LWS_PRE],
+ wsi->ws->rx_ubuf_head))
+ return -1;
+
+ lwsl_parser("server sees client close packet\n");
+ lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
+ /* deal with the close packet contents as a PONG */
+ wsi->ws->payload_is_close = 1;
+ goto process_as_ping;
+
+ case LWSWSOPC_PING:
+ lwsl_info("received %d byte ping, sending pong\n",
+ wsi->ws->rx_ubuf_head);
+
+ if (wsi->ws->ping_pending_flag) {
+ /*
+ * there is already a pending ping payload
+ * we should just log and drop
+ */
+ lwsl_parser("DROP PING since one pending\n");
+ goto ping_drop;
+ }
+process_as_ping:
+ /* control packets can only be < 128 bytes long */
+ if (wsi->ws->rx_ubuf_head > 128 - 3) {
+ lwsl_parser("DROP PING payload too large\n");
+ goto ping_drop;
+ }
+
+ /* stash the pong payload */
+ memcpy(wsi->ws->ping_payload_buf + LWS_PRE,
+ &wsi->ws->rx_ubuf[LWS_PRE],
+ wsi->ws->rx_ubuf_head);
+
+ wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head;
+ wsi->ws->ping_pending_flag = 1;
+
+ /* get it sent as soon as possible */
+ lws_callback_on_writable(wsi);
+ping_drop:
+ wsi->ws->rx_ubuf_head = 0;
+ return 0;
+
+ case LWSWSOPC_PONG:
+ lwsl_info("received pong\n");
+ lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
+ wsi->ws->rx_ubuf_head);
+
+ if (wsi->pending_timeout ==
+ PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
+ lwsl_info("received expected PONG on wsi %p\n",
+ wsi);
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+ }
+
+ /* issue it */
+ callback_action = LWS_CALLBACK_RECEIVE_PONG;
+ break;
+
+ case LWSWSOPC_TEXT_FRAME:
+ case LWSWSOPC_BINARY_FRAME:
+ case LWSWSOPC_CONTINUATION:
+ break;
+
+ default:
+ lwsl_parser("unknown opc %x\n", wsi->ws->opcode);
+
+ return -1;
+ }
+
+ /*
+ * No it's real payload, pass it up to the user callback.
+ * It's nicely buffered with the pre-padding taken care of
+ * so it can be sent straight out again using lws_write
+ */
+
+ ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
+ ebuf.len = wsi->ws->rx_ubuf_head;
+
+ if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len)
+ goto already_done;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+drain_extension:
+#endif
+ // lwsl_notice("%s: passing %d to ext\n", __func__, ebuf.len);
+
+ if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
+ lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
+ goto already_done;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ lin = ebuf.len;
+ //if (lin)
+ // lwsl_hexdump_notice(ebuf.token, ebuf.len);
+ n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
+ lwsl_debug("%s: ext says %d / ebuf.len %d\n", __func__, n, ebuf.len);
+ if (wsi->ws->rx_draining_ext)
+ already_processed &= ~ALREADY_PROCESSED_NO_CB;
+#endif
+ /*
+ * ebuf may be pointing somewhere completely different now,
+ * it's the output
+ */
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (n < 0) {
+ /*
+ * we may rely on this to get RX, just drop connection
+ */
+ wsi->socket_is_permanently_unusable = 1;
+ return -1;
+ }
+#endif
+ if (
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ rx_draining_ext &&
+#endif
+ ebuf.len == 0)
+ goto already_done;
+
+ if (
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ n &&
+#endif
+ ebuf.len)
+ /* extension had more... main loop will come back */
+ lws_add_wsi_to_draining_ext_list(wsi);
+ else
+ lws_remove_wsi_from_draining_ext_list(wsi);
+
+ if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
+ if (lws_check_utf8(&wsi->ws->utf8,
+ (unsigned char *)ebuf.token,
+ ebuf.len)) {
+ lws_close_reason(wsi,
+ LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+ (uint8_t *)"bad utf8", 8);
+ goto utf8_fail;
+ }
+
+ /* we are ending partway through utf-8 character? */
+ if (!wsi->ws->rx_packet_length && wsi->ws->final &&
+ wsi->ws->utf8 && !n) {
+ lwsl_info("FINAL utf8 error\n");
+ lws_close_reason(wsi,
+ LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+ (uint8_t *)"partial utf8", 12);
+utf8_fail:
+ lwsl_notice("utf8 error\n");
+ lwsl_hexdump_notice(ebuf.token, ebuf.len);
+
+ return -1;
+ }
+ }
+
+ if (!wsi->wsistate_pre_close && (ebuf.len >= 0 ||
+ callback_action == LWS_CALLBACK_RECEIVE_PONG)) {
+ if (ebuf.len)
+ ebuf.token[ebuf.len] = '\0';
+
+ if (wsi->protocol->callback &&
+ !(already_processed & ALREADY_PROCESSED_NO_CB)) {
+ if (callback_action == LWS_CALLBACK_RECEIVE_PONG)
+ lwsl_info("Doing pong callback\n");
+
+ ret = user_callback_handle_rxflow(
+ wsi->protocol->callback,
+ wsi, (enum lws_callback_reasons)
+ callback_action,
+ wsi->user_space,
+ ebuf.token,
+ ebuf.len);
+ }
+ wsi->ws->first_fragment = 0;
+ }
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (!lin)
+ break;
+#endif
+
+already_done:
+ wsi->ws->rx_ubuf_head = 0;
+ break;
+ }
+
+ return ret;
+
+illegal_ctl_length:
+
+ lwsl_warn("Control frame with xtended length is illegal\n");
+ /* kill the connection */
+ return -1;
+}
+
+
+LWS_VISIBLE size_t
+lws_remaining_packet_payload(struct lws *wsi)
+{
+ return wsi->ws->rx_packet_length;
+}
+
+LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi)
+{
+ return wsi->ws->frame_is_binary;
+}
+
+void
+lws_add_wsi_to_draining_ext_list(struct lws *wsi)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+ if (wsi->ws->rx_draining_ext)
+ return;
+
+ lwsl_debug("%s: RX EXT DRAINING: Adding to list\n", __func__);
+
+ wsi->ws->rx_draining_ext = 1;
+ wsi->ws->rx_draining_ext_list = pt->ws.rx_draining_ext_list;
+ pt->ws.rx_draining_ext_list = wsi;
+#endif
+}
+
+void
+lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ struct lws **w = &pt->ws.rx_draining_ext_list;
+
+ if (!wsi->ws->rx_draining_ext)
+ return;
+
+ lwsl_debug("%s: RX EXT DRAINING: Removing from list\n", __func__);
+
+ wsi->ws->rx_draining_ext = 0;
+
+ /* remove us from context draining ext list */
+ while (*w) {
+ if (*w == wsi) {
+ /* if us, point it instead to who we were pointing to */
+ *w = wsi->ws->rx_draining_ext_list;
+ break;
+ }
+ w = &((*w)->ws->rx_draining_ext_list);
+ }
+ wsi->ws->rx_draining_ext_list = NULL;
+#endif
+}
+
+LWS_EXTERN void
+lws_restart_ws_ping_pong_timer(struct lws *wsi)
+{
+ if (!wsi->context->ws_ping_pong_interval ||
+ !lwsi_role_ws(wsi))
+ return;
+
+ wsi->ws->time_next_ping_check = (time_t)lws_now_secs();
+}
+
+static int
+lws_0405_frame_mask_generate(struct lws *wsi)
+{
+ int n;
+ /* fetch the per-frame nonce */
+
+ n = lws_get_random(lws_get_context(wsi), wsi->ws->mask, 4);
+ if (n != 4) {
+ lwsl_parser("Unable to read from random device %s %d\n",
+ SYSTEM_RANDOM_FILEPATH, n);
+ return 1;
+ }
+
+ /* start masking from first byte of masking key buffer */
+ wsi->ws->mask_idx = 0;
+
+ return 0;
+}
+
+int
+lws_server_init_wsi_for_ws(struct lws *wsi)
+{
+ int n;
+
+ lwsi_set_state(wsi, LRS_ESTABLISHED);
+ lws_restart_ws_ping_pong_timer(wsi);
+
+ /*
+ * create the frame buffer for this connection according to the
+ * size mentioned in the protocol definition. If 0 there, use
+ * a big default for compatibility
+ */
+
+ n = (int)wsi->protocol->rx_buffer_size;
+ if (!n)
+ n = wsi->context->pt_serv_buf_size;
+ n += LWS_PRE;
+ wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "rx_ubuf");
+ if (!wsi->ws->rx_ubuf) {
+ lwsl_err("Out of Mem allocating rx buffer %d\n", n);
+ return 1;
+ }
+ wsi->ws->rx_ubuf_alloc = n;
+ lwsl_debug("Allocating RX buffer %d\n", n);
+
+#if !defined(LWS_WITH_ESP32)
+ if (!wsi->parent_carries_io &&
+ !wsi->h2_stream_carries_ws)
+ if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
+ (const char *)&n, sizeof n)) {
+ lwsl_warn("Failed to set SNDBUF to %d", n);
+ return 1;
+ }
+#endif
+
+ /* notify user code that we're ready to roll */
+
+ if (wsi->protocol->callback)
+ if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,
+ wsi->user_space,
+#ifdef LWS_WITH_TLS
+ wsi->tls.ssl,
+#else
+ NULL,
+#endif
+ wsi->h2_stream_carries_ws))
+ return 1;
+
+ lwsl_debug("ws established\n");
+
+ return 0;
+}
+
+
+
+LWS_VISIBLE int
+lws_is_final_fragment(struct lws *wsi)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ lwsl_debug("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
+ wsi->ws->final, (long)wsi->ws->rx_packet_length,
+ (long)wsi->ws->rx_draining_ext);
+ return wsi->ws->final && !wsi->ws->rx_packet_length &&
+ !wsi->ws->rx_draining_ext;
+#else
+ return wsi->ws->final && !wsi->ws->rx_packet_length;
+#endif
+}
+
+LWS_VISIBLE int
+lws_is_first_fragment(struct lws *wsi)
+{
+ return wsi->ws->first_fragment;
+}
+
+LWS_VISIBLE unsigned char
+lws_get_reserved_bits(struct lws *wsi)
+{
+ return wsi->ws->rsv;
+}
+
+LWS_VISIBLE LWS_EXTERN int
+lws_get_close_length(struct lws *wsi)
+{
+ return wsi->ws->close_in_ping_buffer_len;
+}
+
+LWS_VISIBLE LWS_EXTERN unsigned char *
+lws_get_close_payload(struct lws *wsi)
+{
+ return &wsi->ws->ping_payload_buf[LWS_PRE];
+}
+
+LWS_VISIBLE LWS_EXTERN void
+lws_close_reason(struct lws *wsi, enum lws_close_status status,
+ unsigned char *buf, size_t len)
+{
+ unsigned char *p, *start;
+ int budget = sizeof(wsi->ws->ping_payload_buf) - LWS_PRE;
+
+ assert(lwsi_role_ws(wsi));
+
+ start = p = &wsi->ws->ping_payload_buf[LWS_PRE];
+
+ *p++ = (((int)status) >> 8) & 0xff;
+ *p++ = ((int)status) & 0xff;
+
+ if (buf)
+ while (len-- && p < start + budget)
+ *p++ = *buf++;
+
+ wsi->ws->close_in_ping_buffer_len = lws_ptr_diff(p, start);
+}
+
+static int
+lws_is_ws_with_ext(struct lws *wsi)
+{
+#if defined(LWS_WITHOUT_EXTENSIONS)
+ return 0;
+#else
+ return lwsi_role_ws(wsi) && !!wsi->ws->count_act_ext;
+#endif
+}
+
+static int
+rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
+ struct lws_pollfd *pollfd)
+{
+ struct lws_tokens ebuf;
+ unsigned int pending = 0;
+ char buffered = 0;
+ int n = 0, m;
+#if defined(LWS_WITH_HTTP2)
+ struct lws *wsi1;
+#endif
+
+ if (!wsi->ws) {
+ lwsl_err("ws role wsi with no ws\n");
+ return 1;
+ }
+
+ // lwsl_notice("%s: %s\n", __func__, wsi->protocol->name);
+
+ //lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__,
+ // wsi->wsistate, pollfd->revents & LWS_POLLOUT);
+
+ /*
+ * something went wrong with parsing the handshake, and
+ * we ended up back in the event loop without completing it
+ */
+ if (lwsi_state(wsi) == LRS_PRE_WS_SERVING_ACCEPT) {
+ wsi->socket_is_permanently_unusable = 1;
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ }
+
+ ebuf.token = NULL;
+ ebuf.len = 0;
+
+ if (lwsi_state(wsi) == LRS_WAITING_CONNECT) {
+#if !defined(LWS_NO_CLIENT)
+ if ((pollfd->revents & LWS_POLLOUT) &&
+ lws_handle_POLLOUT_event(wsi, pollfd)) {
+ lwsl_debug("POLLOUT event closed it\n");
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ }
+
+ n = lws_client_socket_service(wsi, pollfd, NULL);
+ if (n)
+ return LWS_HPI_RET_WSI_ALREADY_DIED;
+#endif
+ return LWS_HPI_RET_HANDLED;
+ }
+
+ //lwsl_notice("%s: wsi->ws->tx_draining_ext %d revents 0x%x 0x%x %d\n", __func__, wsi->ws->tx_draining_ext, pollfd->revents, wsi->wsistate, lwsi_state_can_handle_POLLOUT(wsi));
+
+ /* 1: something requested a callback when it was OK to write */
+
+ if ((pollfd->revents & LWS_POLLOUT) &&
+ lwsi_state_can_handle_POLLOUT(wsi) &&
+ lws_handle_POLLOUT_event(wsi, pollfd)) {
+ if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
+ lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
+
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ }
+
+ if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
+ lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) {
+ /*
+ * we stopped caring about anything except control
+ * packets. Force flow control off, defeat tx
+ * draining.
+ */
+ lws_rx_flow_control(wsi, 1);
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (wsi->ws)
+ wsi->ws->tx_draining_ext = 0;
+#endif
+ }
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (wsi->ws->tx_draining_ext)
+ /*
+ * We cannot deal with new RX until the TX ext path has
+ * been drained. It's because new rx will, eg, crap on
+ * the wsi rx buf that may be needed to retain state.
+ *
+ * TX ext drain path MUST go through event loop to avoid
+ * blocking.
+ */
+ return LWS_HPI_RET_HANDLED;
+#endif
+ if (lws_is_flowcontrolled(wsi)) {
+ /* We cannot deal with any kind of new RX because we are
+ * RX-flowcontrolled.
+ */
+ lwsl_info("flowcontrolled\n");
+ return LWS_HPI_RET_HANDLED;
+ }
+
+#if defined(LWS_WITH_HTTP2)
+ if (wsi->http2_substream || wsi->upgraded_to_http2) {
+ wsi1 = lws_get_network_wsi(wsi);
+ if (wsi1 && wsi1->trunc_len)
+ /* We cannot deal with any kind of new RX
+ * because we are dealing with a partial send
+ * (new RX may trigger new http_action() that
+ * expect to be able to send)
+ */
+ return LWS_HPI_RET_HANDLED;
+ }
+#endif
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ /* 2: RX Extension needs to be drained
+ */
+
+ if (wsi->ws->rx_draining_ext) {
+
+ lwsl_debug("%s: RX EXT DRAINING: Service\n", __func__);
+#ifndef LWS_NO_CLIENT
+ if (lwsi_role_client(wsi)) {
+ n = lws_ws_client_rx_sm(wsi, 0);
+ if (n < 0)
+ /* we closed wsi */
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ } else
+#endif
+ n = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
+
+ return LWS_HPI_RET_HANDLED;
+ }
+
+ if (wsi->ws->rx_draining_ext)
+ /*
+ * We have RX EXT content to drain, but can't do it
+ * right now. That means we cannot do anything lower
+ * priority either.
+ */
+ return LWS_HPI_RET_HANDLED;
+#endif
+
+ /* 3: buflist needs to be drained
+ */
+read:
+ //lws_buflist_describe(&wsi->buflist, wsi);
+ ebuf.len = (int)lws_buflist_next_segment_len(&wsi->buflist,
+ (uint8_t **)&ebuf.token);
+ if (ebuf.len) {
+ lwsl_info("draining buflist (len %d)\n", ebuf.len);
+ buffered = 1;
+ goto drain;
+ }
+
+ if (!(pollfd->revents & pollfd->events & LWS_POLLIN) && !wsi->http.ah)
+ return LWS_HPI_RET_HANDLED;
+
+ if (lws_is_flowcontrolled(wsi)) {
+ lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n",
+ __func__, wsi, wsi->rxflow_bitmap);
+ return LWS_HPI_RET_HANDLED;
+ }
+
+ if (!(lwsi_role_client(wsi) &&
+ (lwsi_state(wsi) != LRS_ESTABLISHED &&
+ lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK &&
+ lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS))) {
+ /*
+ * In case we are going to react to this rx by scheduling
+ * writes, we need to restrict the amount of rx to the size
+ * the protocol reported for rx buffer.
+ *
+ * Otherwise we get a situation we have to absorb possibly a
+ * lot of reads before we get a chance to drain them by writing
+ * them, eg, with echo type tests in autobahn.
+ */
+
+ buffered = 0;
+ ebuf.token = (char *)pt->serv_buf;
+ if (lwsi_role_ws(wsi))
+ ebuf.len = wsi->ws->rx_ubuf_alloc;
+ else
+ ebuf.len = wsi->context->pt_serv_buf_size;
+
+ if ((unsigned int)ebuf.len > wsi->context->pt_serv_buf_size)
+ ebuf.len = wsi->context->pt_serv_buf_size;
+
+ if ((int)pending > ebuf.len)
+ pending = ebuf.len;
+
+ ebuf.len = lws_ssl_capable_read(wsi, (uint8_t *)ebuf.token,
+ pending ? (int)pending :
+ ebuf.len);
+ switch (ebuf.len) {
+ case 0:
+ lwsl_info("%s: zero length read\n",
+ __func__);
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ case LWS_SSL_CAPABLE_MORE_SERVICE:
+ lwsl_info("SSL Capable more service\n");
+ return LWS_HPI_RET_HANDLED;
+ case LWS_SSL_CAPABLE_ERROR:
+ lwsl_info("%s: LWS_SSL_CAPABLE_ERROR\n",
+ __func__);
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ }
+ // lwsl_notice("Actual RX %d\n", ebuf.len);
+
+ lws_restart_ws_ping_pong_timer(wsi);
+
+ /*
+ * coverity thinks ssl_capable_read() may read over
+ * 2GB. Dissuade it...
+ */
+ ebuf.len &= 0x7fffffff;
+ }
+
+drain:
+
+ /*
+ * give any active extensions a chance to munge the buffer
+ * before parse. We pass in a pointer to an lws_tokens struct
+ * prepared with the default buffer and content length that's in
+ * there. Rather than rewrite the default buffer, extensions
+ * that expect to grow the buffer can adapt .token to
+ * point to their own per-connection buffer in the extension
+ * user allocation. By default with no extensions or no
+ * extension callback handling, just the normal input buffer is
+ * used then so it is efficient.
+ */
+ m = 0;
+ do {
+
+ /* service incoming data */
+ //lws_buflist_describe(&wsi->buflist, wsi);
+ if (ebuf.len) {
+#if defined(LWS_ROLE_H2)
+ if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
+ n = lws_read_h2(wsi, (unsigned char *)ebuf.token,
+ ebuf.len);
+ else
+#endif
+ n = lws_read_h1(wsi, (unsigned char *)ebuf.token,
+ ebuf.len);
+
+ if (n < 0) {
+ /* we closed wsi */
+ n = 0;
+ return LWS_HPI_RET_WSI_ALREADY_DIED;
+ }
+ //lws_buflist_describe(&wsi->buflist, wsi);
+ //lwsl_notice("%s: consuming %d / %d\n", __func__, n, ebuf.len);
+ if (lws_buflist_aware_consume(wsi, &ebuf, n, buffered))
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ }
+
+ ebuf.token = NULL;
+ ebuf.len = 0;
+ } while (m);
+
+ if (wsi->http.ah
+#if !defined(LWS_NO_CLIENT)
+ && !wsi->client_h2_alpn
+#endif
+ ) {
+ lwsl_info("%s: %p: detaching ah\n", __func__, wsi);
+ lws_header_table_detach(wsi, 0);
+ }
+
+ pending = lws_ssl_pending(wsi);
+ if (pending) {
+ if (lws_is_ws_with_ext(wsi))
+ pending = pending > wsi->ws->rx_ubuf_alloc ?
+ wsi->ws->rx_ubuf_alloc : pending;
+ else
+ pending = pending > wsi->context->pt_serv_buf_size ?
+ wsi->context->pt_serv_buf_size : pending;
+ goto read;
+ }
+
+ if (buffered && /* were draining, now nothing left */
+ !lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
+ lwsl_info("%s: %p flow buf: drained\n", __func__, wsi);
+ /* having drained the rxflow buffer, can rearm POLLIN */
+#ifdef LWS_NO_SERVER
+ n =
+#endif
+ __lws_rx_flow_control(wsi);
+ /* n ignored, needed for NO_SERVER case */
+ }
+
+ /* n = 0 */
+ return LWS_HPI_RET_HANDLED;
+}
+
+
+int rops_handle_POLLOUT_ws(struct lws *wsi)
+{
+ int write_type = LWS_WRITE_PONG;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ struct lws_tokens ebuf;
+ int ret, m;
+#endif
+ int n;
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ lwsl_debug("%s: %s: wsi->ws->tx_draining_ext %d\n", __func__,
+ wsi->protocol->name, wsi->ws->tx_draining_ext);
+#endif
+
+ /* Priority 3: pending control packets (pong or close)
+ *
+ * 3a: close notification packet requested from close api
+ */
+
+ if (lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) {
+ lwsl_debug("sending close packet\n");
+ lwsl_hexdump_debug(&wsi->ws->ping_payload_buf[LWS_PRE],
+ wsi->ws->close_in_ping_buffer_len);
+ wsi->waiting_to_send_close_frame = 0;
+ n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
+ wsi->ws->close_in_ping_buffer_len,
+ LWS_WRITE_CLOSE);
+ if (n >= 0) {
+ if (wsi->close_needs_ack) {
+ lwsi_set_state(wsi, LRS_AWAITING_CLOSE_ACK);
+ lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 5);
+ lwsl_debug("sent close indication, awaiting ack\n");
+
+ return LWS_HP_RET_BAIL_OK;
+ }
+ wsi->close_needs_ack = 0;
+ lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
+ }
+
+ return LWS_HP_RET_BAIL_DIE;
+ }
+
+ /* else, the send failed and we should just hang up */
+
+ if ((lwsi_role_ws(wsi) && wsi->ws->ping_pending_flag) ||
+ (lwsi_state(wsi) == LRS_RETURNED_CLOSE &&
+ wsi->ws->payload_is_close)) {
+
+ if (wsi->ws->payload_is_close)
+ write_type = LWS_WRITE_CLOSE;
+ else {
+ if (wsi->wsistate_pre_close) {
+ /* we started close flow, forget pong */
+ wsi->ws->ping_pending_flag = 0;
+ return LWS_HP_RET_BAIL_OK;
+ }
+ lwsl_info("issuing pong %d on wsi %p\n", wsi->ws->ping_payload_len, wsi);
+ }
+
+ n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
+ wsi->ws->ping_payload_len, write_type);
+ if (n < 0)
+ return LWS_HP_RET_BAIL_DIE;
+
+ /* well he is sent, mark him done */
+ wsi->ws->ping_pending_flag = 0;
+ if (wsi->ws->payload_is_close) {
+ // assert(0);
+ /* oh... a close frame was it... then we are done */
+ return LWS_HP_RET_BAIL_DIE;
+ }
+
+ /* otherwise for PING, leave POLLOUT active either way */
+ return LWS_HP_RET_BAIL_OK;
+ }
+
+ if (lwsi_role_client(wsi) && !wsi->socket_is_permanently_unusable &&
+ wsi->ws->send_check_ping) {
+
+ lwsl_info("issuing ping on wsi %p\n", wsi);
+ wsi->ws->send_check_ping = 0;
+ n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
+ 0, LWS_WRITE_PING);
+ if (n < 0)
+ return LWS_HP_RET_BAIL_DIE;
+
+ /*
+ * we apparently were able to send the PING in a reasonable time
+ * now reset the clock on our peer to be able to send the
+ * PONG in a reasonable time.
+ */
+
+ lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG,
+ wsi->context->timeout_secs);
+
+ return LWS_HP_RET_BAIL_OK;
+ }
+
+ /* Priority 4: if we are closing, not allowed to send more data frags
+ * which means user callback or tx ext flush banned now
+ */
+ if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
+ return LWS_HP_RET_USER_SERVICE;
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ /* Priority 5: Tx path extension with more to send
+ *
+ * These are handled as new fragments each time around
+ * So while we must block new writeable callback to enforce
+ * payload ordering, but since they are always complete
+ * fragments control packets can interleave OK.
+ */
+ if (lwsi_role_client(wsi) && wsi->ws->tx_draining_ext) {
+ lwsl_ext("SERVICING TX EXT DRAINING\n");
+ if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0)
+ return LWS_HP_RET_BAIL_DIE;
+ /* leave POLLOUT active */
+ return LWS_HP_RET_BAIL_OK;
+ }
+
+ /* Priority 6: extensions
+ */
+ if (!wsi->ws->extension_data_pending)
+ return LWS_HP_RET_USER_SERVICE;
+
+ /*
+ * check in on the active extensions, see if they
+ * had pending stuff to spill... they need to get the
+ * first look-in otherwise sequence will be disordered
+ *
+ * NULL, zero-length ebuf means just spill pending
+ */
+
+ ret = 1;
+ if (wsi->role_ops == &role_ops_raw_skt ||
+ wsi->role_ops == &role_ops_raw_file)
+ ret = 0;
+
+ while (ret == 1) {
+
+ /* default to nobody has more to spill */
+
+ ret = 0;
+ ebuf.token = NULL;
+ ebuf.len = 0;
+
+ /* give every extension a chance to spill */
+
+ m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND,
+ &ebuf, 0);
+ if (m < 0) {
+ lwsl_err("ext reports fatal error\n");
+ return LWS_HP_RET_BAIL_DIE;
+ }
+ if (m)
+ /*
+ * at least one extension told us he has more
+ * to spill, so we will go around again after
+ */
+ ret = 1;
+
+ /* assuming they gave us something to send, send it */
+
+ if (ebuf.len) {
+ n = lws_issue_raw(wsi, (unsigned char *)ebuf.token,
+ ebuf.len);
+ if (n < 0) {
+ lwsl_info("closing from POLLOUT spill\n");
+ return LWS_HP_RET_BAIL_DIE;
+ }
+ /*
+ * Keep amount spilled small to minimize chance of this
+ */
+ if (n != ebuf.len) {
+ lwsl_err("Unable to spill ext %d vs %d\n",
+ ebuf.len, n);
+ return LWS_HP_RET_BAIL_DIE;
+ }
+ } else
+ continue;
+
+ /* no extension has more to spill */
+
+ if (!ret)
+ continue;
+
+ /*
+ * There's more to spill from an extension, but we just sent
+ * something... did that leave the pipe choked?
+ */
+
+ if (!lws_send_pipe_choked(wsi))
+ /* no we could add more */
+ continue;
+
+ lwsl_info("choked in POLLOUT service\n");
+
+ /*
+ * Yes, he's choked. Leave the POLLOUT masked on so we will
+ * come back here when he is unchoked. Don't call the user
+ * callback to enforce ordering of spilling, he'll get called
+ * when we come back here and there's nothing more to spill.
+ */
+
+ return LWS_HP_RET_BAIL_OK;
+ }
+
+ wsi->ws->extension_data_pending = 0;
+#endif
+
+ return LWS_HP_RET_USER_SERVICE;
+}
+
+static int
+rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now)
+{
+ struct lws_vhost *vh;
+
+ if (!context->ws_ping_pong_interval ||
+ context->last_ws_ping_pong_check_s >= now + 10)
+ return 0;
+
+ vh = context->vhost_list;
+ context->last_ws_ping_pong_check_s = now;
+
+ while (vh) {
+ int n;
+
+ lws_vhost_lock(vh);
+
+ for (n = 0; n < vh->count_protocols; n++) {
+ struct lws *wsi = vh->same_vh_protocol_list[n];
+
+ while (wsi) {
+ if (lwsi_role_ws(wsi) &&
+ !wsi->socket_is_permanently_unusable &&
+ !wsi->ws->send_check_ping &&
+ wsi->ws->time_next_ping_check &&
+ lws_compare_time_t(context, now,
+ wsi->ws->time_next_ping_check) >
+ context->ws_ping_pong_interval) {
+
+ lwsl_info("req pp on wsi %p\n",
+ wsi);
+ wsi->ws->send_check_ping = 1;
+ lws_set_timeout(wsi,
+ PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING,
+ context->timeout_secs);
+ lws_callback_on_writable(wsi);
+ wsi->ws->time_next_ping_check =
+ now;
+ }
+ wsi = wsi->same_vh_protocol_next;
+ }
+ }
+
+ lws_vhost_unlock(vh);
+ vh = vh->vhost_next;
+ }
+
+ return 0;
+}
+
+static int
+rops_service_flag_pending_ws(struct lws_context *context, int tsi)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ struct lws_context_per_thread *pt = &context->pt[tsi];
+ struct lws *wsi;
+ int forced = 0;
+
+ /* POLLIN faking (the pt lock is taken by the parent) */
+
+ /*
+ * 1) For all guys with already-available ext data to drain, if they are
+ * not flowcontrolled, fake their POLLIN status
+ */
+ wsi = pt->ws.rx_draining_ext_list;
+ while (wsi && wsi->position_in_fds_table != LWS_NO_FDS_POS) {
+ pt->fds[wsi->position_in_fds_table].revents |=
+ pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
+ if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN)
+ forced = 1;
+
+ wsi = wsi->ws->rx_draining_ext_list;
+ }
+
+ return forced;
+#else
+ return 0;
+#endif
+}
+
+static int
+rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason)
+{
+ if (!wsi->ws->close_in_ping_buffer_len && /* already a reason */
+ (reason == LWS_CLOSE_STATUS_NOSTATUS ||
+ reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY))
+ return 0;
+
+ lwsl_debug("%s: sending close indication...\n", __func__);
+
+ /* if no prepared close reason, use 1000 and no aux data */
+
+ if (!wsi->ws->close_in_ping_buffer_len) {
+ wsi->ws->close_in_ping_buffer_len = 2;
+ wsi->ws->ping_payload_buf[LWS_PRE] = (reason >> 8) & 0xff;
+ wsi->ws->ping_payload_buf[LWS_PRE + 1] = reason & 0xff;
+ }
+
+ wsi->waiting_to_send_close_frame = 1;
+ wsi->close_needs_ack = 1;
+ lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
+ __lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 5);
+
+ lws_callback_on_writable(wsi);
+
+ return 1;
+}
+
+static int
+rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (wsi->ws->rx_draining_ext) {
+ struct lws **w = &pt->ws.rx_draining_ext_list;
+
+ wsi->ws->rx_draining_ext = 0;
+ /* remove us from context draining ext list */
+ while (*w) {
+ if (*w == wsi) {
+ *w = wsi->ws->rx_draining_ext_list;
+ break;
+ }
+ w = &((*w)->ws->rx_draining_ext_list);
+ }
+ wsi->ws->rx_draining_ext_list = NULL;
+ }
+
+ if (wsi->ws->tx_draining_ext) {
+ struct lws **w = &pt->ws.tx_draining_ext_list;
+ lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
+ wsi->ws->tx_draining_ext = 0;
+ /* remove us from context draining ext list */
+ while (*w) {
+ if (*w == wsi) {
+ *w = wsi->ws->tx_draining_ext_list;
+ break;
+ }
+ w = &((*w)->ws->tx_draining_ext_list);
+ }
+ wsi->ws->tx_draining_ext_list = NULL;
+ }
+#endif
+ lws_free_set_NULL(wsi->ws->rx_ubuf);
+
+ if (wsi->trunc_alloc)
+ /* not going to be completed... nuke it */
+ lws_free_set_NULL(wsi->trunc_alloc);
+
+ wsi->ws->ping_payload_len = 0;
+ wsi->ws->ping_pending_flag = 0;
+
+ /* deallocate any active extension contexts */
+
+ if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
+ lwsl_warn("extension destruction failed\n");
+
+ return 0;
+}
+
+static int
+rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
+ enum lws_write_protocol *wp)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ enum lws_write_protocol wpt;
+#endif
+ int masked7 = lwsi_role_client(wsi);
+ unsigned char is_masked_bit = 0;
+ unsigned char *dropmask = NULL;
+ struct lws_tokens ebuf;
+ size_t orig_len = len;
+ int pre = 0, n = 0;
+
+ // lwsl_err("%s: wp 0x%x len %d\n", __func__, *wp, (int)len);
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (wsi->ws->tx_draining_ext) {
+ /* remove us from the list */
+ struct lws **w = &pt->ws.tx_draining_ext_list;
+
+ lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
+ wsi->ws->tx_draining_ext = 0;
+ /* remove us from context draining ext list */
+ while (*w) {
+ if (*w == wsi) {
+ *w = wsi->ws->tx_draining_ext_list;
+ break;
+ }
+ w = &((*w)->ws->tx_draining_ext_list);
+ }
+ wsi->ws->tx_draining_ext_list = NULL;
+
+ wpt = *wp;
+ *wp = (wsi->ws->tx_draining_stashed_wp & 0xc0)|
+ LWS_WRITE_CONTINUATION;
+
+ /*
+ * When we are just flushing (len == 0), we can trust the
+ * stashed wp info completely. Otherwise adjust it to the
+ * FIN status of the incoming packet.
+ */
+
+ if (!(wpt & LWS_WRITE_NO_FIN) && len)
+ *wp &= ~LWS_WRITE_NO_FIN;
+
+ lwsl_notice("FORCED draining wp to 0x%02X (stashed 0x%02X, incoming 0x%02X)\n", *wp,
+ wsi->ws->tx_draining_stashed_wp, wpt);
+ // assert(0);
+ }
+#endif
+ lws_restart_ws_ping_pong_timer(wsi);
+
+ if (((*wp) & 0x1f) == LWS_WRITE_HTTP ||
+ ((*wp) & 0x1f) == LWS_WRITE_HTTP_FINAL ||
+ ((*wp) & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION ||
+ ((*wp) & 0x1f) == LWS_WRITE_HTTP_HEADERS)
+ goto send_raw;
+
+
+
+ /* if we are continuing a frame that already had its header done */
+
+ if (wsi->ws->inside_frame) {
+ lwsl_debug("INSIDE FRAME\n");
+ goto do_more_inside_frame;
+ }
+
+ wsi->ws->clean_buffer = 1;
+
+ /*
+ * give a chance to the extensions to modify payload
+ * the extension may decide to produce unlimited payload erratically
+ * (eg, compression extension), so we require only that if he produces
+ * something, it will be a complete fragment of the length known at
+ * the time (just the fragment length known), and if he has
+ * more we will come back next time he is writeable and allow him to
+ * produce more fragments until he's drained.
+ *
+ * This allows what is sent each time it is writeable to be limited to
+ * a size that can be sent without partial sends or blocking, allows
+ * interleaving of control frames and other connection service.
+ */
+ ebuf.token = (char *)buf;
+ ebuf.len = (int)len;
+
+ switch ((int)*wp) {
+ case LWS_WRITE_PING:
+ case LWS_WRITE_PONG:
+ case LWS_WRITE_CLOSE:
+ break;
+ default:
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ // lwsl_notice("LWS_EXT_CB_PAYLOAD_TX\n");
+ // m = (int)ebuf.len;
+ /* returns 0 if no more tx pending, 1 if more pending */
+ n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &ebuf, *wp);
+ if (n < 0)
+ return -1;
+ // lwsl_notice("ext processed %d plaintext into %d compressed (wp 0x%x)\n", m, (int)ebuf.len, *wp);
+
+ if (n && ebuf.len) {
+ lwsl_notice("write drain len %d (wp 0x%x) SETTING tx_draining_ext\n", (int)ebuf.len, *wp);
+ /* extension requires further draining */
+ wsi->ws->tx_draining_ext = 1;
+ wsi->ws->tx_draining_ext_list = pt->ws.tx_draining_ext_list;
+ pt->ws.tx_draining_ext_list = wsi;
+ /* we must come back to do more */
+ lws_callback_on_writable(wsi);
+ /*
+ * keep a copy of the write type for the overall
+ * action that has provoked generation of these
+ * fragments, so the last guy can use its FIN state.
+ */
+ wsi->ws->tx_draining_stashed_wp = *wp;
+ /* this is definitely not actually the last fragment
+ * because the extension asserted he has more coming
+ * So make sure this intermediate one doesn't go out
+ * with a FIN.
+ */
+ *wp |= LWS_WRITE_NO_FIN;
+ }
+#endif
+ if (ebuf.len && wsi->ws->stashed_write_pending) {
+ wsi->ws->stashed_write_pending = 0;
+ *wp = ((*wp) & 0xc0) | (int)wsi->ws->stashed_write_type;
+ }
+ }
+
+ /*
+ * an extension did something we need to keep... for example, if
+ * compression extension, it has already updated its state according
+ * to this being issued
+ */
+ if ((char *)buf != ebuf.token) {
+ /*
+ * ext might eat it, but not have anything to issue yet.
+ * In that case we have to follow his lead, but stash and
+ * replace the write type that was lost here the first time.
+ */
+ if (len && !ebuf.len) {
+ if (!wsi->ws->stashed_write_pending)
+ wsi->ws->stashed_write_type = (char)(*wp) & 0x3f;
+ wsi->ws->stashed_write_pending = 1;
+ return (int)len;
+ }
+ /*
+ * extension recreated it:
+ * need to buffer this if not all sent
+ */
+ wsi->ws->clean_buffer = 0;
+ }
+
+ buf = (unsigned char *)ebuf.token;
+ len = ebuf.len;
+
+ if (!buf) {
+ lwsl_err("null buf (%d)\n", (int)len);
+ return -1;
+ }
+
+ switch (wsi->ws->ietf_spec_revision) {
+ case 13:
+ if (masked7) {
+ pre += 4;
+ dropmask = &buf[0 - pre];
+ is_masked_bit = 0x80;
+ }
+
+ switch ((*wp) & 0xf) {
+ case LWS_WRITE_TEXT:
+ n = LWSWSOPC_TEXT_FRAME;
+ break;
+ case LWS_WRITE_BINARY:
+ n = LWSWSOPC_BINARY_FRAME;
+ break;
+ case LWS_WRITE_CONTINUATION:
+ n = LWSWSOPC_CONTINUATION;
+ break;
+
+ case LWS_WRITE_CLOSE:
+ n = LWSWSOPC_CLOSE;
+ break;
+ case LWS_WRITE_PING:
+ n = LWSWSOPC_PING;
+ break;
+ case LWS_WRITE_PONG:
+ n = LWSWSOPC_PONG;
+ break;
+ default:
+ lwsl_warn("lws_write: unknown write opc / wp\n");
+ return -1;
+ }
+
+ if (!((*wp) & LWS_WRITE_NO_FIN))
+ n |= 1 << 7;
+
+ if (len < 126) {
+ pre += 2;
+ buf[-pre] = n;
+ buf[-pre + 1] = (unsigned char)(len | is_masked_bit);
+ } else {
+ if (len < 65536) {
+ pre += 4;
+ buf[-pre] = n;
+ buf[-pre + 1] = 126 | is_masked_bit;
+ buf[-pre + 2] = (unsigned char)(len >> 8);
+ buf[-pre + 3] = (unsigned char)len;
+ } else {
+ pre += 10;
+ buf[-pre] = n;
+ buf[-pre + 1] = 127 | is_masked_bit;
+#if defined __LP64__
+ buf[-pre + 2] = (len >> 56) & 0x7f;
+ buf[-pre + 3] = len >> 48;
+ buf[-pre + 4] = len >> 40;
+ buf[-pre + 5] = len >> 32;
+#else
+ buf[-pre + 2] = 0;
+ buf[-pre + 3] = 0;
+ buf[-pre + 4] = 0;
+ buf[-pre + 5] = 0;
+#endif
+ buf[-pre + 6] = (unsigned char)(len >> 24);
+ buf[-pre + 7] = (unsigned char)(len >> 16);
+ buf[-pre + 8] = (unsigned char)(len >> 8);
+ buf[-pre + 9] = (unsigned char)len;
+ }
+ }
+ break;
+ }
+
+do_more_inside_frame:
+
+ /*
+ * Deal with masking if we are in client -> server direction and
+ * the wp demands it
+ */
+
+ if (masked7) {
+ if (!wsi->ws->inside_frame)
+ if (lws_0405_frame_mask_generate(wsi)) {
+ lwsl_err("frame mask generation failed\n");
+ return -1;
+ }
+
+ /*
+ * in v7, just mask the payload
+ */
+ if (dropmask) { /* never set if already inside frame */
+ for (n = 4; n < (int)len + 4; n++)
+ dropmask[n] = dropmask[n] ^ wsi->ws->mask[
+ (wsi->ws->mask_idx++) & 3];
+
+ /* copy the frame nonce into place */
+ memcpy(dropmask, wsi->ws->mask, 4);
+ }
+ }
+
+ if (lwsi_role_h2_ENCAPSULATION(wsi)) {
+ struct lws *encap = lws_get_network_wsi(wsi);
+
+ assert(encap != wsi);
+ return encap->role_ops->write_role_protocol(wsi, buf - pre,
+ len + pre, wp);
+ }
+
+ switch ((*wp) & 0x1f) {
+ case LWS_WRITE_TEXT:
+ case LWS_WRITE_BINARY:
+ case LWS_WRITE_CONTINUATION:
+ if (!wsi->h2_stream_carries_ws) {
+
+ /*
+ * give any active extensions a chance to munge the
+ * buffer before send. We pass in a pointer to an
+ * lws_tokens struct prepared with the default buffer
+ * and content length that's in there. Rather than
+ * rewrite the default buffer, extensions that expect
+ * to grow the buffer can adapt .token to point to their
+ * own per-connection buffer in the extension user
+ * allocation. By default with no extensions or no
+ * extension callback handling, just the normal input
+ * buffer is used then so it is efficient.
+ *
+ * callback returns 1 in case it wants to spill more
+ * buffers
+ *
+ * This takes care of holding the buffer if send is
+ * incomplete, ie, if wsi->ws->clean_buffer is 0
+ * (meaning an extension meddled with the buffer). If
+ * wsi->ws->clean_buffer is 1, it will instead return
+ * to the user code how much OF THE USER BUFFER was
+ * consumed.
+ */
+
+ n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre);
+ wsi->ws->inside_frame = 1;
+ if (n <= 0)
+ return n;
+
+ if (n == (int)len + pre) {
+ /* everything in the buffer was handled
+ * (or rebuffered...) */
+ wsi->ws->inside_frame = 0;
+ return (int)orig_len;
+ }
+
+ /*
+ * it is how many bytes of user buffer got sent... may
+ * be < orig_len in which case callback when writable
+ * has already been arranged and user code can call
+ * lws_write() again with the rest later.
+ */
+
+ return n - pre;
+ }
+ break;
+ default:
+ break;
+ }
+
+send_raw:
+ return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre);
+}
+
+static int
+rops_close_kill_connection_ws(struct lws *wsi, enum lws_close_status reason)
+{
+ /* deal with ws encapsulation in h2 */
+#if defined(LWS_WITH_HTTP2)
+ if (wsi->http2_substream && wsi->h2_stream_carries_ws)
+ return role_ops_h2.close_kill_connection(wsi, reason);
+
+ return 0;
+#else
+ return 0;
+#endif
+}
+
+static int
+rops_callback_on_writable_ws(struct lws *wsi)
+{
+#if defined(LWS_WITH_HTTP2)
+ if (lwsi_role_h2_ENCAPSULATION(wsi)) {
+ /* we know then that it has an h2 parent */
+ struct lws *enc = role_ops_h2.encapsulation_parent(wsi);
+
+ assert(enc);
+ if (enc->role_ops->callback_on_writable(wsi))
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+static int
+rops_init_vhost_ws(struct lws_vhost *vh,
+ const struct lws_context_creation_info *info)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+#ifdef LWS_WITH_PLUGINS
+ struct lws_plugin *plugin = vh->context->plugin_list;
+ int m;
+
+ if (vh->context->plugin_extension_count) {
+
+ m = 0;
+ while (info->extensions && info->extensions[m].callback)
+ m++;
+
+ /*
+ * give the vhost a unified list of extensions including the
+ * ones that came from plugins
+ */
+ vh->ws.extensions = lws_zalloc(sizeof(struct lws_extension) *
+ (m + vh->context->plugin_extension_count + 1),
+ "extensions");
+ if (!vh->ws.extensions)
+ return 1;
+
+ memcpy((struct lws_extension *)vh->ws.extensions, info->extensions,
+ sizeof(struct lws_extension) * m);
+ plugin = vh->context->plugin_list;
+ while (plugin) {
+ memcpy((struct lws_extension *)&vh->ws.extensions[m],
+ plugin->caps.extensions,
+ sizeof(struct lws_extension) *
+ plugin->caps.count_extensions);
+ m += plugin->caps.count_extensions;
+ plugin = plugin->list;
+ }
+ } else
+#endif
+ vh->ws.extensions = info->extensions;
+#endif
+
+ return 0;
+}
+
+static int
+rops_destroy_vhost_ws(struct lws_vhost *vh)
+{
+#ifdef LWS_WITH_PLUGINS
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (vh->context->plugin_extension_count)
+ lws_free((void *)vh->ws.extensions);
+#endif
+#endif
+
+ return 0;
+}
+
+static int
+rops_destroy_role_ws(struct lws *wsi)
+{
+ lws_free_set_NULL(wsi->ws);
+
+ return 0;
+}
+
+struct lws_role_ops role_ops_ws = {
+ /* role name */ "ws",
+ /* alpn id */ NULL,
+ /* check_upgrades */ NULL,
+ /* init_context */ NULL,
+ /* init_vhost */ rops_init_vhost_ws,
+ /* destroy_vhost */ rops_destroy_vhost_ws,
+ /* periodic_checks */ rops_periodic_checks_ws,
+ /* service_flag_pending */ rops_service_flag_pending_ws,
+ /* handle_POLLIN */ rops_handle_POLLIN_ws,
+ /* handle_POLLOUT */ rops_handle_POLLOUT_ws,
+ /* perform_user_POLLOUT */ NULL,
+ /* callback_on_writable */ rops_callback_on_writable_ws,
+ /* tx_credit */ NULL,
+ /* write_role_protocol */ rops_write_role_protocol_ws,
+ /* encapsulation_parent */ NULL,
+ /* alpn_negotiated */ NULL,
+ /* close_via_role_protocol */ rops_close_via_role_protocol_ws,
+ /* close_role */ rops_close_role_ws,
+ /* close_kill_connection */ rops_close_kill_connection_ws,
+ /* destroy_role */ rops_destroy_role_ws,
+ /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_WRITEABLE,
+ LWS_CALLBACK_SERVER_WRITEABLE },
+ /* close cb clnt, srv */ { LWS_CALLBACK_CLIENT_CLOSED,
+ LWS_CALLBACK_CLOSED },
+ /* file handles */ 0
+};
diff --git a/thirdparty/libwebsockets/roles/ws/private.h b/thirdparty/libwebsockets/roles/ws/private.h
new file mode 100644
index 0000000000..71ffcaea96
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/ws/private.h
@@ -0,0 +1,164 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * This is included from core/private.h if LWS_ROLE_WS
+ */
+
+extern struct lws_role_ops role_ops_ws;
+
+#define lwsi_role_ws(wsi) (wsi->role_ops == &role_ops_ws)
+
+enum lws_rx_parse_state {
+ LWS_RXPS_NEW,
+
+ LWS_RXPS_04_mask_1,
+ LWS_RXPS_04_mask_2,
+ LWS_RXPS_04_mask_3,
+
+ LWS_RXPS_04_FRAME_HDR_1,
+ LWS_RXPS_04_FRAME_HDR_LEN,
+ LWS_RXPS_04_FRAME_HDR_LEN16_2,
+ LWS_RXPS_04_FRAME_HDR_LEN16_1,
+ LWS_RXPS_04_FRAME_HDR_LEN64_8,
+ LWS_RXPS_04_FRAME_HDR_LEN64_7,
+ LWS_RXPS_04_FRAME_HDR_LEN64_6,
+ LWS_RXPS_04_FRAME_HDR_LEN64_5,
+ LWS_RXPS_04_FRAME_HDR_LEN64_4,
+ LWS_RXPS_04_FRAME_HDR_LEN64_3,
+ LWS_RXPS_04_FRAME_HDR_LEN64_2,
+ LWS_RXPS_04_FRAME_HDR_LEN64_1,
+
+ LWS_RXPS_07_COLLECT_FRAME_KEY_1,
+ LWS_RXPS_07_COLLECT_FRAME_KEY_2,
+ LWS_RXPS_07_COLLECT_FRAME_KEY_3,
+ LWS_RXPS_07_COLLECT_FRAME_KEY_4,
+
+ LWS_RXPS_WS_FRAME_PAYLOAD
+};
+
+enum lws_websocket_opcodes_07 {
+ LWSWSOPC_CONTINUATION = 0,
+ LWSWSOPC_TEXT_FRAME = 1,
+ LWSWSOPC_BINARY_FRAME = 2,
+
+ LWSWSOPC_NOSPEC__MUX = 7,
+
+ /* control extensions 8+ */
+
+ LWSWSOPC_CLOSE = 8,
+ LWSWSOPC_PING = 9,
+ LWSWSOPC_PONG = 0xa,
+};
+
+/* this is not usable directly by user code any more, lws_close_reason() */
+#define LWS_WRITE_CLOSE 4
+
+#define ALREADY_PROCESSED_IGNORE_CHAR 1
+#define ALREADY_PROCESSED_NO_CB 2
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+struct lws_vhost_role_ws {
+ const struct lws_extension *extensions;
+};
+
+struct lws_pt_role_ws {
+ struct lws *rx_draining_ext_list;
+ struct lws *tx_draining_ext_list;
+};
+#endif
+
+struct _lws_websocket_related {
+ char *rx_ubuf;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];
+ void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];
+ struct lws *rx_draining_ext_list;
+ struct lws *tx_draining_ext_list;
+#endif
+ /* Also used for close content... control opcode == < 128 */
+ uint8_t ping_payload_buf[128 - 3 + LWS_PRE];
+ uint8_t mask[4];
+
+ time_t time_next_ping_check;
+ size_t rx_packet_length;
+ uint32_t rx_ubuf_head;
+ uint32_t rx_ubuf_alloc;
+
+ uint8_t ping_payload_len;
+ uint8_t mask_idx;
+ uint8_t opcode;
+ uint8_t rsv;
+ uint8_t rsv_first_msg;
+ /* zero if no info, or length including 2-byte close code */
+ uint8_t close_in_ping_buffer_len;
+ uint8_t utf8;
+ uint8_t stashed_write_type;
+ uint8_t tx_draining_stashed_wp;
+ uint8_t ietf_spec_revision;
+
+ unsigned int final:1;
+ unsigned int frame_is_binary:1;
+ unsigned int all_zero_nonce:1;
+ unsigned int this_frame_masked:1;
+ unsigned int inside_frame:1; /* next write will be more of frame */
+ unsigned int clean_buffer:1; /* buffer not rewritten by extension */
+ unsigned int payload_is_close:1; /* process as PONG, but it is close */
+ unsigned int ping_pending_flag:1;
+ unsigned int continuation_possible:1;
+ unsigned int owed_a_fin:1;
+ unsigned int check_utf8:1;
+ unsigned int defeat_check_utf8:1;
+ unsigned int stashed_write_pending:1;
+ unsigned int send_check_ping:1;
+ unsigned int first_fragment:1;
+ unsigned int peer_has_sent_close:1;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ unsigned int extension_data_pending:1;
+ unsigned int rx_draining_ext:1;
+ unsigned int tx_draining_ext:1;
+
+ uint8_t count_act_ext;
+#endif
+};
+
+int
+lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len);
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+LWS_VISIBLE void
+lws_context_init_extensions(const struct lws_context_creation_info *info,
+ struct lws_context *context);
+LWS_EXTERN int
+lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
+ void *v, size_t len);
+
+LWS_EXTERN int
+lws_ext_cb_active(struct lws *wsi, int reason, void *buf, int len);
+LWS_EXTERN int
+lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, int reason,
+ void *arg, int len);
+#endif
+
+int
+handshake_0405(struct lws_context *context, struct lws *wsi);
+int
+lws_process_ws_upgrade(struct lws *wsi);
+int
+lws_server_init_wsi_for_ws(struct lws *wsi);
diff --git a/thirdparty/libwebsockets/roles/ws/server-ws.c b/thirdparty/libwebsockets/roles/ws/server-ws.c
new file mode 100644
index 0000000000..62bcd8524f
--- /dev/null
+++ b/thirdparty/libwebsockets/roles/ws/server-ws.c
@@ -0,0 +1,836 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <core/private.h>
+
+#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+static int
+lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
+{
+ struct lws_context *context = wsi->context;
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ char ext_name[64], *args, *end = (*p) + budget - 1;
+ const struct lws_ext_options *opts, *po;
+ const struct lws_extension *ext;
+ struct lws_ext_option_arg oa;
+ int n, m, more = 1;
+ int ext_count = 0;
+ char ignore;
+ char *c;
+
+ /*
+ * Figure out which extensions the client has that we want to
+ * enable on this connection, and give him back the list
+ */
+ if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS))
+ return 0;
+
+ /*
+ * break down the list of client extensions
+ * and go through them
+ */
+
+ if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size,
+ WSI_TOKEN_EXTENSIONS) < 0)
+ return 1;
+
+ c = (char *)pt->serv_buf;
+ lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
+ wsi->ws->count_act_ext = 0;
+ ignore = 0;
+ n = 0;
+ args = NULL;
+
+ /*
+ * We may get a simple request
+ *
+ * Sec-WebSocket-Extensions: permessage-deflate
+ *
+ * or an elaborated one with requested options
+ *
+ * Sec-WebSocket-Extensions: permessage-deflate; \
+ * server_no_context_takeover; \
+ * client_no_context_takeover
+ */
+
+ while (more) {
+
+ if (c >= (char *)pt->serv_buf + 255)
+ return -1;
+
+ if (*c && (*c != ',' && *c != '\t')) {
+ if (*c == ';') {
+ ignore = 1;
+ if (!args)
+ args = c + 1;
+ }
+ if (ignore || *c == ' ') {
+ c++;
+ continue;
+ }
+ ext_name[n] = *c++;
+ if (n < (int)sizeof(ext_name) - 1)
+ n++;
+ continue;
+ }
+ ext_name[n] = '\0';
+
+ ignore = 0;
+ if (!*c)
+ more = 0;
+ else {
+ c++;
+ if (!n)
+ continue;
+ }
+
+ while (args && *args && *args == ' ')
+ args++;
+
+ /* check a client's extension against our support */
+
+ ext = wsi->vhost->ws.extensions;
+
+ while (ext && ext->callback) {
+
+ if (strcmp(ext_name, ext->name)) {
+ ext++;
+ continue;
+ }
+
+ /*
+ * oh, we do support this one he asked for... but let's
+ * confirm he only gave it once
+ */
+ for (m = 0; m < wsi->ws->count_act_ext; m++)
+ if (wsi->ws->active_extensions[m] == ext) {
+ lwsl_info("extension mentioned twice\n");
+ return 1; /* shenanigans */
+ }
+
+ /*
+ * ask user code if it's OK to apply it on this
+ * particular connection + protocol
+ */
+ m = (wsi->protocol->callback)(wsi,
+ LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
+ wsi->user_space, ext_name, 0);
+
+ /*
+ * zero return from callback means go ahead and allow
+ * the extension, it's what we get if the callback is
+ * unhandled
+ */
+ if (m) {
+ ext++;
+ continue;
+ }
+
+ /* apply it */
+
+ ext_count++;
+
+ /* instantiate the extension on this conn */
+
+ wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext;
+
+ /* allow him to construct his context */
+
+ if (ext->callback(lws_get_context(wsi), ext, wsi,
+ LWS_EXT_CB_CONSTRUCT,
+ (void *)&wsi->ws->act_ext_user[
+ wsi->ws->count_act_ext],
+ (void *)&opts, 0)) {
+ lwsl_info("ext %s failed construction\n",
+ ext_name);
+ ext_count--;
+ ext++;
+
+ continue;
+ }
+
+ if (ext_count > 1)
+ *(*p)++ = ',';
+ else
+ LWS_CPYAPP(*p,
+ "\x0d\x0aSec-WebSocket-Extensions: ");
+ *p += lws_snprintf(*p, (end - *p), "%s", ext_name);
+
+ /*
+ * The client may send a bunch of different option
+ * sets for the same extension, we are supposed to
+ * pick one we like the look of. The option sets are
+ * separated by comma.
+ *
+ * Actually we just either accept the first one or
+ * nothing.
+ *
+ * Go through the options trying to apply the
+ * recognized ones
+ */
+
+ lwsl_info("ext args %s\n", args);
+
+ while (args && *args && *args != ',') {
+ while (*args == ' ')
+ args++;
+ po = opts;
+ while (po->name) {
+ /* only support arg-less options... */
+ if (po->type != EXTARG_NONE ||
+ strncmp(args, po->name,
+ strlen(po->name))) {
+ po++;
+ continue;
+ }
+ oa.option_name = NULL;
+ oa.option_index = (int)(po - opts);
+ oa.start = NULL;
+ oa.len = 0;
+ lwsl_info("setting '%s'\n", po->name);
+ if (!ext->callback(lws_get_context(wsi),
+ ext, wsi,
+ LWS_EXT_CB_OPTION_SET,
+ wsi->ws->act_ext_user[
+ wsi->ws->count_act_ext],
+ &oa, (end - *p))) {
+
+ *p += lws_snprintf(*p, (end - *p),
+ "; %s", po->name);
+ lwsl_debug("adding option %s\n",
+ po->name);
+ }
+ po++;
+ }
+ while (*args && *args != ',' && *args != ';')
+ args++;
+
+ if (*args == ';')
+ args++;
+ }
+
+ wsi->ws->count_act_ext++;
+ lwsl_parser("cnt_act_ext <- %d\n", wsi->ws->count_act_ext);
+
+ if (args && *args == ',')
+ more = 0;
+
+ ext++;
+ }
+
+ n = 0;
+ args = NULL;
+ }
+
+ return 0;
+}
+#endif
+
+
+
+int
+lws_process_ws_upgrade(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ char protocol_list[128], protocol_name[64], *p;
+ int protocol_len, hit, n = 0, non_space_char_found = 0;
+
+ if (!wsi->protocol)
+ lwsl_err("NULL protocol at lws_read\n");
+
+ /*
+ * It's either websocket or h2->websocket
+ *
+ * Select the first protocol we support from the list
+ * the client sent us.
+ *
+ * Copy it to remove header fragmentation
+ */
+
+ if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
+ WSI_TOKEN_PROTOCOL) < 0) {
+ lwsl_err("protocol list too long");
+ return 1;
+ }
+
+ protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
+ protocol_list[protocol_len] = '\0';
+ p = protocol_list;
+ hit = 0;
+
+ while (*p && !hit) {
+ n = 0;
+ non_space_char_found = 0;
+ while (n < (int)sizeof(protocol_name) - 1 &&
+ *p && *p != ',') {
+ /* ignore leading spaces */
+ if (!non_space_char_found && *p == ' ') {
+ n++;
+ continue;
+ }
+ non_space_char_found = 1;
+ protocol_name[n++] = *p++;
+ }
+ protocol_name[n] = '\0';
+ if (*p)
+ p++;
+
+ lwsl_debug("checking %s\n", protocol_name);
+
+ n = 0;
+ while (wsi->vhost->protocols[n].callback) {
+ lwsl_debug("try %s\n",
+ wsi->vhost->protocols[n].name);
+
+ if (wsi->vhost->protocols[n].name &&
+ !strcmp(wsi->vhost->protocols[n].name,
+ protocol_name)) {
+ wsi->protocol = &wsi->vhost->protocols[n];
+ hit = 1;
+ break;
+ }
+
+ n++;
+ }
+ }
+
+ /* we didn't find a protocol he wanted? */
+
+ if (!hit) {
+ if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
+ lwsl_notice("No protocol from \"%s\" supported\n",
+ protocol_list);
+ return 1;
+ }
+ /*
+ * some clients only have one protocol and
+ * do not send the protocol list header...
+ * allow it and match to the vhost's default
+ * protocol (which itself defaults to zero)
+ */
+ lwsl_info("defaulting to prot handler %d\n",
+ wsi->vhost->default_protocol_index);
+ n = wsi->vhost->default_protocol_index;
+ wsi->protocol = &wsi->vhost->protocols[
+ (int)wsi->vhost->default_protocol_index];
+ }
+
+ /* allocate the ws struct for the wsi */
+ wsi->ws = lws_zalloc(sizeof(*wsi->ws), "ws struct");
+ if (!wsi->ws) {
+ lwsl_notice("OOM\n");
+ return 1;
+ }
+
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
+ wsi->ws->ietf_spec_revision =
+ atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
+
+ /* allocate wsi->user storage */
+ if (lws_ensure_user_space(wsi)) {
+ lwsl_notice("problem with user space\n");
+ return 1;
+ }
+
+ /*
+ * Give the user code a chance to study the request and
+ * have the opportunity to deny it
+ */
+ if ((wsi->protocol->callback)(wsi,
+ LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
+ wsi->user_space,
+ lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
+ lwsl_warn("User code denied connection\n");
+ return 1;
+ }
+
+ /*
+ * Perform the handshake according to the protocol version the
+ * client announced
+ */
+
+ switch (wsi->ws->ietf_spec_revision) {
+ default:
+ lwsl_notice("Unknown client spec version %d\n",
+ wsi->ws->ietf_spec_revision);
+ wsi->ws->ietf_spec_revision = 13;
+ //return 1;
+ /* fallthru */
+ case 13:
+#if defined(LWS_WITH_HTTP2)
+ if (wsi->h2_stream_carries_ws) {
+ if (lws_h2_ws_handshake(wsi)) {
+ lwsl_notice("h2 ws handshake failed\n");
+ return 1;
+ }
+ } else
+#endif
+ {
+ lwsl_parser("lws_parse calling handshake_04\n");
+ if (handshake_0405(wsi->context, wsi)) {
+ lwsl_notice("hs0405 has failed the connection\n");
+ return 1;
+ }
+ }
+ break;
+ }
+
+ lws_same_vh_protocol_insert(wsi, n);
+
+ /*
+ * We are upgrading to ws, so http/1.1 + h2 and keepalive + pipelined
+ * header considerations about keeping the ah around no longer apply.
+ *
+ * However it's common for the first ws protocol data to have been
+ * coalesced with the browser upgrade request and to already be in the
+ * ah rx buffer.
+ */
+
+ lws_pt_lock(pt, __func__);
+
+ if (wsi->h2_stream_carries_ws)
+ lws_role_transition(wsi, LWSIFR_SERVER | LWSIFR_P_ENCAP_H2,
+ LRS_ESTABLISHED, &role_ops_ws);
+ else
+ lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED,
+ &role_ops_ws);
+
+ lws_pt_unlock(pt);
+
+ lws_server_init_wsi_for_ws(wsi);
+ lwsl_parser("accepted v%02d connection\n", wsi->ws->ietf_spec_revision);
+
+ lwsl_info("%s: %p: dropping ah on ws upgrade\n", __func__, wsi);
+ lws_header_table_detach(wsi, 1);
+
+ return 0;
+}
+
+int
+handshake_0405(struct lws_context *context, struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ struct lws_process_html_args args;
+ unsigned char hash[20];
+ int n, accept_len;
+ char *response;
+ char *p;
+
+ if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
+ !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
+ lwsl_info("handshake_04 missing pieces\n");
+ /* completed header processing, but missing some bits */
+ goto bail;
+ }
+
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= MAX_WEBSOCKET_04_KEY_LEN) {
+ lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
+ goto bail;
+ }
+
+ /*
+ * since key length is restricted above (currently 128), cannot
+ * overflow
+ */
+ n = sprintf((char *)pt->serv_buf,
+ "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
+ lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
+
+ lws_SHA1(pt->serv_buf, n, hash);
+
+ accept_len = lws_b64_encode_string((char *)hash, 20,
+ (char *)pt->serv_buf, context->pt_serv_buf_size);
+ if (accept_len < 0) {
+ lwsl_warn("Base64 encoded hash too long\n");
+ goto bail;
+ }
+
+ /* allocate the per-connection user memory (if any) */
+ if (lws_ensure_user_space(wsi))
+ goto bail;
+
+ /* create the response packet */
+
+ /* make a buffer big enough for everything */
+
+ response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + 256 + LWS_PRE;
+ p = response;
+ LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
+ "Upgrade: WebSocket\x0d\x0a"
+ "Connection: Upgrade\x0d\x0a"
+ "Sec-WebSocket-Accept: ");
+ strcpy(p, (char *)pt->serv_buf);
+ p += accept_len;
+
+ /* we can only return the protocol header if:
+ * - one came in, and ... */
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
+ /* - it is not an empty string */
+ wsi->protocol->name &&
+ wsi->protocol->name[0]) {
+ LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
+ p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
+ }
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ /*
+ * Figure out which extensions the client has that we want to
+ * enable on this connection, and give him back the list.
+ *
+ * Give him a limited write bugdet
+ */
+ if (lws_extension_server_handshake(wsi, &p, 192))
+ goto bail;
+#endif
+ LWS_CPYAPP(p, "\x0d\x0a");
+
+ args.p = p;
+ args.max_len = lws_ptr_diff((char *)pt->serv_buf +
+ context->pt_serv_buf_size, p);
+ if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
+ LWS_CALLBACK_ADD_HEADERS,
+ wsi->user_space, &args, 0))
+ goto bail;
+
+ p = args.p;
+
+ /* end of response packet */
+
+ LWS_CPYAPP(p, "\x0d\x0a");
+
+ /* okay send the handshake response accepting the connection */
+
+ lwsl_parser("issuing resp pkt %d len\n",
+ lws_ptr_diff(p, response));
+#if defined(DEBUG)
+ fwrite(response, 1, p - response, stderr);
+#endif
+ n = lws_write(wsi, (unsigned char *)response, p - response,
+ LWS_WRITE_HTTP_HEADERS);
+ if (n != (p - response)) {
+ lwsl_info("%s: ERROR writing to socket %d\n", __func__, n);
+ goto bail;
+ }
+
+ /* alright clean up and set ourselves into established state */
+
+ lwsi_set_state(wsi, LRS_ESTABLISHED);
+ wsi->lws_rx_parse_state = LWS_RXPS_NEW;
+
+ {
+ const char * uri_ptr =
+ lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
+ int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
+ const struct lws_http_mount *hit =
+ lws_find_mount(wsi, uri_ptr, uri_len);
+ if (hit && hit->cgienv &&
+ wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
+ wsi->user_space, (void *)hit->cgienv, 0))
+ return 1;
+ }
+
+ return 0;
+
+bail:
+ /* caller will free up his parsing allocations */
+ return -1;
+}
+
+
+
+/*
+ * Once we reach LWS_RXPS_WS_FRAME_PAYLOAD, we know how much
+ * to expect in that state and can deal with it in bulk more efficiently.
+ */
+
+static int
+lws_ws_frame_rest_is_payload(struct lws *wsi, uint8_t **buf, size_t len)
+{
+ uint8_t *buffer = *buf, mask[4];
+ struct lws_tokens ebuf;
+ unsigned int avail = (unsigned int)len;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ unsigned int old_packet_length = (int)wsi->ws->rx_packet_length;
+#endif
+ int n = 0;
+
+ /*
+ * With zlib, we can give it as much input as we like. The pmd
+ * extension will draw it down in chunks (default 1024).
+ *
+ * If we try to restrict how much we give it, because we must go
+ * back to the event loop each time, we will drop the remainder...
+ */
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (!wsi->ws->count_act_ext)
+#endif
+ {
+ if (wsi->protocol->rx_buffer_size)
+ avail = (int)wsi->protocol->rx_buffer_size;
+ else
+ avail = wsi->context->pt_serv_buf_size;
+ }
+
+ /* do not consume more than we should */
+ if (avail > wsi->ws->rx_packet_length)
+ avail = (unsigned int)wsi->ws->rx_packet_length;
+
+ /* do not consume more than what is in the buffer */
+ if (avail > len)
+ avail = (unsigned int)len;
+
+ if (avail <= 0)
+ return 0;
+
+ ebuf.token = (char *)buffer;
+ ebuf.len = avail;
+
+ //lwsl_hexdump_notice(ebuf.token, ebuf.len);
+
+ if (!wsi->ws->all_zero_nonce) {
+
+ for (n = 0; n < 4; n++)
+ mask[n] = wsi->ws->mask[(wsi->ws->mask_idx + n) & 3];
+
+ /* deal with 4-byte chunks using unwrapped loop */
+ n = avail >> 2;
+ while (n--) {
+ *(buffer) = *(buffer) ^ mask[0];
+ buffer++;
+ *(buffer) = *(buffer) ^ mask[1];
+ buffer++;
+ *(buffer) = *(buffer) ^ mask[2];
+ buffer++;
+ *(buffer) = *(buffer) ^ mask[3];
+ buffer++;
+ }
+ /* and the remaining bytes bytewise */
+ for (n = 0; n < (int)(avail & 3); n++) {
+ *(buffer) = *(buffer) ^ mask[n];
+ buffer++;
+ }
+
+ wsi->ws->mask_idx = (wsi->ws->mask_idx + avail) & 3;
+ }
+
+ lwsl_info("%s: using %d of raw input (total %d on offer)\n", __func__,
+ avail, (int)len);
+
+ (*buf) += avail;
+ len -= avail;
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
+ lwsl_info("%s: ext says %d / ebuf.len %d\n", __func__, n, ebuf.len);
+#endif
+ /*
+ * ebuf may be pointing somewhere completely different now,
+ * it's the output
+ */
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (n < 0) {
+ /*
+ * we may rely on this to get RX, just drop connection
+ */
+ lwsl_notice("%s: LWS_EXT_CB_PAYLOAD_RX blew out\n", __func__);
+ wsi->socket_is_permanently_unusable = 1;
+ return -1;
+ }
+#endif
+
+ wsi->ws->rx_packet_length -= avail;
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ /*
+ * if we had an rx fragment right at the last compressed byte of the
+ * message, we can get a zero length inflated output, where no prior
+ * rx inflated output marked themselves with FIN, since there was
+ * raw ws payload still to drain at that time.
+ *
+ * Then we need to generate a zero length ws rx that can be understood
+ * as the message completion.
+ */
+
+ if (!ebuf.len && /* zero-length inflation output */
+ !n && /* nothing left to drain from the inflator */
+ wsi->ws->count_act_ext && /* we are using pmd */
+ old_packet_length && /* we gave the inflator new input */
+ !wsi->ws->rx_packet_length && /* raw ws packet payload all gone */
+ wsi->ws->final && /* the raw ws packet is a FIN guy */
+ wsi->protocol->callback &&
+ !wsi->wsistate_pre_close) {
+
+ if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
+ LWS_CALLBACK_RECEIVE,
+ wsi->user_space, NULL, 0))
+ return -1;
+
+ return avail;
+ }
+#endif
+
+ if (!ebuf.len)
+ return avail;
+
+ if (
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ n &&
+#endif
+ ebuf.len)
+ /* extension had more... main loop will come back */
+ lws_add_wsi_to_draining_ext_list(wsi);
+ else
+ lws_remove_wsi_from_draining_ext_list(wsi);
+
+ if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
+ if (lws_check_utf8(&wsi->ws->utf8,
+ (unsigned char *)ebuf.token, ebuf.len)) {
+ lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+ (uint8_t *)"bad utf8", 8);
+ goto utf8_fail;
+ }
+
+ /* we are ending partway through utf-8 character? */
+ if (!wsi->ws->rx_packet_length && wsi->ws->final &&
+ wsi->ws->utf8 && !n) {
+ lwsl_info("FINAL utf8 error\n");
+ lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+ (uint8_t *)"partial utf8", 12);
+
+utf8_fail:
+ lwsl_info("utf8 error\n");
+ lwsl_hexdump_info(ebuf.token, ebuf.len);
+
+ return -1;
+ }
+ }
+
+ if (wsi->protocol->callback && !wsi->wsistate_pre_close)
+ if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
+ LWS_CALLBACK_RECEIVE,
+ wsi->user_space,
+ ebuf.token, ebuf.len))
+ return -1;
+
+ wsi->ws->first_fragment = 0;
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ lwsl_info("%s: input used %d, output %d, rem len %d, rx_draining_ext %d\n",
+ __func__, avail, ebuf.len, (int)len, wsi->ws->rx_draining_ext);
+#endif
+
+ return avail; /* how much we used from the input */
+}
+
+
+int
+lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
+{
+ int m, bulk = 0;
+
+ lwsl_debug("%s: received %d byte packet\n", __func__, (int)len);
+
+ //lwsl_hexdump_notice(*buf, len);
+
+ /* let the rx protocol state machine have as much as it needs */
+
+ while (len) {
+ /*
+ * we were accepting input but now we stopped doing so
+ */
+ if (wsi->rxflow_bitmap) {
+ lwsl_info("%s: doing rxflow\n", __func__);
+ lws_rxflow_cache(wsi, *buf, 0, (int)len);
+ lwsl_parser("%s: cached %ld\n", __func__, (long)len);
+ *buf += len; /* stashing it is taking care of it */
+ return 1;
+ }
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ if (wsi->ws->rx_draining_ext) {
+ lwsl_debug("%s: draining rx ext\n", __func__);
+ m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
+ if (m < 0)
+ return -1;
+ continue;
+ }
+#endif
+
+ /* consume payload bytes efficiently */
+ while (wsi->lws_rx_parse_state == LWS_RXPS_WS_FRAME_PAYLOAD &&
+ (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
+ wsi->ws->opcode == LWSWSOPC_BINARY_FRAME ||
+ wsi->ws->opcode == LWSWSOPC_CONTINUATION) &&
+ len) {
+ uint8_t *bin = *buf;
+
+ bulk = 1;
+ m = lws_ws_frame_rest_is_payload(wsi, buf, len);
+ assert((int)lws_ptr_diff(*buf, bin) <= (int)len);
+ len -= lws_ptr_diff(*buf, bin);
+
+ if (!m) {
+
+ break;
+ }
+ if (m < 0) {
+ lwsl_info("%s: rest_is_payload bailed\n",
+ __func__);
+ return -1;
+ }
+ }
+
+ if (!bulk) {
+ /* process the byte */
+ m = lws_ws_rx_sm(wsi, 0, *(*buf)++);
+ len--;
+ } else {
+ /*
+ * We already handled this byte in bulk, just deal
+ * with the ramifications
+ */
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+ lwsl_debug("%s: coming out of bulk with len %d, "
+ "wsi->ws->rx_draining_ext %d\n",
+ __func__, (int)len,
+ wsi->ws->rx_draining_ext);
+#endif
+ m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR |
+ ALREADY_PROCESSED_NO_CB, 0);
+ }
+
+ if (m < 0) {
+ lwsl_info("%s: lws_ws_rx_sm bailed %d\n", __func__,
+ bulk);
+
+ return -1;
+ }
+
+ bulk = 0;
+ }
+
+ lwsl_debug("%s: exit with %d unused\n", __func__, (int)len);
+
+ return 0;
+}
diff --git a/thirdparty/libwebsockets/tls/mbedtls/lws-genhash.c b/thirdparty/libwebsockets/tls/mbedtls/lws-genhash.c
new file mode 100644
index 0000000000..ce4ee6e382
--- /dev/null
+++ b/thirdparty/libwebsockets/tls/mbedtls/lws-genhash.c
@@ -0,0 +1,202 @@
+/*
+ * libwebsockets - generic hash and HMAC api hiding the backend
+ *
+ * Copyright (C) 2017 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * lws_genhash provides a hash / hmac abstraction api in lws that works the
+ * same whether you are using openssl or mbedtls hash functions underneath.
+ */
+#include "libwebsockets.h"
+#include <mbedtls/version.h>
+
+#if (MBEDTLS_VERSION_NUMBER >= 0x02070000)
+#define MBA(fn) fn##_ret
+#else
+#define MBA(fn) fn
+#endif
+
+size_t
+lws_genhash_size(enum lws_genhash_types type)
+{
+ switch(type) {
+ case LWS_GENHASH_TYPE_SHA1:
+ return 20;
+ case LWS_GENHASH_TYPE_SHA256:
+ return 32;
+ case LWS_GENHASH_TYPE_SHA384:
+ return 48;
+ case LWS_GENHASH_TYPE_SHA512:
+ return 64;
+ }
+
+ return 0;
+}
+
+int
+lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type)
+{
+ ctx->type = type;
+
+ switch (ctx->type) {
+ case LWS_GENHASH_TYPE_SHA1:
+ mbedtls_sha1_init(&ctx->u.sha1);
+ MBA(mbedtls_sha1_starts)(&ctx->u.sha1);
+ break;
+ case LWS_GENHASH_TYPE_SHA256:
+ mbedtls_sha256_init(&ctx->u.sha256);
+ MBA(mbedtls_sha256_starts)(&ctx->u.sha256, 0);
+ break;
+ case LWS_GENHASH_TYPE_SHA384:
+ mbedtls_sha512_init(&ctx->u.sha512);
+ MBA(mbedtls_sha512_starts)(&ctx->u.sha512, 1 /* is384 */);
+ break;
+ case LWS_GENHASH_TYPE_SHA512:
+ mbedtls_sha512_init(&ctx->u.sha512);
+ MBA(mbedtls_sha512_starts)(&ctx->u.sha512, 0);
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len)
+{
+ switch (ctx->type) {
+ case LWS_GENHASH_TYPE_SHA1:
+ MBA(mbedtls_sha1_update)(&ctx->u.sha1, in, len);
+ break;
+ case LWS_GENHASH_TYPE_SHA256:
+ MBA(mbedtls_sha256_update)(&ctx->u.sha256, in, len);
+ break;
+ case LWS_GENHASH_TYPE_SHA384:
+ MBA(mbedtls_sha512_update)(&ctx->u.sha512, in, len);
+ break;
+ case LWS_GENHASH_TYPE_SHA512:
+ MBA(mbedtls_sha512_update)(&ctx->u.sha512, in, len);
+ break;
+ }
+
+ return 0;
+}
+
+int
+lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result)
+{
+ switch (ctx->type) {
+ case LWS_GENHASH_TYPE_SHA1:
+ MBA(mbedtls_sha1_finish)(&ctx->u.sha1, result);
+ mbedtls_sha1_free(&ctx->u.sha1);
+ break;
+ case LWS_GENHASH_TYPE_SHA256:
+ MBA(mbedtls_sha256_finish)(&ctx->u.sha256, result);
+ mbedtls_sha256_free(&ctx->u.sha256);
+ break;
+ case LWS_GENHASH_TYPE_SHA384:
+ MBA(mbedtls_sha512_finish)(&ctx->u.sha512, result);
+ mbedtls_sha512_free(&ctx->u.sha512);
+ break;
+ case LWS_GENHASH_TYPE_SHA512:
+ MBA(mbedtls_sha512_finish)(&ctx->u.sha512, result);
+ mbedtls_sha512_free(&ctx->u.sha512);
+ break;
+ }
+
+ return 0;
+}
+
+size_t
+lws_genhmac_size(enum lws_genhmac_types type)
+{
+ switch(type) {
+ case LWS_GENHMAC_TYPE_SHA256:
+ return 32;
+ case LWS_GENHMAC_TYPE_SHA384:
+ return 48;
+ case LWS_GENHMAC_TYPE_SHA512:
+ return 64;
+ }
+
+ return 0;
+}
+
+int
+lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
+ const uint8_t *key, size_t key_len)
+{
+ int t;
+
+ ctx->type = type;
+
+ switch (type) {
+ case LWS_GENHMAC_TYPE_SHA256:
+ t = MBEDTLS_MD_SHA256;
+ break;
+ case LWS_GENHMAC_TYPE_SHA384:
+ t = MBEDTLS_MD_SHA384;
+ break;
+ case LWS_GENHMAC_TYPE_SHA512:
+ t = MBEDTLS_MD_SHA512;
+ break;
+ default:
+ return -1;
+ }
+
+ ctx->hmac = mbedtls_md_info_from_type(t);
+ if (!ctx->hmac)
+ return -1;
+
+ if (mbedtls_md_init_ctx(&ctx->ctx, ctx->hmac))
+ return -1;
+
+ if (mbedtls_md_hmac_starts(&ctx->ctx, key, key_len)) {
+ mbedtls_md_free(&ctx->ctx);
+ ctx->hmac = NULL;
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len)
+{
+ if (mbedtls_md_hmac_update(&ctx->ctx, in, len))
+ return -1;
+
+ return 0;
+}
+
+int
+lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result)
+{
+ int n = 0;
+
+ if (result)
+ n = mbedtls_md_hmac_finish(&ctx->ctx, result);
+
+ mbedtls_md_free(&ctx->ctx);
+ ctx->hmac = NULL;
+ if (n)
+ return -1;
+
+ return 0;
+}
diff --git a/thirdparty/libwebsockets/tls/mbedtls/lws-genrsa.c b/thirdparty/libwebsockets/tls/mbedtls/lws-genrsa.c
new file mode 100644
index 0000000000..70a9fcf42c
--- /dev/null
+++ b/thirdparty/libwebsockets/tls/mbedtls/lws-genrsa.c
@@ -0,0 +1,329 @@
+/*
+ * libwebsockets - generic RSA api hiding the backend
+ *
+ * Copyright (C) 2017 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * lws_genhash provides a hash / hmac abstraction api in lws that works the
+ * same whether you are using openssl or mbedtls hash functions underneath.
+ */
+#include "core/private.h"
+
+LWS_VISIBLE void
+lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el)
+{
+ int n;
+
+ for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
+ if (el->e[n].buf)
+ lws_free_set_NULL(el->e[n].buf);
+}
+
+LWS_VISIBLE int
+lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el)
+{
+ int n;
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa");
+ if (!ctx->ctx)
+ return 1;
+
+ mbedtls_rsa_init(ctx->ctx, MBEDTLS_RSA_PKCS_V15, 0);
+
+ {
+ mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
+ &ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P,
+ &ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
+ &ctx->ctx->QP,
+ };
+
+ for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
+ if (el->e[n].buf &&
+ mbedtls_mpi_read_binary(mpi[n], el->e[n].buf,
+ el->e[n].len)) {
+ lwsl_notice("mpi load failed\n");
+ lws_free_set_NULL(ctx->ctx);
+
+ return -1;
+ }
+ }
+
+ ctx->ctx->len = el->e[JWK_KEY_N].len;
+
+ return 0;
+}
+
+static int
+_rngf(void *context, unsigned char *buf, size_t len)
+{
+ if ((size_t)lws_get_random(context, buf, len) == len)
+ return 0;
+
+ return -1;
+}
+
+LWS_VISIBLE int
+lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
+ struct lws_genrsa_elements *el, int bits)
+{
+ int n;
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa");
+ if (!ctx->ctx)
+ return -1;
+
+ mbedtls_rsa_init(ctx->ctx, MBEDTLS_RSA_PKCS_V15, 0);
+
+ n = mbedtls_rsa_gen_key(ctx->ctx, _rngf, context, bits, 65537);
+ if (n) {
+ lwsl_err("mbedtls_rsa_gen_key failed 0x%x\n", -n);
+ goto cleanup_1;
+ }
+
+ {
+ mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
+ &ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P,
+ &ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
+ &ctx->ctx->QP,
+ };
+
+ for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
+ if (mbedtls_mpi_size(mpi[n])) {
+ el->e[n].buf = lws_malloc(
+ mbedtls_mpi_size(mpi[n]), "genrsakey");
+ if (!el->e[n].buf)
+ goto cleanup;
+ el->e[n].len = mbedtls_mpi_size(mpi[n]);
+ mbedtls_mpi_write_binary(mpi[n], el->e[n].buf,
+ el->e[n].len);
+ }
+ }
+
+ return 0;
+
+cleanup:
+ for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
+ if (el->e[n].buf)
+ lws_free_set_NULL(el->e[n].buf);
+cleanup_1:
+ lws_free(ctx->ctx);
+
+ return -1;
+}
+
+LWS_VISIBLE int
+lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out, size_t out_max)
+{
+ size_t olen = 0;
+ int n;
+
+ ctx->ctx->len = in_len;
+ n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, NULL, NULL,
+ MBEDTLS_RSA_PUBLIC,
+ &olen, in, out, out_max);
+ if (n) {
+ lwsl_notice("%s: -0x%x\n", __func__, -n);
+
+ return -1;
+ }
+
+ return olen;
+}
+
+LWS_VISIBLE int
+lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out)
+{
+ int n;
+
+ ctx->ctx->len = in_len;
+ n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, NULL, NULL,
+ MBEDTLS_RSA_PRIVATE,
+ in_len, in, out);
+ if (n) {
+ lwsl_notice("%s: -0x%x\n", __func__, -n);
+
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+lws_genrsa_genrsa_hash_to_mbed_hash(enum lws_genhash_types hash_type)
+{
+ int h = -1;
+
+ switch (hash_type) {
+ case LWS_GENHASH_TYPE_SHA1:
+ h = MBEDTLS_MD_SHA1;
+ break;
+ case LWS_GENHASH_TYPE_SHA256:
+ h = MBEDTLS_MD_SHA256;
+ break;
+ case LWS_GENHASH_TYPE_SHA384:
+ h = MBEDTLS_MD_SHA384;
+ break;
+ case LWS_GENHASH_TYPE_SHA512:
+ h = MBEDTLS_MD_SHA512;
+ break;
+ }
+
+ return h;
+}
+
+LWS_VISIBLE int
+lws_genrsa_public_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type, const uint8_t *sig,
+ size_t sig_len)
+{
+ int n, h = lws_genrsa_genrsa_hash_to_mbed_hash(hash_type);
+
+ if (h < 0)
+ return -1;
+
+ n = mbedtls_rsa_rsassa_pkcs1_v15_verify(ctx->ctx, NULL, NULL,
+ MBEDTLS_RSA_PUBLIC,
+ h, 0, in, sig);
+ if (n < 0) {
+ lwsl_notice("%s: -0x%x\n", __func__, -n);
+
+ return -1;
+ }
+
+ return n;
+}
+
+LWS_VISIBLE int
+lws_genrsa_public_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type, uint8_t *sig,
+ size_t sig_len)
+{
+ int n, h = lws_genrsa_genrsa_hash_to_mbed_hash(hash_type);
+
+ if (h < 0)
+ return -1;
+
+ /*
+ * The "sig" buffer must be as large as the size of ctx->N
+ * (eg. 128 bytes if RSA-1024 is used).
+ */
+ if (sig_len < ctx->ctx->len)
+ return -1;
+
+ n = mbedtls_rsa_rsassa_pkcs1_v15_sign(ctx->ctx, NULL, NULL,
+ MBEDTLS_RSA_PRIVATE, h, 0, in,
+ sig);
+ if (n < 0) {
+ lwsl_notice("%s: -0x%x\n", __func__, -n);
+
+ return -1;
+ }
+
+ return ctx->ctx->len;
+}
+
+LWS_VISIBLE int
+lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private,
+ uint8_t *pkey_asn1, size_t pkey_asn1_len)
+{
+ uint8_t *p = pkey_asn1, *totlen, *end = pkey_asn1 + pkey_asn1_len - 1;
+ mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
+ &ctx->ctx->N, &ctx->ctx->E, &ctx->ctx->D, &ctx->ctx->P,
+ &ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
+ &ctx->ctx->QP,
+ };
+ int n;
+
+ /* 30 82 - sequence
+ * 09 29 <-- length(0x0929) less 4 bytes
+ * 02 01 <- length (1)
+ * 00
+ * 02 82
+ * 02 01 <- length (513) N
+ * ...
+ *
+ * 02 03 <- length (3) E
+ * 01 00 01
+ *
+ * 02 82
+ * 02 00 <- length (512) D P Q EXP1 EXP2 COEFF
+ *
+ * */
+
+ *p++ = 0x30;
+ *p++ = 0x82;
+ totlen = p;
+ p += 2;
+
+ *p++ = 0x02;
+ *p++ = 0x01;
+ *p++ = 0x00;
+
+ for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++) {
+ int m = mbedtls_mpi_size(mpi[n]);
+ uint8_t *elen;
+
+ *p++ = 0x02;
+ elen = p;
+ if (m < 0x7f)
+ *p++ = m;
+ else {
+ *p++ = 0x82;
+ *p++ = m >> 8;
+ *p++ = m & 0xff;
+ }
+
+ if (p + m > end)
+ return -1;
+
+ mbedtls_mpi_write_binary(mpi[n], p, m);
+ if (p[0] & 0x80) {
+ p[0] = 0x00;
+ mbedtls_mpi_write_binary(mpi[n], &p[1], m);
+ m++;
+ }
+ if (m < 0x7f)
+ *elen = m;
+ else {
+ *elen++ = 0x82;
+ *elen++ = m >> 8;
+ *elen = m & 0xff;
+ }
+ p += m;
+ }
+
+ n = lws_ptr_diff(p, pkey_asn1);
+
+ *totlen++ = (n - 4) >> 8;
+ *totlen = (n - 4) & 0xff;
+
+ return n;
+}
+
+LWS_VISIBLE void
+lws_genrsa_destroy(struct lws_genrsa_ctx *ctx)
+{
+ if (!ctx->ctx)
+ return;
+ mbedtls_rsa_free(ctx->ctx);
+ lws_free(ctx->ctx);
+ ctx->ctx = NULL;
+}
diff --git a/thirdparty/libwebsockets/tls/mbedtls/mbedtls-client.c b/thirdparty/libwebsockets/tls/mbedtls/mbedtls-client.c
new file mode 100644
index 0000000000..a7864ab790
--- /dev/null
+++ b/thirdparty/libwebsockets/tls/mbedtls/mbedtls-client.c
@@ -0,0 +1,240 @@
+/*
+ * libwebsockets - mbedtls-specific client TLS code
+ *
+ * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+
+static int
+OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+ return 0;
+}
+
+int
+lws_ssl_client_bio_create(struct lws *wsi)
+{
+ X509_VERIFY_PARAM *param;
+ char hostname[128], *p;
+ const char *alpn_comma = wsi->context->tls.alpn_default;
+ struct alpn_ctx protos;
+
+ if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
+ _WSI_TOKEN_CLIENT_HOST) <= 0) {
+ lwsl_err("%s: Unable to get hostname\n", __func__);
+
+ return -1;
+ }
+
+ /*
+ * remove any :port part on the hostname... necessary for network
+ * connection but typical certificates do not contain it
+ */
+ p = hostname;
+ while (*p) {
+ if (*p == ':') {
+ *p = '\0';
+ break;
+ }
+ p++;
+ }
+
+ wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_client_ctx);
+ if (!wsi->tls.ssl)
+ return -1;
+
+ if (wsi->vhost->tls.ssl_info_event_mask)
+ SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback);
+
+ if (!(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
+ param = SSL_get0_param(wsi->tls.ssl);
+ /* Enable automatic hostname checks */
+ // X509_VERIFY_PARAM_set_hostflags(param,
+ // X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+ X509_VERIFY_PARAM_set1_host(param, hostname, 0);
+ }
+
+ if (wsi->vhost->tls.alpn)
+ alpn_comma = wsi->vhost->tls.alpn;
+
+ if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
+ _WSI_TOKEN_CLIENT_ALPN) > 0)
+ alpn_comma = hostname;
+
+ lwsl_info("%s: %p: client conn sending ALPN list '%s'\n",
+ __func__, wsi, alpn_comma);
+
+ protos.len = lws_alpn_comma_to_openssl(alpn_comma, protos.data,
+ sizeof(protos.data) - 1);
+
+ /* with mbedtls, protos is not pointed to after exit from this call */
+ SSL_set_alpn_select_cb(wsi->tls.ssl, &protos);
+
+ /*
+ * use server name indication (SNI), if supported,
+ * when establishing connection
+ */
+ SSL_set_verify(wsi->tls.ssl, SSL_VERIFY_PEER,
+ OpenSSL_client_verify_callback);
+
+ SSL_set_fd(wsi->tls.ssl, wsi->desc.sockfd);
+
+ return 0;
+}
+
+int ERR_get_error(void)
+{
+ return 0;
+}
+
+enum lws_ssl_capable_status
+lws_tls_client_connect(struct lws *wsi)
+{
+ int m, n = SSL_connect(wsi->tls.ssl);
+ const unsigned char *prot;
+ unsigned int len;
+
+ if (n == 1) {
+ SSL_get0_alpn_selected(wsi->tls.ssl, &prot, &len);
+ lws_role_call_alpn_negotiated(wsi, (const char *)prot);
+ lwsl_info("client connect OK\n");
+ return LWS_SSL_CAPABLE_DONE;
+ }
+
+ m = SSL_get_error(wsi->tls.ssl, n);
+
+ if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl))
+ return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
+
+ if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl))
+ return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
+
+ if (!n) /* we don't know what he wants, but he says to retry */
+ return LWS_SSL_CAPABLE_MORE_SERVICE;
+
+ return LWS_SSL_CAPABLE_ERROR;
+}
+
+int
+lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len)
+{
+ int n;
+ X509 *peer = SSL_get_peer_certificate(wsi->tls.ssl);
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ char *sb = (char *)&pt->serv_buf[0];
+
+ if (!peer) {
+ lwsl_info("peer did not provide cert\n");
+
+ return -1;
+ }
+ lwsl_info("peer provided cert\n");
+
+ n = SSL_get_verify_result(wsi->tls.ssl);
+ lws_latency(wsi->context, wsi,
+ "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0);
+
+ lwsl_debug("get_verify says %d\n", n);
+
+ if (n == X509_V_OK)
+ return 0;
+
+ if (n == X509_V_ERR_HOSTNAME_MISMATCH &&
+ (wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
+ lwsl_info("accepting certificate for invalid hostname\n");
+ return 0;
+ }
+
+ if (n == X509_V_ERR_INVALID_CA &&
+ (wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED)) {
+ lwsl_info("accepting certificate from untrusted CA\n");
+ return 0;
+ }
+
+ if ((n == X509_V_ERR_CERT_NOT_YET_VALID ||
+ n == X509_V_ERR_CERT_HAS_EXPIRED) &&
+ (wsi->tls.use_ssl & LCCSCF_ALLOW_EXPIRED)) {
+ lwsl_info("accepting expired or not yet valid certificate\n");
+
+ return 0;
+ }
+ lws_snprintf(ebuf, ebuf_len,
+ "server's cert didn't look good, X509_V_ERR = %d: %s\n",
+ n, ERR_error_string(n, sb));
+ lwsl_info("%s\n", ebuf);
+ lws_ssl_elaborate_error();
+
+ return -1;
+}
+
+int
+lws_tls_client_create_vhost_context(struct lws_vhost *vh,
+ const struct lws_context_creation_info *info,
+ const char *cipher_list,
+ const char *ca_filepath,
+ const char *cert_filepath,
+ const char *private_key_filepath)
+{
+ X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len);
+ SSL_METHOD *method = (SSL_METHOD *)TLS_client_method();
+ unsigned long error;
+ lws_filepos_t len;
+ uint8_t *buf;
+
+ if (!method) {
+ error = ERR_get_error();
+ lwsl_err("problem creating ssl method %lu: %s\n",
+ error, ERR_error_string(error,
+ (char *)vh->context->pt[0].serv_buf));
+ return 1;
+ }
+ /* create context */
+ vh->tls.ssl_client_ctx = SSL_CTX_new(method);
+ if (!vh->tls.ssl_client_ctx) {
+ error = ERR_get_error();
+ lwsl_err("problem creating ssl context %lu: %s\n",
+ error, ERR_error_string(error,
+ (char *)vh->context->pt[0].serv_buf));
+ return 1;
+ }
+
+ if (!ca_filepath)
+ return 0;
+
+ if (alloc_file(vh->context, ca_filepath, &buf, &len)) {
+ lwsl_err("Load CA cert file %s failed\n", ca_filepath);
+ return 1;
+ }
+
+ vh->tls.x509_client_CA = d2i_X509(NULL, buf, len);
+ free(buf);
+ if (!vh->tls.x509_client_CA) {
+ lwsl_err("client CA: x509 parse failed\n");
+ return 1;
+ }
+
+ if (!vh->tls.ssl_ctx)
+ SSL_CTX_add_client_CA(vh->tls.ssl_client_ctx, vh->tls.x509_client_CA);
+ else
+ SSL_CTX_add_client_CA(vh->tls.ssl_ctx, vh->tls.x509_client_CA);
+
+ lwsl_notice("client loaded CA for verification %s\n", ca_filepath);
+
+ return 0;
+}
diff --git a/thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c b/thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c
new file mode 100644
index 0000000000..2de6d422e3
--- /dev/null
+++ b/thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c
@@ -0,0 +1,694 @@
+/*
+ * libwebsockets - mbedTLS-specific server functions
+ *
+ * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+#include <mbedtls/x509_csr.h>
+
+int
+lws_tls_server_client_cert_verify_config(struct lws_vhost *vh)
+{
+ int verify_options = SSL_VERIFY_PEER;
+
+ /* as a server, are we requiring clients to identify themselves? */
+ if (!lws_check_opt(vh->options,
+ LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) {
+ lwsl_notice("no client cert required\n");
+ return 0;
+ }
+
+ /*
+ * The wrapper has this messed-up mapping:
+ *
+ * else if (ctx->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+ * mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
+ *
+ * ie the meaning is inverted. So where we should test for ! we don't
+ */
+ if (lws_check_opt(vh->options, LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED))
+ verify_options = SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+
+ lwsl_notice("%s: vh %s requires client cert %d\n", __func__, vh->name,
+ verify_options);
+
+ SSL_CTX_set_verify(vh->tls.ssl_ctx, verify_options, NULL);
+
+ return 0;
+}
+
+static int
+lws_mbedtls_sni_cb(void *arg, mbedtls_ssl_context *mbedtls_ctx,
+ const unsigned char *servername, size_t len)
+{
+ SSL *ssl = SSL_SSL_from_mbedtls_ssl_context(mbedtls_ctx);
+ struct lws_context *context = (struct lws_context *)arg;
+ struct lws_vhost *vhost, *vh;
+
+ lwsl_notice("%s: %s\n", __func__, servername);
+
+ /*
+ * We can only get ssl accepted connections by using a vhost's ssl_ctx
+ * find out which listening one took us and only match vhosts on the
+ * same port.
+ */
+ vh = context->vhost_list;
+ while (vh) {
+ if (!vh->being_destroyed &&
+ vh->tls.ssl_ctx == SSL_get_SSL_CTX(ssl))
+ break;
+ vh = vh->vhost_next;
+ }
+
+ if (!vh) {
+ assert(vh); /* can't match the incoming vh? */
+ return 0;
+ }
+
+ vhost = lws_select_vhost(context, vh->listen_port,
+ (const char *)servername);
+ if (!vhost) {
+ lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port);
+
+ return 0;
+ }
+
+ lwsl_info("SNI: Found: %s:%d at vhost '%s'\n", servername,
+ vh->listen_port, vhost->name);
+
+ /* select the ssl ctx from the selected vhost for this conn */
+ SSL_set_SSL_CTX(ssl, vhost->tls.ssl_ctx);
+
+ return 0;
+}
+
+int
+lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi,
+ const char *cert, const char *private_key,
+ const char *mem_cert, size_t len_mem_cert,
+ const char *mem_privkey, size_t mem_privkey_len)
+{
+ int n, f = 0;
+ const char *filepath = private_key;
+ uint8_t *mem = NULL, *p = NULL;
+ size_t mem_len = 0;
+ lws_filepos_t flen;
+ long err;
+
+ if ((!cert || !private_key) && (!mem_cert || !mem_privkey)) {
+ lwsl_notice("%s: no usable input\n", __func__);
+ return 0;
+ }
+
+ n = lws_tls_generic_cert_checks(vhost, cert, private_key);
+
+ if (n == LWS_TLS_EXTANT_NO && (!mem_cert || !mem_privkey))
+ return 0;
+
+ /*
+ * we can't read the root-privs files. But if mem_cert is provided,
+ * we should use that.
+ */
+ if (n == LWS_TLS_EXTANT_NO)
+ n = LWS_TLS_EXTANT_ALTERNATIVE;
+
+ if (n == LWS_TLS_EXTANT_ALTERNATIVE && (!mem_cert || !mem_privkey))
+ return 1; /* no alternative */
+
+ if (n == LWS_TLS_EXTANT_ALTERNATIVE) {
+ /*
+ * Although we have prepared update certs, we no longer have
+ * the rights to read our own cert + key we saved.
+ *
+ * If we were passed copies in memory buffers, use those
+ * instead.
+ *
+ * The passed memory-buffer cert image is in DER, and the
+ * memory-buffer private key image is PEM.
+ */
+ /* mem cert is already DER */
+ p = (uint8_t *)mem_cert;
+ flen = len_mem_cert;
+ /* mem private key is PEM, so go through the motions */
+ mem = (uint8_t *)mem_privkey;
+ mem_len = mem_privkey_len;
+ filepath = NULL;
+ } else {
+ if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, NULL,
+ 0, &p, &flen)) {
+ lwsl_err("couldn't find cert file %s\n", cert);
+
+ return 1;
+ }
+ f = 1;
+ }
+ err = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, flen, p);
+ if (!err) {
+ free(p);
+ lwsl_err("Problem loading cert\n");
+ return 1;
+ }
+
+ if (f)
+ free(p);
+ p = NULL;
+
+ if (private_key || n == LWS_TLS_EXTANT_ALTERNATIVE) {
+ if (lws_tls_alloc_pem_to_der_file(vhost->context, filepath,
+ (char *)mem, mem_len, &p,
+ &flen)) {
+ lwsl_err("couldn't find private key file %s\n",
+ private_key);
+
+ return 1;
+ }
+ err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->tls.ssl_ctx, p, flen);
+ if (!err) {
+ free(p);
+ lwsl_err("Problem loading key\n");
+
+ return 1;
+ }
+ }
+
+ if (p && !mem_privkey) {
+ free(p);
+ p = NULL;
+ }
+
+ if (!private_key && !mem_privkey &&
+ vhost->protocols[0].callback(wsi,
+ LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
+ vhost->tls.ssl_ctx, NULL, 0)) {
+ lwsl_err("ssl private key not set\n");
+
+ return 1;
+ }
+
+ vhost->tls.skipped_certs = 0;
+
+ return 0;
+}
+
+int
+lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info,
+ struct lws_vhost *vhost, struct lws *wsi)
+{
+ const SSL_METHOD *method = TLS_server_method();
+ uint8_t *p;
+ lws_filepos_t flen;
+ int n;
+
+ vhost->tls.ssl_ctx = SSL_CTX_new(method); /* create context */
+ if (!vhost->tls.ssl_ctx) {
+ lwsl_err("problem creating ssl context\n");
+ return 1;
+ }
+
+ if (!vhost->tls.use_ssl || !info->ssl_cert_filepath)
+ return 0;
+
+ if (info->ssl_ca_filepath) {
+ lwsl_notice("%s: vh %s: loading CA filepath %s\n", __func__,
+ vhost->name, info->ssl_ca_filepath);
+ if (lws_tls_alloc_pem_to_der_file(vhost->context,
+ info->ssl_ca_filepath, NULL, 0, &p, &flen)) {
+ lwsl_err("couldn't find client CA file %s\n",
+ info->ssl_ca_filepath);
+
+ return 1;
+ }
+
+ if (SSL_CTX_add_client_CA_ASN1(vhost->tls.ssl_ctx, (int)flen, p) != 1) {
+ lwsl_err("%s: SSL_CTX_add_client_CA_ASN1 unhappy\n",
+ __func__);
+ free(p);
+ return 1;
+ }
+ free(p);
+ }
+
+ n = lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath,
+ info->ssl_private_key_filepath, NULL,
+ 0, NULL, 0);
+ if (n)
+ return n;
+
+ return 0;
+}
+
+int
+lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd)
+{
+ errno = 0;
+ wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_ctx);
+ if (wsi->tls.ssl == NULL) {
+ lwsl_err("SSL_new failed: errno %d\n", errno);
+
+ lws_ssl_elaborate_error();
+ return 1;
+ }
+
+ SSL_set_fd(wsi->tls.ssl, accept_fd);
+
+ if (wsi->vhost->tls.ssl_info_event_mask)
+ SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback);
+
+ SSL_set_sni_callback(wsi->tls.ssl, lws_mbedtls_sni_cb, wsi->context);
+
+ return 0;
+}
+
+int
+lws_tls_server_abort_connection(struct lws *wsi)
+{
+ __lws_tls_shutdown(wsi);
+ SSL_free(wsi->tls.ssl);
+
+ return 0;
+}
+
+enum lws_ssl_capable_status
+lws_tls_server_accept(struct lws *wsi)
+{
+ union lws_tls_cert_info_results ir;
+ int m, n;
+
+ n = SSL_accept(wsi->tls.ssl);
+ if (n == 1) {
+
+ if (strstr(wsi->vhost->name, ".invalid")) {
+ lwsl_notice("%s: vhost has .invalid, rejecting accept\n", __func__);
+
+ return LWS_SSL_CAPABLE_ERROR;
+ }
+
+ n = lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME, &ir,
+ sizeof(ir.ns.name));
+ if (!n)
+ lwsl_notice("%s: client cert CN '%s'\n",
+ __func__, ir.ns.name);
+ else
+ lwsl_info("%s: couldn't get client cert CN\n", __func__);
+ return LWS_SSL_CAPABLE_DONE;
+ }
+
+ m = SSL_get_error(wsi->tls.ssl, n);
+ lwsl_debug("%s: %p: accept SSL_get_error %d errno %d\n", __func__,
+ wsi, m, errno);
+
+ // mbedtls wrapper only
+ if (m == SSL_ERROR_SYSCALL && errno == 11)
+ return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
+
+ if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
+ return LWS_SSL_CAPABLE_ERROR;
+
+ if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) {
+ if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
+ lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__);
+ return LWS_SSL_CAPABLE_ERROR;
+ }
+
+ lwsl_info("SSL_ERROR_WANT_READ\n");
+ return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
+ }
+ if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) {
+ lwsl_debug("%s: WANT_WRITE\n", __func__);
+
+ if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
+ lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__);
+ return LWS_SSL_CAPABLE_ERROR;
+ }
+ return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
+ }
+
+ return LWS_SSL_CAPABLE_ERROR;
+}
+
+#if defined(LWS_WITH_ACME)
+/*
+ * mbedtls doesn't support SAN for cert creation. So we use a known-good
+ * tls-sni-01 cert from OpenSSL that worked on Let's Encrypt, and just replace
+ * the pubkey n part and the signature part.
+ *
+ * This will need redoing for tls-sni-02...
+ */
+
+static uint8_t ss_cert_leadin[] = {
+ 0x30, 0x82,
+ 0x05, 0x56, /* total length: LEN1 (+2 / +3) (correct for 513 + 512)*/
+
+ 0x30, 0x82, /* length: LEN2 (+6 / +7) (correct for 513) */
+ 0x03, 0x3e,
+
+ /* addition: v3 cert (+5 bytes)*/
+ 0xa0, 0x03,
+ 0x02, 0x01, 0x02,
+
+ 0x02, 0x01, 0x01,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x3f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47,
+ 0x42, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b,
+ 0x73, 0x6f, 0x6d, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x31,
+ 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11, 0x74, 0x65,
+ 0x6d, 0x70, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69, 0x6e, 0x76, 0x61,
+ 0x6c, 0x69, 0x64, 0x30, 0x1e, 0x17, 0x0d,
+
+ /* from 2017-10-29 ... */
+ 0x31, 0x37, 0x31, 0x30, 0x32, 0x39, 0x31, 0x31, 0x34, 0x39, 0x34, 0x35,
+ 0x5a, 0x17, 0x0d,
+
+ /* thru 2049-10-29 we immediately discard the private key, no worries */
+ 0x34, 0x39, 0x31, 0x30, 0x32, 0x39, 0x31, 0x32, 0x34, 0x39, 0x34, 0x35,
+ 0x5a,
+
+ 0x30, 0x3f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x47, 0x42, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x0c, 0x0b, 0x73, 0x6f, 0x6d, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e,
+ 0x79, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11,
+ 0x74, 0x65, 0x6d, 0x70, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69, 0x6e,
+ 0x76, 0x61, 0x6c, 0x69, 0x64, 0x30,
+
+ 0x82,
+ 0x02, 0x22, /* LEN3 (+C3 / C4) */
+ 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03,
+
+ 0x82,
+ 0x02, 0x0f, /* LEN4 (+D6 / D7) */
+
+ 0x00, 0x30, 0x82,
+
+ 0x02, 0x0a, /* LEN5 (+ DB / DC) */
+
+ 0x02, 0x82,
+
+ //0x02, 0x01, /* length of n in bytes (including leading 00 if any) */
+ },
+
+ /* 1 + (keybits / 8) bytes N */
+
+ ss_cert_san_leadin[] = {
+ /* e - fixed */
+ 0x02, 0x03, 0x01, 0x00, 0x01,
+
+ 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x59, 0x06, 0x03, 0x55, 0x1d,
+ 0x11, 0x04, 0x52, 0x30, 0x50, /* <-- SAN length + 2 */
+
+ 0x82, 0x4e, /* <-- SAN length */
+ },
+
+ /* 78 bytes of SAN (tls-sni-01)
+ 0x61, 0x64, 0x34, 0x31, 0x61, 0x66, 0x62, 0x65, 0x30, 0x63, 0x61, 0x34,
+ 0x36, 0x34, 0x32, 0x66, 0x30, 0x61, 0x34, 0x34, 0x39, 0x64, 0x39, 0x63,
+ 0x61, 0x37, 0x36, 0x65, 0x62, 0x61, 0x61, 0x62, 0x2e, 0x32, 0x38, 0x39,
+ 0x34, 0x64, 0x34, 0x31, 0x36, 0x63, 0x39, 0x38, 0x33, 0x66, 0x31, 0x32,
+ 0x65, 0x64, 0x37, 0x33, 0x31, 0x61, 0x33, 0x30, 0x66, 0x35, 0x63, 0x34,
+ 0x34, 0x37, 0x37, 0x66, 0x65, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69,
+ 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, */
+
+ /* end of LEN2 area */
+
+ ss_cert_sig_leadin[] = {
+ /* it's saying that the signature is SHA256 + RSA */
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
+
+ 0x82,
+ 0x02, 0x01,
+ 0x00,
+ };
+
+ /* (keybits / 8) bytes signature to end of LEN1 area */
+
+#define SAN_A_LENGTH 78
+
+LWS_VISIBLE int
+lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a,
+ const char *san_b)
+{
+ int buflen = 0x560;
+ uint8_t *buf = lws_malloc(buflen, "tmp cert buf"), *p = buf, *pkey_asn1;
+ struct lws_genrsa_ctx ctx;
+ struct lws_genrsa_elements el;
+ uint8_t digest[32];
+ struct lws_genhash_ctx hash_ctx;
+ int pkey_asn1_len = 3 * 1024;
+ int n, m, keybits = lws_plat_recommended_rsa_bits(), adj;
+
+ if (!buf)
+ return 1;
+
+ n = lws_genrsa_new_keypair(vhost->context, &ctx, &el, keybits);
+ if (n < 0) {
+ lws_jwk_destroy_genrsa_elements(&el);
+ goto bail1;
+ }
+
+ n = sizeof(ss_cert_leadin);
+ memcpy(p, ss_cert_leadin, n);
+ p += n;
+
+ adj = (0x0556 - 0x401) + (keybits / 4) + 1;
+ buf[2] = adj >> 8;
+ buf[3] = adj & 0xff;
+
+ adj = (0x033e - 0x201) + (keybits / 8) + 1;
+ buf[6] = adj >> 8;
+ buf[7] = adj & 0xff;
+
+ adj = (0x0222 - 0x201) + (keybits / 8) + 1;
+ buf[0xc3] = adj >> 8;
+ buf[0xc4] = adj & 0xff;
+
+ adj = (0x020f - 0x201) + (keybits / 8) + 1;
+ buf[0xd6] = adj >> 8;
+ buf[0xd7] = adj & 0xff;
+
+ adj = (0x020a - 0x201) + (keybits / 8) + 1;
+ buf[0xdb] = adj >> 8;
+ buf[0xdc] = adj & 0xff;
+
+ *p++ = ((keybits / 8) + 1) >> 8;
+ *p++ = ((keybits / 8) + 1) & 0xff;
+
+ /* we need to drop 1 + (keybits / 8) bytes of n in here, 00 + key */
+
+ *p++ = 0x00;
+ memcpy(p, el.e[JWK_KEY_N].buf, el.e[JWK_KEY_N].len);
+ p += el.e[JWK_KEY_N].len;
+
+ memcpy(p, ss_cert_san_leadin, sizeof(ss_cert_san_leadin));
+ p += sizeof(ss_cert_san_leadin);
+
+ /* drop in 78 bytes of san_a */
+
+ memcpy(p, san_a, SAN_A_LENGTH);
+ p += SAN_A_LENGTH;
+ memcpy(p, ss_cert_sig_leadin, sizeof(ss_cert_sig_leadin));
+
+ p[17] = ((keybits / 8) + 1) >> 8;
+ p[18] = ((keybits / 8) + 1) & 0xff;
+
+ p += sizeof(ss_cert_sig_leadin);
+
+ /* hash the cert plaintext */
+
+ if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
+ goto bail2;
+
+ if (lws_genhash_update(&hash_ctx, buf, lws_ptr_diff(p, buf))) {
+ lws_genhash_destroy(&hash_ctx, NULL);
+
+ goto bail2;
+ }
+ if (lws_genhash_destroy(&hash_ctx, digest))
+ goto bail2;
+
+ /* sign the hash */
+
+ n = lws_genrsa_public_sign(&ctx, digest, LWS_GENHASH_TYPE_SHA256, p,
+ buflen - lws_ptr_diff(p, buf));
+ if (n < 0)
+ goto bail2;
+ p += n;
+
+ pkey_asn1 = lws_malloc(pkey_asn1_len, "mbed crt tmp");
+ if (!pkey_asn1)
+ goto bail2;
+
+ m = lws_genrsa_render_pkey_asn1(&ctx, 1, pkey_asn1, pkey_asn1_len);
+ if (m < 0) {
+ lws_free(pkey_asn1);
+ goto bail2;
+ }
+
+// lwsl_hexdump_level(LLL_DEBUG, buf, lws_ptr_diff(p, buf));
+ n = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx,
+ lws_ptr_diff(p, buf), buf);
+ if (n != 1) {
+ lws_free(pkey_asn1);
+ lwsl_err("%s: generated cert failed to load 0x%x\n",
+ __func__, -n);
+ } else {
+ //lwsl_debug("private key\n");
+ //lwsl_hexdump_level(LLL_DEBUG, pkey_asn1, n);
+
+ /* and to use our generated private key */
+ n = SSL_CTX_use_PrivateKey_ASN1(0, vhost->tls.ssl_ctx, pkey_asn1, m);
+ lws_free(pkey_asn1);
+ if (n != 1) {
+ lwsl_err("%s: SSL_CTX_use_PrivateKey_ASN1 failed\n",
+ __func__);
+ }
+ }
+
+ lws_genrsa_destroy(&ctx);
+ lws_jwk_destroy_genrsa_elements(&el);
+
+ lws_free(buf);
+
+ return n != 1;
+
+bail2:
+ lws_genrsa_destroy(&ctx);
+ lws_jwk_destroy_genrsa_elements(&el);
+bail1:
+ lws_free(buf);
+
+ return -1;
+}
+
+void
+lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost)
+{
+}
+
+#if defined(LWS_WITH_JWS)
+static int
+_rngf(void *context, unsigned char *buf, size_t len)
+{
+ if ((size_t)lws_get_random(context, buf, len) == len)
+ return 0;
+
+ return -1;
+}
+
+static const char *x5[] = { "C", "ST", "L", "O", "CN" };
+
+/*
+ * CSR is output formatted as b64url(DER)
+ * Private key is output as a PEM in memory
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[],
+ uint8_t *dcsr, size_t csr_len, char **privkey_pem,
+ size_t *privkey_len)
+{
+ mbedtls_x509write_csr csr;
+ mbedtls_pk_context mpk;
+ int buf_size = 4096, n;
+ char subject[200], *p = subject, *end = p + sizeof(subject) - 1;
+ uint8_t *buf = malloc(buf_size); /* malloc because given to user code */
+
+ if (!buf)
+ return -1;
+
+ mbedtls_x509write_csr_init(&csr);
+
+ mbedtls_pk_init(&mpk);
+ if (mbedtls_pk_setup(&mpk, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA))) {
+ lwsl_notice("%s: pk_setup failed\n", __func__);
+ goto fail;
+ }
+
+ n = mbedtls_rsa_gen_key(mbedtls_pk_rsa(mpk), _rngf, context,
+ lws_plat_recommended_rsa_bits(), 65537);
+ if (n) {
+ lwsl_notice("%s: failed to generate keys\n", __func__);
+
+ goto fail1;
+ }
+
+ /* subject must be formatted like "C=TW,O=warmcat,CN=myserver" */
+
+ for (n = 0; n < (int)ARRAY_SIZE(x5); n++) {
+ if (p != subject)
+ *p++ = ',';
+ if (elements[n])
+ p += lws_snprintf(p, end - p, "%s=%s", x5[n],
+ elements[n]);
+ }
+
+ if (mbedtls_x509write_csr_set_subject_name(&csr, subject))
+ goto fail1;
+
+ mbedtls_x509write_csr_set_key(&csr, &mpk);
+ mbedtls_x509write_csr_set_md_alg(&csr, MBEDTLS_MD_SHA256);
+
+ /*
+ * data is written at the end of the buffer! Use the
+ * return value to determine where you should start
+ * using the buffer
+ */
+ n = mbedtls_x509write_csr_der(&csr, buf, buf_size, _rngf, context);
+ if (n < 0) {
+ lwsl_notice("%s: write csr der failed\n", __func__);
+ goto fail1;
+ }
+
+ /* we have it in DER, we need it in b64URL */
+
+ n = lws_jws_base64_enc((char *)(buf + buf_size) - n, n,
+ (char *)dcsr, csr_len);
+ if (n < 0)
+ goto fail1;
+
+ /*
+ * okay, the CSR is done, last we need the private key in PEM
+ * re-use the DER CSR buf as the result buffer since we cn do it in
+ * one step
+ */
+
+ if (mbedtls_pk_write_key_pem(&mpk, buf, buf_size)) {
+ lwsl_notice("write key pem failed\n");
+ goto fail1;
+ }
+
+ *privkey_pem = (char *)buf;
+ *privkey_len = strlen((const char *)buf);
+
+ mbedtls_pk_free(&mpk);
+ mbedtls_x509write_csr_free(&csr);
+
+ return n;
+
+fail1:
+ mbedtls_pk_free(&mpk);
+fail:
+ mbedtls_x509write_csr_free(&csr);
+ free(buf);
+
+ return -1;
+}
+#endif
+#endif
diff --git a/thirdparty/libwebsockets/tls/mbedtls/ssl.c b/thirdparty/libwebsockets/tls/mbedtls/ssl.c
new file mode 100644
index 0000000000..6ae9d2556b
--- /dev/null
+++ b/thirdparty/libwebsockets/tls/mbedtls/ssl.c
@@ -0,0 +1,520 @@
+/*
+ * libwebsockets - mbedTLS-specific lws apis
+ *
+ * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+#include <mbedtls/oid.h>
+
+void
+lws_ssl_elaborate_error(void)
+{
+}
+
+int
+lws_context_init_ssl_library(const struct lws_context_creation_info *info)
+{
+ lwsl_info(" Compiled with MbedTLS support\n");
+
+ if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
+ lwsl_info(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
+
+ return 0;
+}
+
+LWS_VISIBLE void
+lws_ssl_destroy(struct lws_vhost *vhost)
+{
+ if (!lws_check_opt(vhost->context->options,
+ LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
+ return;
+
+ if (vhost->tls.ssl_ctx)
+ SSL_CTX_free(vhost->tls.ssl_ctx);
+ if (!vhost->tls.user_supplied_ssl_ctx && vhost->tls.ssl_client_ctx)
+ SSL_CTX_free(vhost->tls.ssl_client_ctx);
+
+ if (vhost->tls.x509_client_CA)
+ X509_free(vhost->tls.x509_client_CA);
+}
+
+LWS_VISIBLE int
+lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
+{
+ struct lws_context *context = wsi->context;
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ int n = 0, m;
+
+ if (!wsi->tls.ssl)
+ return lws_ssl_capable_read_no_ssl(wsi, buf, len);
+
+ lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
+
+ errno = 0;
+ n = SSL_read(wsi->tls.ssl, buf, len);
+#if defined(LWS_WITH_ESP32)
+ if (!n && errno == ENOTCONN) {
+ lwsl_debug("%p: SSL_read ENOTCONN\n", wsi);
+ return LWS_SSL_CAPABLE_ERROR;
+ }
+#endif
+#if defined(LWS_WITH_STATS)
+ if (!wsi->seen_rx) {
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_MS_SSL_RX_DELAY,
+ time_in_microseconds() - wsi->accept_start_us);
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_C_SSL_CONNS_HAD_RX, 1);
+ wsi->seen_rx = 1;
+ }
+#endif
+
+
+ lwsl_debug("%p: SSL_read says %d\n", wsi, n);
+ /* manpage: returning 0 means connection shut down */
+ if (!n) {
+ wsi->socket_is_permanently_unusable = 1;
+
+ return LWS_SSL_CAPABLE_ERROR;
+ }
+
+ if (n < 0) {
+ m = SSL_get_error(wsi->tls.ssl, n);
+ lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
+ if (m == SSL_ERROR_ZERO_RETURN ||
+ m == SSL_ERROR_SYSCALL)
+ return LWS_SSL_CAPABLE_ERROR;
+
+ if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) {
+ lwsl_debug("%s: WANT_READ\n", __func__);
+ lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
+ return LWS_SSL_CAPABLE_MORE_SERVICE;
+ }
+ if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) {
+ lwsl_debug("%s: WANT_WRITE\n", __func__);
+ lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
+ return LWS_SSL_CAPABLE_MORE_SERVICE;
+ }
+ wsi->socket_is_permanently_unusable = 1;
+
+ return LWS_SSL_CAPABLE_ERROR;
+ }
+
+ lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
+
+ if (wsi->vhost)
+ wsi->vhost->conn_stats.rx += n;
+
+ lws_restart_ws_ping_pong_timer(wsi);
+
+ /*
+ * if it was our buffer that limited what we read,
+ * check if SSL has additional data pending inside SSL buffers.
+ *
+ * Because these won't signal at the network layer with POLLIN
+ * and if we don't realize, this data will sit there forever
+ */
+ if (n != len)
+ goto bail;
+ if (!wsi->tls.ssl)
+ goto bail;
+
+ if (!SSL_pending(wsi->tls.ssl))
+ goto bail;
+
+ if (wsi->tls.pending_read_list_next)
+ return n;
+ if (wsi->tls.pending_read_list_prev)
+ return n;
+ if (pt->tls.pending_read_list == wsi)
+ return n;
+
+ /* add us to the linked list of guys with pending ssl */
+ if (pt->tls.pending_read_list)
+ pt->tls.pending_read_list->tls.pending_read_list_prev = wsi;
+
+ wsi->tls.pending_read_list_next = pt->tls.pending_read_list;
+ wsi->tls.pending_read_list_prev = NULL;
+ pt->tls.pending_read_list = wsi;
+
+ return n;
+bail:
+ lws_ssl_remove_wsi_from_buffered_list(wsi);
+
+ return n;
+}
+
+LWS_VISIBLE int
+lws_ssl_pending(struct lws *wsi)
+{
+ if (!wsi->tls.ssl)
+ return 0;
+
+ return SSL_pending(wsi->tls.ssl);
+}
+
+LWS_VISIBLE int
+lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
+{
+ int n, m;
+
+ if (!wsi->tls.ssl)
+ return lws_ssl_capable_write_no_ssl(wsi, buf, len);
+
+ n = SSL_write(wsi->tls.ssl, buf, len);
+ if (n > 0)
+ return n;
+
+ m = SSL_get_error(wsi->tls.ssl, n);
+ if (m != SSL_ERROR_SYSCALL) {
+ if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) {
+ lwsl_notice("%s: want read\n", __func__);
+
+ return LWS_SSL_CAPABLE_MORE_SERVICE;
+ }
+
+ if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) {
+ lws_set_blocking_send(wsi);
+ lwsl_notice("%s: want write\n", __func__);
+
+ return LWS_SSL_CAPABLE_MORE_SERVICE;
+ }
+ }
+
+ lwsl_debug("%s failed: %d\n",__func__, m);
+ wsi->socket_is_permanently_unusable = 1;
+
+ return LWS_SSL_CAPABLE_ERROR;
+}
+
+int openssl_SSL_CTX_private_data_index;
+
+void
+lws_ssl_info_callback(const SSL *ssl, int where, int ret)
+{
+ struct lws *wsi;
+ struct lws_context *context;
+ struct lws_ssl_info si;
+
+ context = (struct lws_context *)SSL_CTX_get_ex_data(
+ SSL_get_SSL_CTX(ssl),
+ openssl_SSL_CTX_private_data_index);
+ if (!context)
+ return;
+ wsi = wsi_from_fd(context, SSL_get_fd(ssl));
+ if (!wsi)
+ return;
+
+ if (!(where & wsi->vhost->tls.ssl_info_event_mask))
+ return;
+
+ si.where = where;
+ si.ret = ret;
+
+ if (user_callback_handle_rxflow(wsi->protocol->callback,
+ wsi, LWS_CALLBACK_SSL_INFO,
+ wsi->user_space, &si, 0))
+ lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1);
+}
+
+
+LWS_VISIBLE int
+lws_ssl_close(struct lws *wsi)
+{
+ lws_sockfd_type n;
+
+ if (!wsi->tls.ssl)
+ return 0; /* not handled */
+
+#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
+ /* kill ssl callbacks, becausse we will remove the fd from the
+ * table linking it to the wsi
+ */
+ if (wsi->vhost->tls.ssl_info_event_mask)
+ SSL_set_info_callback(wsi->tls.ssl, NULL);
+#endif
+
+ n = SSL_get_fd(wsi->tls.ssl);
+ if (!wsi->socket_is_permanently_unusable)
+ SSL_shutdown(wsi->tls.ssl);
+ compatible_close(n);
+ SSL_free(wsi->tls.ssl);
+ wsi->tls.ssl = NULL;
+
+ if (!lwsi_role_client(wsi) &&
+ wsi->context->simultaneous_ssl_restriction &&
+ wsi->context->simultaneous_ssl-- ==
+ wsi->context->simultaneous_ssl_restriction)
+ /* we made space and can do an accept */
+ lws_gate_accepts(wsi->context, 1);
+
+#if defined(LWS_WITH_STATS)
+ wsi->context->updated = 1;
+#endif
+
+ return 1; /* handled */
+}
+
+void
+lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
+{
+ if (vhost->tls.ssl_ctx)
+ SSL_CTX_free(vhost->tls.ssl_ctx);
+
+ if (!vhost->tls.user_supplied_ssl_ctx && vhost->tls.ssl_client_ctx)
+ SSL_CTX_free(vhost->tls.ssl_client_ctx);
+#if defined(LWS_WITH_ACME)
+ lws_tls_acme_sni_cert_destroy(vhost);
+#endif
+}
+
+void
+lws_ssl_context_destroy(struct lws_context *context)
+{
+}
+
+lws_tls_ctx *
+lws_tls_ctx_from_wsi(struct lws *wsi)
+{
+ if (!wsi->tls.ssl)
+ return NULL;
+
+ return SSL_get_SSL_CTX(wsi->tls.ssl);
+}
+
+enum lws_ssl_capable_status
+__lws_tls_shutdown(struct lws *wsi)
+{
+ int n = SSL_shutdown(wsi->tls.ssl);
+
+ lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd);
+
+ switch (n) {
+ case 1: /* successful completion */
+ n = shutdown(wsi->desc.sockfd, SHUT_WR);
+ return LWS_SSL_CAPABLE_DONE;
+
+ case 0: /* needs a retry */
+ __lws_change_pollfd(wsi, 0, LWS_POLLIN);
+ return LWS_SSL_CAPABLE_MORE_SERVICE;
+
+ default: /* fatal error, or WANT */
+ n = SSL_get_error(wsi->tls.ssl, n);
+ if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
+ if (SSL_want_read(wsi->tls.ssl)) {
+ lwsl_debug("(wants read)\n");
+ __lws_change_pollfd(wsi, 0, LWS_POLLIN);
+ return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
+ }
+ if (SSL_want_write(wsi->tls.ssl)) {
+ lwsl_debug("(wants write)\n");
+ __lws_change_pollfd(wsi, 0, LWS_POLLOUT);
+ return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
+ }
+ }
+ return LWS_SSL_CAPABLE_ERROR;
+ }
+}
+
+static time_t
+lws_tls_mbedtls_time_to_unix(mbedtls_x509_time *xtime)
+{
+ struct tm t;
+
+ if (!xtime || !xtime->year || xtime->year < 0)
+ return (time_t)(long long)-1;
+
+ memset(&t, 0, sizeof(t));
+
+ t.tm_year = xtime->year - 1900;
+ t.tm_mon = xtime->mon - 1; /* mbedtls months are 1+, tm are 0+ */
+ t.tm_mday = xtime->day - 1; /* mbedtls days are 1+, tm are 0+ */
+ t.tm_hour = xtime->hour;
+ t.tm_min = xtime->min;
+ t.tm_sec = xtime->sec;
+ t.tm_isdst = -1;
+
+ return mktime(&t);
+}
+
+static int
+lws_tls_mbedtls_get_x509_name(mbedtls_x509_name *name,
+ union lws_tls_cert_info_results *buf, size_t len)
+{
+ while (name) {
+ if (MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid)) {
+ name = name->next;
+ continue;
+ }
+
+ if (len - 1 < name->val.len)
+ return -1;
+
+ memcpy(&buf->ns.name[0], name->val.p, name->val.len);
+ buf->ns.name[name->val.len] = '\0';
+ buf->ns.len = name->val.len;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+lws_tls_mbedtls_cert_info(mbedtls_x509_crt *x509, enum lws_tls_cert_info type,
+ union lws_tls_cert_info_results *buf, size_t len)
+{
+ if (!x509)
+ return -1;
+
+ switch (type) {
+ case LWS_TLS_CERT_INFO_VALIDITY_FROM:
+ buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_from);
+ if (buf->time == (time_t)(long long)-1)
+ return -1;
+ break;
+
+ case LWS_TLS_CERT_INFO_VALIDITY_TO:
+ buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_to);
+ if (buf->time == (time_t)(long long)-1)
+ return -1;
+ break;
+
+ case LWS_TLS_CERT_INFO_COMMON_NAME:
+ return lws_tls_mbedtls_get_x509_name(&x509->subject, buf, len);
+
+ case LWS_TLS_CERT_INFO_ISSUER_NAME:
+ return lws_tls_mbedtls_get_x509_name(&x509->issuer, buf, len);
+
+ case LWS_TLS_CERT_INFO_USAGE:
+ buf->usage = x509->key_usage;
+ break;
+
+ case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY:
+ {
+ char *p = buf->ns.name;
+ size_t r = len, u;
+
+ switch (mbedtls_pk_get_type(&x509->pk)) {
+ case MBEDTLS_PK_RSA:
+ {
+ mbedtls_rsa_context *rsa = mbedtls_pk_rsa(x509->pk);
+
+ if (mbedtls_mpi_write_string(&rsa->N, 16, p, r, &u))
+ return -1;
+ r -= u;
+ p += u;
+ if (mbedtls_mpi_write_string(&rsa->E, 16, p, r, &u))
+ return -1;
+
+ p += u;
+ buf->ns.len = lws_ptr_diff(p, buf->ns.name);
+ break;
+ }
+ case MBEDTLS_PK_ECKEY:
+ {
+ mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(x509->pk);
+
+ if (mbedtls_mpi_write_string(&ecp->Q.X, 16, p, r, &u))
+ return -1;
+ r -= u;
+ p += u;
+ if (mbedtls_mpi_write_string(&ecp->Q.Y, 16, p, r, &u))
+ return -1;
+ r -= u;
+ p += u;
+ if (mbedtls_mpi_write_string(&ecp->Q.Z, 16, p, r, &u))
+ return -1;
+ p += u;
+ buf->ns.len = lws_ptr_diff(p, buf->ns.name);
+ break;
+ }
+ default:
+ lwsl_notice("%s: x509 has unsupported pubkey type %d\n",
+ __func__,
+ mbedtls_pk_get_type(&x509->pk));
+
+ return -1;
+ }
+ break;
+ }
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type,
+ union lws_tls_cert_info_results *buf, size_t len)
+{
+ mbedtls_x509_crt *x509 = ssl_ctx_get_mbedtls_x509_crt(vhost->tls.ssl_ctx);
+
+ return lws_tls_mbedtls_cert_info(x509, type, buf, len);
+}
+
+LWS_VISIBLE int
+lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
+ union lws_tls_cert_info_results *buf, size_t len)
+{
+ mbedtls_x509_crt *x509;
+
+ wsi = lws_get_network_wsi(wsi);
+
+ x509 = ssl_get_peer_mbedtls_x509_crt(wsi->tls.ssl);
+
+ if (!x509)
+ return -1;
+
+ switch (type) {
+ case LWS_TLS_CERT_INFO_VERIFIED:
+ buf->verified = SSL_get_verify_result(wsi->tls.ssl) == X509_V_OK;
+ return 0;
+ default:
+ return lws_tls_mbedtls_cert_info(x509, type, buf, len);
+ }
+
+ return -1;
+}
+
+static int
+tops_fake_POLLIN_for_buffered_mbedtls(struct lws_context_per_thread *pt)
+{
+ return lws_tls_fake_POLLIN_for_buffered(pt);
+}
+
+static int
+tops_periodic_housekeeping_mbedtls(struct lws_context *context, time_t now)
+{
+ int n;
+
+ n = lws_compare_time_t(context, now, context->tls.last_cert_check_s);
+ if ((!context->tls.last_cert_check_s || n > (24 * 60 * 60)) &&
+ !lws_tls_check_all_cert_lifetimes(context))
+ context->tls.last_cert_check_s = now;
+
+ return 0;
+}
+
+const struct lws_tls_ops tls_ops_mbedtls = {
+ /* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_mbedtls,
+ /* periodic_housekeeping */ tops_periodic_housekeeping_mbedtls,
+};
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl3.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl3.h
index 007b392f3e..007b392f3e 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl3.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl3.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_cert.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_cert.h
index 86cf31ad51..86cf31ad51 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_cert.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_cert.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_code.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_code.h
index 80fdbb20f3..80fdbb20f3 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_code.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_code.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_dbg.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_dbg.h
index ad32cb92ff..ad32cb92ff 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_dbg.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_dbg.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_lib.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_lib.h
index 42b2de7501..42b2de7501 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_lib.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_lib.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_methods.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_methods.h
index cd2f8c0533..cd2f8c0533 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_methods.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_methods.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_pkey.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_pkey.h
index e790fcc995..e790fcc995 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_pkey.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_pkey.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_stack.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_stack.h
index 7a7051a026..7a7051a026 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_stack.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_stack.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h
index 2ca438c422..ba19663d9e 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h
@@ -203,6 +203,8 @@ struct ssl_st
const SSL_METHOD *method;
+ const char **alpn_protos;
+
RECORD_LAYER rlayer;
/* where we are */
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_x509.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_x509.h
index 7594d064b4..7594d064b4 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_x509.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_x509.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/tls1.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/tls1.h
index 7af1b0157d..7af1b0157d 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/tls1.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/tls1.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/x509_vfy.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/x509_vfy.h
index 26bf6c88a8..26bf6c88a8 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/x509_vfy.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/x509_vfy.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/openssl/ssl.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/openssl/ssl.h
index 5a84b4552e..e2b74fc6af 100644..100755
--- a/thirdparty/lws/mbedtls_wrapper/include/openssl/ssl.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/openssl/ssl.h
@@ -35,6 +35,22 @@
#define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS (1 << 3)
#define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS (1 << 4)
+ mbedtls_x509_crt *
+ ssl_ctx_get_mbedtls_x509_crt(SSL_CTX *ssl_ctx);
+
+ mbedtls_x509_crt *
+ ssl_get_peer_mbedtls_x509_crt(SSL *ssl);
+
+ int SSL_set_sni_callback(SSL *ssl, int(*cb)(void *, mbedtls_ssl_context *,
+ const unsigned char *, size_t), void *param);
+
+ void SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx);
+
+ int SSL_CTX_add_client_CA_ASN1(SSL_CTX *ssl, int len,
+ const unsigned char *d);
+
+ SSL *SSL_SSL_from_mbedtls_ssl_context(mbedtls_ssl_context *msc);
+
/**
* @brief create a SSL context
*
@@ -305,6 +321,7 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
void *arg),
void *arg);
+void SSL_set_alpn_select_cb(SSL *ssl, void *arg);
/**
* @brief set the SSL context ALPN select protocol
diff --git a/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_pm.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/platform/ssl_pm.h
index cbbe3aa3a2..cbbe3aa3a2 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_pm.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/platform/ssl_pm.h
diff --git a/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/platform/ssl_port.h
index eca68f20d1..74c7634355 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/platform/ssl_port.h
@@ -19,19 +19,11 @@
extern "C" {
#endif
-/*
-#include "esp_types.h"
-#include "esp_log.h"
-*/
#include "string.h"
-
-/* GODOT ADDITION */
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
-#include <stdlib.h>
-#else
+#include "stdlib.h"
+#if defined(LWS_HAVE_MALLOC_H)
#include "malloc.h"
#endif
-/* END GODOT ADDITION */
void *ssl_mem_zalloc(size_t size);
diff --git a/thirdparty/lws/mbedtls_wrapper/library/ssl_cert.c b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_cert.c
index 5c608125ac..5c608125ac 100644
--- a/thirdparty/lws/mbedtls_wrapper/library/ssl_cert.c
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_cert.c
diff --git a/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_lib.c
index d8fdd06fad..2f688ca9ef 100644
--- a/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_lib.c
@@ -19,6 +19,9 @@
#include "ssl_dbg.h"
#include "ssl_port.h"
+char *
+lws_strncpy(char *dest, const char *src, size_t size);
+
#define SSL_SEND_DATA_MAX_LENGTH 1460
/**
@@ -348,6 +351,9 @@ void SSL_free(SSL *ssl)
SSL_SESSION_free(ssl->session);
+ if (ssl->alpn_protos)
+ ssl_mem_free(ssl->alpn_protos);
+
ssl_mem_free(ssl);
}
@@ -1582,7 +1588,7 @@ void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_C
void ERR_error_string_n(unsigned long e, char *buf, size_t len)
{
- strncpy(buf, "unknown", len);
+ lws_strncpy(buf, "unknown", len);
}
void ERR_free_strings(void)
@@ -1591,11 +1597,34 @@ void ERR_free_strings(void)
char *ERR_error_string(unsigned long e, char *buf)
{
- if (buf) {
- strcpy(buf, "unknown");
+ if (!buf)
+ return "unknown";
+
+ switch(e) {
+ case X509_V_ERR_INVALID_CA:
+ strcpy(buf, "CA is not trusted");
+ break;
+ case X509_V_ERR_HOSTNAME_MISMATCH:
+ strcpy(buf, "Hostname mismatch");
+ break;
+ case X509_V_ERR_CA_KEY_TOO_SMALL:
+ strcpy(buf, "CA key too small");
+ break;
+ case X509_V_ERR_CA_MD_TOO_WEAK:
+ strcpy(buf, "MD key too weak");
+ break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ strcpy(buf, "Cert from the future");
+ break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ strcpy(buf, "Cert expired");
+ break;
+ default:
+ strcpy(buf, "unknown");
+ break;
}
- return "unknown";
+ return buf;
}
void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx)
@@ -1619,15 +1648,16 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx)
*/
struct alpn_ctx {
- unsigned char *data;
- unsigned short len;
+ unsigned char data[23];
+ unsigned char len;
};
-void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
+static void
+_openssl_alpn_to_mbedtls(struct alpn_ctx *ac, char ***palpn_protos)
{
- struct alpn_ctx *ac = arg;
unsigned char *p = ac->data, *q;
unsigned char len;
+ char **alpn_protos;
int count = 0;
/* find out how many entries he gave us */
@@ -1644,23 +1674,28 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
break;
}
+ if (!len)
+ count++;
+
if (!count)
return;
/* allocate space for count + 1 pointers and the data afterwards */
- ctx->alpn_protos = ssl_mem_zalloc((count + 1) * sizeof(char *) + ac->len + 1);
- if (!ctx->alpn_protos)
+ alpn_protos = ssl_mem_zalloc((count + 1) * sizeof(char *) + ac->len + 1);
+ if (!alpn_protos)
return;
+ *palpn_protos = alpn_protos;
+
/* convert to mbedtls format */
- q = (unsigned char *)ctx->alpn_protos + (count + 1) * sizeof(char *);
+ q = (unsigned char *)alpn_protos + (count + 1) * sizeof(char *);
p = ac->data;
count = 0;
len = *p++;
- ctx->alpn_protos[count] = (char *)q;
+ alpn_protos[count] = (char *)q;
while (p - ac->data < ac->len) {
if (len--) {
*q++ = *p++;
@@ -1669,11 +1704,33 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
*q++ = '\0';
count++;
len = *p++;
- ctx->alpn_protos[count] = (char *)q;
+ alpn_protos[count] = (char *)q;
if (!len)
break;
}
- ctx->alpn_protos[count] = NULL; /* last pointer ends list with NULL */
+ if (!len) {
+ *q++ = '\0';
+ count++;
+ len = *p++;
+ alpn_protos[count] = (char *)q;
+ }
+ alpn_protos[count] = NULL; /* last pointer ends list with NULL */
+}
+
+void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
+{
+ struct alpn_ctx *ac = arg;
ctx->alpn_cb = cb;
+
+ _openssl_alpn_to_mbedtls(ac, (char ***)&ctx->alpn_protos);
+}
+
+void SSL_set_alpn_select_cb(SSL *ssl, void *arg)
+{
+ struct alpn_ctx *ac = arg;
+
+ _openssl_alpn_to_mbedtls(ac, (char ***)&ssl->alpn_protos);
+
+ _ssl_set_alpn_list(ssl);
}
diff --git a/thirdparty/lws/mbedtls_wrapper/library/ssl_methods.c b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_methods.c
index 0002360846..0002360846 100644
--- a/thirdparty/lws/mbedtls_wrapper/library/ssl_methods.c
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_methods.c
diff --git a/thirdparty/lws/mbedtls_wrapper/library/ssl_pkey.c b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_pkey.c
index 567a33e2c2..567a33e2c2 100644
--- a/thirdparty/lws/mbedtls_wrapper/library/ssl_pkey.c
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_pkey.c
diff --git a/thirdparty/lws/mbedtls_wrapper/library/ssl_stack.c b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_stack.c
index da836daf9c..da836daf9c 100644
--- a/thirdparty/lws/mbedtls_wrapper/library/ssl_stack.c
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_stack.c
diff --git a/thirdparty/lws/mbedtls_wrapper/library/ssl_x509.c b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_x509.c
index 4441490a03..ed79150831 100644
--- a/thirdparty/lws/mbedtls_wrapper/library/ssl_x509.c
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_x509.c
@@ -17,6 +17,8 @@
#include "ssl_dbg.h"
#include "ssl_port.h"
+#include <assert.h>
+
/**
* @brief show X509 certification information
*/
@@ -155,7 +157,7 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x)
{
SSL_ASSERT1(ctx);
SSL_ASSERT1(x);
-
+ assert(ctx);
if (ctx->client_CA == x)
return 1;
@@ -169,6 +171,28 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x)
/**
* @brief add CA client certification into the SSL
*/
+int SSL_CTX_add_client_CA_ASN1(SSL_CTX *ctx, int len,
+ const unsigned char *d)
+{
+ X509 *x;
+
+ x = d2i_X509(NULL, d, len);
+ if (!x) {
+ SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_X509() return NULL");
+ return 0;
+ }
+ SSL_ASSERT1(ctx);
+
+ X509_free(ctx->client_CA);
+
+ ctx->client_CA = x;
+
+ return 1;
+}
+
+/**
+ * @brief add CA client certification into the SSL
+ */
int SSL_add_client_CA(SSL *ssl, X509 *x)
{
SSL_ASSERT1(ssl);
diff --git a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c b/thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_pm.c
index 4e3d611095..4716c1ff56 100644..100755
--- a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_pm.c
@@ -25,6 +25,8 @@
#include "mbedtls/error.h"
#include "mbedtls/certs.h"
+#include <libwebsockets.h>
+
#define X509_INFO_STRING_LENGTH 8192
struct ssl_pm
@@ -41,6 +43,8 @@ struct ssl_pm
mbedtls_ssl_context ssl;
mbedtls_entropy_context entropy;
+
+ SSL *owner;
};
struct x509_pm
@@ -62,7 +66,7 @@ unsigned int max_content_len;
/*********************************************************************************************/
/************************************ SSL arch interface *************************************/
-#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG
+//#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG
/* mbedtls debug level */
#define MBEDTLS_DEBUG_LEVEL 4
@@ -79,13 +83,13 @@ static void ssl_platform_debug(void *ctx, int level,
This is a bit wasteful because the macros are compiled in with
the full _FILE_ path in each case.
*/
- char *file_sep = rindex(file, '/');
- if(file_sep)
- file = file_sep + 1;
+// char *file_sep = rindex(file, '/');
+ // if(file_sep)
+ // file = file_sep + 1;
- SSL_DEBUG(SSL_DEBUG_ON, "%s:%d %s", file, line, str);
+ printf("%s:%d %s", file, line, str);
}
-#endif
+//#endif
/**
* @brief create SSL low-level object
@@ -109,6 +113,8 @@ int ssl_pm_new(SSL *ssl)
goto no_mem;
}
+ ssl_pm->owner = ssl;
+
if (!ssl->ctx->read_buffer_len)
ssl->ctx->read_buffer_len = 2048;
@@ -159,12 +165,12 @@ int ssl_pm_new(SSL *ssl)
mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg);
-#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG
- mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL);
+//#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG
+ // mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL);
+// mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL);
+//#else
mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL);
-#else
- mbedtls_ssl_conf_dbg(&ssl_pm->conf, NULL, NULL);
-#endif
+//#endif
ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf);
if (ret) {
@@ -261,7 +267,7 @@ static int mbedtls_handshake( mbedtls_ssl_context *ssl )
while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) {
ret = mbedtls_ssl_handshake_step(ssl);
- SSL_DEBUG(SSL_PLATFORM_DEBUG_LEVEL, "ssl ret %d state %d", ret, ssl->state);
+ lwsl_info("%s: ssl ret -%x state %d\n", __func__, -ret, ssl->state);
if (ret != 0)
break;
@@ -270,14 +276,21 @@ static int mbedtls_handshake( mbedtls_ssl_context *ssl )
return ret;
}
+#include <errno.h>
+
int ssl_pm_handshake(SSL *ssl)
{
int ret;
struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
+ ssl->err = 0;
+ errno = 0;
+
ret = ssl_pm_reload_crt(ssl);
- if (ret)
+ if (ret) {
+ printf("%s: cert reload failed\n", __func__);
return 0;
+ }
if (ssl_pm->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) {
ssl_speed_up_enter();
@@ -298,6 +311,7 @@ int ssl_pm_handshake(SSL *ssl)
* <0 = death
*/
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ ssl->err = ret;
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_handshake() return -0x%x", -ret);
return 0; /* OpenSSL: did not complete but may be retried */
}
@@ -309,6 +323,14 @@ int ssl_pm_handshake(SSL *ssl)
return 1; /* openssl successful */
}
+ if (errno == 11) {
+ ssl->err = ret == MBEDTLS_ERR_SSL_WANT_READ;
+
+ return 0;
+ }
+
+ printf("%s: mbedtls_ssl_handshake() returned -0x%x\n", __func__, -ret);
+
/* it's had it */
ssl->err = SSL_ERROR_SYSCALL;
@@ -316,6 +338,28 @@ int ssl_pm_handshake(SSL *ssl)
return -1; /* openssl death */
}
+mbedtls_x509_crt *
+ssl_ctx_get_mbedtls_x509_crt(SSL_CTX *ssl_ctx)
+{
+ struct x509_pm *x509_pm = (struct x509_pm *)ssl_ctx->cert->x509->x509_pm;
+
+ if (!x509_pm)
+ return NULL;
+
+ return x509_pm->x509_crt;
+}
+
+mbedtls_x509_crt *
+ssl_get_peer_mbedtls_x509_crt(SSL *ssl)
+{
+ struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm;
+
+ if (!x509_pm)
+ return NULL;
+
+ return x509_pm->ex_crt;
+}
+
int ssl_pm_shutdown(SSL *ssl)
{
int ret;
@@ -351,8 +395,10 @@ int ssl_pm_read(SSL *ssl, void *buffer, int len)
ret = mbedtls_ssl_read(&ssl_pm->ssl, buffer, len);
if (ret < 0) {
+ // lwsl_notice("%s: mbedtls_ssl_read says -0x%x\n", __func__, -ret);
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_read() return -0x%x", -ret);
- if (ret == MBEDTLS_ERR_NET_CONN_RESET)
+ if (ret == MBEDTLS_ERR_NET_CONN_RESET ||
+ ret <= MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE) /* fatal errors */
ssl->err = SSL_ERROR_SYSCALL;
ret = -1;
}
@@ -392,6 +438,7 @@ int ssl_pm_send(SSL *ssl, const void *buffer, int len)
if (ret < 0) {
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret);
switch (ret) {
+ case MBEDTLS_ERR_NET_SEND_FAILED:
case MBEDTLS_ERR_NET_CONN_RESET:
ssl->err = SSL_ERROR_SYSCALL;
break;
@@ -589,22 +636,27 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len)
}
}
- load_buf = ssl_mem_malloc(len + 1);
- if (!load_buf) {
- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)");
- goto failed;
- }
-
- ssl_memcpy(load_buf, buffer, len);
- load_buf[len] = '\0';
-
mbedtls_x509_crt_init(x509_pm->x509_crt);
+ if (buffer[0] != 0x30) {
+ load_buf = ssl_mem_malloc(len + 1);
+ if (!load_buf) {
+ SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)");
+ goto failed;
+ }
+
+ ssl_memcpy(load_buf, buffer, len);
+ load_buf[len] = '\0';
+
+ ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1);
+ ssl_mem_free(load_buf);
+ } else {
+ printf("parsing as der\n");
- ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1);
- ssl_mem_free(load_buf);
+ ret = mbedtls_x509_crt_parse_der(x509_pm->x509_crt, buffer, len);
+ }
if (ret) {
- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_x509_crt_parse return -0x%x", -ret);
+ printf("mbedtls_x509_crt_parse return -0x%x", -ret);
goto failed;
}
@@ -707,46 +759,44 @@ void ssl_pm_set_bufflen(SSL *ssl, int len)
long ssl_pm_get_verify_result(const SSL *ssl)
{
- uint32_t ret;
- long verify_result;
- struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
-
- ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
+ uint32_t ret;
+ long verify_result;
+ struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
- if (!ret)
- return X509_V_OK;
+ ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
+ if (!ret)
+ return X509_V_OK;
- if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
- (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
- // Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification
- verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+ if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
+ (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
+ verify_result = X509_V_ERR_INVALID_CA;
- else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
- verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
+ else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
+ verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
- else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
- (ret & MBEDTLS_X509_BADCRL_BAD_KEY))
- verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
+ else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
+ (ret & MBEDTLS_X509_BADCRL_BAD_KEY))
+ verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
- else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
- (ret & MBEDTLS_X509_BADCRL_BAD_MD))
- verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
+ else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
+ (ret & MBEDTLS_X509_BADCRL_BAD_MD))
+ verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
- else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
- (ret & MBEDTLS_X509_BADCRL_FUTURE))
- verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
+ else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
+ (ret & MBEDTLS_X509_BADCRL_FUTURE))
+ verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
- else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
- (ret & MBEDTLS_X509_BADCRL_EXPIRED))
- verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
+ else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
+ (ret & MBEDTLS_X509_BADCRL_EXPIRED))
+ verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
- else
- verify_result = X509_V_ERR_UNSPECIFIED;
+ else
+ verify_result = X509_V_ERR_UNSPECIFIED;
- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
- "mbedtls_ssl_get_verify_result() return 0x%x", ret);
+ SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
+ "mbedtls_ssl_get_verify_result() return 0x%x", ret);
- return verify_result;
+ return verify_result;
}
/**
@@ -779,6 +829,12 @@ int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
void _ssl_set_alpn_list(const SSL *ssl)
{
+ if (ssl->alpn_protos) {
+ if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->alpn_protos))
+ fprintf(stderr, "mbedtls_ssl_conf_alpn_protocols failed\n");
+
+ return;
+ }
if (!ssl->ctx->alpn_protos)
return;
if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->ctx->alpn_protos))
@@ -797,3 +853,55 @@ void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data,
*len = 0;
}
+int SSL_set_sni_callback(SSL *ssl, int(*cb)(void *, mbedtls_ssl_context *,
+ const unsigned char *, size_t), void *param)
+{
+ struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
+
+ mbedtls_ssl_conf_sni(&ssl_pm->conf, cb, param);
+
+ return 0;
+}
+
+SSL *SSL_SSL_from_mbedtls_ssl_context(mbedtls_ssl_context *msc)
+{
+ struct ssl_pm *ssl_pm = (struct ssl_pm *)((char *)msc - offsetof(struct ssl_pm, ssl));
+
+ return ssl_pm->owner;
+}
+
+#include "ssl_cert.h"
+
+void SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx)
+{
+ struct ssl_pm *ssl_pm = ssl->ssl_pm;
+ struct x509_pm *x509_pm = (struct x509_pm *)ctx->cert->x509->x509_pm;
+ struct x509_pm *x509_pm_ca = (struct x509_pm *)ctx->client_CA->x509_pm;
+
+ struct pkey_pm *pkey_pm = (struct pkey_pm *)ctx->cert->pkey->pkey_pm;
+ int mode;
+
+ if (ssl->cert)
+ ssl_cert_free(ssl->cert);
+ ssl->ctx = ctx;
+ ssl->cert = __ssl_cert_new(ctx->cert);
+
+ if (ctx->verify_mode == SSL_VERIFY_PEER)
+ mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
+ else if (ctx->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+ mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
+ else if (ctx->verify_mode == SSL_VERIFY_CLIENT_ONCE)
+ mode = MBEDTLS_SSL_VERIFY_UNSET;
+ else
+ mode = MBEDTLS_SSL_VERIFY_NONE;
+
+ // printf("ssl: %p, client ca x509_crt %p, mbedtls mode %d\n", ssl, x509_pm_ca->x509_crt, mode);
+
+ /* apply new ctx cert to ssl */
+
+ ssl->verify_mode = ctx->verify_mode;
+
+ mbedtls_ssl_set_hs_ca_chain(&ssl_pm->ssl, x509_pm_ca->x509_crt, NULL);
+ mbedtls_ssl_set_hs_own_cert(&ssl_pm->ssl, x509_pm->x509_crt, pkey_pm->pkey);
+ mbedtls_ssl_set_hs_authmode(&ssl_pm->ssl, mode);
+}
diff --git a/thirdparty/lws/mbedtls_wrapper/platform/ssl_port.c b/thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_port.c
index 8c7a31338b..8c7a31338b 100644
--- a/thirdparty/lws/mbedtls_wrapper/platform/ssl_port.c
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_port.c
diff --git a/thirdparty/libwebsockets/tls/private.h b/thirdparty/libwebsockets/tls/private.h
new file mode 100644
index 0000000000..606e2574dc
--- /dev/null
+++ b/thirdparty/libwebsockets/tls/private.h
@@ -0,0 +1,281 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * This is included from core/private.h if LWS_WITH_TLS
+ */
+
+#if defined(LWS_WITH_TLS)
+
+#if defined(USE_WOLFSSL)
+ #if defined(USE_OLD_CYASSL)
+ #if defined(_WIN32)
+ #include <IDE/WIN/user_settings.h>
+ #include <cyassl/ctaocrypt/settings.h>
+ #else
+ #include <cyassl/options.h>
+ #endif
+ #include <cyassl/openssl/ssl.h>
+ #include <cyassl/error-ssl.h>
+ #else
+ #if defined(_WIN32)
+ #include <IDE/WIN/user_settings.h>
+ #include <wolfssl/wolfcrypt/settings.h>
+ #else
+ #include <wolfssl/options.h>
+ #endif
+ #include <wolfssl/openssl/ssl.h>
+ #include <wolfssl/error-ssl.h>
+ #define OPENSSL_NO_TLSEXT
+ #endif /* not USE_OLD_CYASSL */
+#else /* WOLFSSL */
+ #if defined(LWS_WITH_ESP32)
+ #define OPENSSL_NO_TLSEXT
+ #undef MBEDTLS_CONFIG_FILE
+ #define MBEDTLS_CONFIG_FILE <mbedtls/esp_config.h>
+ #include <mbedtls/ssl.h>
+ #include <mbedtls/x509_crt.h>
+ #include "tls/mbedtls/wrapper/include/openssl/ssl.h" /* wrapper !!!! */
+ #else /* not esp32 */
+ #if defined(LWS_WITH_MBEDTLS)
+ #include <mbedtls/ssl.h>
+ #include <mbedtls/x509_crt.h>
+ #include <mbedtls/x509_csr.h>
+ #include "tls/mbedtls/wrapper/include/openssl/ssl.h" /* wrapper !!!! */
+ #else
+ #include <openssl/ssl.h>
+ #include <openssl/evp.h>
+ #include <openssl/err.h>
+ #include <openssl/md5.h>
+ #include <openssl/sha.h>
+ #ifdef LWS_HAVE_OPENSSL_ECDH_H
+ #include <openssl/ecdh.h>
+ #endif
+ #include <openssl/x509v3.h>
+ #endif /* not mbedtls */
+ #if defined(OPENSSL_VERSION_NUMBER)
+ #if (OPENSSL_VERSION_NUMBER < 0x0009080afL)
+/* later openssl defines this to negate the presence of tlsext... but it was only
+ * introduced at 0.9.8j. Earlier versions don't know it exists so don't
+ * define it... making it look like the feature exists...
+ */
+ #define OPENSSL_NO_TLSEXT
+ #endif
+ #endif
+ #endif /* not ESP32 */
+#endif /* not USE_WOLFSSL */
+
+#endif /* LWS_WITH_TLS */
+
+enum lws_tls_extant {
+ LWS_TLS_EXTANT_NO,
+ LWS_TLS_EXTANT_YES,
+ LWS_TLS_EXTANT_ALTERNATIVE
+};
+
+struct lws_context_per_thread;
+
+struct lws_tls_ops {
+ int (*fake_POLLIN_for_buffered)(struct lws_context_per_thread *pt);
+ int (*periodic_housekeeping)(struct lws_context *context, time_t now);
+};
+
+#if defined(LWS_WITH_TLS)
+
+typedef SSL lws_tls_conn;
+typedef SSL_CTX lws_tls_ctx;
+typedef BIO lws_tls_bio;
+typedef X509 lws_tls_x509;
+
+
+#define LWS_SSL_ENABLED(context) (context->tls.use_ssl)
+
+extern const struct lws_tls_ops tls_ops_openssl, tls_ops_mbedtls;
+
+struct lws_context_tls {
+ char alpn_discovered[32];
+ const char *alpn_default;
+ time_t last_cert_check_s;
+};
+
+struct lws_pt_tls {
+ struct lws *pending_read_list; /* linked list */
+};
+
+struct lws_tls_ss_pieces;
+
+struct alpn_ctx {
+ uint8_t data[23];
+ uint8_t len;
+};
+
+struct lws_vhost_tls {
+ lws_tls_ctx *ssl_ctx;
+ lws_tls_ctx *ssl_client_ctx;
+ const char *alpn;
+ struct lws_tls_ss_pieces *ss; /* for acme tls certs */
+ char *alloc_cert_path;
+ char *key_path;
+#if defined(LWS_WITH_MBEDTLS)
+ lws_tls_x509 *x509_client_CA;
+#endif
+ char ecdh_curve[16];
+ struct alpn_ctx alpn_ctx;
+
+ int use_ssl;
+ int allow_non_ssl_on_ssl_port;
+ int ssl_info_event_mask;
+
+ unsigned int user_supplied_ssl_ctx:1;
+ unsigned int skipped_certs:1;
+};
+
+struct lws_lws_tls {
+ lws_tls_conn *ssl;
+ lws_tls_bio *client_bio;
+ struct lws *pending_read_list_prev, *pending_read_list_next;
+ unsigned int use_ssl;
+ unsigned int redirect_to_https:1;
+};
+
+LWS_EXTERN void
+lws_context_init_alpn(struct lws_vhost *vhost);
+LWS_EXTERN enum lws_tls_extant
+lws_tls_use_any_upgrade_check_extant(const char *name);
+LWS_EXTERN int openssl_websocket_private_data_index;
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_pending(struct lws *wsi);
+LWS_EXTERN int
+lws_context_init_ssl_library(const struct lws_context_creation_info *info);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd);
+LWS_EXTERN int
+lws_ssl_close(struct lws *wsi);
+LWS_EXTERN void
+lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost);
+LWS_EXTERN void
+lws_ssl_context_destroy(struct lws_context *context);
+void
+__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi);
+LWS_VISIBLE void
+lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi);
+LWS_EXTERN int
+lws_ssl_client_bio_create(struct lws *wsi);
+LWS_EXTERN int
+lws_ssl_client_connect1(struct lws *wsi);
+LWS_EXTERN int
+lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len);
+LWS_EXTERN void
+lws_ssl_elaborate_error(void);
+LWS_EXTERN int
+lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt);
+LWS_EXTERN int
+lws_gate_accepts(struct lws_context *context, int on);
+LWS_EXTERN void
+lws_ssl_bind_passphrase(lws_tls_ctx *ssl_ctx,
+ const struct lws_context_creation_info *info);
+LWS_EXTERN void
+lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret);
+LWS_EXTERN int
+lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type,
+ union lws_tls_cert_info_results *buf, size_t len);
+LWS_EXTERN int
+lws_tls_check_all_cert_lifetimes(struct lws_context *context);
+LWS_EXTERN int
+lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi,
+ const char *cert, const char *private_key,
+ const char *mem_cert, size_t len_mem_cert,
+ const char *mem_privkey, size_t mem_privkey_len);
+LWS_EXTERN enum lws_tls_extant
+lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert,
+ const char *private_key);
+LWS_EXTERN int
+lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
+ const char *inbuf, lws_filepos_t inlen,
+ uint8_t **buf, lws_filepos_t *amount);
+
+#if !defined(LWS_NO_SERVER)
+ LWS_EXTERN int
+ lws_context_init_server_ssl(const struct lws_context_creation_info *info,
+ struct lws_vhost *vhost);
+ void
+ lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost);
+#else
+ #define lws_context_init_server_ssl(_a, _b) (0)
+ #define lws_tls_acme_sni_cert_destroy(_a)
+#endif
+
+LWS_EXTERN void
+lws_ssl_destroy(struct lws_vhost *vhost);
+LWS_EXTERN char *
+lws_ssl_get_error_string(int status, int ret, char *buf, size_t len);
+
+/*
+ * lws_tls_ abstract backend implementations
+ */
+
+LWS_EXTERN int
+lws_tls_server_client_cert_verify_config(struct lws_vhost *vh);
+LWS_EXTERN int
+lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info,
+ struct lws_vhost *vhost, struct lws *wsi);
+LWS_EXTERN int
+lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd);
+
+LWS_EXTERN enum lws_ssl_capable_status
+lws_tls_server_accept(struct lws *wsi);
+
+LWS_EXTERN enum lws_ssl_capable_status
+lws_tls_server_abort_connection(struct lws *wsi);
+
+LWS_EXTERN enum lws_ssl_capable_status
+__lws_tls_shutdown(struct lws *wsi);
+
+LWS_EXTERN enum lws_ssl_capable_status
+lws_tls_client_connect(struct lws *wsi);
+LWS_EXTERN int
+lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len);
+LWS_EXTERN int
+lws_tls_client_create_vhost_context(struct lws_vhost *vh,
+ const struct lws_context_creation_info *info,
+ const char *cipher_list,
+ const char *ca_filepath,
+ const char *cert_filepath,
+ const char *private_key_filepath);
+
+LWS_EXTERN lws_tls_ctx *
+lws_tls_ctx_from_wsi(struct lws *wsi);
+LWS_EXTERN int
+lws_ssl_get_error(struct lws *wsi, int n);
+
+LWS_EXTERN int
+lws_context_init_client_ssl(const struct lws_context_creation_info *info,
+ struct lws_vhost *vhost);
+
+LWS_EXTERN void
+lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret);
+
+int
+lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt);
+
+#endif \ No newline at end of file
diff --git a/thirdparty/libwebsockets/tls/tls-client.c b/thirdparty/libwebsockets/tls/tls-client.c
new file mode 100644
index 0000000000..70eb6f6078
--- /dev/null
+++ b/thirdparty/libwebsockets/tls/tls-client.c
@@ -0,0 +1,150 @@
+/*
+ * libwebsockets - client-related ssl code independent of backend
+ *
+ * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+
+int
+lws_ssl_client_connect1(struct lws *wsi)
+{
+ struct lws_context *context = wsi->context;
+ int n = 0;
+
+ lws_latency_pre(context, wsi);
+ n = lws_tls_client_connect(wsi);
+ lws_latency(context, wsi, "SSL_connect hs", n, n > 0);
+
+ switch (n) {
+ case LWS_SSL_CAPABLE_ERROR:
+ return -1;
+ case LWS_SSL_CAPABLE_DONE:
+ return 1; /* connected */
+ case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
+ lws_callback_on_writable(wsi);
+ /* fallthru */
+ case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
+ lwsi_set_state(wsi, LRS_WAITING_SSL);
+ break;
+ case LWS_SSL_CAPABLE_MORE_SERVICE:
+ break;
+ }
+
+ return 0; /* retry */
+}
+
+int
+lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len)
+{
+ int n = 0;
+
+ if (lwsi_state(wsi) == LRS_WAITING_SSL) {
+ lws_latency_pre(wsi->context, wsi);
+
+ n = lws_tls_client_connect(wsi);
+ lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
+ lws_latency(wsi->context, wsi,
+ "SSL_connect LRS_WAITING_SSL", n, n > 0);
+
+ switch (n) {
+ case LWS_SSL_CAPABLE_ERROR:
+ lws_snprintf(errbuf, len, "client connect failed");
+ return -1;
+ case LWS_SSL_CAPABLE_DONE:
+ break; /* connected */
+ case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
+ lws_callback_on_writable(wsi);
+ /* fallthru */
+ case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
+ lwsi_set_state(wsi, LRS_WAITING_SSL);
+ /* fallthru */
+ case LWS_SSL_CAPABLE_MORE_SERVICE:
+ return 0;
+ }
+ }
+
+ if (lws_tls_client_confirm_peer_cert(wsi, errbuf, len))
+ return -1;
+
+ return 1;
+}
+
+
+int lws_context_init_client_ssl(const struct lws_context_creation_info *info,
+ struct lws_vhost *vhost)
+{
+ const char *ca_filepath = info->ssl_ca_filepath;
+ const char *cipher_list = info->ssl_cipher_list;
+ const char *private_key_filepath = info->ssl_private_key_filepath;
+ const char *cert_filepath = info->ssl_cert_filepath;
+ struct lws wsi;
+
+ if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW)
+ return 0;
+
+ /*
+ * for backwards-compatibility default to using ssl_... members, but
+ * if the newer client-specific ones are given, use those
+ */
+ if (info->client_ssl_cipher_list)
+ cipher_list = info->client_ssl_cipher_list;
+ if (info->client_ssl_cert_filepath)
+ cert_filepath = info->client_ssl_cert_filepath;
+ if (info->client_ssl_private_key_filepath)
+ private_key_filepath = info->client_ssl_private_key_filepath;
+
+ if (info->client_ssl_ca_filepath)
+ ca_filepath = info->client_ssl_ca_filepath;
+
+ if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
+ return 0;
+
+ if (vhost->tls.ssl_client_ctx)
+ return 0;
+
+ if (info->provided_client_ssl_ctx) {
+ /* use the provided OpenSSL context if given one */
+ vhost->tls.ssl_client_ctx = info->provided_client_ssl_ctx;
+ /* nothing for lib to delete */
+ vhost->tls.user_supplied_ssl_ctx = 1;
+
+ return 0;
+ }
+
+ if (lws_tls_client_create_vhost_context(vhost, info, cipher_list,
+ ca_filepath, cert_filepath,
+ private_key_filepath))
+ return 1;
+
+ lwsl_notice("created client ssl context for %s\n", vhost->name);
+
+ /*
+ * give him a fake wsi with context set, so he can use
+ * lws_get_context() in the callback
+ */
+ memset(&wsi, 0, sizeof(wsi));
+ wsi.vhost = vhost;
+ wsi.context = vhost->context;
+
+ vhost->protocols[0].callback(&wsi,
+ LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
+ vhost->tls.ssl_client_ctx, NULL, 0);
+
+ return 0;
+}
diff --git a/thirdparty/libwebsockets/tls/tls-server.c b/thirdparty/libwebsockets/tls/tls-server.c
new file mode 100644
index 0000000000..440e790660
--- /dev/null
+++ b/thirdparty/libwebsockets/tls/tls-server.c
@@ -0,0 +1,382 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+
+#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
+ OPENSSL_VERSION_NUMBER >= 0x10002000L)
+static int
+alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg)
+{
+#if !defined(LWS_WITH_MBEDTLS)
+ struct alpn_ctx *alpn_ctx = (struct alpn_ctx *)arg;
+
+ if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_ctx->data,
+ alpn_ctx->len, in, inlen) !=
+ OPENSSL_NPN_NEGOTIATED)
+ return SSL_TLSEXT_ERR_NOACK;
+#endif
+
+ return SSL_TLSEXT_ERR_OK;
+}
+#endif
+
+void
+lws_context_init_alpn(struct lws_vhost *vhost)
+{
+#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
+ OPENSSL_VERSION_NUMBER >= 0x10002000L)
+ const char *alpn_comma = vhost->context->tls.alpn_default;
+
+ if (vhost->tls.alpn)
+ alpn_comma = vhost->tls.alpn;
+
+ lwsl_info(" Server '%s' advertising ALPN: %s\n",
+ vhost->name, alpn_comma);
+ vhost->tls.alpn_ctx.len = lws_alpn_comma_to_openssl(alpn_comma,
+ vhost->tls.alpn_ctx.data,
+ sizeof(vhost->tls.alpn_ctx.data) - 1);
+
+ SSL_CTX_set_alpn_select_cb(vhost->tls.ssl_ctx, alpn_cb, &vhost->tls.alpn_ctx);
+#else
+ lwsl_err(
+ " HTTP2 / ALPN configured but not supported by OpenSSL 0x%lx\n",
+ OPENSSL_VERSION_NUMBER);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+}
+
+int
+lws_tls_server_conn_alpn(struct lws *wsi)
+{
+#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
+ OPENSSL_VERSION_NUMBER >= 0x10002000L)
+ const unsigned char *name = NULL;
+ char cstr[10];
+ unsigned len;
+
+ SSL_get0_alpn_selected(wsi->tls.ssl, &name, &len);
+ if (!len) {
+ lwsl_info("no ALPN upgrade\n");
+ return 0;
+ }
+
+ if (len > sizeof(cstr) - 1)
+ len = sizeof(cstr) - 1;
+
+ memcpy(cstr, name, len);
+ cstr[len] = '\0';
+
+ lwsl_info("negotiated '%s' using ALPN\n", cstr);
+ wsi->tls.use_ssl |= LCCSCF_USE_SSL;
+
+ return lws_role_call_alpn_negotiated(wsi, (const char *)cstr);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+
+ return 0;
+}
+
+LWS_VISIBLE int
+lws_context_init_server_ssl(const struct lws_context_creation_info *info,
+ struct lws_vhost *vhost)
+{
+ struct lws_context *context = vhost->context;
+ struct lws wsi;
+
+ if (!lws_check_opt(info->options,
+ LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
+ vhost->tls.use_ssl = 0;
+
+ return 0;
+ }
+
+ /*
+ * If he is giving a cert filepath, take it as a sign he wants to use
+ * it on this vhost. User code can leave the cert filepath NULL and
+ * set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in
+ * which case he's expected to set up the cert himself at
+ * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
+ * provides the vhost SSL_CTX * in the user parameter.
+ */
+ if (info->ssl_cert_filepath)
+ vhost->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX;
+
+ if (info->port != CONTEXT_PORT_NO_LISTEN) {
+
+ vhost->tls.use_ssl = lws_check_opt(vhost->options,
+ LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX);
+
+ if (vhost->tls.use_ssl && info->ssl_cipher_list)
+ lwsl_notice(" SSL ciphers: '%s'\n",
+ info->ssl_cipher_list);
+
+ if (vhost->tls.use_ssl)
+ lwsl_notice(" Using SSL mode\n");
+ else
+ lwsl_notice(" Using non-SSL mode\n");
+ }
+
+ /*
+ * give him a fake wsi with context + vhost set, so he can use
+ * lws_get_context() in the callback
+ */
+ memset(&wsi, 0, sizeof(wsi));
+ wsi.vhost = vhost;
+ wsi.context = context;
+
+ /*
+ * as a server, if we are requiring clients to identify themselves
+ * then set the backend up for it
+ */
+ if (lws_check_opt(info->options,
+ LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT))
+ /* Normally SSL listener rejects non-ssl, optionally allow */
+ vhost->tls.allow_non_ssl_on_ssl_port = 1;
+
+ /*
+ * give user code a chance to load certs into the server
+ * allowing it to verify incoming client certs
+ */
+ if (vhost->tls.use_ssl) {
+ if (lws_tls_server_vhost_backend_init(info, vhost, &wsi))
+ return -1;
+
+ lws_tls_server_client_cert_verify_config(vhost);
+
+ if (vhost->protocols[0].callback(&wsi,
+ LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
+ vhost->tls.ssl_ctx, vhost, 0))
+ return -1;
+ }
+
+ if (vhost->tls.use_ssl)
+ lws_context_init_alpn(vhost);
+
+ return 0;
+}
+
+LWS_VISIBLE int
+lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
+{
+ struct lws_context *context = wsi->context;
+ struct lws_vhost *vh;
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ int n;
+ char buf[256];
+
+ (void)buf;
+
+ if (!LWS_SSL_ENABLED(wsi->vhost))
+ return 0;
+
+ switch (lwsi_state(wsi)) {
+ case LRS_SSL_INIT:
+
+ if (wsi->tls.ssl)
+ lwsl_err("%s: leaking ssl\n", __func__);
+ if (accept_fd == LWS_SOCK_INVALID)
+ assert(0);
+ if (context->simultaneous_ssl_restriction &&
+ context->simultaneous_ssl >=
+ context->simultaneous_ssl_restriction) {
+ lwsl_notice("unable to deal with SSL connection\n");
+ return 1;
+ }
+
+ if (lws_tls_server_new_nonblocking(wsi, accept_fd)) {
+ if (accept_fd != LWS_SOCK_INVALID)
+ compatible_close(accept_fd);
+ goto fail;
+ }
+
+ if (context->simultaneous_ssl_restriction &&
+ ++context->simultaneous_ssl ==
+ context->simultaneous_ssl_restriction)
+ /* that was the last allowed SSL connection */
+ lws_gate_accepts(context, 0);
+
+#if defined(LWS_WITH_STATS)
+ context->updated = 1;
+#endif
+ /*
+ * we are not accepted yet, but we need to enter ourselves
+ * as a live connection. That way we can retry when more
+ * pieces come if we're not sorted yet
+ */
+ lwsi_set_state(wsi, LRS_SSL_ACK_PENDING);
+
+ lws_pt_lock(pt, __func__);
+ if (__insert_wsi_socket_into_fds(context, wsi)) {
+ lwsl_err("%s: failed to insert into fds\n", __func__);
+ goto fail;
+ }
+ lws_pt_unlock(pt);
+
+ lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
+ context->timeout_secs);
+
+ lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n");
+
+ /* fallthru */
+
+ case LRS_SSL_ACK_PENDING:
+
+ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+ lwsl_err("%s: lws_change_pollfd failed\n", __func__);
+ goto fail;
+ }
+
+ lws_latency_pre(context, wsi);
+
+ if (wsi->vhost->tls.allow_non_ssl_on_ssl_port) {
+
+ n = recv(wsi->desc.sockfd, (char *)pt->serv_buf,
+ context->pt_serv_buf_size, MSG_PEEK);
+
+ /*
+ * optionally allow non-SSL connect on SSL listening socket
+ * This is disabled by default, if enabled it goes around any
+ * SSL-level access control (eg, client-side certs) so leave
+ * it disabled unless you know it's not a problem for you
+ */
+ if (n >= 1 && pt->serv_buf[0] >= ' ') {
+ /*
+ * TLS content-type for Handshake is 0x16, and
+ * for ChangeCipherSpec Record, it's 0x14
+ *
+ * A non-ssl session will start with the HTTP
+ * method in ASCII. If we see it's not a legit
+ * SSL handshake kill the SSL for this
+ * connection and try to handle as a HTTP
+ * connection upgrade directly.
+ */
+ wsi->tls.use_ssl = 0;
+
+ lws_tls_server_abort_connection(wsi);
+ /*
+ * care... this creates wsi with no ssl
+ * when ssl is enabled and normally
+ * mandatory
+ */
+ wsi->tls.ssl = NULL;
+ if (lws_check_opt(context->options,
+ LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
+ wsi->tls.redirect_to_https = 1;
+ lwsl_debug("accepted as non-ssl\n");
+ goto accepted;
+ }
+ if (!n) {
+ /*
+ * connection is gone, fail out
+ */
+ lwsl_debug("PEEKed 0\n");
+ goto fail;
+ }
+ if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
+ LWS_ERRNO == LWS_EWOULDBLOCK)) {
+ /*
+ * well, we get no way to know ssl or not
+ * so go around again waiting for something
+ * to come and give us a hint, or timeout the
+ * connection.
+ */
+ if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
+ lwsl_info("%s: change_pollfd failed\n",
+ __func__);
+ return -1;
+ }
+
+ lwsl_info("SSL_ERROR_WANT_READ\n");
+ return 0;
+ }
+ }
+
+ /* normal SSL connection processing path */
+
+#if defined(LWS_WITH_STATS)
+ if (!wsi->accept_start_us)
+ wsi->accept_start_us = time_in_microseconds();
+#endif
+ errno = 0;
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
+ n = lws_tls_server_accept(wsi);
+ lws_latency(context, wsi,
+ "SSL_accept LRS_SSL_ACK_PENDING\n", n, n == 1);
+ lwsl_info("SSL_accept says %d\n", n);
+ switch (n) {
+ case LWS_SSL_CAPABLE_DONE:
+ break;
+ case LWS_SSL_CAPABLE_ERROR:
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
+ lwsl_info("SSL_accept failed socket %u: %d\n",
+ wsi->desc.sockfd, n);
+ wsi->socket_is_permanently_unusable = 1;
+ goto fail;
+
+ default: /* MORE_SERVICE */
+ return 0;
+ }
+
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1);
+#if defined(LWS_WITH_STATS)
+ lws_stats_atomic_bump(wsi->context, pt,
+ LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY,
+ time_in_microseconds() - wsi->accept_start_us);
+ wsi->accept_start_us = time_in_microseconds();
+#endif
+
+accepted:
+
+ /* adapt our vhost to match the SNI SSL_CTX that was chosen */
+ vh = context->vhost_list;
+ while (vh) {
+ if (!vh->being_destroyed && wsi->tls.ssl &&
+ vh->tls.ssl_ctx == lws_tls_ctx_from_wsi(wsi)) {
+ lwsl_info("setting wsi to vh %s\n", vh->name);
+ wsi->vhost = vh;
+ break;
+ }
+ vh = vh->vhost_next;
+ }
+
+ /* OK, we are accepted... give him some time to negotiate */
+ lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
+ context->timeout_secs);
+
+ lwsi_set_state(wsi, LRS_ESTABLISHED);
+ if (lws_tls_server_conn_alpn(wsi))
+ goto fail;
+ lwsl_debug("accepted new SSL conn\n");
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+
+fail:
+ return 1;
+}
+
diff --git a/thirdparty/libwebsockets/tls/tls.c b/thirdparty/libwebsockets/tls/tls.c
new file mode 100644
index 0000000000..92b7c5593c
--- /dev/null
+++ b/thirdparty/libwebsockets/tls/tls.c
@@ -0,0 +1,522 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+
+/*
+ * fakes POLLIN on all tls guys with buffered rx
+ *
+ * returns nonzero if any tls guys had POLLIN faked
+ */
+
+int
+lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt)
+{
+ struct lws *wsi, *wsi_next;
+ int ret = 0;
+
+ wsi = pt->tls.pending_read_list;
+ while (wsi && wsi->position_in_fds_table != LWS_NO_FDS_POS) {
+ wsi_next = wsi->tls.pending_read_list_next;
+ pt->fds[wsi->position_in_fds_table].revents |=
+ pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
+ ret |= pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN;
+
+ wsi = wsi_next;
+ }
+
+ return !!ret;
+}
+
+void
+__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
+{
+ struct lws_context *context = wsi->context;
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+
+ if (!wsi->tls.pending_read_list_prev &&
+ !wsi->tls.pending_read_list_next &&
+ pt->tls.pending_read_list != wsi)
+ /* we are not on the list */
+ return;
+
+ /* point previous guy's next to our next */
+ if (!wsi->tls.pending_read_list_prev)
+ pt->tls.pending_read_list = wsi->tls.pending_read_list_next;
+ else
+ wsi->tls.pending_read_list_prev->tls.pending_read_list_next =
+ wsi->tls.pending_read_list_next;
+
+ /* point next guy's previous to our previous */
+ if (wsi->tls.pending_read_list_next)
+ wsi->tls.pending_read_list_next->tls.pending_read_list_prev =
+ wsi->tls.pending_read_list_prev;
+
+ wsi->tls.pending_read_list_prev = NULL;
+ wsi->tls.pending_read_list_next = NULL;
+}
+
+void
+lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+ lws_pt_lock(pt, __func__);
+ __lws_ssl_remove_wsi_from_buffered_list(wsi);
+ lws_pt_unlock(pt);
+}
+
+#if defined(LWS_WITH_ESP32)
+int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
+ lws_filepos_t *amount)
+{
+ nvs_handle nvh;
+ size_t s;
+ int n = 0;
+
+ ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
+ if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
+ n = 1;
+ goto bail;
+ }
+ *buf = lws_malloc(s + 1, "alloc_file");
+ if (!*buf) {
+ n = 2;
+ goto bail;
+ }
+ if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
+ lws_free(*buf);
+ n = 1;
+ goto bail;
+ }
+
+ *amount = s;
+ (*buf)[s] = '\0';
+
+ lwsl_notice("%s: nvs: read %s, %d bytes\n", __func__, filename, (int)s);
+
+bail:
+ nvs_close(nvh);
+
+ return n;
+}
+#else
+int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
+ lws_filepos_t *amount)
+{
+ FILE *f;
+ size_t s;
+ int n = 0;
+
+ f = fopen(filename, "rb");
+ if (f == NULL) {
+ n = 1;
+ goto bail;
+ }
+
+ if (fseek(f, 0, SEEK_END) != 0) {
+ n = 1;
+ goto bail;
+ }
+
+ s = ftell(f);
+ if (s == (size_t)-1) {
+ n = 1;
+ goto bail;
+ }
+
+ if (fseek(f, 0, SEEK_SET) != 0) {
+ n = 1;
+ goto bail;
+ }
+
+ *buf = lws_malloc(s, "alloc_file");
+ if (!*buf) {
+ n = 2;
+ goto bail;
+ }
+
+ if (fread(*buf, s, 1, f) != 1) {
+ lws_free(*buf);
+ n = 1;
+ goto bail;
+ }
+
+ *amount = s;
+
+bail:
+ if (f)
+ fclose(f);
+
+ return n;
+
+}
+#endif
+
+int
+lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
+ const char *inbuf, lws_filepos_t inlen,
+ uint8_t **buf, lws_filepos_t *amount)
+{
+ const uint8_t *pem, *p, *end;
+ uint8_t *q;
+ lws_filepos_t len;
+ int n;
+
+ if (filename) {
+ n = alloc_file(context, filename, (uint8_t **)&pem, &len);
+ if (n)
+ return n;
+ } else {
+ pem = (const uint8_t *)inbuf;
+ len = inlen;
+ }
+
+ /* trim the first line */
+
+ p = pem;
+ end = p + len;
+ if (strncmp((char *)p, "-----", 5))
+ goto bail;
+ p += 5;
+ while (p < end && *p != '\n' && *p != '-')
+ p++;
+
+ if (*p != '-')
+ goto bail;
+
+ while (p < end && *p != '\n')
+ p++;
+
+ if (p >= end)
+ goto bail;
+
+ p++;
+
+ /* trim the last line */
+
+ q = (uint8_t *)end - 2;
+
+ while (q > pem && *q != '\n')
+ q--;
+
+ if (*q != '\n')
+ goto bail;
+
+ *q = '\0';
+
+ *amount = lws_b64_decode_string((char *)p, (char *)pem,
+ (int)(long long)len);
+ *buf = (uint8_t *)pem;
+
+ return 0;
+
+bail:
+ lws_free((uint8_t *)pem);
+
+ return 4;
+}
+
+int
+lws_tls_check_cert_lifetime(struct lws_vhost *v)
+{
+ union lws_tls_cert_info_results ir;
+ time_t now = (time_t)lws_now_secs(), life = 0;
+ struct lws_acme_cert_aging_args caa;
+ int n;
+
+ if (v->tls.ssl_ctx && !v->tls.skipped_certs) {
+
+ if (now < 1464083026) /* May 2016 */
+ /* our clock is wrong and we can't judge the certs */
+ return -1;
+
+ n = lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, &ir, 0);
+ if (n)
+ return 1;
+
+ life = (ir.time - now) / (24 * 3600);
+ lwsl_notice(" vhost %s: cert expiry: %dd\n", v->name, (int)life);
+ } else
+ lwsl_notice(" vhost %s: no cert\n", v->name);
+
+ memset(&caa, 0, sizeof(caa));
+ caa.vh = v;
+ lws_broadcast(v->context, LWS_CALLBACK_VHOST_CERT_AGING, (void *)&caa,
+ (size_t)(ssize_t)life);
+
+ return 0;
+}
+
+int
+lws_tls_check_all_cert_lifetimes(struct lws_context *context)
+{
+ struct lws_vhost *v = context->vhost_list;
+
+ while (v) {
+ if (lws_tls_check_cert_lifetime(v) < 0)
+ return -1;
+ v = v->vhost_next;
+ }
+
+ return 0;
+}
+#if !defined(LWS_WITH_ESP32) && !defined(LWS_PLAT_OPTEE)
+static int
+lws_tls_extant(const char *name)
+{
+ /* it exists if we can open it... */
+ int fd = open(name, O_RDONLY), n;
+ char buf[1];
+
+ if (fd < 0)
+ return 1;
+
+ /* and we can read at least one byte out of it */
+ n = read(fd, buf, 1);
+ close(fd);
+
+ return n != 1;
+}
+#endif
+/*
+ * Returns 0 if the filepath "name" exists and can be read from.
+ *
+ * In addition, if "name".upd exists, backup "name" to "name.old.1"
+ * and rename "name".upd to "name" before reporting its existence.
+ *
+ * There are four situations and three results possible:
+ *
+ * 1) LWS_TLS_EXTANT_NO: There are no certs at all (we are waiting for them to
+ * be provisioned). We also feel like this if we need privs we don't have
+ * any more to look in the directory.
+ *
+ * 2) There are provisioned certs written (xxx.upd) and we still have root
+ * privs... in this case we rename any existing cert to have a backup name
+ * and move the upd cert into place with the correct name. This then becomes
+ * situation 4 for the caller.
+ *
+ * 3) LWS_TLS_EXTANT_ALTERNATIVE: There are provisioned certs written (xxx.upd)
+ * but we no longer have the privs needed to read or rename them. In this
+ * case, indicate that the caller should use temp copies if any we do have
+ * rights to access. This is normal after we have updated the cert.
+ *
+ * But if we dropped privs, we can't detect the provisioned xxx.upd cert +
+ * key, because we can't see in the dir. So we have to upgrade NO to
+ * ALTERNATIVE when we actually have the in-memory alternative.
+ *
+ * 4) LWS_TLS_EXTANT_YES: The certs are present with the correct name and we
+ * have the rights to read them.
+ */
+enum lws_tls_extant
+lws_tls_use_any_upgrade_check_extant(const char *name)
+{
+#if !defined(LWS_PLAT_OPTEE)
+
+ int n;
+
+#if !defined(LWS_WITH_ESP32)
+ char buf[256];
+
+ lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
+ if (!lws_tls_extant(buf)) {
+ /* ah there is an updated file... how about the desired file? */
+ if (!lws_tls_extant(name)) {
+ /* rename the desired file */
+ for (n = 0; n < 50; n++) {
+ lws_snprintf(buf, sizeof(buf) - 1,
+ "%s.old.%d", name, n);
+ if (!rename(name, buf))
+ break;
+ }
+ if (n == 50) {
+ lwsl_notice("unable to rename %s\n", name);
+
+ return LWS_TLS_EXTANT_ALTERNATIVE;
+ }
+ lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
+ }
+ /* desired file is out of the way, rename the updated file */
+ if (rename(buf, name)) {
+ lwsl_notice("unable to rename %s to %s\n", buf, name);
+
+ return LWS_TLS_EXTANT_ALTERNATIVE;
+ }
+ }
+
+ if (lws_tls_extant(name))
+ return LWS_TLS_EXTANT_NO;
+#else
+ nvs_handle nvh;
+ size_t s = 8192;
+
+ if (nvs_open("lws-station", NVS_READWRITE, &nvh)) {
+ lwsl_notice("%s: can't open nvs\n", __func__);
+ return LWS_TLS_EXTANT_NO;
+ }
+
+ n = nvs_get_blob(nvh, name, NULL, &s);
+ nvs_close(nvh);
+
+ if (n)
+ return LWS_TLS_EXTANT_NO;
+#endif
+#endif
+ return LWS_TLS_EXTANT_YES;
+}
+
+/*
+ * LWS_TLS_EXTANT_NO : skip adding the cert
+ * LWS_TLS_EXTANT_YES : use the cert and private key paths normally
+ * LWS_TLS_EXTANT_ALTERNATIVE: normal paths not usable, try alternate if poss
+ */
+enum lws_tls_extant
+lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert,
+ const char *private_key)
+{
+ int n, m;
+
+ /*
+ * The user code can choose to either pass the cert and
+ * key filepaths using the info members like this, or it can
+ * leave them NULL; force the vhost SSL_CTX init using the info
+ * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
+ * set up the cert himself using the user callback
+ * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
+ * happened just above and has the vhost SSL_CTX * in the user
+ * parameter.
+ */
+
+ if (!cert || !private_key)
+ return LWS_TLS_EXTANT_NO;
+
+ n = lws_tls_use_any_upgrade_check_extant(cert);
+ if (n == LWS_TLS_EXTANT_ALTERNATIVE)
+ return LWS_TLS_EXTANT_ALTERNATIVE;
+ m = lws_tls_use_any_upgrade_check_extant(private_key);
+ if (m == LWS_TLS_EXTANT_ALTERNATIVE)
+ return LWS_TLS_EXTANT_ALTERNATIVE;
+
+ if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) &&
+ (vhost->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) {
+ lwsl_notice("Ignoring missing %s or %s\n", cert, private_key);
+ vhost->tls.skipped_certs = 1;
+
+ return LWS_TLS_EXTANT_NO;
+ }
+
+ /*
+ * the cert + key exist
+ */
+
+ return LWS_TLS_EXTANT_YES;
+}
+
+#if !defined(LWS_NO_SERVER)
+/*
+ * update the cert for every vhost using the given path
+ */
+
+LWS_VISIBLE int
+lws_tls_cert_updated(struct lws_context *context, const char *certpath,
+ const char *keypath,
+ const char *mem_cert, size_t len_mem_cert,
+ const char *mem_privkey, size_t len_mem_privkey)
+{
+ struct lws wsi;
+
+ wsi.context = context;
+
+ lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
+ wsi.vhost = v;
+ if (v->tls.alloc_cert_path && v->tls.key_path &&
+ !strcmp(v->tls.alloc_cert_path, certpath) &&
+ !strcmp(v->tls.key_path, keypath)) {
+ lws_tls_server_certs_load(v, &wsi, certpath, keypath,
+ mem_cert, len_mem_cert,
+ mem_privkey, len_mem_privkey);
+
+ if (v->tls.skipped_certs)
+ lwsl_notice("%s: vhost %s: cert unset\n",
+ __func__, v->name);
+ }
+ } lws_end_foreach_ll(v, vhost_next);
+
+ return 0;
+}
+#endif
+
+int
+lws_gate_accepts(struct lws_context *context, int on)
+{
+ struct lws_vhost *v = context->vhost_list;
+
+ lwsl_notice("%s: on = %d\n", __func__, on);
+
+#if defined(LWS_WITH_STATS)
+ context->updated = 1;
+#endif
+
+ while (v) {
+ if (v->tls.use_ssl && v->lserv_wsi &&
+ lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on,
+ (LWS_POLLIN) * on))
+ lwsl_notice("Unable to set accept POLLIN %d\n", on);
+
+ v = v->vhost_next;
+ }
+
+ return 0;
+}
+
+/* comma-separated alpn list, like "h2,http/1.1" to openssl alpn format */
+
+int
+lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len)
+{
+ uint8_t *oos = os, *plen = NULL;
+
+ while (*comma && len > 1) {
+ if (!plen && *comma == ' ') {
+ comma++;
+ continue;
+ }
+ if (!plen) {
+ plen = os++;
+ len--;
+ }
+
+ if (*comma == ',') {
+ *plen = lws_ptr_diff(os, plen + 1);
+ plen = NULL;
+ comma++;
+ } else {
+ *os++ = *comma++;
+ len--;
+ }
+ }
+
+ if (plen)
+ *plen = lws_ptr_diff(os, plen + 1);
+
+ return lws_ptr_diff(os, oos);
+}
+
diff --git a/thirdparty/lws/win32helpers/getopt.c b/thirdparty/libwebsockets/win32helpers/getopt.c
index 3bb21f6f28..2181f1cb12 100644
--- a/thirdparty/lws/win32helpers/getopt.c
+++ b/thirdparty/libwebsockets/win32helpers/getopt.c
@@ -1,153 +1,153 @@
-/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */
-
-/*
- * Copyright (c) 1987, 1993, 1994
- * The Regents of the University of California. 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.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
- */
-
-#if 0
-static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
-#endif
-
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#define __P(x) x
-#define _DIAGASSERT(x) assert(x)
-
-#ifdef __weak_alias
-__weak_alias(getopt,_getopt);
-#endif
-
-
-int opterr = 1, /* if error message should be printed */
- optind = 1, /* index into parent argv vector */
- optopt, /* character checked for validity */
- optreset; /* reset getopt */
-char *optarg; /* argument associated with option */
-
-static char * _progname __P((char *));
-int getopt_internal __P((int, char * const *, const char *));
-
-static char *
-_progname(nargv0)
- char * nargv0;
-{
- char * tmp;
-
- _DIAGASSERT(nargv0 != NULL);
-
- tmp = strrchr(nargv0, '/');
- if (tmp)
- tmp++;
- else
- tmp = nargv0;
- return(tmp);
-}
-
-#define BADCH (int)'?'
-#define BADARG (int)':'
-#define EMSG ""
-
-/*
- * getopt --
- * Parse argc/argv argument vector.
- */
-int
-getopt(nargc, nargv, ostr)
- int nargc;
- char * const nargv[];
- const char *ostr;
-{
- static char *__progname = 0;
- static char *place = EMSG; /* option letter processing */
- char *oli; /* option letter list index */
- __progname = __progname?__progname:_progname(*nargv);
-
- _DIAGASSERT(nargv != NULL);
- _DIAGASSERT(ostr != NULL);
-
- if (optreset || !*place) { /* update scanning pointer */
- optreset = 0;
- if (optind >= nargc || *(place = nargv[optind]) != '-') {
- place = EMSG;
- return (-1);
- }
- if (place[1] && *++place == '-' /* found "--" */
- && place[1] == '\0') {
- ++optind;
- place = EMSG;
- return (-1);
- }
- } /* option letter okay? */
- if ((optopt = (int)*place++) == (int)':' ||
- !(oli = strchr(ostr, optopt))) {
- /*
- * if the user didn't specify '-' as an option,
- * assume it means -1.
- */
- if (optopt == (int)'-')
- return (-1);
- if (!*place)
- ++optind;
- if (opterr && *ostr != ':')
- (void)fprintf(stderr,
- "%s: illegal option -- %c\n", __progname, optopt);
- return (BADCH);
- }
- if (*++oli != ':') { /* don't need argument */
- optarg = NULL;
- if (!*place)
- ++optind;
- }
- else { /* need an argument */
- if (*place) /* no white space */
- optarg = place;
- else if (nargc <= ++optind) { /* no arg */
- place = EMSG;
- if (*ostr == ':')
- return (BADARG);
- if (opterr)
- (void)fprintf(stderr,
- "%s: option requires an argument -- %c\n",
- __progname, optopt);
- return (BADCH);
- }
- else /* white space */
- optarg = nargv[optind];
- place = EMSG;
- ++optind;
- }
- return (optopt); /* dump back option letter */
-}
-
+/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */
+
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ */
+
+#if 0
+static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#define __P(x) x
+#define _DIAGASSERT(x) assert(x)
+
+#ifdef __weak_alias
+__weak_alias(getopt,_getopt);
+#endif
+
+
+int opterr = 1, /* if error message should be printed */
+ optind = 1, /* index into parent argv vector */
+ optopt, /* character checked for validity */
+ optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+
+static char * _progname __P((char *));
+int getopt_internal __P((int, char * const *, const char *));
+
+static char *
+_progname(nargv0)
+ char * nargv0;
+{
+ char * tmp;
+
+ _DIAGASSERT(nargv0 != NULL);
+
+ tmp = strrchr(nargv0, '/');
+ if (tmp)
+ tmp++;
+ else
+ tmp = nargv0;
+ return(tmp);
+}
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt(nargc, nargv, ostr)
+ int nargc;
+ char * const nargv[];
+ const char *ostr;
+{
+ static char *__progname = 0;
+ static char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+ __progname = __progname?__progname:_progname(*nargv);
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(ostr != NULL);
+
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-' /* found "--" */
+ && place[1] == '\0') {
+ ++optind;
+ place = EMSG;
+ return (-1);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (optopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)fprintf(stderr,
+ "%s: illegal option -- %c\n", __progname, optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ }
+ else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if (*ostr == ':')
+ return (BADARG);
+ if (opterr)
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ __progname, optopt);
+ return (BADCH);
+ }
+ else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
+
diff --git a/thirdparty/lws/win32helpers/getopt.h b/thirdparty/libwebsockets/win32helpers/getopt.h
index 7137f0379c..7137f0379c 100644
--- a/thirdparty/lws/win32helpers/getopt.h
+++ b/thirdparty/libwebsockets/win32helpers/getopt.h
diff --git a/thirdparty/lws/win32helpers/getopt_long.c b/thirdparty/libwebsockets/win32helpers/getopt_long.c
index 5bcf40060f..22e5fa8945 100644
--- a/thirdparty/lws/win32helpers/getopt_long.c
+++ b/thirdparty/libwebsockets/win32helpers/getopt_long.c
@@ -1,237 +1,240 @@
-
-/*
- * Copyright (c) 1987, 1993, 1994, 1996
- * The Regents of the University of California. 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.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
- */
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "getopt.h"
-
-extern int opterr; /* if error message should be printed */
-extern int optind; /* index into parent argv vector */
-extern int optopt; /* character checked for validity */
-extern int optreset; /* reset getopt */
-extern char *optarg; /* argument associated with option */
-
-#define __P(x) x
-#define _DIAGASSERT(x) assert(x)
-
-static char * __progname __P((char *));
-int getopt_internal __P((int, char * const *, const char *));
-
-static char *
-__progname(nargv0)
- char * nargv0;
-{
- char * tmp;
-
- _DIAGASSERT(nargv0 != NULL);
-
- tmp = strrchr(nargv0, '/');
- if (tmp)
- tmp++;
- else
- tmp = nargv0;
- return(tmp);
-}
-
-#define BADCH (int)'?'
-#define BADARG (int)':'
-#define EMSG ""
-
-/*
- * getopt --
- * Parse argc/argv argument vector.
- */
-int
-getopt_internal(nargc, nargv, ostr)
- int nargc;
- char * const *nargv;
- const char *ostr;
-{
- static char *place = EMSG; /* option letter processing */
- char *oli; /* option letter list index */
-
- _DIAGASSERT(nargv != NULL);
- _DIAGASSERT(ostr != NULL);
-
- if (optreset || !*place) { /* update scanning pointer */
- optreset = 0;
- if (optind >= nargc || *(place = nargv[optind]) != '-') {
- place = EMSG;
- return (-1);
- }
- if (place[1] && *++place == '-') { /* found "--" */
- /* ++optind; */
- place = EMSG;
- return (-2);
- }
- } /* option letter okay? */
- if ((optopt = (int)*place++) == (int)':' ||
- !(oli = strchr(ostr, optopt))) {
- /*
- * if the user didn't specify '-' as an option,
- * assume it means -1.
- */
- if (optopt == (int)'-')
- return (-1);
- if (!*place)
- ++optind;
- if (opterr && *ostr != ':')
- (void)fprintf(stderr,
- "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
- return (BADCH);
- }
- if (*++oli != ':') { /* don't need argument */
- optarg = NULL;
- if (!*place)
- ++optind;
- } else { /* need an argument */
- if (*place) /* no white space */
- optarg = place;
- else if (nargc <= ++optind) { /* no arg */
- place = EMSG;
- if ((opterr) && (*ostr != ':'))
- (void)fprintf(stderr,
- "%s: option requires an argument -- %c\n",
- __progname(nargv[0]), optopt);
- return (BADARG);
- } else /* white space */
- optarg = nargv[optind];
- place = EMSG;
- ++optind;
- }
- return (optopt); /* dump back option letter */
-}
-
-#if 0
-/*
- * getopt --
- * Parse argc/argv argument vector.
- */
-int
-getopt2(nargc, nargv, ostr)
- int nargc;
- char * const *nargv;
- const char *ostr;
-{
- int retval;
-
- if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
- retval = -1;
- ++optind;
- }
- return(retval);
-}
-#endif
-
-/*
- * getopt_long --
- * Parse argc/argv argument vector.
- */
-int
-getopt_long(nargc, nargv, options, long_options, index)
- int nargc;
- char ** nargv;
- char * options;
- struct option * long_options;
- int * index;
-{
- int retval;
-
- _DIAGASSERT(nargv != NULL);
- _DIAGASSERT(options != NULL);
- _DIAGASSERT(long_options != NULL);
- /* index may be NULL */
-
- if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
- char *current_argv = nargv[optind++] + 2, *has_equal;
- int i, current_argv_len, match = -1;
-
- if (*current_argv == '\0') {
- return(-1);
- }
- if ((has_equal = strchr(current_argv, '=')) != NULL) {
- current_argv_len = has_equal - current_argv;
- has_equal++;
- } else
- current_argv_len = strlen(current_argv);
-
- for (i = 0; long_options[i].name; i++) {
- if (strncmp(current_argv, long_options[i].name, current_argv_len))
- continue;
-
- if (strlen(long_options[i].name) == (unsigned)current_argv_len) {
- match = i;
- break;
- }
- if (match == -1)
- match = i;
- }
- if (match != -1) {
- if (long_options[match].has_arg == required_argument ||
- long_options[match].has_arg == optional_argument) {
- if (has_equal)
- optarg = has_equal;
- else
- optarg = nargv[optind++];
- }
- if ((long_options[match].has_arg == required_argument)
- && (optarg == NULL)) {
- /*
- * Missing argument, leading :
- * indicates no error should be generated
- */
- if ((opterr) && (*options != ':'))
- (void)fprintf(stderr,
- "%s: option requires an argument -- %s\n",
- __progname(nargv[0]), current_argv);
- return (BADARG);
- }
- } else { /* No matching argument */
- if ((opterr) && (*options != ':'))
- (void)fprintf(stderr,
- "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
- return (BADCH);
- }
- if (long_options[match].flag) {
- *long_options[match].flag = long_options[match].val;
- retval = 0;
- } else
- retval = long_options[match].val;
- if (index)
- *index = match;
- }
- return(retval);
-}
+
+/*
+ * Copyright (c) 1987, 1993, 1994, 1996
+ * The Regents of the University of California. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ */
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "getopt.h"
+
+#define lws_ptr_diff(head, tail) \
+ ((int)((char *)(head) - (char *)(tail)))
+
+extern int opterr; /* if error message should be printed */
+extern int optind; /* index into parent argv vector */
+extern int optopt; /* character checked for validity */
+extern int optreset; /* reset getopt */
+extern char *optarg; /* argument associated with option */
+
+#define __P(x) x
+#define _DIAGASSERT(x) assert(x)
+
+static char * __progname __P((char *));
+int getopt_internal __P((int, char * const *, const char *));
+
+static char *
+__progname(nargv0)
+ char * nargv0;
+{
+ char * tmp;
+
+ _DIAGASSERT(nargv0 != NULL);
+
+ tmp = strrchr(nargv0, '/');
+ if (tmp)
+ tmp++;
+ else
+ tmp = nargv0;
+ return(tmp);
+}
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_internal(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ static char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(ostr != NULL);
+
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ /* ++optind; */
+ place = EMSG;
+ return (-2);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (optopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)fprintf(stderr,
+ "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ } else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if ((opterr) && (*ostr != ':'))
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ __progname(nargv[0]), optopt);
+ return (BADARG);
+ } else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
+
+#if 0
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt2(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ int retval;
+
+ if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
+ retval = -1;
+ ++optind;
+ }
+ return(retval);
+}
+#endif
+
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long(nargc, nargv, options, long_options, index)
+ int nargc;
+ char ** nargv;
+ char * options;
+ struct option * long_options;
+ int * index;
+{
+ int retval;
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(options != NULL);
+ _DIAGASSERT(long_options != NULL);
+ /* index may be NULL */
+
+ if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
+ char *current_argv = nargv[optind++] + 2, *has_equal;
+ int i, current_argv_len, match = -1;
+
+ if (*current_argv == '\0') {
+ return(-1);
+ }
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ current_argv_len = lws_ptr_diff(has_equal, current_argv);
+ has_equal++;
+ } else
+ current_argv_len = (int)strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ if (strncmp(current_argv, long_options[i].name, current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == (unsigned)current_argv_len) {
+ match = i;
+ break;
+ }
+ if (match == -1)
+ match = i;
+ }
+ if (match != -1) {
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else
+ optarg = nargv[optind++];
+ }
+ if ((long_options[match].has_arg == required_argument)
+ && (optarg == NULL)) {
+ /*
+ * Missing argument, leading :
+ * indicates no error should be generated
+ */
+ if ((opterr) && (*options != ':'))
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %s\n",
+ __progname(nargv[0]), current_argv);
+ return (BADARG);
+ }
+ } else { /* No matching argument */
+ if ((opterr) && (*options != ':'))
+ (void)fprintf(stderr,
+ "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
+ return (BADCH);
+ }
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ retval = 0;
+ } else
+ retval = long_options[match].val;
+ if (index)
+ *index = match;
+ }
+ return(retval);
+}
diff --git a/thirdparty/lws/win32helpers/gettimeofday.c b/thirdparty/libwebsockets/win32helpers/gettimeofday.c
index 35dd73531d..08385c2320 100644
--- a/thirdparty/lws/win32helpers/gettimeofday.c
+++ b/thirdparty/libwebsockets/win32helpers/gettimeofday.c
@@ -1,36 +1,36 @@
-#include <time.h>
-#include <windows.h> //I've omitted context line
-
-#include "gettimeofday.h"
-
-int gettimeofday(struct timeval *tv, struct timezone *tz)
-{
- FILETIME ft;
- unsigned __int64 tmpres = 0;
- static int tzflag;
-
- if (NULL != tv) {
- GetSystemTimeAsFileTime(&ft);
-
- tmpres |= ft.dwHighDateTime;
- tmpres <<= 32;
- tmpres |= ft.dwLowDateTime;
-
- /*converting file time to unix epoch*/
- tmpres /= 10; /*convert into microseconds*/
+#include <time.h>
+#include <windows.h> //I've omitted context line
+
+#include "gettimeofday.h"
+
+int gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+ FILETIME ft;
+ unsigned __int64 tmpres = 0;
+ static int tzflag;
+
+ if (NULL != tv) {
+ GetSystemTimeAsFileTime(&ft);
+
+ tmpres |= ft.dwHighDateTime;
+ tmpres <<= 32;
+ tmpres |= ft.dwLowDateTime;
+
+ /*converting file time to unix epoch*/
+ tmpres /= 10; /*convert into microseconds*/
tmpres -= DELTA_EPOCH_IN_MICROSECS;
- tv->tv_sec = (long)(tmpres / 1000000UL);
- tv->tv_usec = (long)(tmpres % 1000000UL);
- }
-
- if (NULL != tz) {
- if (!tzflag) {
- _tzset();
- tzflag++;
- }
- tz->tz_minuteswest = _timezone / 60;
- tz->tz_dsttime = _daylight;
- }
-
- return 0;
-}
+ tv->tv_sec = (long)(tmpres / 1000000UL);
+ tv->tv_usec = (long)(tmpres % 1000000UL);
+ }
+
+ if (NULL != tz) {
+ if (!tzflag) {
+ _tzset();
+ tzflag++;
+ }
+ tz->tz_minuteswest = _timezone / 60;
+ tz->tz_dsttime = _daylight;
+ }
+
+ return 0;
+}
diff --git a/thirdparty/lws/win32helpers/gettimeofday.h b/thirdparty/libwebsockets/win32helpers/gettimeofday.h
index 33e7a750fe..33e7a750fe 100644
--- a/thirdparty/lws/win32helpers/gettimeofday.h
+++ b/thirdparty/libwebsockets/win32helpers/gettimeofday.h
diff --git a/thirdparty/lws/client/client.c b/thirdparty/lws/client/client.c
deleted file mode 100644
index ded4e4bf0b..0000000000
--- a/thirdparty/lws/client/client.c
+++ /dev/null
@@ -1,1304 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-int
-lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
-{
- int m;
-
- switch (wsi->mode) {
- case LWSCM_WSCL_WAITING_PROXY_REPLY:
- case LWSCM_WSCL_ISSUE_HANDSHAKE:
- case LWSCM_WSCL_WAITING_SERVER_REPLY:
- case LWSCM_WSCL_WAITING_EXTENSION_CONNECT:
- case LWSCM_WS_CLIENT:
- while (len) {
- /*
- * we were accepting input but now we stopped doing so
- */
- if (lws_is_flowcontrolled(wsi)) {
- lwsl_debug("%s: caching %ld\n", __func__, (long)len);
- lws_rxflow_cache(wsi, *buf, 0, len);
- return 0;
- }
- if (wsi->u.ws.rx_draining_ext) {
-#if !defined(LWS_NO_CLIENT)
- if (wsi->mode == LWSCM_WS_CLIENT)
- m = lws_client_rx_sm(wsi, 0);
- else
-#endif
- m = lws_rx_sm(wsi, 0);
- if (m < 0)
- return -1;
- continue;
- }
- /* account for what we're using in rxflow buffer */
- if (wsi->rxflow_buffer)
- wsi->rxflow_pos++;
-
- if (lws_client_rx_sm(wsi, *(*buf)++)) {
- lwsl_debug("client_rx_sm exited\n");
- return -1;
- }
- len--;
- }
- lwsl_debug("%s: finished with %ld\n", __func__, (long)len);
- return 0;
- default:
- break;
- }
-
- return 0;
-}
-
-LWS_VISIBLE LWS_EXTERN void
-lws_client_http_body_pending(struct lws *wsi, int something_left_to_send)
-{
- wsi->client_http_body_pending = !!something_left_to_send;
-}
-
-int
-lws_client_socket_service(struct lws_context *context, struct lws *wsi,
- struct lws_pollfd *pollfd)
-{
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- char *p = (char *)&pt->serv_buf[0];
- const char *cce = NULL;
- unsigned char c;
- char *sb = p;
- int n = 0;
- ssize_t len = 0;
-#if defined(LWS_WITH_SOCKS5)
- char conn_mode = 0, pending_timeout = 0;
-#endif
-
- switch (wsi->mode) {
-
- case LWSCM_WSCL_WAITING_CONNECT:
-
- /*
- * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
- * timeout protection set in client-handshake.c
- */
-
- if (!lws_client_connect_2(wsi)) {
- /* closed */
- lwsl_client("closed\n");
- return -1;
- }
-
- /* either still pending connection, or changed mode */
- return 0;
-
-#if defined(LWS_WITH_SOCKS5)
- /* SOCKS Greeting Reply */
- case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY:
- case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY:
- case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY:
-
- /* handle proxy hung up on us */
-
- if (pollfd->revents & LWS_POLLHUP) {
- lwsl_warn("SOCKS connection %p (fd=%d) dead\n",
- (void *)wsi, pollfd->fd);
- goto bail3;
- }
-
- n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
- if (n < 0) {
- if (LWS_ERRNO == LWS_EAGAIN) {
- lwsl_debug("SOCKS read EAGAIN, retrying\n");
- return 0;
- }
- lwsl_err("ERROR reading from SOCKS socket\n");
- goto bail3;
- }
-
- switch (wsi->mode) {
-
- case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY:
- if (pt->serv_buf[0] != SOCKS_VERSION_5)
- goto socks_reply_fail;
-
- if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) {
- lwsl_client("SOCKS greeting reply: No Auth Method\n");
- socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
- conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY;
- pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
- goto socks_send;
- }
-
- if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) {
- lwsl_client("SOCKS greeting reply: User/Pw Method\n");
- socks_generate_msg(wsi, SOCKS_MSG_USERNAME_PASSWORD, &len);
- conn_mode = LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY;
- pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY;
- goto socks_send;
- }
- goto socks_reply_fail;
-
- case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY:
- if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 ||
- pt->serv_buf[1] != SOCKS_SUBNEGOTIATION_STATUS_SUCCESS)
- goto socks_reply_fail;
-
- lwsl_client("SOCKS password OK, sending connect\n");
- socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
- conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY;
- pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
-socks_send:
- n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len,
- MSG_NOSIGNAL);
- if (n < 0) {
- lwsl_debug("ERROR writing to socks proxy\n");
- goto bail3;
- }
-
- lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT);
- wsi->mode = conn_mode;
- break;
-
-socks_reply_fail:
- lwsl_notice("socks reply: v%d, err %d\n",
- pt->serv_buf[0], pt->serv_buf[1]);
- goto bail3;
-
- case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY:
- if (pt->serv_buf[0] != SOCKS_VERSION_5 ||
- pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS)
- goto socks_reply_fail;
-
- lwsl_client("socks connect OK\n");
-
- /* free stash since we are done with it */
- lws_free_set_NULL(wsi->u.hdr.stash);
- if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
- wsi->vhost->socks_proxy_address))
- goto bail3;
-
- wsi->c_port = wsi->vhost->socks_proxy_port;
-
- /* clear his proxy connection timeout */
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
- goto start_ws_handshake;
- }
- break;
-#endif
-
- case LWSCM_WSCL_WAITING_PROXY_REPLY:
-
- /* handle proxy hung up on us */
-
- if (pollfd->revents & LWS_POLLHUP) {
-
- lwsl_warn("Proxy connection %p (fd=%d) dead\n",
- (void *)wsi, pollfd->fd);
-
- goto bail3;
- }
-
- n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
- if (n < 0) {
- if (LWS_ERRNO == LWS_EAGAIN) {
- lwsl_debug("Proxy read returned EAGAIN... retrying\n");
- return 0;
- }
- lwsl_err("ERROR reading from proxy socket\n");
- goto bail3;
- }
-
- pt->serv_buf[13] = '\0';
- if (strcmp(sb, "HTTP/1.0 200 ") &&
- strcmp(sb, "HTTP/1.1 200 ")) {
- lwsl_err("ERROR proxy: %s\n", sb);
- goto bail3;
- }
-
- /* clear his proxy connection timeout */
-
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
- /* fallthru */
-
- case LWSCM_WSCL_ISSUE_HANDSHAKE:
-
- /*
- * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
- * timeout protection set in client-handshake.c
- *
- * take care of our lws_callback_on_writable
- * happening at a time when there's no real connection yet
- */
-#if defined(LWS_WITH_SOCKS5)
-start_ws_handshake:
-#endif
- if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
- return -1;
-
-#ifdef LWS_OPENSSL_SUPPORT
- /* we can retry this... just cook the SSL BIO the first time */
-
- if (wsi->use_ssl && !wsi->ssl &&
- lws_ssl_client_bio_create(wsi) < 0) {
- cce = "bio_create failed";
- goto bail3;
- }
-
- if (wsi->use_ssl) {
- n = lws_ssl_client_connect1(wsi);
- if (!n)
- return 0;
- if (n < 0) {
- cce = "lws_ssl_client_connect1 failed";
- goto bail3;
- }
- } else
- wsi->ssl = NULL;
-
- /* fallthru */
-
- case LWSCM_WSCL_WAITING_SSL:
-
- if (wsi->use_ssl) {
- n = lws_ssl_client_connect2(wsi);
- if (!n)
- return 0;
- if (n < 0) {
- cce = "lws_ssl_client_connect2 failed";
- goto bail3;
- }
- } else
- wsi->ssl = NULL;
-#endif
-
- wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2;
- lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
- context->timeout_secs);
-
- /* fallthru */
-
- case LWSCM_WSCL_ISSUE_HANDSHAKE2:
- p = lws_generate_client_handshake(wsi, p);
- if (p == NULL) {
- if (wsi->mode == LWSCM_RAW)
- return 0;
-
- lwsl_err("Failed to generate handshake for client\n");
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
- return 0;
- }
-
- /* send our request to the server */
- lws_latency_pre(context, wsi);
-
- n = lws_ssl_capable_write(wsi, (unsigned char *)sb, p - sb);
- lws_latency(context, wsi, "send lws_issue_raw", n,
- n == p - sb);
- switch (n) {
- case LWS_SSL_CAPABLE_ERROR:
- lwsl_debug("ERROR writing to client socket\n");
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
- return 0;
- case LWS_SSL_CAPABLE_MORE_SERVICE:
- lws_callback_on_writable(wsi);
- break;
- }
-
- if (wsi->client_http_body_pending) {
- wsi->mode = LWSCM_WSCL_ISSUE_HTTP_BODY;
- lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
- context->timeout_secs);
- /* user code must ask for writable callback */
- break;
- }
-
- goto client_http_body_sent;
-
- case LWSCM_WSCL_ISSUE_HTTP_BODY:
- if (wsi->client_http_body_pending) {
- lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
- context->timeout_secs);
- /* user code must ask for writable callback */
- break;
- }
-client_http_body_sent:
- wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
- wsi->u.hdr.lextable_pos = 0;
- wsi->mode = LWSCM_WSCL_WAITING_SERVER_REPLY;
- lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
- context->timeout_secs);
- break;
-
- case LWSCM_WSCL_WAITING_SERVER_REPLY:
- /*
- * handle server hanging up on us...
- * but if there is POLLIN waiting, handle that first
- */
- if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) ==
- LWS_POLLHUP) {
-
- lwsl_debug("Server connection %p (fd=%d) dead\n",
- (void *)wsi, pollfd->fd);
- cce = "Peer hung up";
- goto bail3;
- }
-
- if (!(pollfd->revents & LWS_POLLIN))
- break;
-
- /* interpret the server response
- *
- * HTTP/1.1 101 Switching Protocols
- * Upgrade: websocket
- * Connection: Upgrade
- * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
- * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
- * Sec-WebSocket-Protocol: chat
- *
- * we have to take some care here to only take from the
- * socket bytewise. The browser may (and has been seen to
- * in the case that onopen() performs websocket traffic)
- * coalesce both handshake response and websocket traffic
- * in one packet, since at that point the connection is
- * definitively ready from browser pov.
- */
- len = 1;
- while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE &&
- len > 0) {
- n = lws_ssl_capable_read(wsi, &c, 1);
- lws_latency(context, wsi, "send lws_issue_raw", n,
- n == 1);
- switch (n) {
- case 0:
- case LWS_SSL_CAPABLE_ERROR:
- cce = "read failed";
- goto bail3;
- case LWS_SSL_CAPABLE_MORE_SERVICE:
- return 0;
- }
-
- if (lws_parse(wsi, c)) {
- lwsl_warn("problems parsing header\n");
- goto bail3;
- }
- }
-
- /*
- * hs may also be coming in multiple packets, there is a 5-sec
- * libwebsocket timeout still active here too, so if parsing did
- * not complete just wait for next packet coming in this state
- */
- if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
- break;
-
- /*
- * otherwise deal with the handshake. If there's any
- * packet traffic already arrived we'll trigger poll() again
- * right away and deal with it that way
- */
- return lws_client_interpret_server_handshake(wsi);
-
-bail3:
- lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n");
- if (cce)
- lwsl_info("reason: %s\n", cce);
- wsi->protocol->callback(wsi,
- LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
- wsi->user_space, (void *)cce, cce ? strlen(cce) : 0);
- wsi->already_did_cce = 1;
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
- return -1;
-
- case LWSCM_WSCL_WAITING_EXTENSION_CONNECT:
- lwsl_ext("LWSCM_WSCL_WAITING_EXTENSION_CONNECT\n");
- break;
-
- case LWSCM_WSCL_PENDING_CANDIDATE_CHILD:
- lwsl_ext("LWSCM_WSCL_PENDING_CANDIDATE_CHILD\n");
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-/*
- * In-place str to lower case
- */
-
-static void
-strtolower(char *s)
-{
- while (*s) {
-#ifdef LWS_PLAT_OPTEE
- int tolower_optee(int c);
- *s = tolower_optee((int)*s);
-#else
- *s = tolower((int)*s);
-#endif
- s++;
- }
-}
-
-int LWS_WARN_UNUSED_RESULT
-lws_http_transaction_completed_client(struct lws *wsi)
-{
- lwsl_debug("%s: wsi %p\n", __func__, wsi);
- /* if we can't go back to accept new headers, drop the connection */
- if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
- lwsl_info("%s: %p: close connection\n", __func__, wsi);
- return 1;
- }
-
- /* we don't support chained client connections yet */
- return 1;
-#if 0
- /* otherwise set ourselves up ready to go again */
- wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
- wsi->mode = LWSCM_HTTP_CLIENT_ACCEPTED;
- wsi->u.http.rx_content_length = 0;
- wsi->hdr_parsing_completed = 0;
-
- /* He asked for it to stay alive indefinitely */
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
- /*
- * As client, nothing new is going to come until we ask for it
- * we can drop the ah, if any
- */
- if (wsi->u.hdr.ah) {
- lws_header_table_force_to_detachable_state(wsi);
- lws_header_table_detach(wsi, 0);
- }
-
- /* If we're (re)starting on headers, need other implied init */
- wsi->u.hdr.ues = URIES_IDLE;
-
- lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi);
-
- return 0;
-#endif
-}
-
-LWS_VISIBLE LWS_EXTERN unsigned int
-lws_http_client_http_response(struct lws *wsi)
-{
- if (!wsi->u.http.ah)
- return 0;
-
- return wsi->u.http.ah->http_response;
-}
-
-int
-lws_client_interpret_server_handshake(struct lws *wsi)
-{
- int n, len, okay = 0, port = 0, ssl = 0;
- int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
- struct lws_context *context = wsi->context;
- const char *pc, *prot, *ads = NULL, *path, *cce = NULL;
- struct allocated_headers *ah = NULL;
- char *p, *q;
- char new_path[300];
-#ifndef LWS_NO_EXTENSIONS
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- char *sb = (char *)&pt->serv_buf[0];
- const struct lws_ext_options *opts;
- const struct lws_extension *ext;
- char ext_name[128];
- const char *c, *a;
- char ignore;
- int more = 1;
- void *v;
-#endif
- if (wsi->u.hdr.stash)
- lws_free_set_NULL(wsi->u.hdr.stash);
-
- ah = wsi->u.hdr.ah;
- if (!wsi->do_ws) {
- /* we are being an http client...
- */
- lws_union_transition(wsi, LWSCM_HTTP_CLIENT_ACCEPTED);
- wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
- wsi->u.http.ah = ah;
- ah->http_response = 0;
- }
-
- /*
- * well, what the server sent looked reasonable for syntax.
- * Now let's confirm it sent all the necessary headers
- *
- * http (non-ws) client will expect something like this
- *
- * HTTP/1.0.200
- * server:.libwebsockets
- * content-type:.text/html
- * content-length:.17703
- * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000
- *
- *
- *
- */
-
- wsi->u.http.connection_type = HTTP_CONNECTION_KEEP_ALIVE;
- p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
- if (wsi->do_ws && !p) {
- lwsl_info("no URI\n");
- cce = "HS: URI missing";
- goto bail3;
- }
- if (!p) {
- p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0);
- wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE;
- }
- if (!p) {
- cce = "HS: URI missing";
- lwsl_info("no URI\n");
- goto bail3;
- }
- n = atoi(p);
- if (ah)
- ah->http_response = n;
-
- if (n == 301 || n == 302 || n == 303 || n == 307 || n == 308) {
- p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION);
- if (!p) {
- cce = "HS: Redirect code but no Location";
- goto bail3;
- }
-
- /* Relative reference absolute path */
- if (p[0] == '/')
- {
-#ifdef LWS_OPENSSL_SUPPORT
- ssl = wsi->use_ssl;
-#endif
- ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
- port = wsi->c_port;
- path = p + 1; /* +1 as lws_client_reset expects leading / to be omitted */
- }
- /* Absolute (Full) URI */
- else if (strchr(p, ':'))
- {
- if (lws_parse_uri(p, &prot, &ads, &port, &path)) {
- cce = "HS: URI did not parse";
- goto bail3;
- }
-
- if (!strcmp(prot, "wss") || !strcmp(prot, "https"))
- ssl = 1;
- }
- /* Relative reference relative path */
- else
- {
- /* This doesn't try to calculate an absolute path, that will be left to the server */
-#ifdef LWS_OPENSSL_SUPPORT
- ssl = wsi->use_ssl;
-#endif
- ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
- port = wsi->c_port;
- path = new_path + 1; /* +1 as lws_client_reset expects leading / to be omitted */
- strncpy(new_path, lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI), sizeof(new_path));
- new_path[sizeof(new_path) - 1] = '\0';
- q = strrchr(new_path, '/');
- if (q)
- {
- strncpy(q + 1, p, sizeof(new_path) - (q - new_path) - 1);
- new_path[sizeof(new_path) - 1] = '\0';
- }
- else
- {
- path = p;
- }
- }
-
-#ifdef LWS_OPENSSL_SUPPORT
- if (wsi->use_ssl && !ssl) {
- cce = "HS: Redirect attempted SSL downgrade";
- goto bail3;
- }
-#endif
-
- if (!lws_client_reset(&wsi, ssl, ads, port, path, ads)) {
- /* there are two ways to fail out with NULL return...
- * simple, early problem where the wsi is intact, or
- * we went through with the reconnect attempt and the
- * wsi is already closed. In the latter case, the wsi
- * has beet set to NULL additionally.
- */
- lwsl_err("Redirect failed\n");
- cce = "HS: Redirect failed";
- if (wsi)
- goto bail3;
-
- return 1;
- }
- return 0;
- }
-
- if (!wsi->do_ws) {
-
-#ifdef LWS_WITH_HTTP_PROXY
- wsi->perform_rewrite = 0;
- if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
- if (!strncmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE),
- "text/html", 9))
- wsi->perform_rewrite = 1;
- }
-#endif
-
- /* allocate the per-connection user memory (if any) */
- if (lws_ensure_user_space(wsi)) {
- lwsl_err("Problem allocating wsi user mem\n");
- cce = "HS: OOM";
- goto bail2;
- }
-
- /* he may choose to send us stuff in chunked transfer-coding */
- wsi->chunked = 0;
- wsi->chunk_remaining = 0; /* ie, next thing is chunk size */
- if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING)) {
- wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi,
- WSI_TOKEN_HTTP_TRANSFER_ENCODING),
- "chunked");
- /* first thing is hex, after payload there is crlf */
- wsi->chunk_parser = ELCP_HEX;
- }
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
- wsi->u.http.rx_content_length =
- atoll(lws_hdr_simple_ptr(wsi,
- WSI_TOKEN_HTTP_CONTENT_LENGTH));
- lwsl_notice("%s: incoming content length %llu\n", __func__,
- (unsigned long long)wsi->u.http.rx_content_length);
- wsi->u.http.rx_content_remain = wsi->u.http.rx_content_length;
- } else /* can't do 1.1 without a content length or chunked */
- if (!wsi->chunked)
- wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE;
-
- /*
- * we seem to be good to go, give client last chance to check
- * headers and OK it
- */
- if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
- wsi->user_space, NULL, 0)) {
-
- cce = "HS: disallowed by client filter";
- goto bail2;
- }
-
- /* clear his proxy connection timeout */
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
- wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
-
- /* call him back to inform him he is up */
- if (wsi->protocol->callback(wsi,
- LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
- wsi->user_space, NULL, 0)) {
- cce = "HS: disallowed at ESTABLISHED";
- goto bail3;
- }
-
- /* free up his parsing allocations */
- lws_header_table_detach(wsi, 0);
-
- lwsl_notice("%s: client connection up\n", __func__);
-
- return 0;
- }
-
- if (p && !strncmp(p, "401", 3)) {
- lwsl_warn(
- "lws_client_handshake: got bad HTTP response '%s'\n", p);
- cce = "HS: ws upgrade unauthorized";
- goto bail3;
- }
-
- if (p && strncmp(p, "101", 3)) {
- lwsl_warn(
- "lws_client_handshake: got bad HTTP response '%s'\n", p);
- cce = "HS: ws upgrade response not 101";
- goto bail3;
- }
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
- lwsl_info("no ACCEPT\n");
- cce = "HS: ACCEPT missing";
- goto bail3;
- }
-
- p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
- if (!p) {
- lwsl_info("no UPGRADE\n");
- cce = "HS: UPGRADE missing";
- goto bail3;
- }
- strtolower(p);
- if (strcmp(p, "websocket")) {
- lwsl_warn(
- "lws_client_handshake: got bad Upgrade header '%s'\n", p);
- cce = "HS: Upgrade to something other than websocket";
- goto bail3;
- }
-
- p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION);
- if (!p) {
- lwsl_info("no Connection hdr\n");
- cce = "HS: CONNECTION missing";
- goto bail3;
- }
- strtolower(p);
- if (strcmp(p, "upgrade")) {
- lwsl_warn("lws_client_int_s_hs: bad header %s\n", p);
- cce = "HS: UPGRADE malformed";
- goto bail3;
- }
-
- pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
- if (!pc) {
- lwsl_parser("lws_client_int_s_hs: no protocol list\n");
- } else
- lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc);
-
- /*
- * confirm the protocol the server wants to talk was in the list
- * of protocols we offered
- */
-
- len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
- if (!len) {
- lwsl_info("lws_client_int_s_hs: WSI_TOKEN_PROTOCOL is null\n");
- /*
- * no protocol name to work from,
- * default to first protocol
- */
- n = 0;
- wsi->protocol = &wsi->vhost->protocols[0];
- goto check_extensions;
- }
-
- p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
- len = strlen(p);
-
- while (pc && *pc && !okay) {
- if (!strncmp(pc, p, len) &&
- (pc[len] == ',' || pc[len] == '\0')) {
- okay = 1;
- continue;
- }
- while (*pc && *pc++ != ',')
- ;
- while (*pc && *pc == ' ')
- pc++;
- }
-
- if (!okay) {
- lwsl_err("lws_client_int_s_hs: got bad protocol %s\n", p);
- cce = "HS: PROTOCOL malformed";
- goto bail2;
- }
-
- /*
- * identify the selected protocol struct and set it
- */
- n = 0;
- wsi->protocol = NULL;
- while (wsi->vhost->protocols[n].callback && !wsi->protocol) {
- if (strcmp(p, wsi->vhost->protocols[n].name) == 0) {
- wsi->protocol = &wsi->vhost->protocols[n];
- break;
- }
- n++;
- }
-
- if (wsi->protocol == NULL) {
- lwsl_err("lws_client_int_s_hs: fail protocol %s\n", p);
- cce = "HS: Cannot match protocol";
- goto bail2;
- }
-
-check_extensions:
- /*
- * stitch protocol choice into the vh protocol linked list
- * We always insert ourselves at the start of the list
- *
- * X <-> B
- * X <-> pAn <-> pB
- */
- //lwsl_err("%s: pre insert vhost start wsi %p, that wsi prev == %p\n",
- // __func__,
- // wsi->vhost->same_vh_protocol_list[n],
- // wsi->same_vh_protocol_prev);
- wsi->same_vh_protocol_prev = /* guy who points to us */
- &wsi->vhost->same_vh_protocol_list[n];
- wsi->same_vh_protocol_next = /* old first guy is our next */
- wsi->vhost->same_vh_protocol_list[n];
- /* we become the new first guy */
- wsi->vhost->same_vh_protocol_list[n] = wsi;
-
- if (wsi->same_vh_protocol_next)
- /* old first guy points back to us now */
- wsi->same_vh_protocol_next->same_vh_protocol_prev =
- &wsi->same_vh_protocol_next;
-
-#ifndef LWS_NO_EXTENSIONS
- /* instantiate the accepted extensions */
-
- if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
- lwsl_ext("no client extensions allowed by server\n");
- goto check_accept;
- }
-
- /*
- * break down the list of server accepted extensions
- * and go through matching them or identifying bogons
- */
-
- if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size, WSI_TOKEN_EXTENSIONS) < 0) {
- lwsl_warn("ext list from server failed to copy\n");
- cce = "HS: EXT: list too big";
- goto bail2;
- }
-
- c = sb;
- n = 0;
- ignore = 0;
- a = NULL;
- while (more) {
-
- if (*c && (*c != ',' && *c != '\t')) {
- if (*c == ';') {
- ignore = 1;
- if (!a)
- a = c + 1;
- }
- if (ignore || *c == ' ') {
- c++;
- continue;
- }
-
- ext_name[n] = *c++;
- if (n < sizeof(ext_name) - 1)
- n++;
- continue;
- }
- ext_name[n] = '\0';
- ignore = 0;
- if (!*c)
- more = 0;
- else {
- c++;
- if (!n)
- continue;
- }
-
- /* check we actually support it */
-
- lwsl_notice("checking client ext %s\n", ext_name);
-
- n = 0;
- ext = wsi->vhost->extensions;
- while (ext && ext->callback) {
- if (strcmp(ext_name, ext->name)) {
- ext++;
- continue;
- }
-
- n = 1;
- lwsl_notice("instantiating client ext %s\n", ext_name);
-
- /* instantiate the extension on this conn */
-
- wsi->active_extensions[wsi->count_act_ext] = ext;
-
- /* allow him to construct his ext instance */
-
- if (ext->callback(lws_get_context(wsi), ext, wsi,
- LWS_EXT_CB_CLIENT_CONSTRUCT,
- (void *)&wsi->act_ext_user[wsi->count_act_ext],
- (void *)&opts, 0)) {
- lwsl_info(" ext %s failed construction\n", ext_name);
- ext++;
- continue;
- }
-
- /*
- * allow the user code to override ext defaults if it
- * wants to
- */
- ext_name[0] = '\0';
- if (user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_WS_EXT_DEFAULTS,
- (char *)ext->name, ext_name,
- sizeof(ext_name))) {
- cce = "HS: EXT: failed setting defaults";
- goto bail2;
- }
-
- if (ext_name[0] &&
- lws_ext_parse_options(ext, wsi, wsi->act_ext_user[
- wsi->count_act_ext], opts, ext_name,
- strlen(ext_name))) {
- lwsl_err("%s: unable to parse user defaults '%s'",
- __func__, ext_name);
- cce = "HS: EXT: failed parsing defaults";
- goto bail2;
- }
-
- /*
- * give the extension the server options
- */
- if (a && lws_ext_parse_options(ext, wsi,
- wsi->act_ext_user[wsi->count_act_ext],
- opts, a, c - a)) {
- lwsl_err("%s: unable to parse remote def '%s'",
- __func__, a);
- cce = "HS: EXT: failed parsing options";
- goto bail2;
- }
-
- if (ext->callback(lws_get_context(wsi), ext, wsi,
- LWS_EXT_CB_OPTION_CONFIRM,
- wsi->act_ext_user[wsi->count_act_ext],
- NULL, 0)) {
- lwsl_err("%s: ext %s rejects server options %s",
- __func__, ext->name, a);
- cce = "HS: EXT: Rejects server options";
- goto bail2;
- }
-
- wsi->count_act_ext++;
-
- ext++;
- }
-
- if (n == 0) {
- lwsl_warn("Unknown ext '%s'!\n", ext_name);
- cce = "HS: EXT: unknown ext";
- goto bail2;
- }
-
- a = NULL;
- n = 0;
- }
-
-check_accept:
-#endif
-
- /*
- * Confirm his accept token is the one we precomputed
- */
-
- p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT);
- if (strcmp(p, wsi->u.hdr.ah->initial_handshake_hash_base64)) {
- lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p,
- wsi->u.hdr.ah->initial_handshake_hash_base64);
- cce = "HS: Accept hash wrong";
- goto bail2;
- }
-
- /* allocate the per-connection user memory (if any) */
- if (lws_ensure_user_space(wsi)) {
- lwsl_err("Problem allocating wsi user mem\n");
- cce = "HS: OOM";
- goto bail2;
- }
-
- /*
- * we seem to be good to go, give client last chance to check
- * headers and OK it
- */
- if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
- wsi->user_space, NULL, 0)) {
- cce = "HS: Rejected by filter cb";
- goto bail2;
- }
-
- /* clear his proxy connection timeout */
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
- /* free up his parsing allocations */
- lws_header_table_detach(wsi, 0);
-
- lws_union_transition(wsi, LWSCM_WS_CLIENT);
- wsi->state = LWSS_ESTABLISHED;
- lws_restart_ws_ping_pong_timer(wsi);
-
- wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
-
- /*
- * create the frame buffer for this connection according to the
- * size mentioned in the protocol definition. If 0 there, then
- * use a big default for compatibility
- */
- n = wsi->protocol->rx_buffer_size;
- if (!n)
- n = context->pt_serv_buf_size;
- n += LWS_PRE;
- wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "client frame buffer");
- if (!wsi->u.ws.rx_ubuf) {
- lwsl_err("Out of Mem allocating rx buffer %d\n", n);
- cce = "HS: OOM";
- goto bail2;
- }
- wsi->u.ws.rx_ubuf_alloc = n;
- lwsl_info("Allocating client RX buffer %d\n", n);
-
-#if !defined(LWS_WITH_ESP32)
- if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&n,
- sizeof n)) {
- lwsl_warn("Failed to set SNDBUF to %d", n);
- cce = "HS: SO_SNDBUF failed";
- goto bail3;
- }
-#endif
-
- lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);
-
- /* call him back to inform him he is up */
-
- if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED,
- wsi->user_space, NULL, 0)) {
- cce = "HS: Rejected at CLIENT_ESTABLISHED";
- goto bail3;
- }
-#ifndef LWS_NO_EXTENSIONS
- /*
- * inform all extensions, not just active ones since they
- * already know
- */
- ext = wsi->vhost->extensions;
-
- while (ext && ext->callback) {
- v = NULL;
- for (n = 0; n < wsi->count_act_ext; n++)
- if (wsi->active_extensions[n] == ext)
- v = wsi->act_ext_user[n];
-
- ext->callback(context, ext, wsi,
- LWS_EXT_CB_ANY_WSI_ESTABLISHED, v, NULL, 0);
- ext++;
- }
-#endif
-
- return 0;
-
-bail3:
- close_reason = LWS_CLOSE_STATUS_NOSTATUS;
-
-bail2:
- if (wsi->protocol)
- wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
- wsi->user_space, (void *)cce,
- (unsigned int)strlen(cce));
- wsi->already_did_cce = 1;
-
- lwsl_info("closing connection due to bail2 connection error\n");
-
- /* closing will free up his parsing allocations */
- lws_close_free_wsi(wsi, close_reason);
-
- return 1;
-}
-
-
-char *
-lws_generate_client_handshake(struct lws *wsi, char *pkt)
-{
- char buf[128], hash[20], key_b64[40], *p = pkt;
- struct lws_context *context = wsi->context;
- const char *meth;
- int n;
-#ifndef LWS_NO_EXTENSIONS
- const struct lws_extension *ext;
- int ext_count = 0;
-#endif
- const char *pp = lws_hdr_simple_ptr(wsi,
- _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
-
- meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
- if (!meth) {
- meth = "GET";
- wsi->do_ws = 1;
- } else {
- wsi->do_ws = 0;
- }
-
- if (!strcmp(meth, "RAW")) {
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
- lwsl_notice("client transition to raw\n");
-
- if (pp) {
- const struct lws_protocols *pr;
-
- pr = lws_vhost_name_to_protocol(wsi->vhost, pp);
-
- if (!pr) {
- lwsl_err("protocol %s not enabled on vhost\n",
- pp);
- return NULL;
- }
-
- lws_bind_protocol(wsi, pr);
- }
-
- if ((wsi->protocol->callback)(wsi,
- LWS_CALLBACK_RAW_ADOPT,
- wsi->user_space, NULL, 0))
- return NULL;
-
- lws_header_table_force_to_detachable_state(wsi);
- lws_union_transition(wsi, LWSCM_RAW);
- lws_header_table_detach(wsi, 1);
-
- return NULL;
- }
-
- if (wsi->do_ws) {
- /*
- * create the random key
- */
- n = lws_get_random(context, hash, 16);
- if (n != 16) {
- lwsl_err("Unable to read from random dev %s\n",
- SYSTEM_RANDOM_FILEPATH);
- return NULL;
- }
-
- lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
- }
-
- /*
- * 04 example client handshake
- *
- * GET /chat HTTP/1.1
- * Host: server.example.com
- * Upgrade: websocket
- * Connection: Upgrade
- * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
- * Sec-WebSocket-Origin: http://example.com
- * Sec-WebSocket-Protocol: chat, superchat
- * Sec-WebSocket-Version: 4
- */
-
- p += sprintf(p, "%s %s HTTP/1.1\x0d\x0a", meth,
- lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));
-
- p += sprintf(p, "Pragma: no-cache\x0d\x0a"
- "Cache-Control: no-cache\x0d\x0a");
-
- p += sprintf(p, "Host: %s\x0d\x0a",
- lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
-
- if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) {
- if (lws_check_opt(context->options, LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN))
- p += sprintf(p, "Origin: %s\x0d\x0a",
- lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN));
- else
- p += sprintf(p, "Origin: http://%s\x0d\x0a",
- lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN));
- }
-
- if (wsi->do_ws) {
- p += sprintf(p, "Upgrade: websocket\x0d\x0a"
- "Connection: Upgrade\x0d\x0a"
- "Sec-WebSocket-Key: ");
- strcpy(p, key_b64);
- p += strlen(key_b64);
- p += sprintf(p, "\x0d\x0a");
- if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
- p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
- lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));
-
- /* tell the server what extensions we could support */
-
-#ifndef LWS_NO_EXTENSIONS
- ext = wsi->vhost->extensions;
- while (ext && ext->callback) {
- n = lws_ext_cb_all_exts(context, wsi,
- LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION,
- (char *)ext->name, 0);
- if (n) { /* an extension vetos us */
- lwsl_ext("ext %s vetoed\n", (char *)ext->name);
- ext++;
- continue;
- }
- n = wsi->vhost->protocols[0].callback(wsi,
- LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
- wsi->user_space, (char *)ext->name, 0);
-
- /*
- * zero return from callback means
- * go ahead and allow the extension,
- * it's what we get if the callback is
- * unhandled
- */
-
- if (n) {
- ext++;
- continue;
- }
-
- /* apply it */
-
- if (ext_count)
- *p++ = ',';
- else
- p += sprintf(p, "Sec-WebSocket-Extensions: ");
- p += sprintf(p, "%s", ext->client_offer);
- ext_count++;
-
- ext++;
- }
- if (ext_count)
- p += sprintf(p, "\x0d\x0a");
-#endif
-
- if (wsi->ietf_spec_revision)
- p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
- wsi->ietf_spec_revision);
-
- /* prepare the expected server accept response */
-
- key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
- n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64);
-
- lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);
-
- lws_b64_encode_string(hash, 20,
- wsi->u.hdr.ah->initial_handshake_hash_base64,
- sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64));
- }
-
- /* give userland a chance to append, eg, cookies */
-
- if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
- wsi->user_space, &p, (pkt + context->pt_serv_buf_size) - p - 12))
- return NULL;
-
- p += sprintf(p, "\x0d\x0a");
-
- return p;
-}
-
diff --git a/thirdparty/lws/client/ssl-client.c b/thirdparty/lws/client/ssl-client.c
deleted file mode 100644
index 962c6e3cb5..0000000000
--- a/thirdparty/lws/client/ssl-client.c
+++ /dev/null
@@ -1,625 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-extern int openssl_websocket_private_data_index,
- openssl_SSL_CTX_private_data_index;
-
-extern void
-lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info);
-
-extern int lws_ssl_get_error(struct lws *wsi, int n);
-
-#if defined(USE_WOLFSSL)
-#else
-
-static int
-OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
-{
-#if defined(LWS_WITH_MBEDTLS)
- lwsl_notice("%s\n", __func__);
-
- return 0;
-#else
- SSL *ssl;
- int n;
- struct lws *wsi;
-
- /* keep old behaviour accepting self-signed server certs */
- if (!preverify_ok) {
- int err = X509_STORE_CTX_get_error(x509_ctx);
-
- if (err != X509_V_OK) {
- ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
- wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
-
- if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
- err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
- wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED) {
- lwsl_notice("accepting self-signed certificate (verify_callback)\n");
- X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
- return 1; // ok
- } else if ((err == X509_V_ERR_CERT_NOT_YET_VALID ||
- err == X509_V_ERR_CERT_HAS_EXPIRED) &&
- wsi->use_ssl & LCCSCF_ALLOW_EXPIRED) {
- if (err == X509_V_ERR_CERT_NOT_YET_VALID)
- lwsl_notice("accepting not yet valid certificate (verify_callback)\n");
- else if (err == X509_V_ERR_CERT_HAS_EXPIRED)
- lwsl_notice("accepting expired certificate (verify_callback)\n");
- X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
- return 1; // ok
- }
- }
- }
-
- ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
- wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
-
- n = lws_get_context_protocol(wsi->context, 0).callback(wsi,
- LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION,
- x509_ctx, ssl, preverify_ok);
-
- /* keep old behaviour if something wrong with server certs */
- /* if ssl error is overruled in callback and cert is ok,
- * X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); must be set and
- * return value is 0 from callback */
- if (!preverify_ok) {
- int err = X509_STORE_CTX_get_error(x509_ctx);
-
- if (err != X509_V_OK) { /* cert validation error was not handled in callback */
- int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
- const char* msg = X509_verify_cert_error_string(err);
- lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;depth=%d)\n", msg, preverify_ok, err, depth);
- return preverify_ok; // not ok
- }
- }
- /* convert callback return code from 0 = OK to verify callback return value 1 = OK */
- return !n;
-#endif
-}
-#endif
-
-int
-lws_ssl_client_bio_create(struct lws *wsi)
-{
- char hostname[128], *p;
-
- if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
- _WSI_TOKEN_CLIENT_HOST) <= 0) {
- lwsl_err("%s: Unable to get hostname\n", __func__);
-
- return -1;
- }
-
- /*
- * remove any :port part on the hostname... necessary for network
- * connection but typical certificates do not contain it
- */
- p = hostname;
- while (*p) {
- if (*p == ':') {
- *p = '\0';
- break;
- }
- p++;
- }
-
- wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx);
- if (!wsi->ssl) {
- lwsl_err("SSL_new failed: %s\n",
- ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
- lws_ssl_elaborate_error();
- return -1;
- }
-
-#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
- if (wsi->vhost->ssl_info_event_mask)
- SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
-#endif
-
-#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host
- X509_VERIFY_PARAM *param;
- (void)param;
-
- if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
- param = SSL_get0_param(wsi->ssl);
- /* Enable automatic hostname checks */
- X509_VERIFY_PARAM_set_hostflags(param,
- X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
- X509_VERIFY_PARAM_set1_host(param, hostname, 0);
- }
-
-#endif
-
-#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
-#ifndef USE_OLD_CYASSL
- /* OpenSSL_client_verify_callback will be called @ SSL_connect() */
- SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
-#endif
-#endif
-
-#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
- SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-#endif
- /*
- * use server name indication (SNI), if supported,
- * when establishing connection
- */
-#ifdef USE_WOLFSSL
-#ifdef USE_OLD_CYASSL
-#ifdef CYASSL_SNI_HOST_NAME
- CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname));
-#endif
-#else
-#ifdef WOLFSSL_SNI_HOST_NAME
- wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, hostname, strlen(hostname));
-#endif
-#endif
-#else
-#if defined(LWS_WITH_MBEDTLS)
- SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
-#else
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
- SSL_set_tlsext_host_name(wsi->ssl, hostname);
-#endif
-#endif
-#endif
-
-#ifdef USE_WOLFSSL
- /*
- * wolfSSL/CyaSSL does certificate verification differently
- * from OpenSSL.
- * If we should ignore the certificate, we need to set
- * this before SSL_new and SSL_connect is called.
- * Otherwise the connect will simply fail with error code -155
- */
-#ifdef USE_OLD_CYASSL
- if (wsi->use_ssl == 2)
- CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
-#else
- if (wsi->use_ssl == 2)
- wolfSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
-#endif
-#endif /* USE_WOLFSSL */
-
-#if !defined(LWS_WITH_MBEDTLS)
- wsi->client_bio = BIO_new_socket(wsi->desc.sockfd, BIO_NOCLOSE);
- SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
-#else
- SSL_set_fd(wsi->ssl, wsi->desc.sockfd);
-#endif
-
-#ifdef USE_WOLFSSL
-#ifdef USE_OLD_CYASSL
- CyaSSL_set_using_nonblock(wsi->ssl, 1);
-#else
- wolfSSL_set_using_nonblock(wsi->ssl, 1);
-#endif
-#else
-#if !defined(LWS_WITH_MBEDTLS)
- BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */
-#endif
-#endif
-
-#if !defined(LWS_WITH_MBEDTLS)
- SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index,
- wsi);
-#endif
-
- return 0;
-}
-
-#if defined(LWS_WITH_MBEDTLS)
-int ERR_get_error(void)
-{
- return 0;
-}
-#endif
-
-int
-lws_ssl_client_connect1(struct lws *wsi)
-{
- struct lws_context *context = wsi->context;
- int n = 0;
-
- lws_latency_pre(context, wsi);
-
- n = SSL_connect(wsi->ssl);
-
- lws_latency(context, wsi,
- "SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0);
-
- if (n < 0) {
- n = lws_ssl_get_error(wsi, n);
-
- if (n == SSL_ERROR_WANT_READ)
- goto some_wait;
-
- if (n == SSL_ERROR_WANT_WRITE) {
- /*
- * wants us to retry connect due to
- * state of the underlying ssl layer...
- * but since it may be stalled on
- * blocked write, no incoming data may
- * arrive to trigger the retry.
- * Force (possibly many times if the SSL
- * state persists in returning the
- * condition code, but other sockets
- * are getting serviced inbetweentimes)
- * us to get called back when writable.
- */
- lwsl_info("%s: WANT_WRITE... retrying\n", __func__);
- lws_callback_on_writable(wsi);
-some_wait:
- wsi->mode = LWSCM_WSCL_WAITING_SSL;
-
- return 0; /* no error */
- }
-
- {
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- char *p = (char *)&pt->serv_buf[0];
- char *sb = p;
-
- lwsl_err("ssl hs1 error, X509_V_ERR = %d: errno %d: %s\n",
- n, errno, ERR_error_string(n, sb));
- lws_ssl_elaborate_error();
-#if defined(LWS_WITH_MBEDTLS)
- if (n == SSL_ERROR_SYSCALL)
- return -1;
-#endif
- }
-
- n = -1;
- }
-
- if (n <= 0) {
- /*
- * retry if new data comes until we
- * run into the connection timeout or win
- */
-
- unsigned long error = ERR_get_error();
-
- if (error != SSL_ERROR_NONE) {
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- char *p = (char *)&pt->serv_buf[0];
- char *sb = p;
- lwsl_err("SSL connect error %lu: %s\n",
- error, ERR_error_string(error, sb));
- return -1;
- }
-
- return 0;
- }
-
- return 1;
-}
-
-int
-lws_ssl_client_connect2(struct lws *wsi)
-{
- struct lws_context *context = wsi->context;
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- char *p = (char *)&pt->serv_buf[0];
- char *sb = p;
- int n = 0;
-
- if (wsi->mode == LWSCM_WSCL_WAITING_SSL) {
- lws_latency_pre(context, wsi);
- n = SSL_connect(wsi->ssl);
- lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
-
- lws_latency(context, wsi,
- "SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0);
-
- if (n < 0) {
- n = lws_ssl_get_error(wsi, n);
-
- if (n == SSL_ERROR_WANT_READ) {
- lwsl_info("SSL_connect WANT_READ... retrying\n");
-
- wsi->mode = LWSCM_WSCL_WAITING_SSL;
-
- return 0; /* no error */
- }
-
- if (n == SSL_ERROR_WANT_WRITE) {
- /*
- * wants us to retry connect due to
- * state of the underlying ssl layer...
- * but since it may be stalled on
- * blocked write, no incoming data may
- * arrive to trigger the retry.
- * Force (possibly many times if the SSL
- * state persists in returning the
- * condition code, but other sockets
- * are getting serviced inbetweentimes)
- * us to get called back when writable.
- */
- lwsl_info("SSL_connect WANT_WRITE... retrying\n");
- lws_callback_on_writable(wsi);
-
- wsi->mode = LWSCM_WSCL_WAITING_SSL;
-
- return 0; /* no error */
- }
-
- n = -1;
- }
-
- if (n <= 0) {
- /*
- * retry if new data comes until we
- * run into the connection timeout or win
- */
- unsigned long error = ERR_get_error();
- if (error != SSL_ERROR_NONE) {
- lwsl_err("SSL connect error %lu: %s\n",
- error, ERR_error_string(error, sb));
- return -1;
- }
- }
- }
-
-#if defined(LWS_WITH_MBEDTLS)
- {
- X509 *peer = SSL_get_peer_certificate(wsi->ssl);
-
- if (!peer) {
- lwsl_notice("peer did not provide cert\n");
-
- return -1;
- }
- lwsl_notice("peer provided cert\n");
- }
-#endif
-
-#ifndef USE_WOLFSSL
- /*
- * See comment above about wolfSSL certificate
- * verification
- */
- lws_latency_pre(context, wsi);
- n = SSL_get_verify_result(wsi->ssl);
- lws_latency(context, wsi,
- "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0);
-
- lwsl_debug("get_verify says %d\n", n);
-
- if (n != X509_V_OK) {
- if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
- n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
- (wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED)) {
- lwsl_notice("accepting self-signed certificate\n");
- } else if ((n == X509_V_ERR_CERT_NOT_YET_VALID ||
- n == X509_V_ERR_CERT_HAS_EXPIRED) &&
- (wsi->use_ssl & LCCSCF_ALLOW_EXPIRED)) {
- lwsl_notice("accepting expired certificate\n");
- } else if (n == X509_V_ERR_CERT_NOT_YET_VALID) {
- lwsl_notice("Cert is from the future... "
- "probably our clock... accepting...\n");
- } else {
- lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n",
- n, ERR_error_string(n, sb));
- lws_ssl_elaborate_error();
- return -1;
- }
- }
-
-#endif /* USE_WOLFSSL */
-
- return 1;
-}
-
-
-int lws_context_init_client_ssl(struct lws_context_creation_info *info,
- struct lws_vhost *vhost)
-{
- SSL_METHOD *method = NULL;
- struct lws wsi;
- unsigned long error;
- const char *ca_filepath = info->ssl_ca_filepath;
-#if !defined(LWS_WITH_MBEDTLS)
- const char *cipher_list = info->ssl_cipher_list;
- const char *private_key_filepath = info->ssl_private_key_filepath;
- const char *cert_filepath = info->ssl_cert_filepath;
- int n;
-
- if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW)
- return 0;
-
- /*
- * for backwards-compatibility default to using ssl_... members, but
- * if the newer client-specific ones are given, use those
- */
- if (info->client_ssl_cipher_list)
- cipher_list = info->client_ssl_cipher_list;
- if (info->client_ssl_cert_filepath)
- cert_filepath = info->client_ssl_cert_filepath;
- if (info->client_ssl_private_key_filepath)
- private_key_filepath = info->client_ssl_private_key_filepath;
-#endif
- if (info->client_ssl_ca_filepath)
- ca_filepath = info->client_ssl_ca_filepath;
-
- if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
- return 0;
-
- if (vhost->ssl_client_ctx)
- return 0;
-
- if (info->provided_client_ssl_ctx) {
- /* use the provided OpenSSL context if given one */
- vhost->ssl_client_ctx = info->provided_client_ssl_ctx;
- /* nothing for lib to delete */
- vhost->user_supplied_ssl_ctx = 1;
-
- return 0;
- }
-
- /* basic openssl init already happened in context init */
-
- /* choose the most recent spin of the api */
-#if defined(LWS_HAVE_TLS_CLIENT_METHOD)
- method = (SSL_METHOD *)TLS_client_method();
-#elif defined(LWS_HAVE_TLSV1_2_CLIENT_METHOD)
- method = (SSL_METHOD *)TLSv1_2_client_method();
-#else
- method = (SSL_METHOD *)SSLv23_client_method();
-#endif
- if (!method) {
- error = ERR_get_error();
- lwsl_err("problem creating ssl method %lu: %s\n",
- error, ERR_error_string(error,
- (char *)vhost->context->pt[0].serv_buf));
- return 1;
- }
- /* create context */
- vhost->ssl_client_ctx = SSL_CTX_new(method);
- if (!vhost->ssl_client_ctx) {
- error = ERR_get_error();
- lwsl_err("problem creating ssl context %lu: %s\n",
- error, ERR_error_string(error,
- (char *)vhost->context->pt[0].serv_buf));
- return 1;
- }
-
- lwsl_notice("created client ssl context for %s\n", vhost->name);
-
-#ifdef SSL_OP_NO_COMPRESSION
- SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION);
-#endif
-
-#if defined(LWS_WITH_MBEDTLS)
- if (ca_filepath) {
- lws_filepos_t len;
- uint8_t *buf;
- /*
- * prototype this here, the shim does not export it in the
- * header, and we need to use the shim unchanged for ESP32 case
- */
- X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len);
-
- if (alloc_file(vhost->context, ca_filepath, &buf, &len)) {
- lwsl_err("Load CA cert file %s failed\n", ca_filepath);
- return 1;
- }
-
- vhost->x509_client_CA = d2i_X509(NULL, buf, len);
- free(buf);
- if (!vhost->x509_client_CA) {
- lwsl_err("client CA: x509 parse failed\n");
- return 1;
- }
-
- SSL_CTX_add_client_CA(vhost->ssl_client_ctx,
- vhost->x509_client_CA);
-
- lwsl_notice("client loaded CA for verification %s\n", ca_filepath);
- }
-#else
- SSL_CTX_set_options(vhost->ssl_client_ctx,
- SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- if (cipher_list)
- SSL_CTX_set_cipher_list(vhost->ssl_client_ctx, cipher_list);
-
-#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS
- if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS))
- /* loads OS default CA certs */
- SSL_CTX_set_default_verify_paths(vhost->ssl_client_ctx);
-#endif
-
- /* openssl init for cert verification (for client sockets) */
- if (!ca_filepath) {
- if (!SSL_CTX_load_verify_locations(
- vhost->ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS))
- lwsl_err("Unable to load SSL Client certs from %s "
- "(set by LWS_OPENSSL_CLIENT_CERTS) -- "
- "client ssl isn't going to work\n",
- LWS_OPENSSL_CLIENT_CERTS);
- } else
- if (!SSL_CTX_load_verify_locations(
- vhost->ssl_client_ctx, ca_filepath, NULL)) {
- lwsl_err(
- "Unable to load SSL Client certs "
- "file from %s -- client ssl isn't "
- "going to work\n", info->client_ssl_ca_filepath);
- lws_ssl_elaborate_error();
- }
- else
- lwsl_info("loaded ssl_ca_filepath\n");
-
- /*
- * callback allowing user code to load extra verification certs
- * helping the client to verify server identity
- */
-
- /* support for client-side certificate authentication */
- if (cert_filepath) {
- lwsl_notice("%s: doing cert filepath\n", __func__);
- n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx,
- cert_filepath);
- if (n < 1) {
- lwsl_err("problem %d getting cert '%s'\n", n,
- cert_filepath);
- lws_ssl_elaborate_error();
- return 1;
- }
- lwsl_notice("Loaded client cert %s\n", cert_filepath);
- }
- if (private_key_filepath) {
- lwsl_notice("%s: doing private key filepath\n", __func__);
- lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info);
- /* set the private key from KeyFile */
- if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx,
- private_key_filepath, SSL_FILETYPE_PEM) != 1) {
- lwsl_err("use_PrivateKey_file '%s'\n",
- private_key_filepath);
- lws_ssl_elaborate_error();
- return 1;
- }
- lwsl_notice("Loaded client cert private key %s\n",
- private_key_filepath);
-
- /* verify private key */
- if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) {
- lwsl_err("Private SSL key doesn't match cert\n");
- return 1;
- }
- }
-#endif
- /*
- * give him a fake wsi with context set, so he can use
- * lws_get_context() in the callback
- */
- memset(&wsi, 0, sizeof(wsi));
- wsi.vhost = vhost;
- wsi.context = vhost->context;
-
- vhost->protocols[0].callback(&wsi,
- LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
- vhost->ssl_client_ctx, NULL, 0);
-
- return 0;
-}
diff --git a/thirdparty/lws/ext/extension-permessage-deflate.c b/thirdparty/lws/ext/extension-permessage-deflate.c
deleted file mode 100644
index e2be2ae615..0000000000
--- a/thirdparty/lws/ext/extension-permessage-deflate.c
+++ /dev/null
@@ -1,473 +0,0 @@
-/*
- * ./lib/extension-permessage-deflate.c
- *
- * Copyright (C) 2016 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-#include "extension-permessage-deflate.h"
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-
-#define LWS_ZLIB_MEMLEVEL 8
-
-const struct lws_ext_options lws_ext_pm_deflate_options[] = {
- /* public RFC7692 settings */
- { "server_no_context_takeover", EXTARG_NONE },
- { "client_no_context_takeover", EXTARG_NONE },
- { "server_max_window_bits", EXTARG_OPT_DEC },
- { "client_max_window_bits", EXTARG_OPT_DEC },
- /* ones only user code can set */
- { "rx_buf_size", EXTARG_DEC },
- { "tx_buf_size", EXTARG_DEC },
- { "compression_level", EXTARG_DEC },
- { "mem_level", EXTARG_DEC },
- { NULL, 0 }, /* sentinel */
-};
-
-static void
-lws_extension_pmdeflate_restrict_args(struct lws *wsi,
- struct lws_ext_pm_deflate_priv *priv)
-{
- int n, extra;
-
- /* cap the RX buf at the nearest power of 2 to protocol rx buf */
-
- n = wsi->context->pt_serv_buf_size;
- if (wsi->protocol->rx_buffer_size)
- n = wsi->protocol->rx_buffer_size;
-
- extra = 7;
- while (n >= 1 << (extra + 1))
- extra++;
-
- if (extra < priv->args[PMD_RX_BUF_PWR2]) {
- priv->args[PMD_RX_BUF_PWR2] = extra;
- lwsl_info(" Capping pmd rx to %d\n", 1 << extra);
- }
-}
-
-LWS_VISIBLE int
-lws_extension_callback_pm_deflate(struct lws_context *context,
- const struct lws_extension *ext,
- struct lws *wsi,
- enum lws_extension_callback_reasons reason,
- void *user, void *in, size_t len)
-{
- struct lws_ext_pm_deflate_priv *priv =
- (struct lws_ext_pm_deflate_priv *)user;
- struct lws_tokens *eff_buf = (struct lws_tokens *)in;
- static unsigned char trail[] = { 0, 0, 0xff, 0xff };
- int n, ret = 0, was_fin = 0, extra;
- struct lws_ext_option_arg *oa;
-
- switch (reason) {
- case LWS_EXT_CB_NAMED_OPTION_SET:
- oa = in;
- if (!oa->option_name)
- break;
- for (n = 0; n < ARRAY_SIZE(lws_ext_pm_deflate_options); n++)
- if (!strcmp(lws_ext_pm_deflate_options[n].name, oa->option_name))
- break;
-
- if (n == ARRAY_SIZE(lws_ext_pm_deflate_options))
- break;
- oa->option_index = n;
-
- /* fallthru */
-
- case LWS_EXT_CB_OPTION_SET:
- oa = in;
- lwsl_notice("%s: option set: idx %d, %s, len %d\n", __func__,
- oa->option_index, oa->start, oa->len);
- if (oa->start)
- priv->args[oa->option_index] = atoi(oa->start);
- else
- priv->args[oa->option_index] = 1;
-
- if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8)
- priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9;
-
- lws_extension_pmdeflate_restrict_args(wsi, priv);
- break;
-
- case LWS_EXT_CB_OPTION_CONFIRM:
- if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 ||
- priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 ||
- priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 ||
- priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15)
- return -1;
- break;
-
- case LWS_EXT_CB_CLIENT_CONSTRUCT:
- case LWS_EXT_CB_CONSTRUCT:
-
- n = context->pt_serv_buf_size;
- if (wsi->protocol->rx_buffer_size)
- n = wsi->protocol->rx_buffer_size;
-
- if (n < 128) {
- lwsl_info(" permessage-deflate requires the protocol (%s) to have an RX buffer >= 128\n",
- wsi->protocol->name);
- return -1;
- }
-
- /* fill in **user */
- priv = lws_zalloc(sizeof(*priv), "pmd priv");
- *((void **)user) = priv;
- lwsl_ext("%s: LWS_EXT_CB_*CONSTRUCT\n", __func__);
- memset(priv, 0, sizeof(*priv));
-
- /* fill in pointer to options list */
- if (in)
- *((const struct lws_ext_options **)in) =
- lws_ext_pm_deflate_options;
-
- /* fallthru */
-
- case LWS_EXT_CB_OPTION_DEFAULT:
-
- /* set the public, RFC7692 defaults... */
-
- priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0,
- priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0;
- priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15;
- priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15;
-
- /* ...and the ones the user code can override */
-
- priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */
- priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
- priv->args[PMD_COMP_LEVEL] = 1;
- priv->args[PMD_MEM_LEVEL] = 8;
-
- lws_extension_pmdeflate_restrict_args(wsi, priv);
- break;
-
- case LWS_EXT_CB_DESTROY:
- lwsl_ext("%s: LWS_EXT_CB_DESTROY\n", __func__);
- lws_free(priv->buf_rx_inflated);
- lws_free(priv->buf_tx_deflated);
- if (priv->rx_init)
- (void)inflateEnd(&priv->rx);
- if (priv->tx_init)
- (void)deflateEnd(&priv->tx);
- lws_free(priv);
- return ret;
-
- case LWS_EXT_CB_PAYLOAD_RX:
- lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n",
- __func__, eff_buf->token_len, priv->rx.avail_in);
- if (!(wsi->u.ws.rsv_first_msg & 0x40))
- return 0;
-
-#if 0
- for (n = 0; n < eff_buf->token_len; n++) {
- printf("%02X ", (unsigned char)eff_buf->token[n]);
- if ((n & 15) == 15)
- printf("\n");
- }
- printf("\n");
-#endif
- if (!priv->rx_init)
- if (inflateInit2(&priv->rx, -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
- lwsl_err("%s: iniflateInit failed\n", __func__);
- return -1;
- }
- priv->rx_init = 1;
- if (!priv->buf_rx_inflated)
- priv->buf_rx_inflated = lws_malloc(LWS_PRE + 7 + 5 +
- (1 << priv->args[PMD_RX_BUF_PWR2]), "pmd rx inflate buf");
- if (!priv->buf_rx_inflated) {
- lwsl_err("%s: OOM\n", __func__);
- return -1;
- }
-
- /*
- * We have to leave the input stream alone if we didn't
- * finish with it yet. The input stream is held in the wsi
- * rx buffer by the caller, so this assumption is safe while
- * we block new rx while draining the existing rx
- */
- if (!priv->rx.avail_in && eff_buf->token && eff_buf->token_len) {
- priv->rx.next_in = (unsigned char *)eff_buf->token;
- priv->rx.avail_in = eff_buf->token_len;
- }
- priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
- eff_buf->token = (char *)priv->rx.next_out;
- priv->rx.avail_out = 1 << priv->args[PMD_RX_BUF_PWR2];
-
- if (priv->rx_held_valid) {
- lwsl_ext("-- RX piling on held byte --\n");
- *(priv->rx.next_out++) = priv->rx_held;
- priv->rx.avail_out--;
- priv->rx_held_valid = 0;
- }
-
- /* if...
- *
- * - he has no remaining input content for this message, and
- * - and this is the final fragment, and
- * - we used everything that could be drained on the input side
- *
- * ...then put back the 00 00 FF FF the sender stripped as our
- * input to zlib
- */
- if (!priv->rx.avail_in && wsi->u.ws.final &&
- !wsi->u.ws.rx_packet_length) {
- lwsl_ext("RX APPEND_TRAILER-DO\n");
- was_fin = 1;
- priv->rx.next_in = trail;
- priv->rx.avail_in = sizeof(trail);
- }
-
- n = inflate(&priv->rx, Z_NO_FLUSH);
- lwsl_ext("inflate ret %d, avi %d, avo %d, wsifinal %d\n", n,
- priv->rx.avail_in, priv->rx.avail_out, wsi->u.ws.final);
- switch (n) {
- case Z_NEED_DICT:
- case Z_STREAM_ERROR:
- case Z_DATA_ERROR:
- case Z_MEM_ERROR:
- lwsl_info("zlib error inflate %d: %s\n",
- n, priv->rx.msg);
- return -1;
- }
- /*
- * If we did not already send in the 00 00 FF FF, and he's
- * out of input, he did not EXACTLY fill the output buffer
- * (which is ambiguous and we will force it to go around
- * again by withholding a byte), and he's otherwise working on
- * being a FIN fragment, then do the FIN message processing
- * of faking up the 00 00 FF FF that the sender stripped.
- */
- if (!priv->rx.avail_in && wsi->u.ws.final &&
- !wsi->u.ws.rx_packet_length && !was_fin &&
- priv->rx.avail_out /* ambiguous as to if it is the end */
- ) {
- lwsl_ext("RX APPEND_TRAILER-DO\n");
- was_fin = 1;
- priv->rx.next_in = trail;
- priv->rx.avail_in = sizeof(trail);
- n = inflate(&priv->rx, Z_SYNC_FLUSH);
- lwsl_ext("RX trailer inf returned %d, avi %d, avo %d\n", n,
- priv->rx.avail_in, priv->rx.avail_out);
- switch (n) {
- case Z_NEED_DICT:
- case Z_STREAM_ERROR:
- case Z_DATA_ERROR:
- case Z_MEM_ERROR:
- lwsl_info("zlib error inflate %d: %s\n",
- n, priv->rx.msg);
- return -1;
- }
- }
- /*
- * we must announce in our returncode now if there is more
- * output to be expected from inflate, so we can decide to
- * set the FIN bit on this bufferload or not. However zlib
- * is ambiguous when we exactly filled the inflate buffer. It
- * does not give us a clue as to whether we should understand
- * that to mean he ended on a buffer boundary, or if there is
- * more in the pipeline.
- *
- * So to work around that safely, if it used all output space
- * exactly, we ALWAYS say there is more coming and we withhold
- * the last byte of the buffer to guarantee that is true.
- *
- * That still leaves us at least one byte to finish with a FIN
- * on, even if actually nothing more is coming from the next
- * inflate action itself.
- */
- if (!priv->rx.avail_out) { /* he used all available out buf */
- lwsl_ext("-- rx grabbing held --\n");
- /* snip the last byte and hold it for next time */
- priv->rx_held = *(--priv->rx.next_out);
- priv->rx_held_valid = 1;
- }
-
- eff_buf->token_len = (char *)priv->rx.next_out - eff_buf->token;
- priv->count_rx_between_fin += eff_buf->token_len;
-
- lwsl_ext(" %s: RX leaving with new effbuff len %d, "
- "ret %d, rx.avail_in=%d, TOTAL RX since FIN %lu\n",
- __func__, eff_buf->token_len, priv->rx_held_valid,
- priv->rx.avail_in,
- (unsigned long)priv->count_rx_between_fin);
-
- if (was_fin) {
- priv->count_rx_between_fin = 0;
- if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
- (void)inflateEnd(&priv->rx);
- priv->rx_init = 0;
- }
- }
-#if 0
- for (n = 0; n < eff_buf->token_len; n++)
- putchar(eff_buf->token[n]);
- puts("\n");
-#endif
-
- return priv->rx_held_valid;
-
- case LWS_EXT_CB_PAYLOAD_TX:
-
- if (!priv->tx_init) {
- n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
- Z_DEFLATED,
- -priv->args[PMD_SERVER_MAX_WINDOW_BITS +
- (wsi->vhost->listen_port <= 0)],
- priv->args[PMD_MEM_LEVEL],
- Z_DEFAULT_STRATEGY);
- if (n != Z_OK) {
- lwsl_ext("inflateInit2 failed %d\n", n);
- return 1;
- }
- }
- priv->tx_init = 1;
- if (!priv->buf_tx_deflated)
- priv->buf_tx_deflated = lws_malloc(LWS_PRE + 7 + 5 +
- (1 << priv->args[PMD_TX_BUF_PWR2]), "pmd tx deflate buf");
- if (!priv->buf_tx_deflated) {
- lwsl_err("%s: OOM\n", __func__);
- return -1;
- }
-
- if (eff_buf->token) {
- lwsl_ext("%s: TX: eff_buf length %d\n", __func__,
- eff_buf->token_len);
- priv->tx.next_in = (unsigned char *)eff_buf->token;
- priv->tx.avail_in = eff_buf->token_len;
- }
-
-#if 0
- for (n = 0; n < eff_buf->token_len; n++) {
- printf("%02X ", (unsigned char)eff_buf->token[n]);
- if ((n & 15) == 15)
- printf("\n");
- }
- printf("\n");
-#endif
-
- priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
- eff_buf->token = (char *)priv->tx.next_out;
- priv->tx.avail_out = 1 << priv->args[PMD_TX_BUF_PWR2];
-
- n = deflate(&priv->tx, Z_SYNC_FLUSH);
- if (n == Z_STREAM_ERROR) {
- lwsl_ext("%s: Z_STREAM_ERROR\n", __func__);
- return -1;
- }
-
- if (priv->tx_held_valid) {
- priv->tx_held_valid = 0;
- if (priv->tx.avail_out == 1 << priv->args[PMD_TX_BUF_PWR2])
- /*
- * we can get a situation he took something in
- * but did not generate anything out, at the end
- * of a message (eg, next thing he sends is 80
- * 00, a zero length FIN, like Authobahn can
- * send).
- * If we have come back as a FIN, we must not
- * place the pending trailer 00 00 FF FF, just
- * the 1 byte of live data
- */
- *(--eff_buf->token) = priv->tx_held[0];
- else {
- /* he generated data, prepend whole pending */
- eff_buf->token -= 5;
- for (n = 0; n < 5; n++)
- eff_buf->token[n] = priv->tx_held[n];
-
- }
- }
- priv->compressed_out = 1;
- eff_buf->token_len = (int)(priv->tx.next_out -
- (unsigned char *)eff_buf->token);
-
- /*
- * we must announce in our returncode now if there is more
- * output to be expected from inflate, so we can decide to
- * set the FIN bit on this bufferload or not. However zlib
- * is ambiguous when we exactly filled the inflate buffer. It
- * does not give us a clue as to whether we should understand
- * that to mean he ended on a buffer boundary, or if there is
- * more in the pipeline.
- *
- * Worse, the guy providing the stuff we are sending may not
- * know until after that this was, actually, the last chunk,
- * that can happen even if we did not fill the output buf, ie
- * he may send after this a zero-length FIN fragment.
- *
- * This is super difficult because we must snip the last 4
- * bytes in the case this is the last compressed output of the
- * message. The only way to deal with it is defer sending the
- * last 5 bytes of each frame until the next one, when we will
- * be in a position to understand if that has a FIN or not.
- */
-
- extra = !!(len & LWS_WRITE_NO_FIN) || !priv->tx.avail_out;
-
- if (eff_buf->token_len >= 4 + extra) {
- lwsl_ext("tx held %d\n", 4 + extra);
- priv->tx_held_valid = extra;
- for (n = 3 + extra; n >= 0; n--)
- priv->tx_held[n] = *(--priv->tx.next_out);
- eff_buf->token_len -= 4 + extra;
- }
- lwsl_ext(" TX rewritten with new effbuff len %d, ret %d\n",
- eff_buf->token_len, !priv->tx.avail_out);
-
- return !priv->tx.avail_out; /* 1 == have more tx pending */
-
- case LWS_EXT_CB_PACKET_TX_PRESEND:
- if (!priv->compressed_out)
- break;
- priv->compressed_out = 0;
-
- if ((*(eff_buf->token) & 0x80) &&
- priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
- lwsl_debug("PMD_CLIENT_NO_CONTEXT_TAKEOVER\n");
- (void)deflateEnd(&priv->tx);
- priv->tx_init = 0;
- }
-
- n = *(eff_buf->token) & 15;
- /* set RSV1, but not on CONTINUATION */
- if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
- *eff_buf->token |= 0x40;
-#if 0
- for (n = 0; n < eff_buf->token_len; n++) {
- printf("%02X ", (unsigned char)eff_buf->token[n]);
- if ((n & 15) == 15)
- puts("\n");
- }
- puts("\n");
-#endif
- lwsl_ext("%s: tx opcode 0x%02X\n", __func__,
- (unsigned char)*eff_buf->token);
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
diff --git a/thirdparty/lws/ext/extension-permessage-deflate.h b/thirdparty/lws/ext/extension-permessage-deflate.h
deleted file mode 100644
index 8737736897..0000000000
--- a/thirdparty/lws/ext/extension-permessage-deflate.h
+++ /dev/null
@@ -1,41 +0,0 @@
-
-#include <zlib.h>
-
-#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1
-#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION
-
-enum arg_indexes {
- PMD_SERVER_NO_CONTEXT_TAKEOVER,
- PMD_CLIENT_NO_CONTEXT_TAKEOVER,
- PMD_SERVER_MAX_WINDOW_BITS,
- PMD_CLIENT_MAX_WINDOW_BITS,
- PMD_RX_BUF_PWR2,
- PMD_TX_BUF_PWR2,
- PMD_COMP_LEVEL,
- PMD_MEM_LEVEL,
-
- PMD_ARG_COUNT
-};
-
-struct lws_ext_pm_deflate_priv {
- z_stream rx;
- z_stream tx;
-
- unsigned char *buf_rx_inflated; /* RX inflated output buffer */
- unsigned char *buf_tx_deflated; /* TX deflated output buffer */
-
- size_t count_rx_between_fin;
-
- unsigned char args[PMD_ARG_COUNT];
- unsigned char tx_held[5];
- unsigned char rx_held;
-
- unsigned char tx_init:1;
- unsigned char rx_init:1;
- unsigned char compressed_out:1;
- unsigned char rx_held_valid:1;
- unsigned char tx_held_valid:1;
- unsigned char rx_append_trailer:1;
- unsigned char pending_tx_trailer:1;
-};
-
diff --git a/thirdparty/lws/ext/extension.c b/thirdparty/lws/ext/extension.c
deleted file mode 100644
index ac28204034..0000000000
--- a/thirdparty/lws/ext/extension.c
+++ /dev/null
@@ -1,344 +0,0 @@
-#include "private-libwebsockets.h"
-
-#include "extension-permessage-deflate.h"
-
-LWS_VISIBLE void
-lws_context_init_extensions(struct lws_context_creation_info *info,
- struct lws_context *context)
-{
- lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
-}
-
-enum lws_ext_option_parser_states {
- LEAPS_SEEK_NAME,
- LEAPS_EAT_NAME,
- LEAPS_SEEK_VAL,
- LEAPS_EAT_DEC,
- LEAPS_SEEK_ARG_TERM
-};
-
-LWS_VISIBLE int
-lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
- void *ext_user, const struct lws_ext_options *opts,
- const char *in, int len)
-{
- enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME;
- unsigned int match_map = 0, n, m, w = 0, count_options = 0,
- pending_close_quote = 0;
- struct lws_ext_option_arg oa;
-
- oa.option_name = NULL;
-
- while (opts[count_options].name)
- count_options++;
- while (len) {
- lwsl_ext("'%c' %d", *in, leap);
- switch (leap) {
- case LEAPS_SEEK_NAME:
- if (*in == ' ')
- break;
- if (*in == ',') {
- len = 1;
- break;
- }
- match_map = (1 << count_options) - 1;
- leap = LEAPS_EAT_NAME;
- w = 0;
-
- /* fallthru */
-
- case LEAPS_EAT_NAME:
- oa.start = NULL;
- oa.len = 0;
- m = match_map;
- n = 0;
- pending_close_quote = 0;
- while (m) {
- if (m & 1) {
- lwsl_ext(" m=%d, n=%d, w=%d\n", m, n, w);
-
- if (*in == opts[n].name[w]) {
- if (!opts[n].name[w + 1]) {
- oa.option_index = n;
- lwsl_ext("hit %d\n", oa.option_index);
- leap = LEAPS_SEEK_VAL;
- if (len == 1)
- goto set_arg;
- break;
- }
- } else {
- match_map &= ~(1 << n);
- if (!match_map) {
- lwsl_ext("empty match map\n");
- return -1;
- }
- }
- }
- m >>= 1;
- n++;
- }
- w++;
- break;
- case LEAPS_SEEK_VAL:
- if (*in == ' ')
- break;
- if (*in == ',') {
- len = 1;
- break;
- }
- if (*in == ';' || len == 1) { /* ie,nonoptional */
- if (opts[oa.option_index].type == EXTARG_DEC)
- return -1;
- leap = LEAPS_SEEK_NAME;
- goto set_arg;
- }
- if (*in == '=') {
- w = 0;
- pending_close_quote = 0;
- if (opts[oa.option_index].type == EXTARG_NONE)
- return -1;
-
- leap = LEAPS_EAT_DEC;
- break;
- }
- return -1;
-
- case LEAPS_EAT_DEC:
- if (*in >= '0' && *in <= '9') {
- if (!w)
- oa.start = in;
- w++;
- if (len != 1)
- break;
- }
- if (!w && *in =='"') {
- pending_close_quote = 1;
- break;
- }
- if (!w)
- return -1;
- if (pending_close_quote && *in != '"' && len != 1)
- return -1;
- leap = LEAPS_SEEK_ARG_TERM;
- if (oa.start)
- oa.len = in - oa.start;
- if (len == 1)
- oa.len++;
-
-set_arg:
- ext->callback(lws_get_context(wsi),
- ext, wsi, LWS_EXT_CB_OPTION_SET,
- ext_user, (char *)&oa, 0);
- if (len == 1)
- break;
- if (pending_close_quote && *in == '"')
- break;
-
- /* fallthru */
-
- case LEAPS_SEEK_ARG_TERM:
- if (*in == ' ')
- break;
- if (*in == ';') {
- leap = LEAPS_SEEK_NAME;
- break;
- }
- if (*in == ',') {
- len = 1;
- break;
- }
- return -1;
- }
- len--;
- in++;
- }
-
- return 0;
-}
-
-
-/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */
-
-int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
-{
- int n, m, handled = 0;
-
- for (n = 0; n < wsi->count_act_ext; n++) {
- m = wsi->active_extensions[n]->callback(lws_get_context(wsi),
- wsi->active_extensions[n], wsi, reason,
- wsi->act_ext_user[n], arg, len);
- if (m < 0) {
- lwsl_ext("Ext '%s' failed to handle callback %d!\n",
- wsi->active_extensions[n]->name, reason);
- return -1;
- }
- /* valgrind... */
- if (reason == LWS_EXT_CB_DESTROY)
- wsi->act_ext_user[n] = NULL;
- if (m > handled)
- handled = m;
- }
-
- return handled;
-}
-
-int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
- int reason, void *arg, int len)
-{
- int n = 0, m, handled = 0;
- const struct lws_extension *ext;
-
- if (!wsi || !wsi->vhost)
- return 0;
-
- ext = wsi->vhost->extensions;
-
- while (ext && ext->callback && !handled) {
- m = ext->callback(context, ext, wsi, reason,
- (void *)(lws_intptr_t)n, arg, len);
- if (m < 0) {
- lwsl_ext("Ext '%s' failed to handle callback %d!\n",
- wsi->active_extensions[n]->name, reason);
- return -1;
- }
- if (m)
- handled = 1;
-
- ext++;
- n++;
- }
-
- return 0;
-}
-
-int
-lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
-{
- struct lws_tokens eff_buf;
- int ret, m, n = 0;
-
- eff_buf.token = (char *)buf;
- eff_buf.token_len = len;
-
- /*
- * while we have original buf to spill ourselves, or extensions report
- * more in their pipeline
- */
-
- ret = 1;
- while (ret == 1) {
-
- /* default to nobody has more to spill */
-
- ret = 0;
-
- /* show every extension the new incoming data */
- m = lws_ext_cb_active(wsi,
- LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0);
- if (m < 0)
- return -1;
- if (m) /* handled */
- ret = 1;
-
- if ((char *)buf != eff_buf.token)
- /*
- * extension recreated it:
- * need to buffer this if not all sent
- */
- wsi->u.ws.clean_buffer = 0;
-
- /* assuming they left us something to send, send it */
-
- if (eff_buf.token_len) {
- n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
- eff_buf.token_len);
- if (n < 0) {
- lwsl_info("closing from ext access\n");
- return -1;
- }
-
- /* always either sent it all or privately buffered */
- if (wsi->u.ws.clean_buffer)
- len = n;
- }
-
- lwsl_parser("written %d bytes to client\n", n);
-
- /* no extension has more to spill? Then we can go */
-
- if (!ret)
- break;
-
- /* we used up what we had */
-
- eff_buf.token = NULL;
- eff_buf.token_len = 0;
-
- /*
- * Did that leave the pipe choked?
- * Or we had to hold on to some of it?
- */
-
- if (!lws_send_pipe_choked(wsi) && !wsi->trunc_len)
- /* no we could add more, lets's do that */
- continue;
-
- lwsl_debug("choked\n");
-
- /*
- * Yes, he's choked. Don't spill the rest now get a callback
- * when he is ready to send and take care of it there
- */
- lws_callback_on_writable(wsi);
- wsi->extension_data_pending = 1;
- ret = 0;
- }
-
- return len;
-}
-
-int
-lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
- void *v, size_t len)
-{
- struct lws_context *context = wsi->context;
- int n, handled = 0;
-
- /* maybe an extension will take care of it for us */
-
- for (n = 0; n < wsi->count_act_ext && !handled; n++) {
- if (!wsi->active_extensions[n]->callback)
- continue;
-
- handled |= wsi->active_extensions[n]->callback(context,
- wsi->active_extensions[n], wsi,
- r, wsi->act_ext_user[n], v, len);
- }
-
- return handled;
-}
-
-int
-lws_set_extension_option(struct lws *wsi, const char *ext_name,
- const char *opt_name, const char *opt_val)
-{
- struct lws_ext_option_arg oa;
- int idx = 0;
-
- /* first identify if the ext is active on this wsi */
- while (idx < wsi->count_act_ext &&
- strcmp(wsi->active_extensions[idx]->name, ext_name))
- idx++;
-
- if (idx == wsi->count_act_ext)
- return -1; /* request ext not active on this wsi */
-
- oa.option_name = opt_name;
- oa.option_index = 0;
- oa.start = opt_val;
- oa.len = 0;
-
- return wsi->active_extensions[idx]->callback(
- wsi->context, wsi->active_extensions[idx], wsi,
- LWS_EXT_CB_NAMED_OPTION_SET, wsi->act_ext_user[idx], &oa, 0);
-}
diff --git a/thirdparty/lws/handshake.c b/thirdparty/lws/handshake.c
deleted file mode 100644
index bc7609d920..0000000000
--- a/thirdparty/lws/handshake.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-/*
- * -04 of the protocol (actually the 80th version) has a radically different
- * handshake. The 04 spec gives the following idea
- *
- * The handshake from the client looks as follows:
- *
- * GET /chat HTTP/1.1
- * Host: server.example.com
- * Upgrade: websocket
- * Connection: Upgrade
- * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
- * Sec-WebSocket-Origin: http://example.com
- * Sec-WebSocket-Protocol: chat, superchat
- * Sec-WebSocket-Version: 4
- *
- * The handshake from the server looks as follows:
- *
- * HTTP/1.1 101 Switching Protocols
- * Upgrade: websocket
- * Connection: Upgrade
- * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
- * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
- * Sec-WebSocket-Protocol: chat
- */
-
-#ifndef min
-#define min(a, b) ((a) < (b) ? (a) : (b))
-#endif
-
-/*
- * We have to take care about parsing because the headers may be split
- * into multiple fragments. They may contain unknown headers with arbitrary
- * argument lengths. So, we parse using a single-character at a time state
- * machine that is completely independent of packet size.
- *
- * Returns <0 for error or length of chars consumed from buf (up to len)
- */
-
-LWS_VISIBLE int
-lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
-{
- unsigned char *last_char, *oldbuf = buf;
- lws_filepos_t body_chunk_len;
- size_t n;
-
- switch (wsi->state) {
-#ifdef LWS_WITH_HTTP2
- case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
- case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
- case LWSS_HTTP2_ESTABLISHED:
- n = 0;
- //lwsl_debug("%s: starting new block of %d\n", __func__, (int)len);
- /*
- * wsi here is always the network connection wsi, not a stream
- * wsi.
- */
- while (n < len) {
- /*
- * we were accepting input but now we stopped doing so
- */
- if (lws_is_flowcontrolled(wsi)) {
- lws_rxflow_cache(wsi, buf, n, len);
-
- return 1;
- }
-
- /* account for what we're using in rxflow buffer */
- if (wsi->rxflow_buffer) {
- wsi->rxflow_pos++;
- assert(wsi->rxflow_pos <= wsi->rxflow_len);
- }
-
- if (lws_h2_parser(wsi, buf[n++])) {
- lwsl_debug("%s: http2_parser bailed\n", __func__);
- goto bail;
- }
- }
- lwsl_debug("%s: used up block of %d\n", __func__, (int)len);
- break;
-#endif
-
- case LWSS_HTTP_ISSUING_FILE:
- return 0;
-
- case LWSS_CLIENT_HTTP_ESTABLISHED:
- break;
-
- case LWSS_HTTP:
- wsi->hdr_parsing_completed = 0;
-
- /* fallthru */
-
- case LWSS_HTTP_HEADERS:
- if (!wsi->u.hdr.ah) {
- lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__);
- assert(0);
- }
- lwsl_parser("issuing %d bytes to parser\n", (int)len);
-
- lwsl_hexdump(buf, (size_t)len);
-
- if (lws_handshake_client(wsi, &buf, (size_t)len))
- goto bail;
-
- last_char = buf;
- if (lws_handshake_server(wsi, &buf, (size_t)len))
- /* Handshake indicates this session is done. */
- goto bail;
-
- /* we might have transitioned to RAW */
- if (wsi->mode == LWSCM_RAW)
- /* we gave the read buffer to RAW handler already */
- goto read_ok;
-
- /*
- * It's possible that we've exhausted our data already, or
- * rx flow control has stopped us dealing with this early,
- * but lws_handshake_server doesn't update len for us.
- * Figure out how much was read, so that we can proceed
- * appropriately:
- */
- len -= (buf - last_char);
- lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
-
- if (!wsi->hdr_parsing_completed)
- /* More header content on the way */
- goto read_ok;
-
- switch (wsi->state) {
- case LWSS_HTTP:
- case LWSS_HTTP_HEADERS:
- goto read_ok;
- case LWSS_HTTP_ISSUING_FILE:
- goto read_ok;
- case LWSS_HTTP_BODY:
- wsi->u.http.rx_content_remain =
- wsi->u.http.rx_content_length;
- if (wsi->u.http.rx_content_remain)
- goto http_postbody;
-
- /* there is no POST content */
- goto postbody_completion;
- default:
- break;
- }
- break;
-
- case LWSS_HTTP_BODY:
-http_postbody:
- //lwsl_notice("http post body\n");
- while (len && wsi->u.http.rx_content_remain) {
- /* Copy as much as possible, up to the limit of:
- * what we have in the read buffer (len)
- * remaining portion of the POST body (content_remain)
- */
- body_chunk_len = min(wsi->u.http.rx_content_remain, len);
- wsi->u.http.rx_content_remain -= body_chunk_len;
- len -= body_chunk_len;
-#ifdef LWS_WITH_CGI
- if (wsi->cgi) {
- struct lws_cgi_args args;
-
- args.ch = LWS_STDIN;
- args.stdwsi = &wsi->cgi->stdwsi[0];
- args.data = buf;
- args.len = body_chunk_len;
-
- /* returns how much used */
- n = user_callback_handle_rxflow(
- wsi->protocol->callback,
- wsi, LWS_CALLBACK_CGI_STDIN_DATA,
- wsi->user_space,
- (void *)&args, 0);
- if ((int)n < 0)
- goto bail;
- } else {
-#endif
- n = wsi->protocol->callback(wsi,
- LWS_CALLBACK_HTTP_BODY, wsi->user_space,
- buf, (size_t)body_chunk_len);
- if (n)
- goto bail;
- n = (size_t)body_chunk_len;
-#ifdef LWS_WITH_CGI
- }
-#endif
- buf += n;
-
- if (wsi->u.http.rx_content_remain) {
- lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
- wsi->context->timeout_secs);
- break;
- }
- /* he sent all the content in time */
-postbody_completion:
-#ifdef LWS_WITH_CGI
- /*
- * If we're running a cgi, we can't let him off the
- * hook just because he sent his POST data
- */
- if (wsi->cgi)
- lws_set_timeout(wsi, PENDING_TIMEOUT_CGI,
- wsi->context->timeout_secs);
- else
-#endif
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-#ifdef LWS_WITH_CGI
- if (!wsi->cgi)
-#endif
- {
- lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
- n = wsi->protocol->callback(wsi,
- LWS_CALLBACK_HTTP_BODY_COMPLETION,
- wsi->user_space, NULL, 0);
- if (n)
- goto bail;
-
- if (wsi->http2_substream)
- wsi->state = LWSS_HTTP2_ESTABLISHED;
- }
-
- break;
- }
- break;
-
- case LWSS_ESTABLISHED:
- case LWSS_AWAITING_CLOSE_ACK:
- case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
- case LWSS_SHUTDOWN:
- if (lws_handshake_client(wsi, &buf, (size_t)len))
- goto bail;
- switch (wsi->mode) {
- case LWSCM_WS_SERVING:
-
- if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
- lwsl_info("interpret_incoming_packet has bailed\n");
- goto bail;
- }
- break;
- }
- break;
- default:
- lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
- break;
- }
-
-read_ok:
- /* Nothing more to do for now */
- lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf));
-
- return buf - oldbuf;
-
-bail:
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-
- return -1;
-}
diff --git a/thirdparty/lws/lextable.h b/thirdparty/lws/lextable.h
deleted file mode 100644
index f940afd25b..0000000000
--- a/thirdparty/lws/lextable.h
+++ /dev/null
@@ -1,805 +0,0 @@
-/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */,
- 0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */,
- 0x6F /* 'o' */, 0x51, 0x00 /* (to 0x0057 state 10) */,
- 0x68 /* 'h' */, 0x5D, 0x00 /* (to 0x0066 state 18) */,
- 0x63 /* 'c' */, 0x69, 0x00 /* (to 0x0075 state 23) */,
- 0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x0099 state 34) */,
- 0x73 /* 's' */, 0xA0, 0x00 /* (to 0x00B2 state 48) */,
- 0x0D /* '.' */, 0xD9, 0x00 /* (to 0x00EE state 68) */,
- 0x61 /* 'a' */, 0x31, 0x01 /* (to 0x0149 state 129) */,
- 0x69 /* 'i' */, 0x70, 0x01 /* (to 0x018B state 163) */,
- 0x64 /* 'd' */, 0x19, 0x02 /* (to 0x0237 state 265) */,
- 0x72 /* 'r' */, 0x22, 0x02 /* (to 0x0243 state 270) */,
- 0x3A /* ':' */, 0x53, 0x02 /* (to 0x0277 state 299) */,
- 0x65 /* 'e' */, 0xDF, 0x02 /* (to 0x0306 state 409) */,
- 0x66 /* 'f' */, 0xFB, 0x02 /* (to 0x0325 state 425) */,
- 0x6C /* 'l' */, 0x1D, 0x03 /* (to 0x034A state 458) */,
- 0x6D /* 'm' */, 0x40, 0x03 /* (to 0x0370 state 484) */,
- 0x74 /* 't' */, 0xAF, 0x03 /* (to 0x03E2 state 578) */,
- 0x76 /* 'v' */, 0xD0, 0x03 /* (to 0x0406 state 606) */,
- 0x77 /* 'w' */, 0xDD, 0x03 /* (to 0x0416 state 614) */,
- 0x78 /* 'x' */, 0x04, 0x04 /* (to 0x0440 state 650) */,
- 0x08, /* fail */
-/* pos 0040: 1 */ 0xE5 /* 'e' -> */,
-/* pos 0041: 2 */ 0xF4 /* 't' -> */,
-/* pos 0042: 3 */ 0xA0 /* ' ' -> */,
-/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */,
-/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */,
- 0x72 /* 'r' */, 0x95, 0x01 /* (to 0x01DD state 211) */,
- 0x61 /* 'a' */, 0xDD, 0x03 /* (to 0x0428 state 631) */,
- 0x75 /* 'u' */, 0xDF, 0x03 /* (to 0x042D state 635) */,
- 0x08, /* fail */
-/* pos 0052: 6 */ 0xF3 /* 's' -> */,
-/* pos 0053: 7 */ 0xF4 /* 't' -> */,
-/* pos 0054: 8 */ 0xA0 /* ' ' -> */,
-/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */,
-/* pos 0057: 10 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x005E state 11) */,
- 0x72 /* 'r' */, 0x51, 0x00 /* (to 0x00AB state 42) */,
- 0x08, /* fail */
-/* pos 005e: 11 */ 0xF4 /* 't' -> */,
-/* pos 005f: 12 */ 0xE9 /* 'i' -> */,
-/* pos 0060: 13 */ 0xEF /* 'o' -> */,
-/* pos 0061: 14 */ 0xEE /* 'n' -> */,
-/* pos 0062: 15 */ 0xF3 /* 's' -> */,
-/* pos 0063: 16 */ 0xA0 /* ' ' -> */,
-/* pos 0064: 17 */ 0x00, 0x02 /* - terminal marker 2 - */,
-/* pos 0066: 18 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x0070 state 19) */,
- 0x74 /* 't' */, 0xBF, 0x00 /* (to 0x0128 state 110) */,
- 0x65 /* 'e' */, 0xF8, 0x03 /* (to 0x0464 state 676) */,
- 0x08, /* fail */
-/* pos 0070: 19 */ 0xF3 /* 's' -> */,
-/* pos 0071: 20 */ 0xF4 /* 't' -> */,
-/* pos 0072: 21 */ 0xBA /* ':' -> */,
-/* pos 0073: 22 */ 0x00, 0x03 /* - terminal marker 3 - */,
-/* pos 0075: 23 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x007C state 24) */,
- 0x61 /* 'a' */, 0x72, 0x01 /* (to 0x01EA state 217) */,
- 0x08, /* fail */
-/* pos 007c: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0083 state 25) */,
- 0x6F /* 'o' */, 0x87, 0x01 /* (to 0x0206 state 243) */,
- 0x08, /* fail */
-/* pos 0083: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x008A state 26) */,
- 0x74 /* 't' */, 0x86, 0x01 /* (to 0x020C state 248) */,
- 0x08, /* fail */
-/* pos 008a: 26 */ 0xE5 /* 'e' -> */,
-/* pos 008b: 27 */ 0xE3 /* 'c' -> */,
-/* pos 008c: 28 */ 0xF4 /* 't' -> */,
-/* pos 008d: 29 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0094 state 30) */,
- 0x20 /* ' ' */, 0xD2, 0x03 /* (to 0x0462 state 675) */,
- 0x08, /* fail */
-/* pos 0094: 30 */ 0xEF /* 'o' -> */,
-/* pos 0095: 31 */ 0xEE /* 'n' -> */,
-/* pos 0096: 32 */ 0xBA /* ':' -> */,
-/* pos 0097: 33 */ 0x00, 0x04 /* - terminal marker 4 - */,
-/* pos 0099: 34 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x00A3 state 35) */,
- 0x73 /* 's' */, 0x5F, 0x03 /* (to 0x03FB state 596) */,
- 0x72 /* 'r' */, 0x97, 0x03 /* (to 0x0436 state 642) */,
- 0x08, /* fail */
-/* pos 00a3: 35 */ 0xE7 /* 'g' -> */,
-/* pos 00a4: 36 */ 0xF2 /* 'r' -> */,
-/* pos 00a5: 37 */ 0xE1 /* 'a' -> */,
-/* pos 00a6: 38 */ 0xE4 /* 'd' -> */,
-/* pos 00a7: 39 */ 0xE5 /* 'e' -> */,
-/* pos 00a8: 40 */ 0xBA /* ':' -> */,
-/* pos 00a9: 41 */ 0x00, 0x05 /* - terminal marker 5 - */,
-/* pos 00ab: 42 */ 0xE9 /* 'i' -> */,
-/* pos 00ac: 43 */ 0xE7 /* 'g' -> */,
-/* pos 00ad: 44 */ 0xE9 /* 'i' -> */,
-/* pos 00ae: 45 */ 0xEE /* 'n' -> */,
-/* pos 00af: 46 */ 0xBA /* ':' -> */,
-/* pos 00b0: 47 */ 0x00, 0x06 /* - terminal marker 6 - */,
-/* pos 00b2: 48 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x00B9 state 49) */,
- 0x74 /* 't' */, 0x13, 0x03 /* (to 0x03C8 state 553) */,
- 0x08, /* fail */
-/* pos 00b9: 49 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x00C3 state 50) */,
- 0x72 /* 'r' */, 0xFC, 0x02 /* (to 0x03B8 state 539) */,
- 0x74 /* 't' */, 0xFF, 0x02 /* (to 0x03BE state 544) */,
- 0x08, /* fail */
-/* pos 00c3: 50 */ 0xAD /* '-' -> */,
-/* pos 00c4: 51 */ 0xF7 /* 'w' -> */,
-/* pos 00c5: 52 */ 0xE5 /* 'e' -> */,
-/* pos 00c6: 53 */ 0xE2 /* 'b' -> */,
-/* pos 00c7: 54 */ 0xF3 /* 's' -> */,
-/* pos 00c8: 55 */ 0xEF /* 'o' -> */,
-/* pos 00c9: 56 */ 0xE3 /* 'c' -> */,
-/* pos 00ca: 57 */ 0xEB /* 'k' -> */,
-/* pos 00cb: 58 */ 0xE5 /* 'e' -> */,
-/* pos 00cc: 59 */ 0xF4 /* 't' -> */,
-/* pos 00cd: 60 */ 0xAD /* '-' -> */,
-/* pos 00ce: 61 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x00E7 state 62) */,
- 0x65 /* 'e' */, 0x20, 0x00 /* (to 0x00F1 state 70) */,
- 0x6B /* 'k' */, 0x29, 0x00 /* (to 0x00FD state 81) */,
- 0x70 /* 'p' */, 0x38, 0x00 /* (to 0x010F state 88) */,
- 0x61 /* 'a' */, 0x3F, 0x00 /* (to 0x0119 state 97) */,
- 0x6E /* 'n' */, 0x44, 0x00 /* (to 0x0121 state 104) */,
- 0x76 /* 'v' */, 0x86, 0x01 /* (to 0x0266 state 284) */,
- 0x6F /* 'o' */, 0x8C, 0x01 /* (to 0x026F state 292) */,
- 0x08, /* fail */
-/* pos 00e7: 62 */ 0xF2 /* 'r' -> */,
-/* pos 00e8: 63 */ 0xE1 /* 'a' -> */,
-/* pos 00e9: 64 */ 0xE6 /* 'f' -> */,
-/* pos 00ea: 65 */ 0xF4 /* 't' -> */,
-/* pos 00eb: 66 */ 0xBA /* ':' -> */,
-/* pos 00ec: 67 */ 0x00, 0x07 /* - terminal marker 7 - */,
-/* pos 00ee: 68 */ 0x8A /* '.' -> */,
-/* pos 00ef: 69 */ 0x00, 0x08 /* - terminal marker 8 - */,
-/* pos 00f1: 70 */ 0xF8 /* 'x' -> */,
-/* pos 00f2: 71 */ 0xF4 /* 't' -> */,
-/* pos 00f3: 72 */ 0xE5 /* 'e' -> */,
-/* pos 00f4: 73 */ 0xEE /* 'n' -> */,
-/* pos 00f5: 74 */ 0xF3 /* 's' -> */,
-/* pos 00f6: 75 */ 0xE9 /* 'i' -> */,
-/* pos 00f7: 76 */ 0xEF /* 'o' -> */,
-/* pos 00f8: 77 */ 0xEE /* 'n' -> */,
-/* pos 00f9: 78 */ 0xF3 /* 's' -> */,
-/* pos 00fa: 79 */ 0xBA /* ':' -> */,
-/* pos 00fb: 80 */ 0x00, 0x09 /* - terminal marker 9 - */,
-/* pos 00fd: 81 */ 0xE5 /* 'e' -> */,
-/* pos 00fe: 82 */ 0xF9 /* 'y' -> */,
-/* pos 00ff: 83 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0109 state 84) */,
- 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x010C state 86) */,
- 0x3A /* ':' */, 0x5F, 0x01 /* (to 0x0264 state 283) */,
- 0x08, /* fail */
-/* pos 0109: 84 */ 0xBA /* ':' -> */,
-/* pos 010a: 85 */ 0x00, 0x0A /* - terminal marker 10 - */,
-/* pos 010c: 86 */ 0xBA /* ':' -> */,
-/* pos 010d: 87 */ 0x00, 0x0B /* - terminal marker 11 - */,
-/* pos 010f: 88 */ 0xF2 /* 'r' -> */,
-/* pos 0110: 89 */ 0xEF /* 'o' -> */,
-/* pos 0111: 90 */ 0xF4 /* 't' -> */,
-/* pos 0112: 91 */ 0xEF /* 'o' -> */,
-/* pos 0113: 92 */ 0xE3 /* 'c' -> */,
-/* pos 0114: 93 */ 0xEF /* 'o' -> */,
-/* pos 0115: 94 */ 0xEC /* 'l' -> */,
-/* pos 0116: 95 */ 0xBA /* ':' -> */,
-/* pos 0117: 96 */ 0x00, 0x0C /* - terminal marker 12 - */,
-/* pos 0119: 97 */ 0xE3 /* 'c' -> */,
-/* pos 011a: 98 */ 0xE3 /* 'c' -> */,
-/* pos 011b: 99 */ 0xE5 /* 'e' -> */,
-/* pos 011c: 100 */ 0xF0 /* 'p' -> */,
-/* pos 011d: 101 */ 0xF4 /* 't' -> */,
-/* pos 011e: 102 */ 0xBA /* ':' -> */,
-/* pos 011f: 103 */ 0x00, 0x0D /* - terminal marker 13 - */,
-/* pos 0121: 104 */ 0xEF /* 'o' -> */,
-/* pos 0122: 105 */ 0xEE /* 'n' -> */,
-/* pos 0123: 106 */ 0xE3 /* 'c' -> */,
-/* pos 0124: 107 */ 0xE5 /* 'e' -> */,
-/* pos 0125: 108 */ 0xBA /* ':' -> */,
-/* pos 0126: 109 */ 0x00, 0x0E /* - terminal marker 14 - */,
-/* pos 0128: 110 */ 0xF4 /* 't' -> */,
-/* pos 0129: 111 */ 0xF0 /* 'p' -> */,
-/* pos 012a: 112 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x0131 state 113) */,
- 0x32 /* '2' */, 0x10, 0x00 /* (to 0x013D state 118) */,
- 0x08, /* fail */
-/* pos 0131: 113 */ 0xB1 /* '1' -> */,
-/* pos 0132: 114 */ 0xAE /* '.' -> */,
-/* pos 0133: 115 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x013A state 116) */,
- 0x30 /* '0' */, 0x1B, 0x03 /* (to 0x0451 state 660) */,
- 0x08, /* fail */
-/* pos 013a: 116 */ 0xA0 /* ' ' -> */,
-/* pos 013b: 117 */ 0x00, 0x0F /* - terminal marker 15 - */,
-/* pos 013d: 118 */ 0xAD /* '-' -> */,
-/* pos 013e: 119 */ 0xF3 /* 's' -> */,
-/* pos 013f: 120 */ 0xE5 /* 'e' -> */,
-/* pos 0140: 121 */ 0xF4 /* 't' -> */,
-/* pos 0141: 122 */ 0xF4 /* 't' -> */,
-/* pos 0142: 123 */ 0xE9 /* 'i' -> */,
-/* pos 0143: 124 */ 0xEE /* 'n' -> */,
-/* pos 0144: 125 */ 0xE7 /* 'g' -> */,
-/* pos 0145: 126 */ 0xF3 /* 's' -> */,
-/* pos 0146: 127 */ 0xBA /* ':' -> */,
-/* pos 0147: 128 */ 0x00, 0x10 /* - terminal marker 16 - */,
-/* pos 0149: 129 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x0156 state 130) */,
- 0x75 /* 'u' */, 0xAC, 0x00 /* (to 0x01F8 state 230) */,
- 0x67 /* 'g' */, 0x7D, 0x01 /* (to 0x02CC state 358) */,
- 0x6C /* 'l' */, 0x7E, 0x01 /* (to 0x02D0 state 361) */,
- 0x08, /* fail */
-/* pos 0156: 130 */ 0xE3 /* 'c' -> */,
-/* pos 0157: 131 */ 0xE5 /* 'e' -> */,
-/* pos 0158: 132 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x015F state 133) */,
- 0x73 /* 's' */, 0x0E, 0x00 /* (to 0x0169 state 136) */,
- 0x08, /* fail */
-/* pos 015f: 133 */ 0xF4 /* 't' -> */,
-/* pos 0160: 134 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0167 state 135) */,
- 0x2D /* '-' */, 0x59, 0x00 /* (to 0x01BC state 192) */,
- 0x08, /* fail */
-/* pos 0167: 135 */ 0x00, 0x11 /* - terminal marker 17 - */,
-/* pos 0169: 136 */ 0xF3 /* 's' -> */,
-/* pos 016a: 137 */ 0xAD /* '-' -> */,
-/* pos 016b: 138 */ 0xE3 /* 'c' -> */,
-/* pos 016c: 139 */ 0xEF /* 'o' -> */,
-/* pos 016d: 140 */ 0xEE /* 'n' -> */,
-/* pos 016e: 141 */ 0xF4 /* 't' -> */,
-/* pos 016f: 142 */ 0xF2 /* 'r' -> */,
-/* pos 0170: 143 */ 0xEF /* 'o' -> */,
-/* pos 0171: 144 */ 0xEC /* 'l' -> */,
-/* pos 0172: 145 */ 0xAD /* '-' -> */,
-/* pos 0173: 146 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x017A state 147) */,
- 0x61 /* 'a' */, 0x48, 0x01 /* (to 0x02BE state 345) */,
- 0x08, /* fail */
-/* pos 017a: 147 */ 0xE5 /* 'e' -> */,
-/* pos 017b: 148 */ 0xF1 /* 'q' -> */,
-/* pos 017c: 149 */ 0xF5 /* 'u' -> */,
-/* pos 017d: 150 */ 0xE5 /* 'e' -> */,
-/* pos 017e: 151 */ 0xF3 /* 's' -> */,
-/* pos 017f: 152 */ 0xF4 /* 't' -> */,
-/* pos 0180: 153 */ 0xAD /* '-' -> */,
-/* pos 0181: 154 */ 0xE8 /* 'h' -> */,
-/* pos 0182: 155 */ 0xE5 /* 'e' -> */,
-/* pos 0183: 156 */ 0xE1 /* 'a' -> */,
-/* pos 0184: 157 */ 0xE4 /* 'd' -> */,
-/* pos 0185: 158 */ 0xE5 /* 'e' -> */,
-/* pos 0186: 159 */ 0xF2 /* 'r' -> */,
-/* pos 0187: 160 */ 0xF3 /* 's' -> */,
-/* pos 0188: 161 */ 0xBA /* ':' -> */,
-/* pos 0189: 162 */ 0x00, 0x12 /* - terminal marker 18 - */,
-/* pos 018b: 163 */ 0xE6 /* 'f' -> */,
-/* pos 018c: 164 */ 0xAD /* '-' -> */,
-/* pos 018d: 165 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x019A state 166) */,
- 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x01B0 state 181) */,
- 0x72 /* 'r' */, 0x9E, 0x01 /* (to 0x0331 state 435) */,
- 0x75 /* 'u' */, 0xA2, 0x01 /* (to 0x0338 state 441) */,
- 0x08, /* fail */
-/* pos 019a: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x01A1 state 167) */,
- 0x61 /* 'a' */, 0x8E, 0x01 /* (to 0x032B state 430) */,
- 0x08, /* fail */
-/* pos 01a1: 167 */ 0xE4 /* 'd' -> */,
-/* pos 01a2: 168 */ 0xE9 /* 'i' -> */,
-/* pos 01a3: 169 */ 0xE6 /* 'f' -> */,
-/* pos 01a4: 170 */ 0xE9 /* 'i' -> */,
-/* pos 01a5: 171 */ 0xE5 /* 'e' -> */,
-/* pos 01a6: 172 */ 0xE4 /* 'd' -> */,
-/* pos 01a7: 173 */ 0xAD /* '-' -> */,
-/* pos 01a8: 174 */ 0xF3 /* 's' -> */,
-/* pos 01a9: 175 */ 0xE9 /* 'i' -> */,
-/* pos 01aa: 176 */ 0xEE /* 'n' -> */,
-/* pos 01ab: 177 */ 0xE3 /* 'c' -> */,
-/* pos 01ac: 178 */ 0xE5 /* 'e' -> */,
-/* pos 01ad: 179 */ 0xBA /* ':' -> */,
-/* pos 01ae: 180 */ 0x00, 0x13 /* - terminal marker 19 - */,
-/* pos 01b0: 181 */ 0xEF /* 'o' -> */,
-/* pos 01b1: 182 */ 0xEE /* 'n' -> */,
-/* pos 01b2: 183 */ 0xE5 /* 'e' -> */,
-/* pos 01b3: 184 */ 0xAD /* '-' -> */,
-/* pos 01b4: 185 */ 0xED /* 'm' -> */,
-/* pos 01b5: 186 */ 0xE1 /* 'a' -> */,
-/* pos 01b6: 187 */ 0xF4 /* 't' -> */,
-/* pos 01b7: 188 */ 0xE3 /* 'c' -> */,
-/* pos 01b8: 189 */ 0xE8 /* 'h' -> */,
-/* pos 01b9: 190 */ 0xBA /* ':' -> */,
-/* pos 01ba: 191 */ 0x00, 0x14 /* - terminal marker 20 - */,
-/* pos 01bc: 192 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x01C9 state 193) */,
- 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x01D3 state 202) */,
- 0x63 /* 'c' */, 0xEB, 0x00 /* (to 0x02AD state 330) */,
- 0x72 /* 'r' */, 0xF1, 0x00 /* (to 0x02B6 state 338) */,
- 0x08, /* fail */
-/* pos 01c9: 193 */ 0xEE /* 'n' -> */,
-/* pos 01ca: 194 */ 0xE3 /* 'c' -> */,
-/* pos 01cb: 195 */ 0xEF /* 'o' -> */,
-/* pos 01cc: 196 */ 0xE4 /* 'd' -> */,
-/* pos 01cd: 197 */ 0xE9 /* 'i' -> */,
-/* pos 01ce: 198 */ 0xEE /* 'n' -> */,
-/* pos 01cf: 199 */ 0xE7 /* 'g' -> */,
-/* pos 01d0: 200 */ 0xBA /* ':' -> */,
-/* pos 01d1: 201 */ 0x00, 0x15 /* - terminal marker 21 - */,
-/* pos 01d3: 202 */ 0xE1 /* 'a' -> */,
-/* pos 01d4: 203 */ 0xEE /* 'n' -> */,
-/* pos 01d5: 204 */ 0xE7 /* 'g' -> */,
-/* pos 01d6: 205 */ 0xF5 /* 'u' -> */,
-/* pos 01d7: 206 */ 0xE1 /* 'a' -> */,
-/* pos 01d8: 207 */ 0xE7 /* 'g' -> */,
-/* pos 01d9: 208 */ 0xE5 /* 'e' -> */,
-/* pos 01da: 209 */ 0xBA /* ':' -> */,
-/* pos 01db: 210 */ 0x00, 0x16 /* - terminal marker 22 - */,
-/* pos 01dd: 211 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01E4 state 212) */,
- 0x6F /* 'o' */, 0x9E, 0x01 /* (to 0x037E state 497) */,
- 0x08, /* fail */
-/* pos 01e4: 212 */ 0xE7 /* 'g' -> */,
-/* pos 01e5: 213 */ 0xED /* 'm' -> */,
-/* pos 01e6: 214 */ 0xE1 /* 'a' -> */,
-/* pos 01e7: 215 */ 0xBA /* ':' -> */,
-/* pos 01e8: 216 */ 0x00, 0x17 /* - terminal marker 23 - */,
-/* pos 01ea: 217 */ 0xE3 /* 'c' -> */,
-/* pos 01eb: 218 */ 0xE8 /* 'h' -> */,
-/* pos 01ec: 219 */ 0xE5 /* 'e' -> */,
-/* pos 01ed: 220 */ 0xAD /* '-' -> */,
-/* pos 01ee: 221 */ 0xE3 /* 'c' -> */,
-/* pos 01ef: 222 */ 0xEF /* 'o' -> */,
-/* pos 01f0: 223 */ 0xEE /* 'n' -> */,
-/* pos 01f1: 224 */ 0xF4 /* 't' -> */,
-/* pos 01f2: 225 */ 0xF2 /* 'r' -> */,
-/* pos 01f3: 226 */ 0xEF /* 'o' -> */,
-/* pos 01f4: 227 */ 0xEC /* 'l' -> */,
-/* pos 01f5: 228 */ 0xBA /* ':' -> */,
-/* pos 01f6: 229 */ 0x00, 0x18 /* - terminal marker 24 - */,
-/* pos 01f8: 230 */ 0xF4 /* 't' -> */,
-/* pos 01f9: 231 */ 0xE8 /* 'h' -> */,
-/* pos 01fa: 232 */ 0xEF /* 'o' -> */,
-/* pos 01fb: 233 */ 0xF2 /* 'r' -> */,
-/* pos 01fc: 234 */ 0xE9 /* 'i' -> */,
-/* pos 01fd: 235 */ 0xFA /* 'z' -> */,
-/* pos 01fe: 236 */ 0xE1 /* 'a' -> */,
-/* pos 01ff: 237 */ 0xF4 /* 't' -> */,
-/* pos 0200: 238 */ 0xE9 /* 'i' -> */,
-/* pos 0201: 239 */ 0xEF /* 'o' -> */,
-/* pos 0202: 240 */ 0xEE /* 'n' -> */,
-/* pos 0203: 241 */ 0xBA /* ':' -> */,
-/* pos 0204: 242 */ 0x00, 0x19 /* - terminal marker 25 - */,
-/* pos 0206: 243 */ 0xEB /* 'k' -> */,
-/* pos 0207: 244 */ 0xE9 /* 'i' -> */,
-/* pos 0208: 245 */ 0xE5 /* 'e' -> */,
-/* pos 0209: 246 */ 0xBA /* ':' -> */,
-/* pos 020a: 247 */ 0x00, 0x1A /* - terminal marker 26 - */,
-/* pos 020c: 248 */ 0xE5 /* 'e' -> */,
-/* pos 020d: 249 */ 0xEE /* 'n' -> */,
-/* pos 020e: 250 */ 0xF4 /* 't' -> */,
-/* pos 020f: 251 */ 0xAD /* '-' -> */,
-/* pos 0210: 252 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0220 state 253) */,
- 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0231 state 260) */,
- 0x64 /* 'd' */, 0xC0, 0x00 /* (to 0x02D6 state 366) */,
- 0x65 /* 'e' */, 0xCA, 0x00 /* (to 0x02E3 state 378) */,
- 0x72 /* 'r' */, 0xE3, 0x00 /* (to 0x02FF state 403) */,
- 0x08, /* fail */
-/* pos 0220: 253 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x022A state 254) */,
- 0x61 /* 'a' */, 0xCA, 0x00 /* (to 0x02ED state 387) */,
- 0x6F /* 'o' */, 0xD0, 0x00 /* (to 0x02F6 state 395) */,
- 0x08, /* fail */
-/* pos 022a: 254 */ 0xEE /* 'n' -> */,
-/* pos 022b: 255 */ 0xE7 /* 'g' -> */,
-/* pos 022c: 256 */ 0xF4 /* 't' -> */,
-/* pos 022d: 257 */ 0xE8 /* 'h' -> */,
-/* pos 022e: 258 */ 0xBA /* ':' -> */,
-/* pos 022f: 259 */ 0x00, 0x1B /* - terminal marker 27 - */,
-/* pos 0231: 260 */ 0xF9 /* 'y' -> */,
-/* pos 0232: 261 */ 0xF0 /* 'p' -> */,
-/* pos 0233: 262 */ 0xE5 /* 'e' -> */,
-/* pos 0234: 263 */ 0xBA /* ':' -> */,
-/* pos 0235: 264 */ 0x00, 0x1C /* - terminal marker 28 - */,
-/* pos 0237: 265 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x023E state 266) */,
- 0x65 /* 'e' */, 0xF6, 0x01 /* (to 0x0430 state 637) */,
- 0x08, /* fail */
-/* pos 023e: 266 */ 0xF4 /* 't' -> */,
-/* pos 023f: 267 */ 0xE5 /* 'e' -> */,
-/* pos 0240: 268 */ 0xBA /* ':' -> */,
-/* pos 0241: 269 */ 0x00, 0x1D /* - terminal marker 29 - */,
-/* pos 0243: 270 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x024A state 271) */,
- 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0250 state 276) */,
- 0x08, /* fail */
-/* pos 024a: 271 */ 0xEE /* 'n' -> */,
-/* pos 024b: 272 */ 0xE7 /* 'g' -> */,
-/* pos 024c: 273 */ 0xE5 /* 'e' -> */,
-/* pos 024d: 274 */ 0xBA /* ':' -> */,
-/* pos 024e: 275 */ 0x00, 0x1E /* - terminal marker 30 - */,
-/* pos 0250: 276 */ 0x66 /* 'f' */, 0x07, 0x00 /* (to 0x0257 state 277) */,
- 0x74 /* 't' */, 0x5A, 0x01 /* (to 0x03AD state 529) */,
- 0x08, /* fail */
-/* pos 0257: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x025E state 278) */,
- 0x72 /* 'r' */, 0x4D, 0x01 /* (to 0x03A7 state 524) */,
- 0x08, /* fail */
-/* pos 025e: 278 */ 0xF2 /* 'r' -> */,
-/* pos 025f: 279 */ 0xE5 /* 'e' -> */,
-/* pos 0260: 280 */ 0xF2 /* 'r' -> */,
-/* pos 0261: 281 */ 0xBA /* ':' -> */,
-/* pos 0262: 282 */ 0x00, 0x1F /* - terminal marker 31 - */,
-/* pos 0264: 283 */ 0x00, 0x20 /* - terminal marker 32 - */,
-/* pos 0266: 284 */ 0xE5 /* 'e' -> */,
-/* pos 0267: 285 */ 0xF2 /* 'r' -> */,
-/* pos 0268: 286 */ 0xF3 /* 's' -> */,
-/* pos 0269: 287 */ 0xE9 /* 'i' -> */,
-/* pos 026a: 288 */ 0xEF /* 'o' -> */,
-/* pos 026b: 289 */ 0xEE /* 'n' -> */,
-/* pos 026c: 290 */ 0xBA /* ':' -> */,
-/* pos 026d: 291 */ 0x00, 0x21 /* - terminal marker 33 - */,
-/* pos 026f: 292 */ 0xF2 /* 'r' -> */,
-/* pos 0270: 293 */ 0xE9 /* 'i' -> */,
-/* pos 0271: 294 */ 0xE7 /* 'g' -> */,
-/* pos 0272: 295 */ 0xE9 /* 'i' -> */,
-/* pos 0273: 296 */ 0xEE /* 'n' -> */,
-/* pos 0274: 297 */ 0xBA /* ':' -> */,
-/* pos 0275: 298 */ 0x00, 0x22 /* - terminal marker 34 - */,
-/* pos 0277: 299 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0284 state 300) */,
- 0x6D /* 'm' */, 0x14, 0x00 /* (to 0x028E state 309) */,
- 0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0295 state 315) */,
- 0x73 /* 's' */, 0x1A, 0x00 /* (to 0x029A state 319) */,
- 0x08, /* fail */
-/* pos 0284: 300 */ 0xF5 /* 'u' -> */,
-/* pos 0285: 301 */ 0xF4 /* 't' -> */,
-/* pos 0286: 302 */ 0xE8 /* 'h' -> */,
-/* pos 0287: 303 */ 0xEF /* 'o' -> */,
-/* pos 0288: 304 */ 0xF2 /* 'r' -> */,
-/* pos 0289: 305 */ 0xE9 /* 'i' -> */,
-/* pos 028a: 306 */ 0xF4 /* 't' -> */,
-/* pos 028b: 307 */ 0xF9 /* 'y' -> */,
-/* pos 028c: 308 */ 0x00, 0x23 /* - terminal marker 35 - */,
-/* pos 028e: 309 */ 0xE5 /* 'e' -> */,
-/* pos 028f: 310 */ 0xF4 /* 't' -> */,
-/* pos 0290: 311 */ 0xE8 /* 'h' -> */,
-/* pos 0291: 312 */ 0xEF /* 'o' -> */,
-/* pos 0292: 313 */ 0xE4 /* 'd' -> */,
-/* pos 0293: 314 */ 0x00, 0x24 /* - terminal marker 36 - */,
-/* pos 0295: 315 */ 0xE1 /* 'a' -> */,
-/* pos 0296: 316 */ 0xF4 /* 't' -> */,
-/* pos 0297: 317 */ 0xE8 /* 'h' -> */,
-/* pos 0298: 318 */ 0x00, 0x25 /* - terminal marker 37 - */,
-/* pos 029a: 319 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x02A1 state 320) */,
- 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x02A7 state 325) */,
- 0x08, /* fail */
-/* pos 02a1: 320 */ 0xE8 /* 'h' -> */,
-/* pos 02a2: 321 */ 0xE5 /* 'e' -> */,
-/* pos 02a3: 322 */ 0xED /* 'm' -> */,
-/* pos 02a4: 323 */ 0xE5 /* 'e' -> */,
-/* pos 02a5: 324 */ 0x00, 0x26 /* - terminal marker 38 - */,
-/* pos 02a7: 325 */ 0xE1 /* 'a' -> */,
-/* pos 02a8: 326 */ 0xF4 /* 't' -> */,
-/* pos 02a9: 327 */ 0xF5 /* 'u' -> */,
-/* pos 02aa: 328 */ 0xF3 /* 's' -> */,
-/* pos 02ab: 329 */ 0x00, 0x27 /* - terminal marker 39 - */,
-/* pos 02ad: 330 */ 0xE8 /* 'h' -> */,
-/* pos 02ae: 331 */ 0xE1 /* 'a' -> */,
-/* pos 02af: 332 */ 0xF2 /* 'r' -> */,
-/* pos 02b0: 333 */ 0xF3 /* 's' -> */,
-/* pos 02b1: 334 */ 0xE5 /* 'e' -> */,
-/* pos 02b2: 335 */ 0xF4 /* 't' -> */,
-/* pos 02b3: 336 */ 0xBA /* ':' -> */,
-/* pos 02b4: 337 */ 0x00, 0x28 /* - terminal marker 40 - */,
-/* pos 02b6: 338 */ 0xE1 /* 'a' -> */,
-/* pos 02b7: 339 */ 0xEE /* 'n' -> */,
-/* pos 02b8: 340 */ 0xE7 /* 'g' -> */,
-/* pos 02b9: 341 */ 0xE5 /* 'e' -> */,
-/* pos 02ba: 342 */ 0xF3 /* 's' -> */,
-/* pos 02bb: 343 */ 0xBA /* ':' -> */,
-/* pos 02bc: 344 */ 0x00, 0x29 /* - terminal marker 41 - */,
-/* pos 02be: 345 */ 0xEC /* 'l' -> */,
-/* pos 02bf: 346 */ 0xEC /* 'l' -> */,
-/* pos 02c0: 347 */ 0xEF /* 'o' -> */,
-/* pos 02c1: 348 */ 0xF7 /* 'w' -> */,
-/* pos 02c2: 349 */ 0xAD /* '-' -> */,
-/* pos 02c3: 350 */ 0xEF /* 'o' -> */,
-/* pos 02c4: 351 */ 0xF2 /* 'r' -> */,
-/* pos 02c5: 352 */ 0xE9 /* 'i' -> */,
-/* pos 02c6: 353 */ 0xE7 /* 'g' -> */,
-/* pos 02c7: 354 */ 0xE9 /* 'i' -> */,
-/* pos 02c8: 355 */ 0xEE /* 'n' -> */,
-/* pos 02c9: 356 */ 0xBA /* ':' -> */,
-/* pos 02ca: 357 */ 0x00, 0x2A /* - terminal marker 42 - */,
-/* pos 02cc: 358 */ 0xE5 /* 'e' -> */,
-/* pos 02cd: 359 */ 0xBA /* ':' -> */,
-/* pos 02ce: 360 */ 0x00, 0x2B /* - terminal marker 43 - */,
-/* pos 02d0: 361 */ 0xEC /* 'l' -> */,
-/* pos 02d1: 362 */ 0xEF /* 'o' -> */,
-/* pos 02d2: 363 */ 0xF7 /* 'w' -> */,
-/* pos 02d3: 364 */ 0xBA /* ':' -> */,
-/* pos 02d4: 365 */ 0x00, 0x2C /* - terminal marker 44 - */,
-/* pos 02d6: 366 */ 0xE9 /* 'i' -> */,
-/* pos 02d7: 367 */ 0xF3 /* 's' -> */,
-/* pos 02d8: 368 */ 0xF0 /* 'p' -> */,
-/* pos 02d9: 369 */ 0xEF /* 'o' -> */,
-/* pos 02da: 370 */ 0xF3 /* 's' -> */,
-/* pos 02db: 371 */ 0xE9 /* 'i' -> */,
-/* pos 02dc: 372 */ 0xF4 /* 't' -> */,
-/* pos 02dd: 373 */ 0xE9 /* 'i' -> */,
-/* pos 02de: 374 */ 0xEF /* 'o' -> */,
-/* pos 02df: 375 */ 0xEE /* 'n' -> */,
-/* pos 02e0: 376 */ 0xBA /* ':' -> */,
-/* pos 02e1: 377 */ 0x00, 0x2D /* - terminal marker 45 - */,
-/* pos 02e3: 378 */ 0xEE /* 'n' -> */,
-/* pos 02e4: 379 */ 0xE3 /* 'c' -> */,
-/* pos 02e5: 380 */ 0xEF /* 'o' -> */,
-/* pos 02e6: 381 */ 0xE4 /* 'd' -> */,
-/* pos 02e7: 382 */ 0xE9 /* 'i' -> */,
-/* pos 02e8: 383 */ 0xEE /* 'n' -> */,
-/* pos 02e9: 384 */ 0xE7 /* 'g' -> */,
-/* pos 02ea: 385 */ 0xBA /* ':' -> */,
-/* pos 02eb: 386 */ 0x00, 0x2E /* - terminal marker 46 - */,
-/* pos 02ed: 387 */ 0xEE /* 'n' -> */,
-/* pos 02ee: 388 */ 0xE7 /* 'g' -> */,
-/* pos 02ef: 389 */ 0xF5 /* 'u' -> */,
-/* pos 02f0: 390 */ 0xE1 /* 'a' -> */,
-/* pos 02f1: 391 */ 0xE7 /* 'g' -> */,
-/* pos 02f2: 392 */ 0xE5 /* 'e' -> */,
-/* pos 02f3: 393 */ 0xBA /* ':' -> */,
-/* pos 02f4: 394 */ 0x00, 0x2F /* - terminal marker 47 - */,
-/* pos 02f6: 395 */ 0xE3 /* 'c' -> */,
-/* pos 02f7: 396 */ 0xE1 /* 'a' -> */,
-/* pos 02f8: 397 */ 0xF4 /* 't' -> */,
-/* pos 02f9: 398 */ 0xE9 /* 'i' -> */,
-/* pos 02fa: 399 */ 0xEF /* 'o' -> */,
-/* pos 02fb: 400 */ 0xEE /* 'n' -> */,
-/* pos 02fc: 401 */ 0xBA /* ':' -> */,
-/* pos 02fd: 402 */ 0x00, 0x30 /* - terminal marker 48 - */,
-/* pos 02ff: 403 */ 0xE1 /* 'a' -> */,
-/* pos 0300: 404 */ 0xEE /* 'n' -> */,
-/* pos 0301: 405 */ 0xE7 /* 'g' -> */,
-/* pos 0302: 406 */ 0xE5 /* 'e' -> */,
-/* pos 0303: 407 */ 0xBA /* ':' -> */,
-/* pos 0304: 408 */ 0x00, 0x31 /* - terminal marker 49 - */,
-/* pos 0306: 409 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x030D state 410) */,
- 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x0312 state 414) */,
- 0x08, /* fail */
-/* pos 030d: 410 */ 0xE1 /* 'a' -> */,
-/* pos 030e: 411 */ 0xE7 /* 'g' -> */,
-/* pos 030f: 412 */ 0xBA /* ':' -> */,
-/* pos 0310: 413 */ 0x00, 0x32 /* - terminal marker 50 - */,
-/* pos 0312: 414 */ 0xF0 /* 'p' -> */,
-/* pos 0313: 415 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x031A state 416) */,
- 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x031F state 420) */,
- 0x08, /* fail */
-/* pos 031a: 416 */ 0xE3 /* 'c' -> */,
-/* pos 031b: 417 */ 0xF4 /* 't' -> */,
-/* pos 031c: 418 */ 0xBA /* ':' -> */,
-/* pos 031d: 419 */ 0x00, 0x33 /* - terminal marker 51 - */,
-/* pos 031f: 420 */ 0xF2 /* 'r' -> */,
-/* pos 0320: 421 */ 0xE5 /* 'e' -> */,
-/* pos 0321: 422 */ 0xF3 /* 's' -> */,
-/* pos 0322: 423 */ 0xBA /* ':' -> */,
-/* pos 0323: 424 */ 0x00, 0x34 /* - terminal marker 52 - */,
-/* pos 0325: 425 */ 0xF2 /* 'r' -> */,
-/* pos 0326: 426 */ 0xEF /* 'o' -> */,
-/* pos 0327: 427 */ 0xED /* 'm' -> */,
-/* pos 0328: 428 */ 0xBA /* ':' -> */,
-/* pos 0329: 429 */ 0x00, 0x35 /* - terminal marker 53 - */,
-/* pos 032b: 430 */ 0xF4 /* 't' -> */,
-/* pos 032c: 431 */ 0xE3 /* 'c' -> */,
-/* pos 032d: 432 */ 0xE8 /* 'h' -> */,
-/* pos 032e: 433 */ 0xBA /* ':' -> */,
-/* pos 032f: 434 */ 0x00, 0x36 /* - terminal marker 54 - */,
-/* pos 0331: 435 */ 0xE1 /* 'a' -> */,
-/* pos 0332: 436 */ 0xEE /* 'n' -> */,
-/* pos 0333: 437 */ 0xE7 /* 'g' -> */,
-/* pos 0334: 438 */ 0xE5 /* 'e' -> */,
-/* pos 0335: 439 */ 0xBA /* ':' -> */,
-/* pos 0336: 440 */ 0x00, 0x37 /* - terminal marker 55 - */,
-/* pos 0338: 441 */ 0xEE /* 'n' -> */,
-/* pos 0339: 442 */ 0xED /* 'm' -> */,
-/* pos 033a: 443 */ 0xEF /* 'o' -> */,
-/* pos 033b: 444 */ 0xE4 /* 'd' -> */,
-/* pos 033c: 445 */ 0xE9 /* 'i' -> */,
-/* pos 033d: 446 */ 0xE6 /* 'f' -> */,
-/* pos 033e: 447 */ 0xE9 /* 'i' -> */,
-/* pos 033f: 448 */ 0xE5 /* 'e' -> */,
-/* pos 0340: 449 */ 0xE4 /* 'd' -> */,
-/* pos 0341: 450 */ 0xAD /* '-' -> */,
-/* pos 0342: 451 */ 0xF3 /* 's' -> */,
-/* pos 0343: 452 */ 0xE9 /* 'i' -> */,
-/* pos 0344: 453 */ 0xEE /* 'n' -> */,
-/* pos 0345: 454 */ 0xE3 /* 'c' -> */,
-/* pos 0346: 455 */ 0xE5 /* 'e' -> */,
-/* pos 0347: 456 */ 0xBA /* ':' -> */,
-/* pos 0348: 457 */ 0x00, 0x38 /* - terminal marker 56 - */,
-/* pos 034a: 458 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0354 state 459) */,
- 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x0362 state 472) */,
- 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0367 state 476) */,
- 0x08, /* fail */
-/* pos 0354: 459 */ 0xF3 /* 's' -> */,
-/* pos 0355: 460 */ 0xF4 /* 't' -> */,
-/* pos 0356: 461 */ 0xAD /* '-' -> */,
-/* pos 0357: 462 */ 0xED /* 'm' -> */,
-/* pos 0358: 463 */ 0xEF /* 'o' -> */,
-/* pos 0359: 464 */ 0xE4 /* 'd' -> */,
-/* pos 035a: 465 */ 0xE9 /* 'i' -> */,
-/* pos 035b: 466 */ 0xE6 /* 'f' -> */,
-/* pos 035c: 467 */ 0xE9 /* 'i' -> */,
-/* pos 035d: 468 */ 0xE5 /* 'e' -> */,
-/* pos 035e: 469 */ 0xE4 /* 'd' -> */,
-/* pos 035f: 470 */ 0xBA /* ':' -> */,
-/* pos 0360: 471 */ 0x00, 0x39 /* - terminal marker 57 - */,
-/* pos 0362: 472 */ 0xEE /* 'n' -> */,
-/* pos 0363: 473 */ 0xEB /* 'k' -> */,
-/* pos 0364: 474 */ 0xBA /* ':' -> */,
-/* pos 0365: 475 */ 0x00, 0x3A /* - terminal marker 58 - */,
-/* pos 0367: 476 */ 0xE3 /* 'c' -> */,
-/* pos 0368: 477 */ 0xE1 /* 'a' -> */,
-/* pos 0369: 478 */ 0xF4 /* 't' -> */,
-/* pos 036a: 479 */ 0xE9 /* 'i' -> */,
-/* pos 036b: 480 */ 0xEF /* 'o' -> */,
-/* pos 036c: 481 */ 0xEE /* 'n' -> */,
-/* pos 036d: 482 */ 0xBA /* ':' -> */,
-/* pos 036e: 483 */ 0x00, 0x3B /* - terminal marker 59 - */,
-/* pos 0370: 484 */ 0xE1 /* 'a' -> */,
-/* pos 0371: 485 */ 0xF8 /* 'x' -> */,
-/* pos 0372: 486 */ 0xAD /* '-' -> */,
-/* pos 0373: 487 */ 0xE6 /* 'f' -> */,
-/* pos 0374: 488 */ 0xEF /* 'o' -> */,
-/* pos 0375: 489 */ 0xF2 /* 'r' -> */,
-/* pos 0376: 490 */ 0xF7 /* 'w' -> */,
-/* pos 0377: 491 */ 0xE1 /* 'a' -> */,
-/* pos 0378: 492 */ 0xF2 /* 'r' -> */,
-/* pos 0379: 493 */ 0xE4 /* 'd' -> */,
-/* pos 037a: 494 */ 0xF3 /* 's' -> */,
-/* pos 037b: 495 */ 0xBA /* ':' -> */,
-/* pos 037c: 496 */ 0x00, 0x3C /* - terminal marker 60 - */,
-/* pos 037e: 497 */ 0xF8 /* 'x' -> */,
-/* pos 037f: 498 */ 0xF9 /* 'y' -> */,
-/* pos 0380: 499 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0387 state 500) */,
- 0x20 /* ' ' */, 0xBB, 0x00 /* (to 0x043E state 649) */,
- 0x08, /* fail */
-/* pos 0387: 500 */ 0xE1 /* 'a' -> */,
-/* pos 0388: 501 */ 0xF5 /* 'u' -> */,
-/* pos 0389: 502 */ 0xF4 /* 't' -> */,
-/* pos 038a: 503 */ 0xE8 /* 'h' -> */,
-/* pos 038b: 504 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0392 state 505) */,
- 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x039C state 514) */,
- 0x08, /* fail */
-/* pos 0392: 505 */ 0xEE /* 'n' -> */,
-/* pos 0393: 506 */ 0xF4 /* 't' -> */,
-/* pos 0394: 507 */ 0xE9 /* 'i' -> */,
-/* pos 0395: 508 */ 0xE3 /* 'c' -> */,
-/* pos 0396: 509 */ 0xE1 /* 'a' -> */,
-/* pos 0397: 510 */ 0xF4 /* 't' -> */,
-/* pos 0398: 511 */ 0xE5 /* 'e' -> */,
-/* pos 0399: 512 */ 0xBA /* ':' -> */,
-/* pos 039a: 513 */ 0x00, 0x3D /* - terminal marker 61 - */,
-/* pos 039c: 514 */ 0xF2 /* 'r' -> */,
-/* pos 039d: 515 */ 0xE9 /* 'i' -> */,
-/* pos 039e: 516 */ 0xFA /* 'z' -> */,
-/* pos 039f: 517 */ 0xE1 /* 'a' -> */,
-/* pos 03a0: 518 */ 0xF4 /* 't' -> */,
-/* pos 03a1: 519 */ 0xE9 /* 'i' -> */,
-/* pos 03a2: 520 */ 0xEF /* 'o' -> */,
-/* pos 03a3: 521 */ 0xEE /* 'n' -> */,
-/* pos 03a4: 522 */ 0xBA /* ':' -> */,
-/* pos 03a5: 523 */ 0x00, 0x3E /* - terminal marker 62 - */,
-/* pos 03a7: 524 */ 0xE5 /* 'e' -> */,
-/* pos 03a8: 525 */ 0xF3 /* 's' -> */,
-/* pos 03a9: 526 */ 0xE8 /* 'h' -> */,
-/* pos 03aa: 527 */ 0xBA /* ':' -> */,
-/* pos 03ab: 528 */ 0x00, 0x3F /* - terminal marker 63 - */,
-/* pos 03ad: 529 */ 0xF2 /* 'r' -> */,
-/* pos 03ae: 530 */ 0xF9 /* 'y' -> */,
-/* pos 03af: 531 */ 0xAD /* '-' -> */,
-/* pos 03b0: 532 */ 0xE1 /* 'a' -> */,
-/* pos 03b1: 533 */ 0xE6 /* 'f' -> */,
-/* pos 03b2: 534 */ 0xF4 /* 't' -> */,
-/* pos 03b3: 535 */ 0xE5 /* 'e' -> */,
-/* pos 03b4: 536 */ 0xF2 /* 'r' -> */,
-/* pos 03b5: 537 */ 0xBA /* ':' -> */,
-/* pos 03b6: 538 */ 0x00, 0x40 /* - terminal marker 64 - */,
-/* pos 03b8: 539 */ 0xF6 /* 'v' -> */,
-/* pos 03b9: 540 */ 0xE5 /* 'e' -> */,
-/* pos 03ba: 541 */ 0xF2 /* 'r' -> */,
-/* pos 03bb: 542 */ 0xBA /* ':' -> */,
-/* pos 03bc: 543 */ 0x00, 0x41 /* - terminal marker 65 - */,
-/* pos 03be: 544 */ 0xAD /* '-' -> */,
-/* pos 03bf: 545 */ 0xE3 /* 'c' -> */,
-/* pos 03c0: 546 */ 0xEF /* 'o' -> */,
-/* pos 03c1: 547 */ 0xEF /* 'o' -> */,
-/* pos 03c2: 548 */ 0xEB /* 'k' -> */,
-/* pos 03c3: 549 */ 0xE9 /* 'i' -> */,
-/* pos 03c4: 550 */ 0xE5 /* 'e' -> */,
-/* pos 03c5: 551 */ 0xBA /* ':' -> */,
-/* pos 03c6: 552 */ 0x00, 0x42 /* - terminal marker 66 - */,
-/* pos 03c8: 553 */ 0xF2 /* 'r' -> */,
-/* pos 03c9: 554 */ 0xE9 /* 'i' -> */,
-/* pos 03ca: 555 */ 0xE3 /* 'c' -> */,
-/* pos 03cb: 556 */ 0xF4 /* 't' -> */,
-/* pos 03cc: 557 */ 0xAD /* '-' -> */,
-/* pos 03cd: 558 */ 0xF4 /* 't' -> */,
-/* pos 03ce: 559 */ 0xF2 /* 'r' -> */,
-/* pos 03cf: 560 */ 0xE1 /* 'a' -> */,
-/* pos 03d0: 561 */ 0xEE /* 'n' -> */,
-/* pos 03d1: 562 */ 0xF3 /* 's' -> */,
-/* pos 03d2: 563 */ 0xF0 /* 'p' -> */,
-/* pos 03d3: 564 */ 0xEF /* 'o' -> */,
-/* pos 03d4: 565 */ 0xF2 /* 'r' -> */,
-/* pos 03d5: 566 */ 0xF4 /* 't' -> */,
-/* pos 03d6: 567 */ 0xAD /* '-' -> */,
-/* pos 03d7: 568 */ 0xF3 /* 's' -> */,
-/* pos 03d8: 569 */ 0xE5 /* 'e' -> */,
-/* pos 03d9: 570 */ 0xE3 /* 'c' -> */,
-/* pos 03da: 571 */ 0xF5 /* 'u' -> */,
-/* pos 03db: 572 */ 0xF2 /* 'r' -> */,
-/* pos 03dc: 573 */ 0xE9 /* 'i' -> */,
-/* pos 03dd: 574 */ 0xF4 /* 't' -> */,
-/* pos 03de: 575 */ 0xF9 /* 'y' -> */,
-/* pos 03df: 576 */ 0xBA /* ':' -> */,
-/* pos 03e0: 577 */ 0x00, 0x43 /* - terminal marker 67 - */,
-/* pos 03e2: 578 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x03E9 state 579) */,
- 0x65 /* 'e' */, 0x84, 0x00 /* (to 0x0469 state 680) */,
- 0x08, /* fail */
-/* pos 03e9: 579 */ 0xE1 /* 'a' -> */,
-/* pos 03ea: 580 */ 0xEE /* 'n' -> */,
-/* pos 03eb: 581 */ 0xF3 /* 's' -> */,
-/* pos 03ec: 582 */ 0xE6 /* 'f' -> */,
-/* pos 03ed: 583 */ 0xE5 /* 'e' -> */,
-/* pos 03ee: 584 */ 0xF2 /* 'r' -> */,
-/* pos 03ef: 585 */ 0xAD /* '-' -> */,
-/* pos 03f0: 586 */ 0xE5 /* 'e' -> */,
-/* pos 03f1: 587 */ 0xEE /* 'n' -> */,
-/* pos 03f2: 588 */ 0xE3 /* 'c' -> */,
-/* pos 03f3: 589 */ 0xEF /* 'o' -> */,
-/* pos 03f4: 590 */ 0xE4 /* 'd' -> */,
-/* pos 03f5: 591 */ 0xE9 /* 'i' -> */,
-/* pos 03f6: 592 */ 0xEE /* 'n' -> */,
-/* pos 03f7: 593 */ 0xE7 /* 'g' -> */,
-/* pos 03f8: 594 */ 0xBA /* ':' -> */,
-/* pos 03f9: 595 */ 0x00, 0x44 /* - terminal marker 68 - */,
-/* pos 03fb: 596 */ 0xE5 /* 'e' -> */,
-/* pos 03fc: 597 */ 0xF2 /* 'r' -> */,
-/* pos 03fd: 598 */ 0xAD /* '-' -> */,
-/* pos 03fe: 599 */ 0xE1 /* 'a' -> */,
-/* pos 03ff: 600 */ 0xE7 /* 'g' -> */,
-/* pos 0400: 601 */ 0xE5 /* 'e' -> */,
-/* pos 0401: 602 */ 0xEE /* 'n' -> */,
-/* pos 0402: 603 */ 0xF4 /* 't' -> */,
-/* pos 0403: 604 */ 0xBA /* ':' -> */,
-/* pos 0404: 605 */ 0x00, 0x45 /* - terminal marker 69 - */,
-/* pos 0406: 606 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x040D state 607) */,
- 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0412 state 611) */,
- 0x08, /* fail */
-/* pos 040d: 607 */ 0xF2 /* 'r' -> */,
-/* pos 040e: 608 */ 0xF9 /* 'y' -> */,
-/* pos 040f: 609 */ 0xBA /* ':' -> */,
-/* pos 0410: 610 */ 0x00, 0x46 /* - terminal marker 70 - */,
-/* pos 0412: 611 */ 0xE1 /* 'a' -> */,
-/* pos 0413: 612 */ 0xBA /* ':' -> */,
-/* pos 0414: 613 */ 0x00, 0x47 /* - terminal marker 71 - */,
-/* pos 0416: 614 */ 0xF7 /* 'w' -> */,
-/* pos 0417: 615 */ 0xF7 /* 'w' -> */,
-/* pos 0418: 616 */ 0xAD /* '-' -> */,
-/* pos 0419: 617 */ 0xE1 /* 'a' -> */,
-/* pos 041a: 618 */ 0xF5 /* 'u' -> */,
-/* pos 041b: 619 */ 0xF4 /* 't' -> */,
-/* pos 041c: 620 */ 0xE8 /* 'h' -> */,
-/* pos 041d: 621 */ 0xE5 /* 'e' -> */,
-/* pos 041e: 622 */ 0xEE /* 'n' -> */,
-/* pos 041f: 623 */ 0xF4 /* 't' -> */,
-/* pos 0420: 624 */ 0xE9 /* 'i' -> */,
-/* pos 0421: 625 */ 0xE3 /* 'c' -> */,
-/* pos 0422: 626 */ 0xE1 /* 'a' -> */,
-/* pos 0423: 627 */ 0xF4 /* 't' -> */,
-/* pos 0424: 628 */ 0xE5 /* 'e' -> */,
-/* pos 0425: 629 */ 0xBA /* ':' -> */,
-/* pos 0426: 630 */ 0x00, 0x48 /* - terminal marker 72 - */,
-/* pos 0428: 631 */ 0xF4 /* 't' -> */,
-/* pos 0429: 632 */ 0xE3 /* 'c' -> */,
-/* pos 042a: 633 */ 0xE8 /* 'h' -> */,
-/* pos 042b: 634 */ 0x00, 0x49 /* - terminal marker 73 - */,
-/* pos 042d: 635 */ 0xF4 /* 't' -> */,
-/* pos 042e: 636 */ 0x00, 0x4A /* - terminal marker 74 - */,
-/* pos 0430: 637 */ 0xEC /* 'l' -> */,
-/* pos 0431: 638 */ 0xE5 /* 'e' -> */,
-/* pos 0432: 639 */ 0xF4 /* 't' -> */,
-/* pos 0433: 640 */ 0xE5 /* 'e' -> */,
-/* pos 0434: 641 */ 0x00, 0x4B /* - terminal marker 75 - */,
-/* pos 0436: 642 */ 0xE9 /* 'i' -> */,
-/* pos 0437: 643 */ 0xAD /* '-' -> */,
-/* pos 0438: 644 */ 0xE1 /* 'a' -> */,
-/* pos 0439: 645 */ 0xF2 /* 'r' -> */,
-/* pos 043a: 646 */ 0xE7 /* 'g' -> */,
-/* pos 043b: 647 */ 0xF3 /* 's' -> */,
-/* pos 043c: 648 */ 0x00, 0x4C /* - terminal marker 76 - */,
-/* pos 043e: 649 */ 0x00, 0x4D /* - terminal marker 77 - */,
-/* pos 0440: 650 */ 0xAD /* '-' -> */,
-/* pos 0441: 651 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0448 state 652) */,
- 0x66 /* 'f' */, 0x10, 0x00 /* (to 0x0454 state 662) */,
- 0x08, /* fail */
-/* pos 0448: 652 */ 0xE5 /* 'e' -> */,
-/* pos 0449: 653 */ 0xE1 /* 'a' -> */,
-/* pos 044a: 654 */ 0xEC /* 'l' -> */,
-/* pos 044b: 655 */ 0xAD /* '-' -> */,
-/* pos 044c: 656 */ 0xE9 /* 'i' -> */,
-/* pos 044d: 657 */ 0xF0 /* 'p' -> */,
-/* pos 044e: 658 */ 0xBA /* ':' -> */,
-/* pos 044f: 659 */ 0x00, 0x4E /* - terminal marker 78 - */,
-/* pos 0451: 660 */ 0xA0 /* ' ' -> */,
-/* pos 0452: 661 */ 0x00, 0x4F /* - terminal marker 79 - */,
-/* pos 0454: 662 */ 0xEF /* 'o' -> */,
-/* pos 0455: 663 */ 0xF2 /* 'r' -> */,
-/* pos 0456: 664 */ 0xF7 /* 'w' -> */,
-/* pos 0457: 665 */ 0xE1 /* 'a' -> */,
-/* pos 0458: 666 */ 0xF2 /* 'r' -> */,
-/* pos 0459: 667 */ 0xE4 /* 'd' -> */,
-/* pos 045a: 668 */ 0xE5 /* 'e' -> */,
-/* pos 045b: 669 */ 0xE4 /* 'd' -> */,
-/* pos 045c: 670 */ 0xAD /* '-' -> */,
-/* pos 045d: 671 */ 0xE6 /* 'f' -> */,
-/* pos 045e: 672 */ 0xEF /* 'o' -> */,
-/* pos 045f: 673 */ 0xF2 /* 'r' -> */,
-/* pos 0460: 674 */ 0x00, 0x50 /* - terminal marker 80 - */,
-/* pos 0462: 675 */ 0x00, 0x51 /* - terminal marker 81 - */,
-/* pos 0464: 676 */ 0xE1 /* 'a' -> */,
-/* pos 0465: 677 */ 0xE4 /* 'd' -> */,
-/* pos 0466: 678 */ 0xA0 /* ' ' -> */,
-/* pos 0467: 679 */ 0x00, 0x52 /* - terminal marker 82 - */,
-/* pos 0469: 680 */ 0xBA /* ':' -> */,
-/* pos 046a: 681 */ 0x00, 0x53 /* - terminal marker 83 - */,
-/* total size 1132 bytes */
diff --git a/thirdparty/lws/mbedtls_verify.diff b/thirdparty/lws/mbedtls_verify.diff
deleted file mode 100644
index d320645d67..0000000000
--- a/thirdparty/lws/mbedtls_verify.diff
+++ /dev/null
@@ -1,74 +0,0 @@
-diff --git a/thirdparty/lws/client/ssl-client.c b/thirdparty/lws/client/ssl-client.c
-index 6626e0844..962c6e3cb 100644
---- a/thirdparty/lws/client/ssl-client.c
-+++ b/thirdparty/lws/client/ssl-client.c
-@@ -176,11 +176,7 @@ lws_ssl_client_bio_create(struct lws *wsi)
- #endif
- #else
- #if defined(LWS_WITH_MBEDTLS)
-- if (wsi->vhost->x509_client_CA)
-- SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
-- else
-- SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback);
--
-+ SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
- #else
- #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
- SSL_set_tlsext_host_name(wsi->ssl, hostname);
-diff --git a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
-index 63504919c..4e3d61109 100644
---- a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
-+++ b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
-@@ -218,7 +218,7 @@ static int ssl_pm_reload_crt(SSL *ssl)
- struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm;
-
- if (ssl->verify_mode == SSL_VERIFY_PEER)
-- mode = MBEDTLS_SSL_VERIFY_REQUIRED;
-+ mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
- else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
- mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
- else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE)
-@@ -712,11 +712,39 @@ long ssl_pm_get_verify_result(const SSL *ssl)
- struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
-
- ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
-- if (ret) {
-- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret);
-+
-+ if (!ret)
-+ return X509_V_OK;
-+
-+ if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
-+ (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
-+ // Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification
-+ verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
-+
-+ else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
-+ verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
-+
-+ else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
-+ (ret & MBEDTLS_X509_BADCRL_BAD_KEY))
-+ verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
-+
-+ else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
-+ (ret & MBEDTLS_X509_BADCRL_BAD_MD))
-+ verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
-+
-+ else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
-+ (ret & MBEDTLS_X509_BADCRL_FUTURE))
-+ verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
-+
-+ else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
-+ (ret & MBEDTLS_X509_BADCRL_EXPIRED))
-+ verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
-+
-+ else
- verify_result = X509_V_ERR_UNSPECIFIED;
-- } else
-- verify_result = X509_V_OK;
-+
-+ SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
-+ "mbedtls_ssl_get_verify_result() return 0x%x", ret);
-
- return verify_result;
- }
diff --git a/thirdparty/lws/misc/lejp.h b/thirdparty/lws/misc/lejp.h
deleted file mode 100644
index 0b37bb3e42..0000000000
--- a/thirdparty/lws/misc/lejp.h
+++ /dev/null
@@ -1,232 +0,0 @@
-#include "libwebsockets.h"
-struct lejp_ctx;
-
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
-#endif
-#define LEJP_FLAG_WS_KEEP 64
-#define LEJP_FLAG_WS_COMMENTLINE 32
-
-enum lejp_states {
- LEJP_IDLE = 0,
- LEJP_MEMBERS = 1,
- LEJP_M_P = 2,
- LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3,
- LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4,
- LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5,
- LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6,
- LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7,
- LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8,
- LEJP_MP_DELIM = 9,
- LEJP_MP_VALUE = 10,
- LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11,
- LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12,
- LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13,
- LEJP_MP_COMMA_OR_END = 14,
- LEJP_MP_ARRAY_END = 15,
-};
-
-enum lejp_reasons {
- LEJP_CONTINUE = -1,
- LEJP_REJECT_IDLE_NO_BRACE = -2,
- LEJP_REJECT_MEMBERS_NO_CLOSE = -3,
- LEJP_REJECT_MP_NO_OPEN_QUOTE = -4,
- LEJP_REJECT_MP_STRING_UNDERRUN = -5,
- LEJP_REJECT_MP_ILLEGAL_CTRL = -6,
- LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7,
- LEJP_REJECT_ILLEGAL_HEX = -8,
- LEJP_REJECT_MP_DELIM_MISSING_COLON = -9,
- LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10,
- LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11,
- LEJP_REJECT_MP_VAL_NUM_FORMAT = -12,
- LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13,
- LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14,
- LEJP_REJECT_MP_C_OR_E_UNDERF = -15,
- LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16,
- LEJP_REJECT_MP_ARRAY_END_MISSING = -17,
- LEJP_REJECT_STACK_OVERFLOW = -18,
- LEJP_REJECT_MP_DELIM_ISTACK = -19,
- LEJP_REJECT_NUM_TOO_LONG = -20,
- LEJP_REJECT_MP_C_OR_E_NEITHER = -21,
- LEJP_REJECT_UNKNOWN = -22,
- LEJP_REJECT_CALLBACK = -23
-};
-
-#define LEJP_FLAG_CB_IS_VALUE 64
-
-enum lejp_callbacks {
- LEJPCB_CONSTRUCTED = 0,
- LEJPCB_DESTRUCTED = 1,
-
- LEJPCB_START = 2,
- LEJPCB_COMPLETE = 3,
- LEJPCB_FAILED = 4,
-
- LEJPCB_PAIR_NAME = 5,
-
- LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6,
- LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7,
- LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8,
- LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9,
- LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10,
- LEJPCB_VAL_STR_START = 11, /* notice handle separately */
- LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12,
- LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13,
-
- LEJPCB_ARRAY_START = 14,
- LEJPCB_ARRAY_END = 15,
-
- LEJPCB_OBJECT_START = 16,
- LEJPCB_OBJECT_END = 17
-};
-
-/**
- * _lejp_callback() - User parser actions
- * \param ctx: LEJP context
- * \param reason: Callback reason
- *
- * Your user callback is associated with the context at construction time,
- * and receives calls as the parsing progresses.
- *
- * All of the callbacks may be ignored and just return 0.
- *
- * The reasons it might get called, found in @reason, are:
- *
- * LEJPCB_CONSTRUCTED: The context was just constructed... you might want to
- * perform one-time allocation for the life of the context.
- *
- * LEJPCB_DESTRUCTED: The context is being destructed... if you made any
- * allocations at construction-time, you can free them now
- *
- * LEJPCB_START: Parsing is beginning at the first byte of input
- *
- * LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or
- * positive return code from lejp_parse indicating the
- * amount of unused bytes left in the input buffer
- *
- * LEJPCB_FAILED: Parsing failed. You'll get a negative error code
- * returned from lejp_parse
- *
- * LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed,
- * this callback occurs. You can find the new name at
- * the end of ctx->path[]
- *
- * LEJPCB_VAL_TRUE: The "true" value appeared
- *
- * LEJPCB_VAL_FALSE: The "false" value appeared
- *
- * LEJPCB_VAL_NULL: The "null" value appeared
- *
- * LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf
- *
- * LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf
- *
- * LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet
- *
- * LEJPCB_VAL_STR_CHUNK: We parsed LEJP_STRING_CHUNK -1 bytes of string data in
- * ctx->buf, which is as much as we can buffer, so we are
- * spilling it. If all your strings are less than
- * LEJP_STRING_CHUNK - 1 bytes, you will never see this
- * callback.
- *
- * LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the
- * string is in ctx->buf.
- *
- * LEJPCB_ARRAY_START: An array started
- *
- * LEJPCB_ARRAY_END: An array ended
- *
- * LEJPCB_OBJECT_START: An object started
- *
- * LEJPCB_OBJECT_END: An object ended
- */
-LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason);
-
-typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason);
-
-#ifndef LEJP_MAX_DEPTH
-#define LEJP_MAX_DEPTH 12
-#endif
-#ifndef LEJP_MAX_INDEX_DEPTH
-#define LEJP_MAX_INDEX_DEPTH 5
-#endif
-#ifndef LEJP_MAX_PATH
-#define LEJP_MAX_PATH 128
-#endif
-#ifndef LEJP_STRING_CHUNK
-/* must be >= 30 to assemble floats */
-#define LEJP_STRING_CHUNK 255
-#endif
-
-enum num_flags {
- LEJP_SEEN_MINUS = (1 << 0),
- LEJP_SEEN_POINT = (1 << 1),
- LEJP_SEEN_POST_POINT = (1 << 2),
- LEJP_SEEN_EXP = (1 << 3)
-};
-
-struct _lejp_stack {
- char s; /* lejp_state stack*/
- char p; /* path length */
- char i; /* index array length */
- char b; /* user bitfield */
-};
-
-struct lejp_ctx {
-
- /* sorted by type for most compact alignment
- *
- * pointers
- */
-
- signed char (*callback)(struct lejp_ctx *ctx, char reason);
- void *user;
- const char * const *paths;
-
- /* arrays */
-
- struct _lejp_stack st[LEJP_MAX_DEPTH];
- unsigned short i[LEJP_MAX_INDEX_DEPTH]; /* index array */
- unsigned short wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
- char path[LEJP_MAX_PATH];
- char buf[LEJP_STRING_CHUNK];
-
- /* int */
-
- unsigned int line;
-
- /* short */
-
- unsigned short uni;
-
- /* char */
-
- unsigned char npos;
- unsigned char dcount;
- unsigned char f;
- unsigned char sp; /* stack head */
- unsigned char ipos; /* index stack depth */
- unsigned char ppos;
- unsigned char count_paths;
- unsigned char path_match;
- unsigned char path_match_len;
- unsigned char wildcount;
-};
-
-LWS_VISIBLE LWS_EXTERN void
-lejp_construct(struct lejp_ctx *ctx,
- signed char (*callback)(struct lejp_ctx *ctx, char reason),
- void *user, const char * const *paths, unsigned char paths_count);
-
-LWS_VISIBLE LWS_EXTERN void
-lejp_destruct(struct lejp_ctx *ctx);
-
-LWS_VISIBLE LWS_EXTERN int
-lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len);
-
-LWS_VISIBLE LWS_EXTERN void
-lejp_change_callback(struct lejp_ctx *ctx,
- signed char (*callback)(struct lejp_ctx *ctx, char reason));
-
-LWS_VISIBLE LWS_EXTERN int
-lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);
diff --git a/thirdparty/lws/output.c b/thirdparty/lws/output.c
deleted file mode 100644
index 375ff3ef99..0000000000
--- a/thirdparty/lws/output.c
+++ /dev/null
@@ -1,883 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-static int
-lws_0405_frame_mask_generate(struct lws *wsi)
-{
-#if 0
- wsi->u.ws.mask[0] = 0;
- wsi->u.ws.mask[1] = 0;
- wsi->u.ws.mask[2] = 0;
- wsi->u.ws.mask[3] = 0;
-#else
- int n;
- /* fetch the per-frame nonce */
-
- n = lws_get_random(lws_get_context(wsi), wsi->u.ws.mask, 4);
- if (n != 4) {
- lwsl_parser("Unable to read from random device %s %d\n",
- SYSTEM_RANDOM_FILEPATH, n);
- return 1;
- }
-#endif
- /* start masking from first byte of masking key buffer */
- wsi->u.ws.mask_idx = 0;
-
- return 0;
-}
-
-/*
- * notice this returns number of bytes consumed, or -1
- */
-int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
-{
- struct lws_context *context = lws_get_context(wsi);
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- size_t real_len = len;
- unsigned int n;
- int m;
-
- lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1);
-
- if (!len)
- return 0;
- /* just ignore sends after we cleared the truncation buffer */
- if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
- !wsi->trunc_len)
- return len;
-
- if (wsi->trunc_len && (buf < wsi->trunc_alloc ||
- buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) {
- char dump[20];
- strncpy(dump, (char *)buf, sizeof(dump) - 1);
- dump[sizeof(dump) - 1] = '\0';
-#if defined(LWS_WITH_ESP8266)
- lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n",
- wsi, (unsigned long)len, dump);
-#else
- lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n"
- " It's illegal to do an lws_write outside of\n"
- " the writable callback: fix your code\n",
- wsi, (unsigned long)len, dump);
-#endif
- assert(0);
-
- return -1;
- }
-
- m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_DO_SEND, &buf, len);
- if (m < 0)
- return -1;
- if (m) /* handled */ {
- n = m;
- goto handle_truncated_send;
- }
-
- if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd))
- lwsl_warn("** error invalid sock but expected to send\n");
-
- /* limit sending */
- if (wsi->protocol->tx_packet_size)
- n = wsi->protocol->tx_packet_size;
- else {
- n = wsi->protocol->rx_buffer_size;
- if (!n)
- n = context->pt_serv_buf_size;
- }
- n += LWS_PRE + 4;
- if (n > len)
- n = len;
-#if defined(LWS_WITH_ESP8266)
- if (wsi->pending_send_completion) {
- n = 0;
- goto handle_truncated_send;
- }
-#endif
-
- /* nope, send it on the socket directly */
- lws_latency_pre(context, wsi);
- n = lws_ssl_capable_write(wsi, buf, n);
- lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
-
- switch (n) {
- case LWS_SSL_CAPABLE_ERROR:
- /* we're going to close, let close know sends aren't possible */
- wsi->socket_is_permanently_unusable = 1;
- return -1;
- case LWS_SSL_CAPABLE_MORE_SERVICE:
- /* nothing got sent, not fatal, retry the whole thing later */
- n = 0;
- break;
- }
-
-handle_truncated_send:
- /*
- * we were already handling a truncated send?
- */
- if (wsi->trunc_len) {
- lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len);
- wsi->trunc_offset += n;
- wsi->trunc_len -= n;
-
- if (!wsi->trunc_len) {
- lwsl_info("***** %p partial send completed\n", wsi);
- /* done with it, but don't free it */
- n = real_len;
- if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
- lwsl_info("***** %p signalling to close now\n", wsi);
- return -1; /* retry closing now */
- }
- }
- /* always callback on writeable */
- lws_callback_on_writable(wsi);
-
- return n;
- }
-
- if ((unsigned int)n == real_len)
- /* what we just sent went out cleanly */
- return n;
-
- /*
- * Newly truncated send. Buffer the remainder (it will get
- * first priority next time the socket is writable)
- */
- lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n,
- (unsigned long)real_len);
-
- lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1);
- lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n);
-
- /*
- * - if we still have a suitable malloc lying around, use it
- * - or, if too small, reallocate it
- * - or, if no buffer, create it
- */
- if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) {
- lws_free(wsi->trunc_alloc);
-
- wsi->trunc_alloc_len = real_len - n;
- wsi->trunc_alloc = lws_malloc(real_len - n, "truncated send alloc");
- if (!wsi->trunc_alloc) {
- lwsl_err("truncated send: unable to malloc %lu\n",
- (unsigned long)(real_len - n));
- return -1;
- }
- }
- wsi->trunc_offset = 0;
- wsi->trunc_len = real_len - n;
- memcpy(wsi->trunc_alloc, buf + n, real_len - n);
-
- /* since something buffered, force it to get another chance to send */
- lws_callback_on_writable(wsi);
-
- return real_len;
-}
-
-LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
- enum lws_write_protocol wp)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- int masked7 = (wsi->mode == LWSCM_WS_CLIENT);
- unsigned char is_masked_bit = 0;
- unsigned char *dropmask = NULL;
- struct lws_tokens eff_buf;
- size_t orig_len = len;
- int pre = 0, n;
-
- if (wsi->parent_carries_io) {
- struct lws_write_passthru pas;
-
- pas.buf = buf;
- pas.len = len;
- pas.wp = wp;
- pas.wsi = wsi;
-
- if (wsi->parent->protocol->callback(wsi->parent,
- LWS_CALLBACK_CHILD_WRITE_VIA_PARENT,
- wsi->parent->user_space,
- (void *)&pas, 0))
- return 1;
-
- return len;
- }
-
- lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1);
-
- if ((int)len < 0) {
- lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__,
- (int)len, (unsigned long)len);
- return -1;
- }
-
- lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_WRITE, len);
-
-#ifdef LWS_WITH_ACCESS_LOG
- wsi->access_log.sent += len;
-#endif
- if (wsi->vhost)
- wsi->vhost->conn_stats.tx += len;
-
- if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) {
- /* remove us from the list */
- struct lws **w = &pt->tx_draining_ext_list;
-
- wsi->u.ws.tx_draining_ext = 0;
- /* remove us from context draining ext list */
- while (*w) {
- if (*w == wsi) {
- *w = wsi->u.ws.tx_draining_ext_list;
- break;
- }
- w = &((*w)->u.ws.tx_draining_ext_list);
- }
- wsi->u.ws.tx_draining_ext_list = NULL;
- wp = (wsi->u.ws.tx_draining_stashed_wp & 0xc0) |
- LWS_WRITE_CONTINUATION;
-
- lwsl_ext("FORCED draining wp to 0x%02X\n", wp);
- }
-
- lws_restart_ws_ping_pong_timer(wsi);
-
- if ((wp & 0x1f) == LWS_WRITE_HTTP ||
- (wp & 0x1f) == LWS_WRITE_HTTP_FINAL ||
- (wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION ||
- (wp & 0x1f) == LWS_WRITE_HTTP_HEADERS)
- goto send_raw;
-
- /* if not in a state to send stuff, then just send nothing */
-
- if (wsi->state != LWSS_ESTABLISHED &&
- ((wsi->state != LWSS_RETURNED_CLOSE_ALREADY &&
- wsi->state != LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION &&
- wsi->state != LWSS_AWAITING_CLOSE_ACK) ||
- wp != LWS_WRITE_CLOSE)) {
- lwsl_debug("binning\n");
- return 0;
- }
-
- /* if we are continuing a frame that already had its header done */
-
- if (wsi->u.ws.inside_frame) {
- lwsl_debug("INSIDE FRAME\n");
- goto do_more_inside_frame;
- }
-
- wsi->u.ws.clean_buffer = 1;
-
- /*
- * give a chance to the extensions to modify payload
- * the extension may decide to produce unlimited payload erratically
- * (eg, compression extension), so we require only that if he produces
- * something, it will be a complete fragment of the length known at
- * the time (just the fragment length known), and if he has
- * more we will come back next time he is writeable and allow him to
- * produce more fragments until he's drained.
- *
- * This allows what is sent each time it is writeable to be limited to
- * a size that can be sent without partial sends or blocking, allows
- * interleaving of control frames and other connection service.
- */
- eff_buf.token = (char *)buf;
- eff_buf.token_len = len;
-
- switch ((int)wp) {
- case LWS_WRITE_PING:
- case LWS_WRITE_PONG:
- case LWS_WRITE_CLOSE:
- break;
- default:
- lwsl_debug("LWS_EXT_CB_PAYLOAD_TX\n");
- n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &eff_buf, wp);
- if (n < 0)
- return -1;
-
- if (n && eff_buf.token_len) {
- lwsl_debug("drain len %d\n", (int)eff_buf.token_len);
- /* extension requires further draining */
- wsi->u.ws.tx_draining_ext = 1;
- wsi->u.ws.tx_draining_ext_list = pt->tx_draining_ext_list;
- pt->tx_draining_ext_list = wsi;
- /* we must come back to do more */
- lws_callback_on_writable(wsi);
- /*
- * keep a copy of the write type for the overall
- * action that has provoked generation of these
- * fragments, so the last guy can use its FIN state.
- */
- wsi->u.ws.tx_draining_stashed_wp = wp;
- /* this is definitely not actually the last fragment
- * because the extension asserted he has more coming
- * So make sure this intermediate one doesn't go out
- * with a FIN.
- */
- wp |= LWS_WRITE_NO_FIN;
- }
-
- if (eff_buf.token_len && wsi->u.ws.stashed_write_pending) {
- wsi->u.ws.stashed_write_pending = 0;
- wp = (wp &0xc0) | (int)wsi->u.ws.stashed_write_type;
- }
- }
-
- /*
- * an extension did something we need to keep... for example, if
- * compression extension, it has already updated its state according
- * to this being issued
- */
- if ((char *)buf != eff_buf.token) {
- /*
- * ext might eat it, but not have anything to issue yet.
- * In that case we have to follow his lead, but stash and
- * replace the write type that was lost here the first time.
- */
- if (len && !eff_buf.token_len) {
- if (!wsi->u.ws.stashed_write_pending)
- wsi->u.ws.stashed_write_type = (char)wp & 0x3f;
- wsi->u.ws.stashed_write_pending = 1;
- return len;
- }
- /*
- * extension recreated it:
- * need to buffer this if not all sent
- */
- wsi->u.ws.clean_buffer = 0;
- }
-
- buf = (unsigned char *)eff_buf.token;
- len = eff_buf.token_len;
-
- if (!buf) {
- lwsl_err("null buf (%d)\n", (int)len);
- return -1;
- }
-
- switch (wsi->ietf_spec_revision) {
- case 13:
- if (masked7) {
- pre += 4;
- dropmask = &buf[0 - pre];
- is_masked_bit = 0x80;
- }
-
- switch (wp & 0xf) {
- case LWS_WRITE_TEXT:
- n = LWSWSOPC_TEXT_FRAME;
- break;
- case LWS_WRITE_BINARY:
- n = LWSWSOPC_BINARY_FRAME;
- break;
- case LWS_WRITE_CONTINUATION:
- n = LWSWSOPC_CONTINUATION;
- break;
-
- case LWS_WRITE_CLOSE:
- n = LWSWSOPC_CLOSE;
- break;
- case LWS_WRITE_PING:
- n = LWSWSOPC_PING;
- break;
- case LWS_WRITE_PONG:
- n = LWSWSOPC_PONG;
- break;
- default:
- lwsl_warn("lws_write: unknown write opc / wp\n");
- return -1;
- }
-
- if (!(wp & LWS_WRITE_NO_FIN))
- n |= 1 << 7;
-
- if (len < 126) {
- pre += 2;
- buf[-pre] = n;
- buf[-pre + 1] = (unsigned char)(len | is_masked_bit);
- } else {
- if (len < 65536) {
- pre += 4;
- buf[-pre] = n;
- buf[-pre + 1] = 126 | is_masked_bit;
- buf[-pre + 2] = (unsigned char)(len >> 8);
- buf[-pre + 3] = (unsigned char)len;
- } else {
- pre += 10;
- buf[-pre] = n;
- buf[-pre + 1] = 127 | is_masked_bit;
-#if defined __LP64__
- buf[-pre + 2] = (len >> 56) & 0x7f;
- buf[-pre + 3] = len >> 48;
- buf[-pre + 4] = len >> 40;
- buf[-pre + 5] = len >> 32;
-#else
- buf[-pre + 2] = 0;
- buf[-pre + 3] = 0;
- buf[-pre + 4] = 0;
- buf[-pre + 5] = 0;
-#endif
- buf[-pre + 6] = (unsigned char)(len >> 24);
- buf[-pre + 7] = (unsigned char)(len >> 16);
- buf[-pre + 8] = (unsigned char)(len >> 8);
- buf[-pre + 9] = (unsigned char)len;
- }
- }
- break;
- }
-
-do_more_inside_frame:
-
- /*
- * Deal with masking if we are in client -> server direction and
- * the wp demands it
- */
-
- if (masked7) {
- if (!wsi->u.ws.inside_frame)
- if (lws_0405_frame_mask_generate(wsi)) {
- lwsl_err("frame mask generation failed\n");
- return -1;
- }
-
- /*
- * in v7, just mask the payload
- */
- if (dropmask) { /* never set if already inside frame */
- for (n = 4; n < (int)len + 4; n++)
- dropmask[n] = dropmask[n] ^ wsi->u.ws.mask[
- (wsi->u.ws.mask_idx++) & 3];
-
- /* copy the frame nonce into place */
- memcpy(dropmask, wsi->u.ws.mask, 4);
- }
- }
-
-send_raw:
- switch ((int)(wp & 0x1f)) {
- case LWS_WRITE_CLOSE:
-/* lwsl_hexdump(&buf[-pre], len); */
- case LWS_WRITE_HTTP:
- case LWS_WRITE_HTTP_FINAL:
- case LWS_WRITE_HTTP_HEADERS:
- case LWS_WRITE_HTTP_HEADERS_CONTINUATION:
- case LWS_WRITE_PONG:
- case LWS_WRITE_PING:
-#ifdef LWS_WITH_HTTP2
- if (wsi->mode == LWSCM_HTTP2_SERVING) {
- unsigned char flags = 0;
-
- n = LWS_H2_FRAME_TYPE_DATA;
- if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS) {
- n = LWS_H2_FRAME_TYPE_HEADERS;
- if (!(wp & LWS_WRITE_NO_FIN))
- flags = LWS_H2_FLAG_END_HEADERS;
- if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) {
- flags |= LWS_H2_FLAG_END_STREAM;
- wsi->u.h2.send_END_STREAM = 1;
- }
- }
-
- if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION) {
- n = LWS_H2_FRAME_TYPE_CONTINUATION;
- if (!(wp & LWS_WRITE_NO_FIN))
- flags = LWS_H2_FLAG_END_HEADERS;
- if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) {
- flags |= LWS_H2_FLAG_END_STREAM;
- wsi->u.h2.send_END_STREAM = 1;
- }
- }
-
- if (((wp & 0x1f) == LWS_WRITE_HTTP ||
- (wp & 0x1f) == LWS_WRITE_HTTP_FINAL) &&
- wsi->u.http.tx_content_length) {
- wsi->u.http.tx_content_remain -= len;
- lwsl_info("%s: wsi %p: tx_content_remain = %llu\n", __func__, wsi,
- (unsigned long long)wsi->u.http.tx_content_remain);
- if (!wsi->u.http.tx_content_remain) {
- lwsl_info("%s: selecting final write mode\n", __func__);
- wp = LWS_WRITE_HTTP_FINAL;
- }
- }
-
- if ((wp & 0x1f) == LWS_WRITE_HTTP_FINAL || (wp & LWS_WRITE_H2_STREAM_END)) {
- //lws_get_network_wsi(wsi)->u.h2.END_STREAM) {
- lwsl_info("%s: setting END_STREAM\n", __func__);
- flags |= LWS_H2_FLAG_END_STREAM;
- wsi->u.h2.send_END_STREAM = 1;
- }
-
- return lws_h2_frame_write(wsi, n, flags,
- wsi->u.h2.my_sid, len, buf);
- }
-#endif
- return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre);
- default:
- break;
- }
-
- /*
- * give any active extensions a chance to munge the buffer
- * before send. We pass in a pointer to an lws_tokens struct
- * prepared with the default buffer and content length that's in
- * there. Rather than rewrite the default buffer, extensions
- * that expect to grow the buffer can adapt .token to
- * point to their own per-connection buffer in the extension
- * user allocation. By default with no extensions or no
- * extension callback handling, just the normal input buffer is
- * used then so it is efficient.
- *
- * callback returns 1 in case it wants to spill more buffers
- *
- * This takes care of holding the buffer if send is incomplete, ie,
- * if wsi->u.ws.clean_buffer is 0 (meaning an extension meddled with
- * the buffer). If wsi->u.ws.clean_buffer is 1, it will instead
- * return to the user code how much OF THE USER BUFFER was consumed.
- */
-
- n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre);
- wsi->u.ws.inside_frame = 1;
- if (n <= 0)
- return n;
-
- if (n == (int)len + pre) {
- /* everything in the buffer was handled (or rebuffered...) */
- wsi->u.ws.inside_frame = 0;
- return orig_len;
- }
-
- /*
- * it is how many bytes of user buffer got sent... may be < orig_len
- * in which case callback when writable has already been arranged
- * and user code can call lws_write() again with the rest
- * later.
- */
-
- return n - pre;
-}
-
-LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
-{
- struct lws_context *context = wsi->context;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- struct lws_process_html_args args;
- lws_filepos_t amount, poss;
- unsigned char *p, *pstart;
-#if defined(LWS_WITH_RANGES)
- unsigned char finished = 0;
-#endif
- int n, m;
-
- lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
-
- while (!lws_send_pipe_choked(wsi)) {
-
- if (wsi->trunc_len) {
- if (lws_issue_raw(wsi, wsi->trunc_alloc +
- wsi->trunc_offset,
- wsi->trunc_len) < 0) {
- lwsl_info("%s: closing\n", __func__);
- goto file_had_it;
- }
- continue;
- }
-
- if (wsi->u.http.filepos == wsi->u.http.filelen)
- goto all_sent;
-
- n = 0;
-
- pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
-
- p = pstart;
-
-#if defined(LWS_WITH_RANGES)
- if (wsi->u.http.range.count_ranges && !wsi->u.http.range.inside) {
-
- lwsl_notice("%s: doing range start %llu\n", __func__, wsi->u.http.range.start);
-
- if ((long long)lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
- wsi->u.http.range.start -
- wsi->u.http.filepos) < 0)
- goto file_had_it;
-
- wsi->u.http.filepos = wsi->u.http.range.start;
-
- if (wsi->u.http.range.count_ranges > 1) {
- n = lws_snprintf((char *)p, context->pt_serv_buf_size - LWS_H2_FRAME_HEADER_LENGTH,
- "_lws\x0d\x0a"
- "Content-Type: %s\x0d\x0a"
- "Content-Range: bytes %llu-%llu/%llu\x0d\x0a"
- "\x0d\x0a",
- wsi->u.http.multipart_content_type,
- wsi->u.http.range.start,
- wsi->u.http.range.end,
- wsi->u.http.range.extent);
- p += n;
- }
-
- wsi->u.http.range.budget = wsi->u.http.range.end -
- wsi->u.http.range.start + 1;
- wsi->u.http.range.inside = 1;
- }
-#endif
-
- poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH;
-
- if (poss > wsi->u.http.tx_content_remain)
- poss = wsi->u.http.tx_content_remain;
-
- /*
- * if there is a hint about how much we will do well to send at one time,
- * restrict ourselves to only trying to send that.
- */
- if (wsi->protocol->tx_packet_size &&
- poss > wsi->protocol->tx_packet_size)
- poss = wsi->protocol->tx_packet_size;
-
-#if defined(LWS_WITH_HTTP2)
- m = lws_h2_tx_cr_get(wsi);
- if (!m) {
- lwsl_info("%s: came here with no tx credit", __func__);
- return 0;
- }
- if (m < poss)
- poss = m;
- /*
- * consumption of the actual payload amount sent will be handled
- * when the http2 data frame is sent
- */
-#endif
-
-#if defined(LWS_WITH_RANGES)
- if (wsi->u.http.range.count_ranges) {
- if (wsi->u.http.range.count_ranges > 1)
- poss -= 7; /* allow for final boundary */
- if (poss > wsi->u.http.range.budget)
- poss = wsi->u.http.range.budget;
- }
-#endif
- if (wsi->sending_chunked) {
- /* we need to drop the chunk size in here */
- p += 10;
- /* allow for the chunk to grow by 128 in translation */
- poss -= 10 + 128;
- }
-
- if (lws_vfs_file_read(wsi->u.http.fop_fd, &amount, p, poss) < 0)
- goto file_had_it; /* caller will close */
-
- if (wsi->sending_chunked)
- n = (int)amount;
- else
- n = (p - pstart) + (int)amount;
-
- lwsl_debug("%s: sending %d\n", __func__, n);
-
- if (n) {
- lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
- context->timeout_secs);
-
- if (wsi->sending_chunked) {
- args.p = (char *)p;
- args.len = n;
- args.max_len = (unsigned int)poss + 128;
- args.final = wsi->u.http.filepos + n ==
- wsi->u.http.filelen;
- if (user_callback_handle_rxflow(
- wsi->vhost->protocols[(int)wsi->protocol_interpret_idx].callback, wsi,
- LWS_CALLBACK_PROCESS_HTML,
- wsi->user_space, &args, 0) < 0)
- goto file_had_it;
- n = args.len;
- p = (unsigned char *)args.p;
- } else
- p = pstart;
-
-#if defined(LWS_WITH_RANGES)
- if (wsi->u.http.range.send_ctr + 1 ==
- wsi->u.http.range.count_ranges && // last range
- wsi->u.http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
- wsi->u.http.range.budget - amount == 0) {// final part
- n += lws_snprintf((char *)pstart + n, 6,
- "_lws\x0d\x0a"); // append trailing boundary
- lwsl_debug("added trailing boundary\n");
- }
-#endif
- m = lws_write(wsi, p, n,
- wsi->u.http.filepos == wsi->u.http.filelen ?
- LWS_WRITE_HTTP_FINAL :
- LWS_WRITE_HTTP
- );
- if (m < 0)
- goto file_had_it;
-
- wsi->u.http.filepos += amount;
-
-#if defined(LWS_WITH_RANGES)
- if (wsi->u.http.range.count_ranges >= 1) {
- wsi->u.http.range.budget -= amount;
- if (wsi->u.http.range.budget == 0) {
- lwsl_notice("range budget exhausted\n");
- wsi->u.http.range.inside = 0;
- wsi->u.http.range.send_ctr++;
-
- if (lws_ranges_next(&wsi->u.http.range) < 1) {
- finished = 1;
- goto all_sent;
- }
- }
- }
-#endif
-
- if (m != n) {
- /* adjust for what was not sent */
- if (lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
- m - n) ==
- (unsigned long)-1)
- goto file_had_it;
- }
- }
-
-all_sent:
- if ((!wsi->trunc_len && wsi->u.http.filepos >= wsi->u.http.filelen)
-#if defined(LWS_WITH_RANGES)
- || finished)
-#else
- )
-#endif
- {
- wsi->state = LWSS_HTTP;
- /* we might be in keepalive, so close it off here */
- lws_vfs_file_close(&wsi->u.http.fop_fd);
-
- lwsl_debug("file completed\n");
-
- if (wsi->protocol->callback &&
- user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
- wsi->user_space, NULL,
- 0) < 0) {
- /*
- * For http/1.x, the choices from
- * transaction_completed are either
- * 0 to use the connection for pipelined
- * or nonzero to hang it up.
- *
- * However for http/2. while we are
- * still interested in hanging up the
- * nwsi if there was a network-level
- * fatal error, simply completing the
- * transaction is a matter of the stream
- * state, not the root connection at the
- * network level
- */
- if (wsi->http2_substream)
- return 1;
- else
- return -1;
- }
-
- return 1; /* >0 indicates completed */
- }
- }
-
- lws_callback_on_writable(wsi);
-
- return 0; /* indicates further processing must be done */
-
-file_had_it:
- lws_vfs_file_close(&wsi->u.http.fop_fd);
-
- return -1;
-}
-
-#if LWS_POSIX
-LWS_VISIBLE int
-lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
-{
- struct lws_context *context = wsi->context;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- int n;
-
- lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
-
- n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
- if (n >= 0) {
- if (wsi->vhost)
- wsi->vhost->conn_stats.rx += n;
- lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
- lws_restart_ws_ping_pong_timer(wsi);
- return n;
- }
-#if LWS_POSIX
- if (LWS_ERRNO == LWS_EAGAIN ||
- LWS_ERRNO == LWS_EWOULDBLOCK ||
- LWS_ERRNO == LWS_EINTR)
- return LWS_SSL_CAPABLE_MORE_SERVICE;
-#endif
- lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO);
- return LWS_SSL_CAPABLE_ERROR;
-}
-
-LWS_VISIBLE int
-lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
-{
- int n = 0;
-
-#if LWS_POSIX
- n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
-// lwsl_info("%s: sent len %d result %d", __func__, len, n);
- if (n >= 0)
- return n;
-
- if (LWS_ERRNO == LWS_EAGAIN ||
- LWS_ERRNO == LWS_EWOULDBLOCK ||
- LWS_ERRNO == LWS_EINTR) {
- if (LWS_ERRNO == LWS_EWOULDBLOCK) {
- lws_set_blocking_send(wsi);
- }
-
- return LWS_SSL_CAPABLE_MORE_SERVICE;
- }
-#else
- (void)n;
- (void)wsi;
- (void)buf;
- (void)len;
- // !!!
-#endif
-
- lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n",
- len, wsi->desc.sockfd, n, LWS_ERRNO);
- return LWS_SSL_CAPABLE_ERROR;
-}
-#endif
-LWS_VISIBLE int
-lws_ssl_pending_no_ssl(struct lws *wsi)
-{
- (void)wsi;
-#if defined(LWS_WITH_ESP32)
- return 100;
-#else
- return 0;
-#endif
-}
diff --git a/thirdparty/lws/private-libwebsockets.h b/thirdparty/lws/private-libwebsockets.h
deleted file mode 100644
index 535fa0be57..0000000000
--- a/thirdparty/lws/private-libwebsockets.h
+++ /dev/null
@@ -1,2615 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010 - 2016 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "lws_config.h"
-#include "lws_config_private.h"
-
-
-#if defined(LWS_WITH_CGI) && defined(LWS_HAVE_VFORK)
-#define _GNU_SOURCE
-#endif
-
-#if defined(__COVERITY__)
-typedef struct { long double x, y; } _Float128;
-#endif
-
-#ifdef LWS_HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <ctype.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <inttypes.h>
-
-#if defined(LWS_WITH_ESP32)
-#define MSG_NOSIGNAL 0
-#define SOMAXCONN 3
-#endif
-
-#if defined(LWS_WITH_ESP8266)
-#include <user_interface.h>
-#define assert(n)
-
-/* rom-provided stdc functions for free, ensure use these instead of libc ones */
-
-int ets_vsprintf(char *str, const char *format, va_list argptr);
-int ets_vsnprintf(char *buffer, size_t sizeOfBuffer, const char *format, va_list argptr);
-int ets_snprintf(char *str, size_t size, const char *format, ...);
-int ets_sprintf(char *str, const char *format, ...);
-int os_printf_plus(const char *format, ...);
-#undef malloc
-#undef realloc
-#undef free
-void *pvPortMalloc(size_t s, const char *f, int line);
-#define malloc(s) pvPortMalloc(s, "", 0)
-void *pvPortRealloc(void *p, size_t s, const char *f, int line);
-#define realloc(p, s) pvPortRealloc(p, s, "", 0)
-void vPortFree(void *p, const char *f, int line);
-#define free(p) vPortFree(p, "", 0)
-#undef memcpy
-void *ets_memcpy(void *dest, const void *src, size_t n);
-#define memcpy ets_memcpy
-void *ets_memset(void *dest, int v, size_t n);
-#define memset ets_memset
-char *ets_strcpy(char *dest, const char *src);
-#define strcpy ets_strcpy
-char *ets_strncpy(char *dest, const char *src, size_t n);
-#define strncpy ets_strncpy
-char *ets_strstr(const char *haystack, const char *needle);
-#define strstr ets_strstr
-int ets_strcmp(const char *s1, const char *s2);
-int ets_strncmp(const char *s1, const char *s2, size_t n);
-#define strcmp ets_strcmp
-#define strncmp ets_strncmp
-size_t ets_strlen(const char *s);
-#define strlen ets_strlen
-void *ets_memmove(void *dest, const void *src, size_t n);
-#define memmove ets_memmove
-char *ets_strchr(const char *s, int c);
-#define strchr_ets_strchr
-#undef _DEBUG
-#include <osapi.h>
-
-#else
-#define STORE_IN_ROM
-#include <assert.h>
-#endif
-#if LWS_MAX_SMP > 1
-#include <pthread.h>
-#endif
-
-#ifdef LWS_HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-
-#if defined(WIN32) || defined(_WIN32)
-
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-
-#if (WINVER < 0x0501)
-#undef WINVER
-#undef _WIN32_WINNT
-#define WINVER 0x0501
-#define _WIN32_WINNT WINVER
-#endif
-#define LWS_NO_DAEMONIZE
-#define LWS_ERRNO WSAGetLastError()
-#define LWS_EAGAIN WSAEWOULDBLOCK
-#define LWS_EALREADY WSAEALREADY
-#define LWS_EINPROGRESS WSAEINPROGRESS
-#define LWS_EINTR WSAEINTR
-#define LWS_EISCONN WSAEISCONN
-#define LWS_EWOULDBLOCK WSAEWOULDBLOCK
-#define MSG_NOSIGNAL 0
-#define SHUT_RDWR SD_BOTH
-#define SOL_TCP IPPROTO_TCP
-#define SHUT_WR SD_SEND
-
-#define compatible_close(fd) closesocket(fd)
-#define lws_set_blocking_send(wsi) wsi->sock_send_blocking = 1
-#define lws_socket_is_valid(x) (!!x)
-#define LWS_SOCK_INVALID 0
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <windows.h>
-#include <tchar.h>
-#ifdef LWS_HAVE_IN6ADDR_H
-#include <in6addr.h>
-#endif
-#include <mstcpip.h>
-#include <io.h>
-
-#if !defined(LWS_HAVE_ATOLL)
-#if defined(LWS_HAVE__ATOI64)
-#define atoll _atoi64
-#else
-#warning No atoll or _atoi64 available, using atoi
-#define atoll atoi
-#endif
-#endif
-
-#ifndef __func__
-#define __func__ __FUNCTION__
-#endif
-
-#ifdef LWS_HAVE__VSNPRINTF
-#define vsnprintf _vsnprintf
-#endif
-
-/* we don't have an implementation for this on windows... */
-int kill(int pid, int sig);
-int fork(void);
-#ifndef SIGINT
-#define SIGINT 2
-#endif
-
-#else /* not windows --> */
-
-#include <fcntl.h>
-#include <strings.h>
-#include <unistd.h>
-#include <sys/types.h>
-
-#ifndef __cplusplus
-#include <errno.h>
-#endif
-#include <netdb.h>
-#include <signal.h>
-#ifdef LWS_WITH_ESP8266
-#include <sockets.h>
-#define vsnprintf ets_vsnprintf
-#define snprintf ets_snprintf
-#define sprintf ets_sprintf
-
-int kill(int pid, int sig);
-
-#else
-#include <sys/socket.h>
-#endif
-#ifdef LWS_WITH_HTTP_PROXY
-#include <hubbub/hubbub.h>
-#include <hubbub/parser.h>
-#endif
-#if defined(LWS_BUILTIN_GETIFADDRS)
- #include "./misc/getifaddrs.h"
-#else
- #if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
- #if defined(__HAIKU__)
- #define _BSD_SOURCE
- #endif
- #include <ifaddrs.h>
- #endif
-#endif
-#if defined (__ANDROID__)
-#include <syslog.h>
-#include <sys/resource.h>
-#elif defined (__sun) || defined(__HAIKU__)
-#include <syslog.h>
-#else
-#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
-#include <sys/syslog.h>
-#endif
-#endif
-#include <netdb.h>
-#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
-#include <sys/mman.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <poll.h>
-#endif
-#ifdef LWS_WITH_LIBEV
-#include <ev.h>
-#endif
-#ifdef LWS_WITH_LIBUV
-#include <uv.h>
-#endif
-#ifdef LWS_WITH_LIBEVENT
-#include <event2/event.h>
-#endif
-
-#ifndef LWS_NO_FORK
-#ifdef LWS_HAVE_SYS_PRCTL_H
-#include <sys/prctl.h>
-#endif
-#endif
-
-#include <sys/time.h>
-
-#define LWS_ERRNO errno
-#define LWS_EAGAIN EAGAIN
-#define LWS_EALREADY EALREADY
-#define LWS_EINPROGRESS EINPROGRESS
-#define LWS_EINTR EINTR
-#define LWS_EISCONN EISCONN
-#define LWS_EWOULDBLOCK EWOULDBLOCK
-
-#define lws_set_blocking_send(wsi)
-
-#if defined(LWS_WITH_ESP8266)
-#define lws_socket_is_valid(x) ((x) != NULL)
-#define LWS_SOCK_INVALID (NULL)
-struct lws;
-const char *
-lws_plat_get_peer_simple(struct lws *wsi, char *name, int namelen);
-#else
-#define lws_socket_is_valid(x) (x >= 0)
-#define LWS_SOCK_INVALID (-1)
-#endif
-#endif
-
-#ifndef LWS_HAVE_BZERO
-#ifndef bzero
-#define bzero(b, len) (memset((b), '\0', (len)), (void) 0)
-#endif
-#endif
-
-#ifndef LWS_HAVE_STRERROR
-#define strerror(x) ""
-#endif
-
-#ifdef LWS_OPENSSL_SUPPORT
-
-#ifdef USE_WOLFSSL
-#ifdef USE_OLD_CYASSL
-#include <cyassl/openssl/ssl.h>
-#include <cyassl/error-ssl.h>
-#else
-#include <wolfssl/openssl/ssl.h>
-#include <wolfssl/error-ssl.h>
-#define OPENSSL_NO_TLSEXT
-#endif /* not USE_OLD_CYASSL */
-#else
-#if defined(LWS_WITH_ESP32)
-#define OPENSSL_NO_TLSEXT
-#else
-#if defined(LWS_WITH_MBEDTLS)
-#include <mbedtls/ssl.h>
-#include <mbedtls/x509_crt.h>
-#else
-#include <openssl/ssl.h>
-#include <openssl/evp.h>
-#include <openssl/err.h>
-#include <openssl/md5.h>
-#include <openssl/sha.h>
-#ifdef LWS_HAVE_OPENSSL_ECDH_H
-#include <openssl/ecdh.h>
-#endif
-#include <openssl/x509v3.h>
-#endif
-#if defined(OPENSSL_VERSION_NUMBER)
-#if (OPENSSL_VERSION_NUMBER < 0x0009080afL)
-/* later openssl defines this to negate the presence of tlsext... but it was only
- * introduced at 0.9.8j. Earlier versions don't know it exists so don't
- * define it... making it look like the feature exists...
- */
-#define OPENSSL_NO_TLSEXT
-#endif
-#endif
-#endif /* not ESP32 */
-#endif /* not USE_WOLFSSL */
-#endif
-
-#include "libwebsockets.h"
-#if defined(WIN32) || defined(_WIN32)
-#else
-static inline int compatible_close(int fd) { return close(fd); }
-#endif
-
-#if defined(WIN32) || defined(_WIN32)
-#include <gettimeofday.h>
-#endif
-
-#if defined(LWS_WITH_ESP8266)
-#undef compatible_close
-#define compatible_close(fd) { fd->state=ESPCONN_CLOSE; espconn_delete(fd); }
-lws_sockfd_type
-esp8266_create_tcp_stream_socket(void);
-void
-esp8266_tcp_stream_bind(lws_sockfd_type fd, int port, struct lws *wsi);
-#ifndef BIG_ENDIAN
-#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */
-#endif
-#ifndef LITTLE_ENDIAN
-#define LITTLE_ENDIAN 1234
-#endif
-#ifndef BYTE_ORDER
-#define BYTE_ORDER LITTLE_ENDIAN
-#endif
-#endif
-
-
-#if defined(WIN32) || defined(_WIN32)
-
-#ifndef BIG_ENDIAN
-#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */
-#endif
-#ifndef LITTLE_ENDIAN
-#define LITTLE_ENDIAN 1234
-#endif
-#ifndef BYTE_ORDER
-#define BYTE_ORDER LITTLE_ENDIAN
-#endif
-
-#undef __P
-#ifndef __P
-#if __STDC__
-#define __P(protos) protos
-#else
-#define __P(protos) ()
-#endif
-#endif
-
-#else
-
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#if defined(__APPLE__)
-#include <machine/endian.h>
-#elif defined(__FreeBSD__)
-#include <sys/endian.h>
-#elif defined(__linux__)
-#include <endian.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if defined(__QNX__)
- #include <gulliver.h>
- #if defined(__LITTLEENDIAN__)
- #define BYTE_ORDER __LITTLEENDIAN__
- #define LITTLE_ENDIAN __LITTLEENDIAN__
- #define BIG_ENDIAN 4321 /* to show byte order (taken from gcc); for suppres warning that BIG_ENDIAN is not defined. */
- #endif
- #if defined(__BIGENDIAN__)
- #define BYTE_ORDER __BIGENDIAN__
- #define LITTLE_ENDIAN 1234 /* to show byte order (taken from gcc); for suppres warning that LITTLE_ENDIAN is not defined. */
- #define BIG_ENDIAN __BIGENDIAN__
- #endif
-#endif
-
-#if defined(__sun) && defined(__GNUC__)
-
-#include <arpa/nameser_compat.h>
-
-#if !defined (BYTE_ORDER)
-# define BYTE_ORDER __BYTE_ORDER__
-#endif
-
-#if !defined(LITTLE_ENDIAN)
-# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
-#endif
-
-#if !defined(BIG_ENDIAN)
-# define BIG_ENDIAN __ORDER_BIG_ENDIAN__
-#endif
-
-#endif /* sun + GNUC */
-
-#if !defined(BYTE_ORDER)
-# define BYTE_ORDER __BYTE_ORDER
-#endif
-#if !defined(LITTLE_ENDIAN)
-# define LITTLE_ENDIAN __LITTLE_ENDIAN
-#endif
-#if !defined(BIG_ENDIAN)
-# define BIG_ENDIAN __BIG_ENDIAN
-#endif
-
-#endif
-
-/*
- * Mac OSX as well as iOS do not define the MSG_NOSIGNAL flag,
- * but happily have something equivalent in the SO_NOSIGPIPE flag.
- */
-#ifdef __APPLE__
-#define MSG_NOSIGNAL SO_NOSIGPIPE
-#endif
-
-/*
- * Solaris 11.X only supports POSIX 2001, MSG_NOSIGNAL appears in
- * POSIX 2008.
- */
-#ifdef __sun
-#define MSG_NOSIGNAL 0
-#endif
-
-#ifdef _WIN32
-#ifndef FD_HASHTABLE_MODULUS
-#define FD_HASHTABLE_MODULUS 32
-#endif
-#endif
-
-#ifndef LWS_DEF_HEADER_LEN
-#define LWS_DEF_HEADER_LEN 4096
-#endif
-#ifndef LWS_DEF_HEADER_POOL
-#define LWS_DEF_HEADER_POOL 4
-#endif
-#ifndef LWS_MAX_PROTOCOLS
-#define LWS_MAX_PROTOCOLS 5
-#endif
-#ifndef LWS_MAX_EXTENSIONS_ACTIVE
-#define LWS_MAX_EXTENSIONS_ACTIVE 2
-#endif
-#ifndef LWS_MAX_EXT_OFFERS
-#define LWS_MAX_EXT_OFFERS 8
-#endif
-#ifndef SPEC_LATEST_SUPPORTED
-#define SPEC_LATEST_SUPPORTED 13
-#endif
-#ifndef AWAITING_TIMEOUT
-#define AWAITING_TIMEOUT 20
-#endif
-#ifndef CIPHERS_LIST_STRING
-#define CIPHERS_LIST_STRING "DEFAULT"
-#endif
-#ifndef LWS_SOMAXCONN
-#define LWS_SOMAXCONN SOMAXCONN
-#endif
-
-#define MAX_WEBSOCKET_04_KEY_LEN 128
-
-#ifndef SYSTEM_RANDOM_FILEPATH
-#define SYSTEM_RANDOM_FILEPATH "/dev/urandom"
-#endif
-
-enum lws_websocket_opcodes_07 {
- LWSWSOPC_CONTINUATION = 0,
- LWSWSOPC_TEXT_FRAME = 1,
- LWSWSOPC_BINARY_FRAME = 2,
-
- LWSWSOPC_NOSPEC__MUX = 7,
-
- /* control extensions 8+ */
-
- LWSWSOPC_CLOSE = 8,
- LWSWSOPC_PING = 9,
- LWSWSOPC_PONG = 0xa,
-};
-
-
-enum lws_connection_states {
- LWSS_HTTP,
- LWSS_HTTP_ISSUING_FILE,
- LWSS_HTTP_HEADERS,
- LWSS_HTTP_BODY,
- LWSS_DEAD_SOCKET,
- LWSS_ESTABLISHED,
- LWSS_CLIENT_HTTP_ESTABLISHED,
- LWSS_CLIENT_UNCONNECTED,
- LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION,
- LWSS_RETURNED_CLOSE_ALREADY,
- LWSS_AWAITING_CLOSE_ACK,
- LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE,
- LWSS_SHUTDOWN,
-
- LWSS_HTTP2_AWAIT_CLIENT_PREFACE,
- LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS,
- LWSS_HTTP2_ESTABLISHED,
-
- LWSS_CGI,
-};
-
-enum http_version {
- HTTP_VERSION_1_0,
- HTTP_VERSION_1_1,
- HTTP_VERSION_2
-};
-
-enum http_connection_type {
- HTTP_CONNECTION_CLOSE,
- HTTP_CONNECTION_KEEP_ALIVE
-};
-
-enum lws_rx_parse_state {
- LWS_RXPS_NEW,
-
- LWS_RXPS_04_mask_1,
- LWS_RXPS_04_mask_2,
- LWS_RXPS_04_mask_3,
-
- LWS_RXPS_04_FRAME_HDR_1,
- LWS_RXPS_04_FRAME_HDR_LEN,
- LWS_RXPS_04_FRAME_HDR_LEN16_2,
- LWS_RXPS_04_FRAME_HDR_LEN16_1,
- LWS_RXPS_04_FRAME_HDR_LEN64_8,
- LWS_RXPS_04_FRAME_HDR_LEN64_7,
- LWS_RXPS_04_FRAME_HDR_LEN64_6,
- LWS_RXPS_04_FRAME_HDR_LEN64_5,
- LWS_RXPS_04_FRAME_HDR_LEN64_4,
- LWS_RXPS_04_FRAME_HDR_LEN64_3,
- LWS_RXPS_04_FRAME_HDR_LEN64_2,
- LWS_RXPS_04_FRAME_HDR_LEN64_1,
-
- LWS_RXPS_07_COLLECT_FRAME_KEY_1,
- LWS_RXPS_07_COLLECT_FRAME_KEY_2,
- LWS_RXPS_07_COLLECT_FRAME_KEY_3,
- LWS_RXPS_07_COLLECT_FRAME_KEY_4,
-
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED
-};
-
-#define LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP 32
-
-enum connection_mode {
- LWSCM_HTTP_SERVING,
- LWSCM_HTTP_SERVING_ACCEPTED, /* actual HTTP service going on */
- LWSCM_PRE_WS_SERVING_ACCEPT,
-
- LWSCM_WS_SERVING,
- LWSCM_WS_CLIENT,
-
- LWSCM_HTTP2_SERVING,
-
- /* transient, ssl delay hiding */
- LWSCM_SSL_ACK_PENDING,
- LWSCM_SSL_INIT,
- /* as above, but complete into LWSCM_RAW */
- LWSCM_SSL_ACK_PENDING_RAW,
- LWSCM_SSL_INIT_RAW,
-
- /* special internal types */
- LWSCM_SERVER_LISTENER,
- LWSCM_CGI, /* stdin, stdout, stderr for another cgi master wsi */
- LWSCM_RAW, /* raw with bulk handling */
- LWSCM_RAW_FILEDESC, /* raw without bulk handling */
-
- /* HTTP Client related */
- LWSCM_HTTP_CLIENT = LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP,
- LWSCM_HTTP_CLIENT_ACCEPTED, /* actual HTTP service going on */
- LWSCM_WSCL_WAITING_CONNECT,
- LWSCM_WSCL_WAITING_PROXY_REPLY,
- LWSCM_WSCL_ISSUE_HANDSHAKE,
- LWSCM_WSCL_ISSUE_HANDSHAKE2,
- LWSCM_WSCL_ISSUE_HTTP_BODY,
- LWSCM_WSCL_WAITING_SSL,
- LWSCM_WSCL_WAITING_SERVER_REPLY,
- LWSCM_WSCL_WAITING_EXTENSION_CONNECT,
- LWSCM_WSCL_PENDING_CANDIDATE_CHILD,
- LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY,
- LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY,
- LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY,
-
- /****** add new things just above ---^ ******/
-
-
-};
-
-/* enums of socks version */
-enum socks_version {
- SOCKS_VERSION_4 = 4,
- SOCKS_VERSION_5 = 5
-};
-
-/* enums of subnegotiation version */
-enum socks_subnegotiation_version {
- SOCKS_SUBNEGOTIATION_VERSION_1 = 1,
-};
-
-/* enums of socks commands */
-enum socks_command {
- SOCKS_COMMAND_CONNECT = 1,
- SOCKS_COMMAND_BIND = 2,
- SOCKS_COMMAND_UDP_ASSOCIATE = 3
-};
-
-/* enums of socks address type */
-enum socks_atyp {
- SOCKS_ATYP_IPV4 = 1,
- SOCKS_ATYP_DOMAINNAME = 3,
- SOCKS_ATYP_IPV6 = 4
-};
-
-/* enums of socks authentication methods */
-enum socks_auth_method {
- SOCKS_AUTH_NO_AUTH = 0,
- SOCKS_AUTH_GSSAPI = 1,
- SOCKS_AUTH_USERNAME_PASSWORD = 2
-};
-
-/* enums of subnegotiation status */
-enum socks_subnegotiation_status {
- SOCKS_SUBNEGOTIATION_STATUS_SUCCESS = 0,
-};
-
-/* enums of socks request reply */
-enum socks_request_reply {
- SOCKS_REQUEST_REPLY_SUCCESS = 0,
- SOCKS_REQUEST_REPLY_FAILURE_GENERAL = 1,
- SOCKS_REQUEST_REPLY_CONNECTION_NOT_ALLOWED = 2,
- SOCKS_REQUEST_REPLY_NETWORK_UNREACHABLE = 3,
- SOCKS_REQUEST_REPLY_HOST_UNREACHABLE = 4,
- SOCKS_REQUEST_REPLY_CONNECTION_REFUSED = 5,
- SOCKS_REQUEST_REPLY_TTL_EXPIRED = 6,
- SOCKS_REQUEST_REPLY_COMMAND_NOT_SUPPORTED = 7,
- SOCKS_REQUEST_REPLY_ATYP_NOT_SUPPORTED = 8
-};
-
-/* enums used to generate socks messages */
-enum socks_msg_type {
- /* greeting */
- SOCKS_MSG_GREETING,
- /* credential, user name and password */
- SOCKS_MSG_USERNAME_PASSWORD,
- /* connect command */
- SOCKS_MSG_CONNECT
-};
-
-enum {
- LWS_RXFLOW_ALLOW = (1 << 0),
- LWS_RXFLOW_PENDING_CHANGE = (1 << 1),
-};
-
-struct lws_ring {
- void *buf;
- void (*destroy_element)(void *element);
- size_t buflen;
- size_t element_len;
- uint32_t head;
- uint32_t oldest_tail;
-};
-
-/* this is not usable directly by user code any more, lws_close_reason() */
-#define LWS_WRITE_CLOSE 4
-
-struct lws_protocols;
-struct lws;
-
-#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
-
-struct lws_io_watcher {
-#ifdef LWS_WITH_LIBEV
- ev_io ev_watcher;
-#endif
-#ifdef LWS_WITH_LIBUV
- uv_poll_t uv_watcher;
-#endif
-#ifdef LWS_WITH_LIBEVENT
- struct event *event_watcher;
-#endif
- struct lws_context *context;
-
- uint8_t actual_events;
-};
-
-struct lws_signal_watcher {
-#ifdef LWS_WITH_LIBEV
- ev_signal ev_watcher;
-#endif
-#ifdef LWS_WITH_LIBUV
- uv_signal_t uv_watcher;
-#endif
-#ifdef LWS_WITH_LIBEVENT
- struct event *event_watcher;
-#endif
- struct lws_context *context;
-};
-#endif
-
-#ifdef _WIN32
-#define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS)
-struct lws_fd_hashtable {
- struct lws **wsi;
- int length;
-};
-#endif
-
-/*
- * This is totally opaque to code using the library. It's exported as a
- * forward-reference pointer-only declaration; the user can use the pointer with
- * other APIs to get information out of it.
- */
-
-#if defined(LWS_WITH_ESP32)
-typedef uint16_t ah_data_idx_t;
-#else
-typedef uint32_t ah_data_idx_t;
-#endif
-
-struct lws_fragments {
- ah_data_idx_t offset;
- uint16_t len;
- uint8_t nfrag; /* which ah->frag[] continues this content, or 0 */
- uint8_t flags; /* only http2 cares */
-};
-
-/*
- * these are assigned from a pool held in the context.
- * Both client and server mode uses them for http header analysis
- */
-
-struct allocated_headers {
- struct allocated_headers *next; /* linked list */
- struct lws *wsi; /* owner */
- char *data; /* prepared by context init to point to dedicated storage */
- ah_data_idx_t data_length;
- /*
- * the randomly ordered fragments, indexed by frag_index and
- * lws_fragments->nfrag for continuation.
- */
- struct lws_fragments frags[WSI_TOKEN_COUNT];
- time_t assigned;
- /*
- * for each recognized token, frag_index says which frag[] his data
- * starts in (0 means the token did not appear)
- * the actual header data gets dumped as it comes in, into data[]
- */
- uint8_t frag_index[WSI_TOKEN_COUNT];
-#if defined(LWS_WITH_ESP32)
- uint8_t rx[256];
-#else
- uint8_t rx[2048];
-#endif
-
- int16_t rxpos;
- int16_t rxlen;
- uint32_t pos;
- uint32_t http_response;
- int hdr_token_idx;
-
-#ifndef LWS_NO_CLIENT
- char initial_handshake_hash_base64[30];
-#endif
-
- uint8_t in_use;
- uint8_t nfrag;
-};
-
-/*
- * so we can have n connections being serviced simultaneously,
- * these things need to be isolated per-thread.
- */
-
-struct lws_context_per_thread {
-#if LWS_MAX_SMP > 1
- pthread_mutex_t lock;
-#endif
- struct lws_pollfd *fds;
-#if defined(LWS_WITH_ESP8266)
- struct lws **lws_vs_fds_index;
-#endif
- struct lws *rx_draining_ext_list;
- struct lws *tx_draining_ext_list;
- struct lws *timeout_list;
-#if defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
- struct lws_context *context;
-#endif
-#ifdef LWS_WITH_CGI
- struct lws_cgi *cgi_list;
-#endif
- void *http_header_data;
- struct allocated_headers *ah_list;
- struct lws *ah_wait_list;
- int ah_wait_list_length;
-#ifdef LWS_OPENSSL_SUPPORT
- struct lws *pending_read_list; /* linked list */
-#endif
-#if defined(LWS_WITH_LIBEV)
- struct ev_loop *io_loop_ev;
-#endif
-#if defined(LWS_WITH_LIBUV)
- uv_loop_t *io_loop_uv;
- uv_signal_t signals[8];
- uv_timer_t uv_timeout_watcher;
- uv_idle_t uv_idle;
-#endif
-#if defined(LWS_WITH_LIBEVENT)
- struct event_base *io_loop_event_base;
-#endif
-#if defined(LWS_WITH_LIBEV)
- struct lws_io_watcher w_accept;
-#endif
-#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
- struct lws_signal_watcher w_sigint;
- unsigned char ev_loop_foreign:1;
-#endif
-
- unsigned long count_conns;
- /*
- * usable by anything in the service code, but only if the scope
- * does not last longer than the service action (since next service
- * of any socket can likewise use it and overwrite)
- */
- unsigned char *serv_buf;
-#ifdef _WIN32
- WSAEVENT *events;
-#else
- lws_sockfd_type dummy_pipe_fds[2];
-#endif
- unsigned int fds_count;
- uint32_t ah_pool_length;
-
- short ah_count_in_use;
- unsigned char tid;
- unsigned char lock_depth;
-};
-
-struct lws_conn_stats {
- unsigned long long rx, tx;
- unsigned long h1_conn, h1_trans, h2_trans, ws_upg, h2_alpn, h2_subs,
- h2_upg, rejected;
-};
-
-void
-lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs);
-
-
-enum lws_h2_settings {
- H2SET_HEADER_TABLE_SIZE = 1,
- H2SET_ENABLE_PUSH,
- H2SET_MAX_CONCURRENT_STREAMS,
- H2SET_INITIAL_WINDOW_SIZE,
- H2SET_MAX_FRAME_SIZE,
- H2SET_MAX_HEADER_LIST_SIZE,
-
- H2SET_COUNT /* always last */
-};
-
-struct http2_settings {
- uint32_t s[H2SET_COUNT];
-};
-
-/*
- * virtual host -related context information
- * vhostwide SSL context
- * vhostwide proxy
- *
- * hierarchy:
- *
- * context -> vhost -> wsi
- *
- * incoming connection non-SSL vhost binding:
- *
- * listen socket -> wsi -> select vhost after first headers
- *
- * incoming connection SSL vhost binding:
- *
- * SSL SNI -> wsi -> bind after SSL negotiation
- */
-
-struct lws_vhost {
-#if !defined(LWS_WITH_ESP8266)
- char http_proxy_address[128];
- char proxy_basic_auth_token[128];
-#if defined(LWS_WITH_HTTP2)
- struct http2_settings set;
-#endif
-#if defined(LWS_WITH_SOCKS5)
- char socks_proxy_address[128];
- char socks_user[96];
- char socks_password[96];
-#endif
-#endif
-#if defined(LWS_WITH_ESP8266)
- /* listen sockets need a place to hang their hat */
- esp_tcp tcp;
-#endif
- struct lws_conn_stats conn_stats;
- struct lws_context *context;
- struct lws_vhost *vhost_next;
- const struct lws_http_mount *mount_list;
- struct lws *lserv_wsi;
- const char *name;
- const char *iface;
-#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32)
- int bind_iface;
-#endif
- const struct lws_protocols *protocols;
- void **protocol_vh_privs;
- const struct lws_protocol_vhost_options *pvo;
- const struct lws_protocol_vhost_options *headers;
- struct lws **same_vh_protocol_list;
-#ifdef LWS_OPENSSL_SUPPORT
- SSL_CTX *ssl_ctx;
- SSL_CTX *ssl_client_ctx;
-#endif
-#if defined(LWS_WITH_MBEDTLS)
- X509 *x509_client_CA;
-#endif
-#ifndef LWS_NO_EXTENSIONS
- const struct lws_extension *extensions;
-#endif
- void *user;
-
- int listen_port;
- unsigned int http_proxy_port;
-#if defined(LWS_WITH_SOCKS5)
- unsigned int socks_proxy_port;
-#endif
- unsigned int options;
- int count_protocols;
- int ka_time;
- int ka_probes;
- int ka_interval;
- int keepalive_timeout;
- int timeout_secs_ah_idle;
- int ssl_info_event_mask;
-#ifdef LWS_WITH_ACCESS_LOG
- int log_fd;
-#endif
-
-#ifdef LWS_OPENSSL_SUPPORT
- int use_ssl;
- int allow_non_ssl_on_ssl_port;
- unsigned int user_supplied_ssl_ctx:1;
-#endif
-
- unsigned int created_vhost_protocols:1;
- unsigned int being_destroyed:1;
-
- unsigned char default_protocol_index;
- unsigned char raw_protocol_index;
-};
-
-struct lws_deferred_free
-{
- struct lws_deferred_free *next;
- time_t deadline;
- void *payload;
-};
-
-typedef union {
-#ifdef LWS_WITH_IPV6
- struct sockaddr_in6 sa6;
-#endif
- struct sockaddr_in sa4;
-} sockaddr46;
-
-
-#if defined(LWS_WITH_PEER_LIMITS)
-struct lws_peer {
- struct lws_peer *next;
- struct lws_peer *peer_wait_list;
-
- time_t time_created;
- time_t time_closed_all;
-
- uint8_t addr[32];
- uint32_t hash;
- uint32_t count_wsi;
- uint32_t count_ah;
-
- uint32_t total_wsi;
- uint32_t total_ah;
-
- uint8_t af;
-};
-#endif
-
-/*
- * the rest is managed per-context, that includes
- *
- * - processwide single fd -> wsi lookup
- * - contextwide headers pool
- */
-
-struct lws_context {
- time_t last_timeout_check_s;
- time_t last_ws_ping_pong_check_s;
- time_t time_up;
- const struct lws_plat_file_ops *fops;
- struct lws_plat_file_ops fops_platform;
-#if defined(LWS_WITH_HTTP2)
- struct http2_settings set;
-#endif
-#if defined(LWS_WITH_ZIP_FOPS)
- struct lws_plat_file_ops fops_zip;
-#endif
- struct lws_context_per_thread pt[LWS_MAX_SMP];
- struct lws_conn_stats conn_stats;
-#if LWS_MAX_SMP > 1
- pthread_mutex_t lock;
- int lock_depth;
-#endif
-#ifdef _WIN32
-/* different implementation between unix and windows */
- struct lws_fd_hashtable fd_hashtable[FD_HASHTABLE_MODULUS];
-#else
-#if defined(LWS_WITH_ESP8266)
- struct espconn **connpool; /* .reverse points to the wsi */
- void *rxd;
- int rxd_len;
- os_timer_t to_timer;
-#else
- struct lws **lws_lookup; /* fd to wsi */
-#endif
-#endif
- struct lws_vhost *vhost_list;
- struct lws_vhost *vhost_pending_destruction_list;
- struct lws_plugin *plugin_list;
- struct lws_deferred_free *deferred_free_list;
-#if defined(LWS_WITH_PEER_LIMITS)
- struct lws_peer **pl_hash_table;
- struct lws_peer *peer_wait_list;
- time_t next_cull;
-#endif
-
- void *external_baggage_free_on_destroy;
- const struct lws_token_limits *token_limits;
- void *user_space;
- const char *server_string;
- const struct lws_protocol_vhost_options *reject_service_keywords;
- lws_reload_func deprecation_cb;
-
-#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
- cap_value_t caps[4];
- char count_caps;
-#endif
-
-#if defined(LWS_WITH_LIBEV)
- lws_ev_signal_cb_t * lws_ev_sigint_cb;
-#endif
-#if defined(LWS_WITH_LIBUV)
- uv_signal_cb lws_uv_sigint_cb;
- uv_loop_t pu_loop;
-#endif
-#if defined(LWS_WITH_LIBEVENT)
- lws_event_signal_cb_t * lws_event_sigint_cb;
-#endif
- char canonical_hostname[128];
-#ifdef LWS_LATENCY
- unsigned long worst_latency;
- char worst_latency_info[256];
-#endif
-
-#if defined(LWS_WITH_STATS)
- uint64_t lws_stats[LWSSTATS_SIZE];
- uint64_t last_dump;
- int updated;
-#endif
-#if defined(LWS_WITH_ESP32)
- unsigned long time_last_state_dump;
- uint32_t last_free_heap;
-#endif
-
- int max_fds;
-#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
- int use_ev_sigint;
-#endif
- int started_with_parent;
- int uid, gid;
-
- int fd_random;
-
- int count_wsi_allocated;
- int count_cgi_spawned;
- unsigned int options;
- unsigned int fd_limit_per_thread;
- unsigned int timeout_secs;
- unsigned int pt_serv_buf_size;
- int max_http_header_data;
- int simultaneous_ssl_restriction;
- int simultaneous_ssl;
-#if defined(LWS_WITH_PEER_LIMITS)
- uint32_t pl_hash_elements; /* protected by context->lock */
- uint32_t count_peers; /* protected by context->lock */
- unsigned short ip_limit_ah;
- unsigned short ip_limit_wsi;
-#endif
- unsigned int deprecated:1;
- unsigned int being_destroyed:1;
- unsigned int being_destroyed1:1;
- unsigned int requested_kill:1;
- unsigned int protocol_init_done:1;
- unsigned int ssl_gate_accepts:1;
- unsigned int doing_protocol_init;
- /*
- * set to the Thread ID that's doing the service loop just before entry
- * to poll indicates service thread likely idling in poll()
- * volatile because other threads may check it as part of processing
- * for pollfd event change.
- */
- volatile int service_tid;
- int service_tid_detected;
-
- short max_http_header_pool;
- short count_threads;
- short plugin_protocol_count;
- short plugin_extension_count;
- short server_string_len;
- unsigned short ws_ping_pong_interval;
- unsigned short deprecation_pending_listen_close_count;
-
- uint8_t max_fi;
-};
-
-int
-lws_check_deferred_free(struct lws_context *context, int force);
-
-#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x]
-#define lws_get_vh_protocol(vh, x) vh->protocols[x]
-
-LWS_EXTERN void
-lws_close_free_wsi_final(struct lws *wsi);
-LWS_EXTERN void
-lws_libuv_closehandle(struct lws *wsi);
-LWS_EXTERN void
-lws_libuv_closehandle_manually(struct lws *wsi);
-LWS_EXTERN int
-lws_libuv_check_watcher_active(struct lws *wsi);
-
-LWS_VISIBLE LWS_EXTERN int
-lws_plat_plugins_init(struct lws_context * context, const char * const *d);
-
-LWS_VISIBLE LWS_EXTERN int
-lws_plat_plugins_destroy(struct lws_context * context);
-
-LWS_EXTERN void
-lws_restart_ws_ping_pong_timer(struct lws *wsi);
-
-struct lws *
-lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd);
-
-
-enum {
- LWS_EV_READ = (1 << 0),
- LWS_EV_WRITE = (1 << 1),
- LWS_EV_START = (1 << 2),
- LWS_EV_STOP = (1 << 3),
-
- LWS_EV_PREPARE_DELETION = (1 << 31),
-};
-
-#if defined(LWS_WITH_LIBEV)
-LWS_EXTERN void
-lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc);
-LWS_EXTERN void
-lws_libev_io(struct lws *wsi, int flags);
-LWS_EXTERN int
-lws_libev_init_fd_table(struct lws_context *context);
-LWS_EXTERN void
-lws_libev_destroyloop(struct lws_context *context, int tsi);
-LWS_EXTERN void
-lws_libev_run(const struct lws_context *context, int tsi);
-#define LWS_LIBEV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV)
-LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info);
-#else
-#define lws_libev_accept(_a, _b) ((void) 0)
-#define lws_libev_io(_a, _b) ((void) 0)
-#define lws_libev_init_fd_table(_a) (0)
-#define lws_libev_run(_a, _b) ((void) 0)
-#define lws_libev_destroyloop(_a, _b) ((void) 0)
-#define LWS_LIBEV_ENABLED(context) (0)
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
-#define lws_feature_status_libev(_a) \
- lwsl_info("libev support not compiled in\n")
-#else
-#define lws_feature_status_libev(_a)
-#endif
-#endif
-
-#if defined(LWS_WITH_LIBUV)
-LWS_EXTERN void
-lws_libuv_accept(struct lws *new_wsi, lws_sock_file_fd_type desc);
-LWS_EXTERN void
-lws_libuv_io(struct lws *wsi, int flags);
-LWS_EXTERN int
-lws_libuv_init_fd_table(struct lws_context *context);
-LWS_EXTERN void
-lws_libuv_run(const struct lws_context *context, int tsi);
-LWS_EXTERN void
-lws_libuv_destroyloop(struct lws_context *context, int tsi);
-LWS_EXTERN int
-lws_uv_initvhost(struct lws_vhost* vh, struct lws*);
-#define LWS_LIBUV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)
-LWS_EXTERN void lws_feature_status_libuv(struct lws_context_creation_info *info);
-#else
-#define lws_libuv_accept(_a, _b) ((void) 0)
-#define lws_libuv_io(_a, _b) ((void) 0)
-#define lws_libuv_init_fd_table(_a) (0)
-#define lws_libuv_run(_a, _b) ((void) 0)
-#define lws_libuv_destroyloop(_a, _b) ((void) 0)
-#define LWS_LIBUV_ENABLED(context) (0)
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
-#define lws_feature_status_libuv(_a) \
- lwsl_notice("libuv support not compiled in\n")
-#else
-#define lws_feature_status_libuv(_a)
-#endif
-#endif
-
-#if defined(LWS_WITH_LIBEVENT)
-LWS_EXTERN void
-lws_libevent_accept(struct lws *new_wsi, lws_sock_file_fd_type desc);
-LWS_EXTERN void
-lws_libevent_io(struct lws *wsi, int flags);
-LWS_EXTERN int
-lws_libevent_init_fd_table(struct lws_context *context);
-LWS_EXTERN void
-lws_libevent_destroyloop(struct lws_context *context, int tsi);
-LWS_EXTERN void
-lws_libevent_run(const struct lws_context *context, int tsi);
-#define LWS_LIBEVENT_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT)
-LWS_EXTERN void lws_feature_status_libevent(struct lws_context_creation_info *info);
-#else
-#define lws_libevent_accept(_a, _b) ((void) 0)
-#define lws_libevent_io(_a, _b) ((void) 0)
-#define lws_libevent_init_fd_table(_a) (0)
-#define lws_libevent_run(_a, _b) ((void) 0)
-#define lws_libevent_destroyloop(_a, _b) ((void) 0)
-#define LWS_LIBEVENT_ENABLED(context) (0)
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
-#define lws_feature_status_libevent(_a) \
- lwsl_notice("libevent support not compiled in\n")
-#else
-#define lws_feature_status_libevent(_a)
-#endif
-#endif
-
-
-#ifdef LWS_WITH_IPV6
-#define LWS_IPV6_ENABLED(vh) \
- (!lws_check_opt(vh->context->options, LWS_SERVER_OPTION_DISABLE_IPV6) && \
- !lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_IPV6))
-#else
-#define LWS_IPV6_ENABLED(context) (0)
-#endif
-
-#ifdef LWS_WITH_UNIX_SOCK
-#define LWS_UNIX_SOCK_ENABLED(vhost) \
- (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK)
-#else
-#define LWS_UNIX_SOCK_ENABLED(vhost) (0)
-#endif
-
-enum uri_path_states {
- URIPS_IDLE,
- URIPS_SEEN_SLASH,
- URIPS_SEEN_SLASH_DOT,
- URIPS_SEEN_SLASH_DOT_DOT,
-};
-
-enum uri_esc_states {
- URIES_IDLE,
- URIES_SEEN_PERCENT,
- URIES_SEEN_PERCENT_H1,
-};
-
-/* notice that these union members:
- *
- * hdr
- * http
- * http2
- *
- * all have a pointer to allocated_headers struct as their first member.
- *
- * It means for allocated_headers access, the three union paths can all be
- * used interchangeably to access the same data
- */
-
-
-#ifndef LWS_NO_CLIENT
-struct client_info_stash {
- char address[256];
- char path[4096];
- char host[256];
- char origin[256];
- char protocol[256];
- char method[16];
- char iface[16];
-};
-#endif
-
-struct _lws_header_related {
- /* MUST be first in struct */
- struct allocated_headers *ah;
- struct lws *ah_wait_list;
- unsigned char *preamble_rx;
-#ifndef LWS_NO_CLIENT
- struct client_info_stash *stash;
-#endif
- unsigned int preamble_rx_len;
- enum uri_path_states ups;
- enum uri_esc_states ues;
- short lextable_pos;
- unsigned int current_token_limit;
-
- char esc_stash;
- char post_literal_equal;
- unsigned char parser_state; /* enum lws_token_indexes */
-};
-
-#if defined(LWS_WITH_RANGES)
-enum range_states {
- LWSRS_NO_ACTIVE_RANGE,
- LWSRS_BYTES_EQ,
- LWSRS_FIRST,
- LWSRS_STARTING,
- LWSRS_ENDING,
- LWSRS_COMPLETED,
- LWSRS_SYNTAX,
-};
-
-struct lws_range_parsing {
- unsigned long long start, end, extent, agg, budget;
- const char buf[128];
- int pos;
- enum range_states state;
- char start_valid, end_valid, ctr, count_ranges, did_try, inside, send_ctr;
-};
-
-int
-lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp, unsigned long long extent);
-int
-lws_ranges_next(struct lws_range_parsing *rp);
-void
-lws_ranges_reset(struct lws_range_parsing *rp);
-#endif
-
-struct _lws_http_mode_related {
- /* MUST be first in struct */
- struct allocated_headers *ah; /* mirroring _lws_header_related */
- struct lws *ah_wait_list;
- unsigned char *preamble_rx;
-#ifndef LWS_NO_CLIENT
- struct client_info_stash *stash;
-#endif
- unsigned int preamble_rx_len;
- struct lws *new_wsi_list;
- lws_filepos_t filepos;
- lws_filepos_t filelen;
- lws_fop_fd_t fop_fd;
-
-#if defined(LWS_WITH_RANGES)
- struct lws_range_parsing range;
- char multipart_content_type[64];
-#endif
-
- enum http_version request_version;
- enum http_connection_type connection_type;
- lws_filepos_t tx_content_length;
- lws_filepos_t tx_content_remain;
- lws_filepos_t rx_content_length;
- lws_filepos_t rx_content_remain;
-};
-
-#define LWS_H2_FRAME_HEADER_LENGTH 9
-
-#ifdef LWS_WITH_HTTP2
-
-enum lws_h2_wellknown_frame_types {
- LWS_H2_FRAME_TYPE_DATA,
- LWS_H2_FRAME_TYPE_HEADERS,
- LWS_H2_FRAME_TYPE_PRIORITY,
- LWS_H2_FRAME_TYPE_RST_STREAM,
- LWS_H2_FRAME_TYPE_SETTINGS,
- LWS_H2_FRAME_TYPE_PUSH_PROMISE,
- LWS_H2_FRAME_TYPE_PING,
- LWS_H2_FRAME_TYPE_GOAWAY,
- LWS_H2_FRAME_TYPE_WINDOW_UPDATE,
- LWS_H2_FRAME_TYPE_CONTINUATION,
-
- LWS_H2_FRAME_TYPE_COUNT /* always last */
-};
-
-enum lws_h2_flags {
- LWS_H2_FLAG_END_STREAM = 1,
- LWS_H2_FLAG_END_HEADERS = 4,
- LWS_H2_FLAG_PADDED = 8,
- LWS_H2_FLAG_PRIORITY = 0x20,
-
- LWS_H2_FLAG_SETTINGS_ACK = 1,
-};
-
-enum lws_h2_errors {
- H2_ERR_NO_ERROR, /* Graceful shutdown */
- H2_ERR_PROTOCOL_ERROR, /* Protocol error detected */
- H2_ERR_INTERNAL_ERROR, /* Implementation fault */
- H2_ERR_FLOW_CONTROL_ERROR, /* Flow-control limits exceeded */
- H2_ERR_SETTINGS_TIMEOUT, /* Settings not acknowledged */
- H2_ERR_STREAM_CLOSED, /* Frame received for closed stream */
- H2_ERR_FRAME_SIZE_ERROR, /* Frame size incorrect */
- H2_ERR_REFUSED_STREAM, /* Stream not processed */
- H2_ERR_CANCEL, /* Stream cancelled */
- H2_ERR_COMPRESSION_ERROR, /* Compression state not updated */
- H2_ERR_CONNECT_ERROR, /* TCP connection error for CONNECT method */
- H2_ERR_ENHANCE_YOUR_CALM, /* Processing capacity exceeded */
- H2_ERR_INADEQUATE_SECURITY, /* Negotiated TLS parameters not acceptable */
- H2_ERR_HTTP_1_1_REQUIRED, /* Use HTTP/1.1 for the request */
-};
-
-enum lws_h2_states {
- LWS_H2_STATE_IDLE,
- /*
- * Send PUSH_PROMISE -> LWS_H2_STATE_RESERVED_LOCAL
- * Recv PUSH_PROMISE -> LWS_H2_STATE_RESERVED_REMOTE
- * Send HEADERS -> LWS_H2_STATE_OPEN
- * Recv HEADERS -> LWS_H2_STATE_OPEN
- *
- * - Only PUSH_PROMISE + HEADERS valid to send
- * - Only HEADERS or PRIORITY valid to receive
- */
- LWS_H2_STATE_RESERVED_LOCAL,
- /*
- * Send RST_STREAM -> LWS_H2_STATE_CLOSED
- * Recv RST_STREAM -> LWS_H2_STATE_CLOSED
- * Send HEADERS -> LWS_H2_STATE_HALF_CLOSED_REMOTE
- *
- * - Only HEADERS, RST_STREAM, or PRIORITY valid to send
- * - Only RST_STREAM, PRIORITY, or WINDOW_UPDATE valid to receive
- */
- LWS_H2_STATE_RESERVED_REMOTE,
- /*
- * Send RST_STREAM -> LWS_H2_STATE_CLOSED
- * Recv RST_STREAM -> LWS_H2_STATE_CLOSED
- * Recv HEADERS -> LWS_H2_STATE_HALF_CLOSED_LOCAL
- *
- * - Only RST_STREAM, WINDOW_UPDATE, or PRIORITY valid to send
- * - Only HEADERS, RST_STREAM, or PRIORITY valid to receive
- */
- LWS_H2_STATE_OPEN,
- /*
- * Send RST_STREAM -> LWS_H2_STATE_CLOSED
- * Recv RST_STREAM -> LWS_H2_STATE_CLOSED
- * Send END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_LOCAL
- * Recv END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_REMOTE
- */
- LWS_H2_STATE_HALF_CLOSED_REMOTE,
- /*
- * Send RST_STREAM -> LWS_H2_STATE_CLOSED
- * Recv RST_STREAM -> LWS_H2_STATE_CLOSED
- * Send END_STREAM flag -> LWS_H2_STATE_CLOSED
- *
- * - Any frame valid to send
- * - Only WINDOW_UPDATE, PRIORITY, or RST_STREAM valid to receive
- */
- LWS_H2_STATE_HALF_CLOSED_LOCAL,
- /*
- * Send RST_STREAM -> LWS_H2_STATE_CLOSED
- * Recv RST_STREAM -> LWS_H2_STATE_CLOSED
- * Recv END_STREAM flag -> LWS_H2_STATE_CLOSED
- *
- * - Only WINDOW_UPDATE, PRIORITY, and RST_STREAM valid to send
- * - Any frame valid to receive
- */
- LWS_H2_STATE_CLOSED,
- /*
- * - Only PRIORITY, WINDOW_UPDATE (IGNORE) and RST_STREAM (IGNORE)
- * may be received
- *
- * - Only PRIORITY valid to send
- */
-};
-
-#define LWS_H2_STREAM_ID_MASTER 0
-#define LWS_H2_SETTINGS_LEN 6
-
-enum http2_hpack_state {
- HPKS_TYPE,
-
- HPKS_IDX_EXT,
-
- HPKS_HLEN,
- HPKS_HLEN_EXT,
-
- HPKS_DATA,
-};
-
-/*
- * lws general parsimonious header strategy is only store values from known
- * headers, and refer to them by index.
- *
- * That means if we can't map the peer header name to one that lws knows, we
- * will drop the content but track the indexing with associated_lws_hdr_idx =
- * LWS_HPACK_IGNORE_ENTRY.
- */
-
-enum http2_hpack_type {
- HPKT_INDEXED_HDR_7, /* 1xxxxxxx: just "header field" */
- HPKT_INDEXED_HDR_6_VALUE_INCR, /* 01xxxxxx: NEW indexed hdr with value */
- HPKT_LITERAL_HDR_VALUE_INCR, /* 01000000: NEW literal hdr with value */
- HPKT_INDEXED_HDR_4_VALUE, /* 0000xxxx: indexed hdr with value */
- HPKT_INDEXED_HDR_4_VALUE_NEVER, /* 0001xxxx: indexed hdr with value NEVER NEW */
- HPKT_LITERAL_HDR_VALUE, /* 00000000: literal hdr with value */
- HPKT_LITERAL_HDR_VALUE_NEVER, /* 00010000: literal hdr with value NEVER NEW */
- HPKT_SIZE_5
-};
-
-#define LWS_HPACK_IGNORE_ENTRY 0xffff
-
-
-struct hpack_dt_entry {
- char *value; /* malloc'd */
- uint16_t value_len;
- uint16_t hdr_len; /* virtual, for accounting */
- uint16_t lws_hdr_idx; /* LWS_HPACK_IGNORE_ENTRY = IGNORE */
-};
-
-struct hpack_dynamic_table {
- struct hpack_dt_entry *entries; /* malloc'd */
- uint32_t virtual_payload_usage;
- uint32_t virtual_payload_max;
- uint16_t pos;
- uint16_t used_entries;
- uint16_t num_entries;
-};
-
-enum lws_h2_protocol_send_type {
- LWS_PPS_NONE,
- LWS_H2_PPS_MY_SETTINGS,
- LWS_H2_PPS_ACK_SETTINGS,
- LWS_H2_PPS_PONG,
- LWS_H2_PPS_GOAWAY,
- LWS_H2_PPS_RST_STREAM,
- LWS_H2_PPS_UPDATE_WINDOW,
-};
-
-struct lws_h2_protocol_send {
- struct lws_h2_protocol_send *next; /* linked list */
- enum lws_h2_protocol_send_type type;
-
- union uu {
- struct {
- char str[32];
- uint32_t highest_sid;
- uint32_t err;
- } ga;
- struct {
- uint32_t sid;
- uint32_t err;
- } rs;
- struct {
- uint8_t ping_payload[8];
- } ping;
- struct {
- uint32_t sid;
- uint32_t credit;
- } update_window;
- } u;
-};
-
-struct lws_h2_ghost_sid {
- struct lws_h2_ghost_sid *next;
- uint32_t sid;
-};
-
-#define LWS_H2_RX_SCRATCH_SIZE 512
-
-/*
- * http/2 connection info that is only used by the root connection that has
- * the network connection.
- *
- * h2 tends to spawn many child connections from one network connection, so
- * it's necessary to make members only needed by the network connection
- * distinct and only malloc'd on network connections.
- *
- * There's only one HPACK parser per network connection.
- *
- * But there is an ah per logical child connection... the network connection
- * fills it but it belongs to the logical child.
- */
-struct lws_h2_netconn {
- struct http2_settings set;
- struct hpack_dynamic_table hpack_dyn_table;
- uint8_t ping_payload[8];
- uint8_t one_setting[LWS_H2_SETTINGS_LEN];
- char goaway_str[32]; /* for rx */
- struct lws *swsi;
- struct lws_h2_protocol_send *pps; /* linked list */
- char *rx_scratch;
-
- enum http2_hpack_state hpack;
- enum http2_hpack_type hpack_type;
-
- unsigned int huff:1;
- unsigned int value:1;
- unsigned int unknown_header:1;
- unsigned int cont_exp:1;
- unsigned int cont_exp_headers:1;
- unsigned int we_told_goaway:1;
- unsigned int pad_length:1;
- unsigned int collected_priority:1;
- unsigned int is_first_header_char:1;
- unsigned int zero_huff_padding:1;
- unsigned int last_action_dyntable_resize:1;
-
- uint32_t hdr_idx;
- uint32_t hpack_len;
- uint32_t hpack_e_dep;
- uint32_t count;
- uint32_t preamble;
- uint32_t length;
- uint32_t sid;
- uint32_t inside;
- uint32_t highest_sid;
- uint32_t highest_sid_opened;
- uint32_t cont_exp_sid;
- uint32_t dep;
- uint32_t goaway_last_sid;
- uint32_t goaway_err;
- uint32_t hpack_hdr_len;
-
- uint32_t rx_scratch_pos;
- uint32_t rx_scratch_len;
-
- uint16_t hpack_pos;
-
- uint8_t frame_state;
- uint8_t type;
- uint8_t flags;
- uint8_t padding;
- uint8_t weight_temp;
- uint8_t huff_pad;
- char first_hdr_char;
- uint8_t hpack_m;
- uint8_t ext_count;
-};
-
-struct _lws_h2_related {
- /*
- * having this first lets us also re-use all HTTP union code
- * and in turn, http_mode_related has allocated headers in right
- * place so we can use the header apis on the wsi directly still
- */
- struct _lws_http_mode_related http; /* MUST BE FIRST IN STRUCT */
-
- struct lws_h2_netconn *h2n; /* malloc'd for root net conn */
- struct lws *parent_wsi;
- struct lws *child_list;
- struct lws *sibling_list;
-
- char *pending_status_body;
-
- int tx_cr;
- int peer_tx_cr_est;
- unsigned int my_sid;
- unsigned int child_count;
- int my_priority;
- uint32_t dependent_on;
-
- unsigned int END_STREAM:1;
- unsigned int END_HEADERS:1;
- unsigned int send_END_STREAM:1;
- unsigned int GOING_AWAY;
- unsigned int requested_POLLOUT:1;
- unsigned int skint:1;
-
- uint16_t round_robin_POLLOUT;
- uint16_t count_POLLOUT_children;
- uint8_t h2_state; /* the RFC7540 state of the connection */
- uint8_t weight;
-
- uint8_t initialized;
-};
-
-#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->u.h2.parent_wsi)
-
-#endif
-
-struct _lws_websocket_related {
- /* cheapest way to deal with ah overlap with ws union transition */
- struct _lws_header_related hdr;
- char *rx_ubuf;
- unsigned int rx_ubuf_alloc;
- struct lws *rx_draining_ext_list;
- struct lws *tx_draining_ext_list;
- time_t time_next_ping_check;
- size_t rx_packet_length;
- unsigned int rx_ubuf_head;
- unsigned char mask[4];
- /* Also used for close content... control opcode == < 128 */
- unsigned char ping_payload_buf[128 - 3 + LWS_PRE];
-
- unsigned char ping_payload_len;
- unsigned char mask_idx;
- unsigned char opcode;
- unsigned char rsv;
- unsigned char rsv_first_msg;
- /* zero if no info, or length including 2-byte close code */
- unsigned char close_in_ping_buffer_len;
- unsigned char utf8;
- unsigned char stashed_write_type;
- unsigned char tx_draining_stashed_wp;
-
- unsigned int final:1;
- unsigned int frame_is_binary:1;
- unsigned int all_zero_nonce:1;
- unsigned int this_frame_masked:1;
- unsigned int inside_frame:1; /* next write will be more of frame */
- unsigned int clean_buffer:1; /* buffer not rewritten by extension */
- unsigned int payload_is_close:1; /* process as PONG, but it is close */
- unsigned int ping_pending_flag:1;
- unsigned int continuation_possible:1;
- unsigned int owed_a_fin:1;
- unsigned int check_utf8:1;
- unsigned int defeat_check_utf8:1;
- unsigned int pmce_compressed_message:1;
- unsigned int stashed_write_pending:1;
- unsigned int rx_draining_ext:1;
- unsigned int tx_draining_ext:1;
- unsigned int send_check_ping:1;
- unsigned int first_fragment:1;
-};
-
-#ifdef LWS_WITH_CGI
-
-#define LWS_HTTP_CHUNK_HDR_SIZE 16
-
-enum {
- SIGNIFICANT_HDR_CONTENT_LENGTH,
- SIGNIFICANT_HDR_LOCATION,
- SIGNIFICANT_HDR_STATUS,
- SIGNIFICANT_HDR_TRANSFER_ENCODING,
-
- SIGNIFICANT_HDR_COUNT
-};
-
-/* wsi who is master of the cgi points to an lws_cgi */
-
-struct lws_cgi {
- struct lws_cgi *cgi_list;
- struct lws *stdwsi[3]; /* points to the associated stdin/out/err wsis */
- struct lws *wsi; /* owner */
- unsigned char *headers_buf;
- unsigned char *headers_start;
- unsigned char *headers_pos;
- unsigned char *headers_dumped;
- unsigned char *headers_end;
- lws_filepos_t content_length;
- lws_filepos_t content_length_seen;
- int pipe_fds[3][2];
- int match[SIGNIFICANT_HDR_COUNT];
- int pid;
- int response_code;
- int lp;
- char l[12];
-
- unsigned int being_closed:1;
- unsigned int explicitly_chunked:1;
-
- unsigned char chunked_grace;
-};
-#endif
-
-signed char char_to_hex(const char c);
-
-#ifndef LWS_NO_CLIENT
-enum lws_chunk_parser {
- ELCP_HEX,
- ELCP_CR,
- ELCP_CONTENT,
- ELCP_POST_CR,
- ELCP_POST_LF,
-};
-#endif
-
-enum lws_parse_urldecode_results {
- LPUR_CONTINUE,
- LPUR_SWALLOW,
- LPUR_FORBID,
- LPUR_EXCESSIVE,
-};
-
-struct lws_rewrite;
-
-#ifdef LWS_WITH_ACCESS_LOG
-struct lws_access_log {
- char *header_log;
- char *user_agent;
- char *referrer;
- unsigned long sent;
- int response;
-};
-#endif
-
-struct lws {
-
- /* structs */
- /* members with mutually exclusive lifetimes are unionized */
-
- union u {
- struct _lws_http_mode_related http;
-#ifdef LWS_WITH_HTTP2
- struct _lws_h2_related h2;
-#endif
- struct _lws_header_related hdr;
- struct _lws_websocket_related ws;
- } u;
-
- /* lifetime members */
-
-#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
- struct lws_io_watcher w_read;
-#endif
-#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBEVENT)
- struct lws_io_watcher w_write;
-#endif
-#ifdef LWS_WITH_ACCESS_LOG
- struct lws_access_log access_log;
-#endif
- time_t pending_timeout_limit;
-
- /* pointers */
-
- struct lws_context *context;
- struct lws_vhost *vhost;
- struct lws *parent; /* points to parent, if any */
- struct lws *child_list; /* points to first child */
- struct lws *sibling_list; /* subsequent children at same level */
-#ifdef LWS_WITH_CGI
- struct lws_cgi *cgi; /* wsi being cgi master have one of these */
-#endif
- const struct lws_protocols *protocol;
- struct lws **same_vh_protocol_prev, *same_vh_protocol_next;
- struct lws *timeout_list;
- struct lws **timeout_list_prev;
-#if defined(LWS_WITH_PEER_LIMITS)
- struct lws_peer *peer;
-#endif
-
- void *user_space;
- void *opaque_parent_data;
- /* rxflow handling */
- unsigned char *rxflow_buffer;
- /* truncated send handling */
- unsigned char *trunc_alloc; /* non-NULL means buffering in progress */
-
-#if defined (LWS_WITH_ESP8266)
- void *premature_rx;
- unsigned short prem_rx_size, prem_rx_pos;
-#endif
-
-#ifndef LWS_NO_EXTENSIONS
- const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];
- void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];
-#endif
-#ifdef LWS_OPENSSL_SUPPORT
- SSL *ssl;
- BIO *client_bio;
- struct lws *pending_read_list_prev, *pending_read_list_next;
-#if defined(LWS_WITH_STATS)
- uint64_t accept_start_us;
- char seen_rx;
-#endif
-#endif
-#ifdef LWS_WITH_HTTP_PROXY
- struct lws_rewrite *rw;
-#endif
-#ifdef LWS_LATENCY
- unsigned long action_start;
- unsigned long latency_start;
-#endif
- lws_sock_file_fd_type desc; /* .filefd / .sockfd */
-#if defined(LWS_WITH_STATS)
- uint64_t active_writable_req_us;
-#endif
- /* ints */
- int position_in_fds_table;
- uint32_t rxflow_len;
- uint32_t rxflow_pos;
- unsigned int trunc_alloc_len; /* size of malloc */
- unsigned int trunc_offset; /* where we are in terms of spilling */
- unsigned int trunc_len; /* how much is buffered */
-#ifndef LWS_NO_CLIENT
- int chunk_remaining;
-#endif
- unsigned int cache_secs;
-
- unsigned int hdr_parsing_completed:1;
- unsigned int http2_substream:1;
- unsigned int upgraded_to_http2:1;
- unsigned int seen_nonpseudoheader:1;
- unsigned int listener:1;
- unsigned int user_space_externally_allocated:1;
- unsigned int socket_is_permanently_unusable:1;
- unsigned int rxflow_change_to:2;
- unsigned int more_rx_waiting:1; /* has to live here since ah may stick to end */
- unsigned int conn_stat_done:1;
- unsigned int cache_reuse:1;
- unsigned int cache_revalidate:1;
- unsigned int cache_intermediaries:1;
- unsigned int favoured_pollin:1;
- unsigned int sending_chunked:1;
- unsigned int already_did_cce:1;
- unsigned int told_user_closed:1;
- unsigned int waiting_to_send_close_frame:1;
- unsigned int ipv6:1;
- unsigned int parent_carries_io:1;
- unsigned int parent_pending_cb_on_writable:1;
- unsigned int cgi_stdout_zero_length:1;
- unsigned int seen_zero_length_recv:1;
- unsigned int rxflow_will_be_applied:1;
-
-#if defined(LWS_WITH_ESP8266)
- unsigned int pending_send_completion:3;
- unsigned int close_is_pending_send_completion:1;
-#endif
-#ifdef LWS_WITH_ACCESS_LOG
- unsigned int access_log_pending:1;
-#endif
-#ifndef LWS_NO_CLIENT
- unsigned int do_ws:1; /* whether we are doing http or ws flow */
- unsigned int chunked:1; /* if the clientside connection is chunked */
- unsigned int client_rx_avail:1;
- unsigned int client_http_body_pending:1;
-#endif
-#ifdef LWS_WITH_HTTP_PROXY
- unsigned int perform_rewrite:1;
-#endif
-#ifndef LWS_NO_EXTENSIONS
- unsigned int extension_data_pending:1;
-#endif
-#ifdef LWS_OPENSSL_SUPPORT
- unsigned int use_ssl:4;
-#endif
-#ifdef _WIN32
- unsigned int sock_send_blocking:1;
-#endif
-#ifdef LWS_OPENSSL_SUPPORT
- unsigned int redirect_to_https:1;
-#endif
-
- /* volatile to make sure code is aware other thread can change */
- volatile unsigned int handling_pollout:1;
- volatile unsigned int leave_pollout_active:1;
-
-#ifndef LWS_NO_CLIENT
- unsigned short c_port;
-#endif
-
- /* chars */
-#ifndef LWS_NO_EXTENSIONS
- unsigned char count_act_ext;
-#endif
- uint8_t ietf_spec_revision;
- char mode; /* enum connection_mode */
- char state; /* enum lws_connection_states */
- char state_pre_close;
- char lws_rx_parse_state; /* enum lws_rx_parse_state */
- char rx_frame_type; /* enum lws_write_protocol */
- char pending_timeout; /* enum pending_timeout */
- char tsi; /* thread service index we belong to */
- char protocol_interpret_idx;
- char redirects;
- uint8_t rxflow_bitmap;
-#ifdef LWS_WITH_CGI
- char cgi_channel; /* which of stdin/out/err */
- char hdr_state;
-#endif
-#ifndef LWS_NO_CLIENT
- char chunk_parser; /* enum lws_chunk_parser */
-#endif
-#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT)
- char reason_bf; /* internal writeable callback reason bitfield */
-#endif
-};
-
-#define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap))
-
-LWS_EXTERN int log_level;
-
-LWS_EXTERN int
-lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
- const char *iface);
-
-#if defined(LWS_WITH_IPV6)
-LWS_EXTERN unsigned long
-lws_get_addr_scope(const char *ipaddr);
-#endif
-
-LWS_EXTERN void
-lws_close_free_wsi(struct lws *wsi, enum lws_close_status);
-
-LWS_EXTERN void
-lws_free_wsi(struct lws *wsi);
-
-LWS_EXTERN int
-remove_wsi_socket_from_fds(struct lws *wsi);
-LWS_EXTERN int
-lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len);
-
-#ifndef LWS_LATENCY
-static inline void
-lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
- int ret, int completion) {
- do {
- (void)context; (void)wsi; (void)action; (void)ret;
- (void)completion;
- } while (0);
-}
-static inline void
-lws_latency_pre(struct lws_context *context, struct lws *wsi) {
- do { (void)context; (void)wsi; } while (0);
-}
-#else
-#define lws_latency_pre(_context, _wsi) lws_latency(_context, _wsi, NULL, 0, 0)
-extern void
-lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
- int ret, int completion);
-#endif
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_client_rx_sm(struct lws *wsi, unsigned char c);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_parse(struct lws *wsi, unsigned char c);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_parse_urldecode(struct lws *wsi, uint8_t *_c);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_http_action(struct lws *wsi);
-
-LWS_EXTERN int
-lws_b64_selftest(void);
-
-LWS_EXTERN int
-lws_service_flag_pending(struct lws_context *context, int tsi);
-
-#if defined(_WIN32) || defined(LWS_WITH_ESP8266)
-LWS_EXTERN struct lws *
-wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd);
-
-LWS_EXTERN int
-insert_wsi(struct lws_context *context, struct lws *wsi);
-
-LWS_EXTERN int
-delete_from_fd(struct lws_context *context, lws_sockfd_type fd);
-#else
-#define wsi_from_fd(A,B) A->lws_lookup[B]
-#define insert_wsi(A,B) assert(A->lws_lookup[B->desc.sockfd] == 0); A->lws_lookup[B->desc.sockfd]=B
-#define delete_from_fd(A,B) A->lws_lookup[B]=0
-#endif
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len);
-
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_service_timeout_check(struct lws *wsi, unsigned int sec);
-
-LWS_EXTERN void
-lws_remove_from_timeout_list(struct lws *wsi);
-
-LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
-lws_client_connect_2(struct lws *wsi);
-
-LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT
-lws_client_reset(struct lws **wsi, int ssl, const char *address, int port,
- const char *path, const char *host);
-
-LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
-lws_create_new_server_wsi(struct lws_vhost *vhost);
-
-LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
-lws_generate_client_handshake(struct lws *wsi, char *pkt);
-
-LWS_EXTERN int
-lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd);
-
-LWS_EXTERN struct lws *
-lws_client_connect_via_info2(struct lws *wsi);
-
-LWS_EXTERN int
-_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah);
-
-/*
- * EXTENSIONS
- */
-
-#ifndef LWS_NO_EXTENSIONS
-LWS_VISIBLE void
-lws_context_init_extensions(struct lws_context_creation_info *info,
- struct lws_context *context);
-LWS_EXTERN int
-lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
- void *v, size_t len);
-
-LWS_EXTERN int
-lws_ext_cb_active(struct lws *wsi, int reason, void *buf, int len);
-LWS_EXTERN int
-lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, int reason,
- void *arg, int len);
-
-#else
-#define lws_any_extension_handled(_a, _b, _c, _d) (0)
-#define lws_ext_cb_active(_a, _b, _c, _d) (0)
-#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0)
-#define lws_issue_raw_ext_access lws_issue_raw
-#define lws_context_init_extensions(_a, _b)
-#endif
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_client_interpret_server_handshake(struct lws *wsi);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_rx_sm(struct lws *wsi, unsigned char c);
-
-LWS_EXTERN int
-lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf, size_t *len);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len);
-
-LWS_EXTERN void
-lws_union_transition(struct lws *wsi, enum connection_mode mode);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-user_callback_handle_rxflow(lws_callback_function, struct lws *wsi,
- enum lws_callback_reasons reason, void *user,
- void *in, size_t len);
-#ifdef LWS_WITH_HTTP2
-struct lws * lws_h2_get_nth_child(struct lws *wsi, int n);
-LWS_EXTERN void lws_h2_init(struct lws *wsi);
-LWS_EXTERN int
-lws_h2_settings(struct lws *nwsi, struct http2_settings *settings,
- unsigned char *buf, int len);
-LWS_EXTERN int
-lws_h2_parser(struct lws *wsi, unsigned char c);
-LWS_EXTERN int lws_h2_do_pps_send(struct lws *wsi);
-LWS_EXTERN int lws_h2_frame_write(struct lws *wsi, int type, int flags,
- unsigned int sid, unsigned int len,
- unsigned char *buf);
-LWS_EXTERN struct lws *
-lws_h2_wsi_from_id(struct lws *wsi, unsigned int sid);
-LWS_EXTERN int lws_hpack_interpret(struct lws *wsi,
- unsigned char c);
-LWS_EXTERN int
-lws_add_http2_header_by_name(struct lws *wsi,
- const unsigned char *name,
- const unsigned char *value, int length,
- unsigned char **p, unsigned char *end);
-LWS_EXTERN int
-lws_add_http2_header_by_token(struct lws *wsi,
- enum lws_token_indexes token,
- const unsigned char *value, int length,
- unsigned char **p, unsigned char *end);
-LWS_EXTERN int
-lws_add_http2_header_status(struct lws *wsi,
- unsigned int code, unsigned char **p,
- unsigned char *end);
-LWS_EXTERN int
-lws_h2_configure_if_upgraded(struct lws *wsi);
-LWS_EXTERN void
-lws_hpack_destroy_dynamic_header(struct lws *wsi);
-LWS_EXTERN int
-lws_hpack_dynamic_size(struct lws *wsi, int size);
-LWS_EXTERN int
-lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason);
-LWS_EXTERN int
-lws_h2_tx_cr_get(struct lws *wsi);
-LWS_EXTERN void
-lws_h2_tx_cr_consume(struct lws *wsi, int consumed);
-LWS_EXTERN int
-lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h);
-LWS_EXTERN void
-lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pss);
-
-LWS_EXTERN const struct http2_settings lws_h2_defaults;
-#else
-#define lws_h2_configure_if_upgraded(x)
-#endif
-
-LWS_EXTERN int
-lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd);
-
-LWS_EXTERN int
-lws_plat_check_connection_error(struct lws *wsi);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_header_table_attach(struct lws *wsi, int autoservice);
-
-LWS_EXTERN int
-lws_header_table_detach(struct lws *wsi, int autoservice);
-
-LWS_EXTERN void
-lws_header_table_reset(struct lws *wsi, int autoservice);
-void
-_lws_header_table_reset(struct allocated_headers *ah);
-
-void
-lws_header_table_force_to_detachable_state(struct lws *wsi);
-int
-lws_header_table_is_in_detachable_state(struct lws *wsi);
-
-LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
-lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ensure_user_space(struct lws *wsi);
-
-LWS_EXTERN int
-lws_change_pollfd(struct lws *wsi, int _and, int _or);
-
-#ifndef LWS_NO_SERVER
-int lws_context_init_server(struct lws_context_creation_info *info,
- struct lws_vhost *vhost);
-LWS_EXTERN struct lws_vhost *
-lws_select_vhost(struct lws_context *context, int port, const char *servername);
-LWS_EXTERN int
-handshake_0405(struct lws_context *context, struct lws *wsi);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len);
-LWS_EXTERN void
-lws_server_get_canonical_hostname(struct lws_context *context,
- struct lws_context_creation_info *info);
-#else
-#define lws_context_init_server(_a, _b) (0)
-#define lws_interpret_incoming_packet(_a, _b, _c) (0)
-#define lws_server_get_canonical_hostname(_a, _b)
-#endif
-
-#ifndef LWS_NO_DAEMONIZE
-LWS_EXTERN int get_daemonize_pid();
-#else
-#define get_daemonize_pid() (0)
-#endif
-
-#if !defined(LWS_WITH_ESP8266)
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-interface_to_sa(struct lws_vhost *vh, const char *ifname,
- struct sockaddr_in *addr, size_t addrlen);
-#endif
-LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
-
-enum lws_ssl_capable_status {
- LWS_SSL_CAPABLE_ERROR = -1,
- LWS_SSL_CAPABLE_MORE_SERVICE = -2,
-};
-
-#ifndef LWS_OPENSSL_SUPPORT
-#define LWS_SSL_ENABLED(context) (0)
-#define lws_context_init_server_ssl(_a, _b) (0)
-#define lws_ssl_destroy(_a)
-#define lws_context_init_http2_ssl(_a)
-#define lws_ssl_capable_read lws_ssl_capable_read_no_ssl
-#define lws_ssl_capable_write lws_ssl_capable_write_no_ssl
-#define lws_ssl_pending lws_ssl_pending_no_ssl
-#define lws_server_socket_service_ssl(_b, _c) (0)
-#define lws_ssl_close(_a) (0)
-#define lws_ssl_context_destroy(_a)
-#define lws_ssl_SSL_CTX_destroy(_a)
-#define lws_ssl_remove_wsi_from_buffered_list(_a)
-#define lws_context_init_ssl_library(_a)
-#define lws_ssl_anybody_has_buffered_read_tsi(_a, _b) (0)
-#else
-#define LWS_SSL_ENABLED(context) (context->use_ssl)
-LWS_EXTERN int openssl_websocket_private_data_index;
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_pending(struct lws *wsi);
-LWS_EXTERN int
-lws_context_init_ssl_library(struct lws_context_creation_info *info);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd);
-LWS_EXTERN int
-lws_ssl_close(struct lws *wsi);
-LWS_EXTERN void
-lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost);
-LWS_EXTERN void
-lws_ssl_context_destroy(struct lws_context *context);
-LWS_VISIBLE void
-lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi);
-LWS_EXTERN int
-lws_ssl_client_bio_create(struct lws *wsi);
-LWS_EXTERN int
-lws_ssl_client_connect1(struct lws *wsi);
-LWS_EXTERN int
-lws_ssl_client_connect2(struct lws *wsi);
-LWS_EXTERN void
-lws_ssl_elaborate_error(void);
-LWS_EXTERN int
-lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi);
-#ifndef LWS_NO_SERVER
-LWS_EXTERN int
-lws_context_init_server_ssl(struct lws_context_creation_info *info,
- struct lws_vhost *vhost);
-#else
-#define lws_context_init_server_ssl(_a, _b) (0)
-#endif
-LWS_EXTERN void
-lws_ssl_destroy(struct lws_vhost *vhost);
-/* HTTP2-related */
-
-#ifdef LWS_WITH_HTTP2
-LWS_EXTERN void
-lws_context_init_http2_ssl(struct lws_vhost *vhost);
-#else
-#define lws_context_init_http2_ssl(_a)
-#endif
-#endif
-
-#if LWS_MAX_SMP > 1
-static LWS_INLINE void
-lws_pt_mutex_init(struct lws_context_per_thread *pt)
-{
- pthread_mutex_init(&pt->lock, NULL);
-}
-
-static LWS_INLINE void
-lws_pt_mutex_destroy(struct lws_context_per_thread *pt)
-{
- pthread_mutex_destroy(&pt->lock);
-}
-
-static LWS_INLINE void
-lws_pt_lock(struct lws_context_per_thread *pt)
-{
- if (!pt->lock_depth++)
- pthread_mutex_lock(&pt->lock);
-}
-
-static LWS_INLINE void
-lws_pt_unlock(struct lws_context_per_thread *pt)
-{
- if (!(--pt->lock_depth))
- pthread_mutex_unlock(&pt->lock);
-}
-static LWS_INLINE void
-lws_context_lock(struct lws_context *context)
-{
- if (!context->lock_depth++)
- pthread_mutex_lock(&context->lock);
-}
-
-static LWS_INLINE void
-lws_context_unlock(struct lws_context *context)
-{
- if (!(--context->lock_depth))
- pthread_mutex_unlock(&context->lock);
-}
-
-#else
-#define lws_pt_mutex_init(_a) (void)(_a)
-#define lws_pt_mutex_destroy(_a) (void)(_a)
-#define lws_pt_lock(_a) (void)(_a)
-#define lws_pt_unlock(_a) (void)(_a)
-#define lws_context_lock(_a) (void)(_a)
-#define lws_context_unlock(_a) (void)(_a)
-#endif
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_pending_no_ssl(struct lws *wsi);
-
-#ifdef LWS_WITH_HTTP_PROXY
-struct lws_rewrite {
- hubbub_parser *parser;
- hubbub_parser_optparams params;
- const char *from, *to;
- int from_len, to_len;
- unsigned char *p, *end;
- struct lws *wsi;
-};
-static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len)
-{
- if (s->len != len)
- return 1;
-
- return strncmp((const char *)s->ptr, p, len);
-}
-typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw);
-LWS_EXTERN struct lws_rewrite *
-lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to);
-LWS_EXTERN void
-lws_rewrite_destroy(struct lws_rewrite *r);
-LWS_EXTERN int
-lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len);
-#endif
-
-#ifndef LWS_NO_CLIENT
-LWS_EXTERN int lws_client_socket_service(struct lws_context *context,
- struct lws *wsi,
- struct lws_pollfd *pollfd);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_http_transaction_completed_client(struct lws *wsi);
-#ifdef LWS_OPENSSL_SUPPORT
-LWS_EXTERN int
-lws_context_init_client_ssl(struct lws_context_creation_info *info,
- struct lws_vhost *vhost);
-
-LWS_EXTERN void
-lws_ssl_info_callback(const SSL *ssl, int where, int ret);
-
-#else
- #define lws_context_init_client_ssl(_a, _b) (0)
-#endif
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len);
-LWS_EXTERN void
-lws_decode_ssl_error(void);
-#else
-#define lws_context_init_client_ssl(_a, _b) (0)
-#define lws_handshake_client(_a, _b, _c) (0)
-#endif
-
-LWS_EXTERN int
-_lws_rx_flow_control(struct lws *wsi);
-
-LWS_EXTERN int
-_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa);
-
-#ifndef LWS_NO_SERVER
-LWS_EXTERN int
-lws_server_socket_service(struct lws_context *context, struct lws *wsi,
- struct lws_pollfd *pollfd);
-LWS_EXTERN int
-lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len);
-#else
-#define lws_server_socket_service(_a, _b, _c) (0)
-#define lws_handshake_server(_a, _b, _c) (0)
-#endif
-
-#ifdef LWS_WITH_ACCESS_LOG
-LWS_EXTERN int
-lws_access_log(struct lws *wsi);
-LWS_EXTERN void
-lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth);
-#else
-#define lws_access_log(_a)
-#endif
-
-LWS_EXTERN int
-lws_cgi_kill_terminated(struct lws_context_per_thread *pt);
-
-LWS_EXTERN void
-lws_cgi_remove_and_kill(struct lws *wsi);
-
-int
-lws_protocol_init(struct lws_context *context);
-
-int
-lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p);
-
-const struct lws_http_mount *
-lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len);
-
-/*
- * custom allocator
- */
-LWS_EXTERN void *
-lws_realloc(void *ptr, size_t size, const char *reason);
-
-LWS_EXTERN void * LWS_WARN_UNUSED_RESULT
-lws_zalloc(size_t size, const char *reason);
-
-#ifdef LWS_PLAT_OPTEE
-void *lws_malloc(size_t size, const char *reason);
-void lws_free(void *p);
-#define lws_free_set_NULL(P) do { lws_free(P); (P) = NULL; } while(0)
-#else
-#define lws_malloc(S, R) lws_realloc(NULL, S, R)
-#define lws_free(P) lws_realloc(P, 0, "lws_free")
-#define lws_free_set_NULL(P) do { lws_realloc(P, 0, "free"); (P) = NULL; } while(0)
-#endif
-
-const struct lws_plat_file_ops *
-lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path,
- const char **vpath);
-
-/* lws_plat_ */
-LWS_EXTERN void
-lws_plat_delete_socket_from_fds(struct lws_context *context,
- struct lws *wsi, int m);
-LWS_EXTERN void
-lws_plat_insert_socket_into_fds(struct lws_context *context,
- struct lws *wsi);
-LWS_EXTERN void
-lws_plat_service_periodic(struct lws_context *context);
-
-LWS_EXTERN int
-lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi,
- struct lws_pollfd *pfd);
-LWS_EXTERN void
-lws_add_wsi_to_draining_ext_list(struct lws *wsi);
-LWS_EXTERN void
-lws_remove_wsi_from_draining_ext_list(struct lws *wsi);
-LWS_EXTERN int
-lws_plat_context_early_init(void);
-LWS_EXTERN void
-lws_plat_context_early_destroy(struct lws_context *context);
-LWS_EXTERN void
-lws_plat_context_late_destroy(struct lws_context *context);
-LWS_EXTERN int
-lws_poll_listen_fd(struct lws_pollfd *fd);
-LWS_EXTERN int
-lws_plat_service(struct lws_context *context, int timeout_ms);
-LWS_EXTERN LWS_VISIBLE int
-_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi);
-LWS_EXTERN int
-lws_plat_init(struct lws_context *context,
- struct lws_context_creation_info *info);
-LWS_EXTERN void
-lws_plat_drop_app_privileges(struct lws_context_creation_info *info);
-LWS_EXTERN unsigned long long
-time_in_microseconds(void);
-LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT
-lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_plat_inet_pton(int af, const char *src, void *dst);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len);
-LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
- lws_filepos_t *amount);
-LWS_EXTERN int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf,
- lws_filepos_t *amount);
-
-LWS_EXTERN void
-lws_same_vh_protocol_remove(struct lws *wsi);
-LWS_EXTERN void
-lws_same_vh_protocol_insert(struct lws *wsi, int n);
-
-#if defined(LWS_WITH_STATS)
-void
-lws_stats_atomic_bump(struct lws_context * context,
- struct lws_context_per_thread *pt, int index, uint64_t bump);
-void
-lws_stats_atomic_max(struct lws_context * context,
- struct lws_context_per_thread *pt, int index, uint64_t val);
-#else
-static inline uint64_t lws_stats_atomic_bump(struct lws_context * context,
- struct lws_context_per_thread *pt, int index, uint64_t bump) {
- (void)context; (void)pt; (void)index; (void)bump; return 0; }
-static inline uint64_t lws_stats_atomic_max(struct lws_context * context,
- struct lws_context_per_thread *pt, int index, uint64_t val) {
- (void)context; (void)pt; (void)index; (void)val; return 0; }
-#endif
-
-/* socks */
-void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
- ssize_t *msg_len);
-
-#if defined(LWS_WITH_PEER_LIMITS)
-void
-lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer);
-int
-lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer);
-void
-lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer);
-void
-lws_peer_cull_peer_wait_list(struct lws_context *context);
-struct lws_peer *
-lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd);
-void
-lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer,
- struct lws *wsi);
-#endif
-
-#ifdef __cplusplus
-};
-#endif
diff --git a/thirdparty/lws/server/parsers.c b/thirdparty/lws/server/parsers.c
deleted file mode 100644
index fb345ab04c..0000000000
--- a/thirdparty/lws/server/parsers.c
+++ /dev/null
@@ -1,1783 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-const unsigned char lextable[] = {
- #include "lextable.h"
-};
-
-#define FAIL_CHAR 0x08
-
-int LWS_WARN_UNUSED_RESULT
-lextable_decode(int pos, char c)
-{
- if (c >= 'A' && c <= 'Z')
- c += 'a' - 'A';
-
- while (1) {
- if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */
- if ((lextable[pos] & 0x7f) != c)
- return -1;
- /* fall thru */
- pos++;
- if (lextable[pos] == FAIL_CHAR)
- return -1;
- return pos;
- }
-
- if (lextable[pos] == FAIL_CHAR)
- return -1;
-
- /* b7 = 0, end or 3-byte */
- if (lextable[pos] < FAIL_CHAR) /* terminal marker */
- return pos;
-
- if (lextable[pos] == c) /* goto */
- return pos + (lextable[pos + 1]) +
- (lextable[pos + 2] << 8);
- /* fall thru goto */
- pos += 3;
- /* continue */
- }
-}
-
-static struct allocated_headers *
-_lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size)
-{
- struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct");
-
- if (!ah)
- return NULL;
-
- ah->data = lws_malloc(data_size, "ah data");
- if (!ah->data) {
- lws_free(ah);
-
- return NULL;
- }
- ah->next = pt->ah_list;
- pt->ah_list = ah;
- ah->data_length = data_size;
- pt->ah_pool_length++;
-
- lwsl_info("%s: created ah %p (size %d): pool length %d\n", __func__,
- ah, (int)data_size, pt->ah_pool_length);
-
- return ah;
-}
-
-int
-_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah)
-{
- lws_start_foreach_llp(struct allocated_headers **, a, pt->ah_list) {
- if ((*a) == ah) {
- *a = ah->next;
- pt->ah_pool_length--;
- lwsl_info("%s: freed ah %p : pool length %d\n",
- __func__, ah, pt->ah_pool_length);
- if (ah->data)
- lws_free(ah->data);
- lws_free(ah);
-
- return 0;
- }
- } lws_end_foreach_llp(a, next);
-
- return 1;
-}
-
-void
-_lws_header_table_reset(struct allocated_headers *ah)
-{
- /* init the ah to reflect no headers or data have appeared yet */
- memset(ah->frag_index, 0, sizeof(ah->frag_index));
- memset(ah->frags, 0, sizeof(ah->frags));
- ah->nfrag = 0;
- ah->pos = 0;
- ah->http_response = 0;
-}
-
-// doesn't scrub the ah rxbuffer by default, parent must do if needed
-
-void
-lws_header_table_reset(struct lws *wsi, int autoservice)
-{
- struct allocated_headers *ah = wsi->u.hdr.ah;
- struct lws_context_per_thread *pt;
- struct lws_pollfd *pfd;
-
- /* if we have the idea we're resetting 'our' ah, must be bound to one */
- assert(ah);
- /* ah also concurs with ownership */
- assert(ah->wsi == wsi);
-
- _lws_header_table_reset(ah);
-
- wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
- wsi->u.hdr.lextable_pos = 0;
-
- /* since we will restart the ah, our new headers are not completed */
- wsi->hdr_parsing_completed = 0;
-
- /* while we hold the ah, keep a timeout on the wsi */
- lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
- wsi->vhost->timeout_secs_ah_idle);
-
- time(&ah->assigned);
-
- /*
- * if we inherited pending rx (from socket adoption deferred
- * processing), apply and free it.
- */
- if (wsi->u.hdr.preamble_rx) {
- memcpy(ah->rx, wsi->u.hdr.preamble_rx,
- wsi->u.hdr.preamble_rx_len);
- ah->rxlen = wsi->u.hdr.preamble_rx_len;
- lws_free_set_NULL(wsi->u.hdr.preamble_rx);
-
- if (autoservice) {
- lwsl_debug("%s: service on readbuf ah\n", __func__);
-
- pt = &wsi->context->pt[(int)wsi->tsi];
- /*
- * Unlike a normal connect, we have the headers already
- * (or the first part of them anyway)
- */
- pfd = &pt->fds[wsi->position_in_fds_table];
- pfd->revents |= LWS_POLLIN;
- lwsl_err("%s: calling service\n", __func__);
- lws_service_fd_tsi(wsi->context, pfd, wsi->tsi);
- }
- }
-}
-
-static void
-_lws_header_ensure_we_are_on_waiting_list(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- struct lws_pollargs pa;
- struct lws **pwsi = &pt->ah_wait_list;
-
- while (*pwsi) {
- if (*pwsi == wsi)
- return;
- pwsi = &(*pwsi)->u.hdr.ah_wait_list;
- }
-
- lwsl_info("%s: wsi: %p\n", __func__, wsi);
- wsi->u.hdr.ah_wait_list = pt->ah_wait_list;
- pt->ah_wait_list = wsi;
- pt->ah_wait_list_length++;
-
- /* we cannot accept input then */
-
- _lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa);
-}
-
-static int
-__lws_remove_from_ah_waiting_list(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- struct lws **pwsi =&pt->ah_wait_list;
-
- while (*pwsi) {
- if (*pwsi == wsi) {
- lwsl_info("%s: wsi %p\n", __func__, wsi);
- /* point prev guy to our next */
- *pwsi = wsi->u.hdr.ah_wait_list;
- /* we shouldn't point anywhere now */
- wsi->u.hdr.ah_wait_list = NULL;
- pt->ah_wait_list_length--;
-
- return 1;
- }
- pwsi = &(*pwsi)->u.hdr.ah_wait_list;
- }
-
- return 0;
-}
-
-int LWS_WARN_UNUSED_RESULT
-lws_header_table_attach(struct lws *wsi, int autoservice)
-{
- struct lws_context *context = wsi->context;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- struct lws_pollargs pa;
- int n;
-
- lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__,
- (void *)wsi, (void *)wsi->u.hdr.ah, wsi->tsi,
- pt->ah_count_in_use);
-
- /* if we are already bound to one, just clear it down */
- if (wsi->u.hdr.ah) {
- lwsl_info("%s: cleardown\n", __func__);
- goto reset;
- }
-
- lws_pt_lock(pt);
-
- n = pt->ah_count_in_use == context->max_http_header_pool;
-#if defined(LWS_WITH_PEER_LIMITS)
- if (!n) {
- n = lws_peer_confirm_ah_attach_ok(context, wsi->peer);
- if (n)
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
- }
-#endif
- if (n) {
- /*
- * Pool is either all busy, or we don't want to give this
- * particular guy an ah right now...
- *
- * Make sure we are on the waiting list, and return that we
- * weren't able to provide the ah
- */
- _lws_header_ensure_we_are_on_waiting_list(wsi);
-
- goto bail;
- }
-
- __lws_remove_from_ah_waiting_list(wsi);
-
- wsi->u.hdr.ah = _lws_create_ah(pt, context->max_http_header_data);
- if (!wsi->u.hdr.ah) { /* we could not create an ah */
- _lws_header_ensure_we_are_on_waiting_list(wsi);
-
- goto bail;
- }
-
- wsi->u.hdr.ah->in_use = 1;
- wsi->u.hdr.ah->wsi = wsi; /* mark our owner */
- pt->ah_count_in_use++;
-
-#if defined(LWS_WITH_PEER_LIMITS)
- if (wsi->peer)
- wsi->peer->count_ah++;
-#endif
-
- _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
-
- lwsl_info("%s: did attach wsi %p: ah %p: count %d (on exit)\n", __func__,
- (void *)wsi, (void *)wsi->u.hdr.ah, pt->ah_count_in_use);
-
- lws_pt_unlock(pt);
-
-reset:
-
- /* and reset the rx state */
- wsi->u.hdr.ah->rxpos = 0;
- wsi->u.hdr.ah->rxlen = 0;
-
- lws_header_table_reset(wsi, autoservice);
-
-#ifndef LWS_NO_CLIENT
- if (wsi->state == LWSS_CLIENT_UNCONNECTED)
- if (!lws_client_connect_via_info2(wsi))
- /* our client connect has failed, the wsi
- * has been closed
- */
- return -1;
-#endif
-
- return 0;
-
-bail:
- lws_pt_unlock(pt);
-
- return 1;
-}
-
-void
-lws_header_table_force_to_detachable_state(struct lws *wsi)
-{
- if (wsi->u.hdr.ah) {
- wsi->u.hdr.ah->rxpos = -1;
- wsi->u.hdr.ah->rxlen = -1;
- wsi->hdr_parsing_completed = 1;
- }
-}
-
-int
-lws_header_table_is_in_detachable_state(struct lws *wsi)
-{
- struct allocated_headers *ah = wsi->u.hdr.ah;
-
- return ah && ah->rxpos == ah->rxlen && wsi->hdr_parsing_completed;
-}
-
-int lws_header_table_detach(struct lws *wsi, int autoservice)
-{
- struct lws_context *context = wsi->context;
- struct allocated_headers *ah = wsi->u.hdr.ah;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- struct lws_pollargs pa;
- struct lws **pwsi, **pwsi_eligible;
- time_t now;
-
- lws_pt_lock(pt);
- __lws_remove_from_ah_waiting_list(wsi);
- lws_pt_unlock(pt);
-
- if (!ah)
- return 0;
-
- lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
- (void *)wsi, (void *)ah, wsi->tsi,
- pt->ah_count_in_use);
-
- if (wsi->u.hdr.preamble_rx)
- lws_free_set_NULL(wsi->u.hdr.preamble_rx);
-
- /* may not be detached while he still has unprocessed rx */
- if (!lws_header_table_is_in_detachable_state(wsi)) {
- lwsl_err("%s: %p: CANNOT DETACH rxpos:%d, rxlen:%d, "
- "wsi->hdr_parsing_completed = %d\n", __func__, wsi,
- ah->rxpos, ah->rxlen, wsi->hdr_parsing_completed);
- return 0;
- }
-
- lws_pt_lock(pt);
-
- /* we did have an ah attached */
- time(&now);
- if (ah->assigned && now - ah->assigned > 3) {
- /*
- * we're detaching the ah, but it was held an
- * unreasonably long time
- */
- lwsl_debug("%s: wsi %p: ah held %ds, "
- "ah.rxpos %d, ah.rxlen %d, mode/state %d %d,"
- "wsi->more_rx_waiting %d\n", __func__, wsi,
- (int)(now - ah->assigned),
- ah->rxpos, ah->rxlen, wsi->mode, wsi->state,
- wsi->more_rx_waiting);
- }
-
- ah->assigned = 0;
-
- /* if we think we're detaching one, there should be one in use */
- assert(pt->ah_count_in_use > 0);
- /* and this specific one should have been in use */
- assert(ah->in_use);
- wsi->u.hdr.ah = NULL;
- ah->wsi = NULL; /* no owner */
-#if defined(LWS_WITH_PEER_LIMITS)
- lws_peer_track_ah_detach(context, wsi->peer);
-#endif
-
- pwsi = &pt->ah_wait_list;
-
- /* oh there is nobody on the waiting list... leave the ah unattached */
- if (!*pwsi)
- goto nobody_usable_waiting;
-
- /*
- * at least one wsi on the same tsi is waiting, give it to oldest guy
- * who is allowed to take it (if any)
- */
- lwsl_info("pt wait list %p\n", *pwsi);
- wsi = NULL;
- pwsi_eligible = NULL;
-
- while (*pwsi) {
-#if defined(LWS_WITH_PEER_LIMITS)
- /* are we willing to give this guy an ah? */
- if (!lws_peer_confirm_ah_attach_ok(context, (*pwsi)->peer))
-#endif
- {
- wsi = *pwsi;
- pwsi_eligible = pwsi;
- }
-#if defined(LWS_WITH_PEER_LIMITS)
- else
- if (!(*pwsi)->u.hdr.ah_wait_list)
- lws_stats_atomic_bump(context, pt,
- LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
-#endif
- pwsi = &(*pwsi)->u.hdr.ah_wait_list;
- }
-
- if (!wsi) /* everybody waiting already has too many ah... */
- goto nobody_usable_waiting;
-
- lwsl_info("%s: last eligible wsi in wait list %p\n", __func__, wsi);
-
- wsi->u.hdr.ah = ah;
- ah->wsi = wsi; /* new owner */
-
- /* and reset the rx state */
- ah->rxpos = 0;
- ah->rxlen = 0;
- lws_header_table_reset(wsi, autoservice);
-#if defined(LWS_WITH_PEER_LIMITS)
- if (wsi->peer)
- wsi->peer->count_ah++;
-#endif
-
- /* clients acquire the ah and then insert themselves in fds table... */
- if (wsi->position_in_fds_table != -1) {
- lwsl_info("%s: Enabling %p POLLIN\n", __func__, wsi);
-
- /* he has been stuck waiting for an ah, but now his wait is
- * over, let him progress */
-
- _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
- }
-
- /* point prev guy to next guy in list instead */
- *pwsi_eligible = wsi->u.hdr.ah_wait_list;
- /* the guy who got one is out of the list */
- wsi->u.hdr.ah_wait_list = NULL;
- pt->ah_wait_list_length--;
-
-#ifndef LWS_NO_CLIENT
- if (wsi->state == LWSS_CLIENT_UNCONNECTED) {
- lws_pt_unlock(pt);
-
- if (!lws_client_connect_via_info2(wsi)) {
- /* our client connect has failed, the wsi
- * has been closed
- */
-
- return -1;
- }
- return 0;
- }
-#endif
-
- assert(!!pt->ah_wait_list_length == !!(lws_intptr_t)pt->ah_wait_list);
-bail:
- lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
- (void *)wsi, (void *)ah, pt->tid, pt->ah_count_in_use);
-
- lws_pt_unlock(pt);
-
- return 0;
-
-nobody_usable_waiting:
- lwsl_info("%s: nobody usable waiting\n", __func__);
- _lws_destroy_ah(pt, ah);
- pt->ah_count_in_use--;
-
- goto bail;
-}
-
-LWS_VISIBLE int
-lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx)
-{
- int n;
-
- if (!wsi->u.hdr.ah)
- return 0;
-
- n = wsi->u.hdr.ah->frag_index[h];
- if (!n)
- return 0;
- do {
- if (!frag_idx)
- return wsi->u.hdr.ah->frags[n].len;
- n = wsi->u.hdr.ah->frags[n].nfrag;
- } while (frag_idx-- && n);
-
- return 0;
-}
-
-LWS_VISIBLE int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h)
-{
- int n;
- int len = 0;
-
- if (!wsi->u.hdr.ah)
- return 0;
-
- n = wsi->u.hdr.ah->frag_index[h];
- if (!n)
- return 0;
- do {
- len += wsi->u.hdr.ah->frags[n].len;
- n = wsi->u.hdr.ah->frags[n].nfrag;
- } while (n);
-
- return len;
-}
-
-LWS_VISIBLE int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len,
- enum lws_token_indexes h, int frag_idx)
-{
- int n = 0;
- int f;
-
- if (!wsi->u.hdr.ah)
- return -1;
-
- f = wsi->u.hdr.ah->frag_index[h];
-
- if (!f)
- return -1;
-
- while (n < frag_idx) {
- f = wsi->u.hdr.ah->frags[f].nfrag;
- if (!f)
- return -1;
- n++;
- }
-
- if (wsi->u.hdr.ah->frags[f].len >= len)
- return -1;
-
- memcpy(dst, wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[f].offset,
- wsi->u.hdr.ah->frags[f].len);
- dst[wsi->u.hdr.ah->frags[f].len] = '\0';
-
- return wsi->u.hdr.ah->frags[f].len;
-}
-
-LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len,
- enum lws_token_indexes h)
-{
- int toklen = lws_hdr_total_length(wsi, h);
- int n;
-
- if (toklen >= len)
- return -1;
-
- if (!wsi->u.hdr.ah)
- return -1;
-
- n = wsi->u.hdr.ah->frag_index[h];
- if (!n)
- return 0;
-
- do {
- if (wsi->u.hdr.ah->frags[n].len >= len)
- return -1;
- strncpy(dst, &wsi->u.hdr.ah->data[wsi->u.hdr.ah->frags[n].offset],
- wsi->u.hdr.ah->frags[n].len);
- dst += wsi->u.hdr.ah->frags[n].len;
- len -= wsi->u.hdr.ah->frags[n].len;
- n = wsi->u.hdr.ah->frags[n].nfrag;
- } while (n);
- *dst = '\0';
-
- return toklen;
-}
-
-char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)
-{
- int n;
-
- n = wsi->u.hdr.ah->frag_index[h];
- if (!n)
- return NULL;
-
- return wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[n].offset;
-}
-
-int LWS_WARN_UNUSED_RESULT
-lws_pos_in_bounds(struct lws *wsi)
-{
- if (wsi->u.hdr.ah->pos <
- (unsigned int)wsi->context->max_http_header_data)
- return 0;
-
- if (wsi->u.hdr.ah->pos == wsi->context->max_http_header_data) {
- lwsl_err("Ran out of header data space\n");
- return 1;
- }
-
- /*
- * with these tests everywhere, it should never be able to exceed
- * the limit, only meet it
- */
- lwsl_err("%s: pos %d, limit %d\n", __func__, wsi->u.hdr.ah->pos,
- wsi->context->max_http_header_data);
- assert(0);
-
- return 1;
-}
-
-int LWS_WARN_UNUSED_RESULT
-lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
-{
- wsi->u.hdr.ah->nfrag++;
- if (wsi->u.hdr.ah->nfrag == ARRAY_SIZE(wsi->u.hdr.ah->frags)) {
- lwsl_warn("More hdr frags than we can deal with, dropping\n");
- return -1;
- }
-
- wsi->u.hdr.ah->frag_index[h] = wsi->u.hdr.ah->nfrag;
-
- wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].offset = wsi->u.hdr.ah->pos;
- wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len = 0;
- wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].nfrag = 0;
-
- do {
- if (lws_pos_in_bounds(wsi))
- return -1;
-
- wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = *s;
- if (*s)
- wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++;
- } while (*s++);
-
- return 0;
-}
-
-signed char char_to_hex(const char c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
-
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
-
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
-
- return -1;
-}
-
-static int LWS_WARN_UNUSED_RESULT
-issue_char(struct lws *wsi, unsigned char c)
-{
- unsigned short frag_len;
-
- if (lws_pos_in_bounds(wsi))
- return -1;
-
- frag_len = wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len;
- /*
- * If we haven't hit the token limit, just copy the character into
- * the header
- */
- if (frag_len < wsi->u.hdr.current_token_limit) {
- wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = c;
- if (c)
- wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++;
- return 0;
- }
-
- /* Insert a null character when we *hit* the limit: */
- if (frag_len == wsi->u.hdr.current_token_limit) {
- if (lws_pos_in_bounds(wsi))
- return -1;
-
- wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = '\0';
- lwsl_warn("header %i exceeds limit %d\n",
- wsi->u.hdr.parser_state,
- wsi->u.hdr.current_token_limit);
- }
-
- return 1;
-}
-
-int
-lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
-{
- struct allocated_headers *ah = wsi->u.hdr.ah;
- unsigned int enc = 0;
- uint8_t c = *_c;
-
- /*
- * PRIORITY 1
- * special URI processing... convert %xx
- */
- switch (wsi->u.hdr.ues) {
- case URIES_IDLE:
- if (c == '%') {
- wsi->u.hdr.ues = URIES_SEEN_PERCENT;
- goto swallow;
- }
- break;
- case URIES_SEEN_PERCENT:
- if (char_to_hex(c) < 0)
- /* illegal post-% char */
- goto forbid;
-
- wsi->u.hdr.esc_stash = c;
- wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1;
- goto swallow;
-
- case URIES_SEEN_PERCENT_H1:
- if (char_to_hex(c) < 0)
- /* illegal post-% char */
- goto forbid;
-
- *_c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) |
- char_to_hex(c);
- c = *_c;
- enc = 1;
- wsi->u.hdr.ues = URIES_IDLE;
- break;
- }
-
- /*
- * PRIORITY 2
- * special URI processing...
- * convert /.. or /... or /../ etc to /
- * convert /./ to /
- * convert // or /// etc to /
- * leave /.dir or whatever alone
- */
-
- switch (wsi->u.hdr.ups) {
- case URIPS_IDLE:
- if (!c)
- return -1;
- /* genuine delimiter */
- if ((c == '&' || c == ';') && !enc) {
- if (issue_char(wsi, c) < 0)
- return -1;
- /* swallow the terminator */
- ah->frags[ah->nfrag].len--;
- /* link to next fragment */
- ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
- ah->nfrag++;
- if (ah->nfrag >= ARRAY_SIZE(ah->frags))
- goto excessive;
- /* start next fragment after the & */
- wsi->u.hdr.post_literal_equal = 0;
- ah->frags[ah->nfrag].offset = ah->pos;
- ah->frags[ah->nfrag].len = 0;
- ah->frags[ah->nfrag].nfrag = 0;
- goto swallow;
- }
- /* uriencoded = in the name part, disallow */
- if (c == '=' && enc &&
- ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
- !wsi->u.hdr.post_literal_equal) {
- c = '_';
- *_c =c;
- }
-
- /* after the real =, we don't care how many = */
- if (c == '=' && !enc)
- wsi->u.hdr.post_literal_equal = 1;
-
- /* + to space */
- if (c == '+' && !enc) {
- c = ' ';
- *_c = c;
- }
- /* issue the first / always */
- if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
- wsi->u.hdr.ups = URIPS_SEEN_SLASH;
- break;
- case URIPS_SEEN_SLASH:
- /* swallow subsequent slashes */
- if (c == '/')
- goto swallow;
- /* track and swallow the first . after / */
- if (c == '.') {
- wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT;
- goto swallow;
- }
- wsi->u.hdr.ups = URIPS_IDLE;
- break;
- case URIPS_SEEN_SLASH_DOT:
- /* swallow second . */
- if (c == '.') {
- wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT;
- goto swallow;
- }
- /* change /./ to / */
- if (c == '/') {
- wsi->u.hdr.ups = URIPS_SEEN_SLASH;
- goto swallow;
- }
- /* it was like /.dir ... regurgitate the . */
- wsi->u.hdr.ups = URIPS_IDLE;
- if (issue_char(wsi, '.') < 0)
- return -1;
- break;
-
- case URIPS_SEEN_SLASH_DOT_DOT:
-
- /* /../ or /..[End of URI] --> backup to last / */
- if (c == '/' || c == '?') {
- /*
- * back up one dir level if possible
- * safe against header fragmentation because
- * the method URI can only be in 1 fragment
- */
- if (ah->frags[ah->nfrag].len > 2) {
- ah->pos--;
- ah->frags[ah->nfrag].len--;
- do {
- ah->pos--;
- ah->frags[ah->nfrag].len--;
- } while (ah->frags[ah->nfrag].len > 1 &&
- ah->data[ah->pos] != '/');
- }
- wsi->u.hdr.ups = URIPS_SEEN_SLASH;
- if (ah->frags[ah->nfrag].len > 1)
- break;
- goto swallow;
- }
-
- /* /..[^/] ... regurgitate and allow */
-
- if (issue_char(wsi, '.') < 0)
- return -1;
- if (issue_char(wsi, '.') < 0)
- return -1;
- wsi->u.hdr.ups = URIPS_IDLE;
- break;
- }
-
- if (c == '?' && !enc &&
- !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */
- if (wsi->u.hdr.ues != URIES_IDLE)
- goto forbid;
-
- /* seal off uri header */
- if (issue_char(wsi, '\0') < 0)
- return -1;
-
- /* move to using WSI_TOKEN_HTTP_URI_ARGS */
- ah->nfrag++;
- if (ah->nfrag >= ARRAY_SIZE(ah->frags))
- goto excessive;
- ah->frags[ah->nfrag].offset = ah->pos;
- ah->frags[ah->nfrag].len = 0;
- ah->frags[ah->nfrag].nfrag = 0;
-
- wsi->u.hdr.post_literal_equal = 0;
- ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
- wsi->u.hdr.ups = URIPS_IDLE;
- goto swallow;
- }
-
- return LPUR_CONTINUE;
-
-swallow:
- return LPUR_SWALLOW;
-
-forbid:
- return LPUR_FORBID;
-
-excessive:
- return LPUR_EXCESSIVE;
-}
-
-static const unsigned char methods[] = {
- WSI_TOKEN_GET_URI,
- WSI_TOKEN_POST_URI,
- WSI_TOKEN_OPTIONS_URI,
- WSI_TOKEN_PUT_URI,
- WSI_TOKEN_PATCH_URI,
- WSI_TOKEN_DELETE_URI,
- WSI_TOKEN_CONNECT,
- WSI_TOKEN_HEAD_URI,
-};
-
-int LWS_WARN_UNUSED_RESULT
-lws_parse(struct lws *wsi, unsigned char c)
-{
- struct allocated_headers *ah = wsi->u.hdr.ah;
- struct lws_context *context = wsi->context;
- unsigned int n, m;
- int r;
-
- assert(wsi->u.hdr.ah);
-
- switch (wsi->u.hdr.parser_state) {
- default:
-
- lwsl_parser("WSI_TOK_(%d) '%c'\n", wsi->u.hdr.parser_state, c);
-
- /* collect into malloc'd buffers */
- /* optional initial space swallow */
- if (!ah->frags[ah->frag_index[wsi->u.hdr.parser_state]].len &&
- c == ' ')
- break;
-
- for (m = 0; m < ARRAY_SIZE(methods); m++)
- if (wsi->u.hdr.parser_state == methods[m])
- break;
- if (m == ARRAY_SIZE(methods))
- /* it was not any of the methods */
- goto check_eol;
-
- /* special URI processing... end at space */
-
- if (c == ' ') {
- /* enforce starting with / */
- if (!ah->frags[ah->nfrag].len)
- if (issue_char(wsi, '/') < 0)
- return -1;
-
- if (wsi->u.hdr.ups == URIPS_SEEN_SLASH_DOT_DOT) {
- /*
- * back up one dir level if possible
- * safe against header fragmentation because
- * the method URI can only be in 1 fragment
- */
- if (ah->frags[ah->nfrag].len > 2) {
- ah->pos--;
- ah->frags[ah->nfrag].len--;
- do {
- ah->pos--;
- ah->frags[ah->nfrag].len--;
- } while (ah->frags[ah->nfrag].len > 1 &&
- ah->data[ah->pos] != '/');
- }
- }
-
- /* begin parsing HTTP version: */
- if (issue_char(wsi, '\0') < 0)
- return -1;
- wsi->u.hdr.parser_state = WSI_TOKEN_HTTP;
- goto start_fragment;
- }
-
- r = lws_parse_urldecode(wsi, &c);
- switch (r) {
- case LPUR_CONTINUE:
- break;
- case LPUR_SWALLOW:
- goto swallow;
- case LPUR_FORBID:
- goto forbid;
- case LPUR_EXCESSIVE:
- goto excessive;
- default:
- return -1;
- }
-check_eol:
- /* bail at EOL */
- if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE &&
- c == '\x0d') {
- if (wsi->u.hdr.ues != URIES_IDLE)
- goto forbid;
-
- c = '\0';
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
- lwsl_parser("*\n");
- }
-
- n = issue_char(wsi, c);
- if ((int)n < 0)
- return -1;
- if (n > 0)
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
-
-swallow:
- /* per-protocol end of headers management */
-
- if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE)
- goto set_parsing_complete;
- break;
-
- /* collecting and checking a name part */
- case WSI_TOKEN_NAME_PART:
- lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X (mode=%d) wsi->u.hdr.lextable_pos=%d\n", c, c, wsi->mode, wsi->u.hdr.lextable_pos);
-
- wsi->u.hdr.lextable_pos =
- lextable_decode(wsi->u.hdr.lextable_pos, c);
- /*
- * Server needs to look out for unknown methods...
- */
- if (wsi->u.hdr.lextable_pos < 0 &&
- (wsi->mode == LWSCM_HTTP_SERVING)) {
- /* this is not a header we know about */
- for (m = 0; m < ARRAY_SIZE(methods); m++)
- if (ah->frag_index[methods[m]]) {
- /*
- * already had the method, no idea what
- * this crap from the client is, ignore
- */
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
- break;
- }
- /*
- * hm it's an unknown http method from a client in fact,
- * it cannot be valid http
- */
- if (m == ARRAY_SIZE(methods)) {
- /*
- * are we set up to accept raw in these cases?
- */
- if (lws_check_opt(wsi->vhost->options,
- LWS_SERVER_OPTION_FALLBACK_TO_RAW))
- return 2; /* transition to raw */
-
- lwsl_info("Unknown method - dropping\n");
- goto forbid;
- }
- break;
- }
- /*
- * ...otherwise for a client, let him ignore unknown headers
- * coming from the server
- */
- if (wsi->u.hdr.lextable_pos < 0) {
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
- break;
- }
-
- if (lextable[wsi->u.hdr.lextable_pos] < FAIL_CHAR) {
- /* terminal state */
-
- n = ((unsigned int)lextable[wsi->u.hdr.lextable_pos] << 8) |
- lextable[wsi->u.hdr.lextable_pos + 1];
-
- lwsl_parser("known hdr %d\n", n);
- for (m = 0; m < ARRAY_SIZE(methods); m++)
- if (n == methods[m] &&
- ah->frag_index[methods[m]]) {
- lwsl_warn("Duplicated method\n");
- return -1;
- }
-
- /*
- * WSORIGIN is protocol equiv to ORIGIN,
- * JWebSocket likes to send it, map to ORIGIN
- */
- if (n == WSI_TOKEN_SWORIGIN)
- n = WSI_TOKEN_ORIGIN;
-
- wsi->u.hdr.parser_state = (enum lws_token_indexes)
- (WSI_TOKEN_GET_URI + n);
-
- if (context->token_limits)
- wsi->u.hdr.current_token_limit =
- context->token_limits->token_limit[
- wsi->u.hdr.parser_state];
- else
- wsi->u.hdr.current_token_limit =
- wsi->context->max_http_header_data;
-
- if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE)
- goto set_parsing_complete;
-
- goto start_fragment;
- }
- break;
-
-start_fragment:
- ah->nfrag++;
-excessive:
- if (ah->nfrag == ARRAY_SIZE(ah->frags)) {
- lwsl_warn("More hdr frags than we can deal with\n");
- return -1;
- }
-
- ah->frags[ah->nfrag].offset = ah->pos;
- ah->frags[ah->nfrag].len = 0;
- ah->frags[ah->nfrag].nfrag = 0;
- ah->frags[ah->nfrag].flags = 2;
-
- n = ah->frag_index[wsi->u.hdr.parser_state];
- if (!n) { /* first fragment */
- ah->frag_index[wsi->u.hdr.parser_state] = ah->nfrag;
- ah->hdr_token_idx = wsi->u.hdr.parser_state;
- break;
- }
- /* continuation */
- while (ah->frags[n].nfrag)
- n = ah->frags[n].nfrag;
- ah->frags[n].nfrag = ah->nfrag;
-
- if (issue_char(wsi, ' ') < 0)
- return -1;
- break;
-
- /* skipping arg part of a name we didn't recognize */
- case WSI_TOKEN_SKIPPING:
- lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c);
-
- if (c == '\x0d')
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
- break;
-
- case WSI_TOKEN_SKIPPING_SAW_CR:
- lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
- if (wsi->u.hdr.ues != URIES_IDLE)
- goto forbid;
- if (c == '\x0a') {
- wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
- wsi->u.hdr.lextable_pos = 0;
- } else
- wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
- break;
- /* we're done, ignore anything else */
-
- case WSI_PARSING_COMPLETE:
- lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c);
- break;
- }
-
- return 0;
-
-set_parsing_complete:
- if (wsi->u.hdr.ues != URIES_IDLE)
- goto forbid;
- if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
- if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
- wsi->ietf_spec_revision =
- atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
-
- lwsl_parser("v%02d hdrs completed\n", wsi->ietf_spec_revision);
- }
- wsi->u.hdr.parser_state = WSI_PARSING_COMPLETE;
- wsi->hdr_parsing_completed = 1;
-
- return 0;
-
-forbid:
- lwsl_notice(" forbidding on uri sanitation\n");
- lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
-
- return -1;
-}
-
-LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi)
-{
- return wsi->u.ws.frame_is_binary;
-}
-
-void
-lws_add_wsi_to_draining_ext_list(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-
- if (wsi->u.ws.rx_draining_ext)
- return;
-
- lwsl_ext("%s: RX EXT DRAINING: Adding to list\n", __func__);
-
- wsi->u.ws.rx_draining_ext = 1;
- wsi->u.ws.rx_draining_ext_list = pt->rx_draining_ext_list;
- pt->rx_draining_ext_list = wsi;
-}
-
-void
-lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- struct lws **w = &pt->rx_draining_ext_list;
-
- if (!wsi->u.ws.rx_draining_ext)
- return;
-
- lwsl_ext("%s: RX EXT DRAINING: Removing from list\n", __func__);
-
- wsi->u.ws.rx_draining_ext = 0;
-
- /* remove us from context draining ext list */
- while (*w) {
- if (*w == wsi) {
- /* if us, point it instead to who we were pointing to */
- *w = wsi->u.ws.rx_draining_ext_list;
- break;
- }
- w = &((*w)->u.ws.rx_draining_ext_list);
- }
- wsi->u.ws.rx_draining_ext_list = NULL;
-}
-
-/*
- * client-parser.c: lws_client_rx_sm() needs to be roughly kept in
- * sync with changes here, esp related to ext draining
- */
-
-int
-lws_rx_sm(struct lws *wsi, unsigned char c)
-{
- int callback_action = LWS_CALLBACK_RECEIVE;
- int ret = 0, n, rx_draining_ext = 0;
- struct lws_tokens eff_buf;
-
- eff_buf.token = NULL;
- eff_buf.token_len = 0;
- if (wsi->socket_is_permanently_unusable)
- return -1;
-
- switch (wsi->lws_rx_parse_state) {
- case LWS_RXPS_NEW:
- if (wsi->u.ws.rx_draining_ext) {
- eff_buf.token = NULL;
- eff_buf.token_len = 0;
- lws_remove_wsi_from_draining_ext_list(wsi);
- rx_draining_ext = 1;
- lwsl_debug("%s: doing draining flow\n", __func__);
-
- goto drain_extension;
- }
- switch (wsi->ietf_spec_revision) {
- case 13:
- /*
- * no prepended frame key any more
- */
- wsi->u.ws.all_zero_nonce = 1;
- goto handle_first;
-
- default:
- lwsl_warn("lws_rx_sm: unknown spec version %d\n",
- wsi->ietf_spec_revision);
- break;
- }
- break;
- case LWS_RXPS_04_mask_1:
- wsi->u.ws.mask[1] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2;
- break;
- case LWS_RXPS_04_mask_2:
- wsi->u.ws.mask[2] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3;
- break;
- case LWS_RXPS_04_mask_3:
- wsi->u.ws.mask[3] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
-
- /*
- * start from the zero'th byte in the XOR key buffer since
- * this is the start of a frame with a new key
- */
-
- wsi->u.ws.mask_idx = 0;
-
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1;
- break;
-
- /*
- * 04 logical framing from the spec (all this is masked when incoming
- * and has to be unmasked)
- *
- * We ignore the possibility of extension data because we don't
- * negotiate any extensions at the moment.
- *
- * 0 1 2 3
- * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- * +-+-+-+-+-------+-+-------------+-------------------------------+
- * |F|R|R|R| opcode|R| Payload len | Extended payload length |
- * |I|S|S|S| (4) |S| (7) | (16/63) |
- * |N|V|V|V| |V| | (if payload len==126/127) |
- * | |1|2|3| |4| | |
- * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
- * | Extended payload length continued, if payload len == 127 |
- * + - - - - - - - - - - - - - - - +-------------------------------+
- * | | Extension data |
- * +-------------------------------+ - - - - - - - - - - - - - - - +
- * : :
- * +---------------------------------------------------------------+
- * : Application data :
- * +---------------------------------------------------------------+
- *
- * We pass payload through to userland as soon as we get it, ignoring
- * FIN. It's up to userland to buffer it up if it wants to see a
- * whole unfragmented block of the original size (which may be up to
- * 2^63 long!)
- */
-
- case LWS_RXPS_04_FRAME_HDR_1:
-handle_first:
-
- wsi->u.ws.opcode = c & 0xf;
- wsi->u.ws.rsv = c & 0x70;
- wsi->u.ws.final = !!((c >> 7) & 1);
-
- switch (wsi->u.ws.opcode) {
- case LWSWSOPC_TEXT_FRAME:
- case LWSWSOPC_BINARY_FRAME:
- wsi->u.ws.rsv_first_msg = (c & 0x70);
- wsi->u.ws.frame_is_binary =
- wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME;
- wsi->u.ws.first_fragment = 1;
- break;
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 0xb:
- case 0xc:
- case 0xd:
- case 0xe:
- case 0xf:
- lwsl_info("illegal opcode\n");
- return -1;
- }
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN:
-
- wsi->u.ws.this_frame_masked = !!(c & 0x80);
-
- switch (c & 0x7f) {
- case 126:
- /* control frames are not allowed to have big lengths */
- if (wsi->u.ws.opcode & 8)
- goto illegal_ctl_length;
-
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
- break;
- case 127:
- /* control frames are not allowed to have big lengths */
- if (wsi->u.ws.opcode & 8)
- goto illegal_ctl_length;
-
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
- break;
- default:
- wsi->u.ws.rx_packet_length = c & 0x7f;
- if (wsi->u.ws.this_frame_masked)
- wsi->lws_rx_parse_state =
- LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else
- if (wsi->u.ws.rx_packet_length)
- wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
- else {
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
- break;
- }
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN16_2:
- wsi->u.ws.rx_packet_length = c << 8;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN16_1:
- wsi->u.ws.rx_packet_length |= c;
- if (wsi->u.ws.this_frame_masked)
- wsi->lws_rx_parse_state =
- LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else
- wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_8:
- if (c & 0x80) {
- lwsl_warn("b63 of length must be zero\n");
- /* kill the connection */
- return -1;
- }
-#if defined __LP64__
- wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
-#else
- wsi->u.ws.rx_packet_length = 0;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_7:
-#if defined __LP64__
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_6:
-#if defined __LP64__
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_5:
-#if defined __LP64__
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_4:
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_3:
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_2:
- wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_1:
- wsi->u.ws.rx_packet_length |= ((size_t)c);
- if (wsi->u.ws.this_frame_masked)
- wsi->lws_rx_parse_state =
- LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else
- wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
- wsi->u.ws.mask[0] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
- wsi->u.ws.mask[1] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
- wsi->u.ws.mask[2] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
- wsi->u.ws.mask[3] = c;
- if (c)
- wsi->u.ws.all_zero_nonce = 0;
- wsi->lws_rx_parse_state =
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
- wsi->u.ws.mask_idx = 0;
- if (wsi->u.ws.rx_packet_length == 0) {
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
- break;
-
-
- case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
- assert(wsi->u.ws.rx_ubuf);
-
- if (wsi->u.ws.rx_draining_ext)
- goto drain_extension;
-
- if (wsi->u.ws.rx_ubuf_head + LWS_PRE >=
- wsi->u.ws.rx_ubuf_alloc) {
- lwsl_err("Attempted overflow \n");
- return -1;
- }
- if (wsi->u.ws.all_zero_nonce)
- wsi->u.ws.rx_ubuf[LWS_PRE +
- (wsi->u.ws.rx_ubuf_head++)] = c;
- else
- wsi->u.ws.rx_ubuf[LWS_PRE +
- (wsi->u.ws.rx_ubuf_head++)] =
- c ^ wsi->u.ws.mask[
- (wsi->u.ws.mask_idx++) & 3];
-
- if (--wsi->u.ws.rx_packet_length == 0) {
- /* spill because we have the whole frame */
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
-
- /*
- * if there's no protocol max frame size given, we are
- * supposed to default to context->pt_serv_buf_size
- */
- if (!wsi->protocol->rx_buffer_size &&
- wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size)
- break;
-
- if (wsi->protocol->rx_buffer_size &&
- wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size)
- break;
-
- /* spill because we filled our rx buffer */
-spill:
- /*
- * is this frame a control packet we should take care of at this
- * layer? If so service it and hide it from the user callback
- */
-
- lwsl_parser("spill on %s\n", wsi->protocol->name);
-
- switch (wsi->u.ws.opcode) {
- case LWSWSOPC_CLOSE:
-
- /* is this an acknowledgement of our close? */
- if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
- /*
- * fine he has told us he is closing too, let's
- * finish our close
- */
- lwsl_parser("seen client close ack\n");
- return -1;
- }
- if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
- /* if he sends us 2 CLOSE, kill him */
- return -1;
-
- if (lws_partial_buffered(wsi)) {
- /*
- * if we're in the middle of something,
- * we can't do a normal close response and
- * have to just close our end.
- */
- wsi->socket_is_permanently_unusable = 1;
- lwsl_parser("Closing on peer close due to Pending tx\n");
- return -1;
- }
-
- if (user_callback_handle_rxflow(
- wsi->protocol->callback, wsi,
- LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
- wsi->user_space,
- &wsi->u.ws.rx_ubuf[LWS_PRE],
- wsi->u.ws.rx_ubuf_head))
- return -1;
-
- lwsl_parser("server sees client close packet\n");
- wsi->state = LWSS_RETURNED_CLOSE_ALREADY;
- /* deal with the close packet contents as a PONG */
- wsi->u.ws.payload_is_close = 1;
- goto process_as_ping;
-
- case LWSWSOPC_PING:
- lwsl_info("received %d byte ping, sending pong\n",
- wsi->u.ws.rx_ubuf_head);
-
- if (wsi->u.ws.ping_pending_flag) {
- /*
- * there is already a pending ping payload
- * we should just log and drop
- */
- lwsl_parser("DROP PING since one pending\n");
- goto ping_drop;
- }
-process_as_ping:
- /* control packets can only be < 128 bytes long */
- if (wsi->u.ws.rx_ubuf_head > 128 - 3) {
- lwsl_parser("DROP PING payload too large\n");
- goto ping_drop;
- }
-
- /* stash the pong payload */
- memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE,
- &wsi->u.ws.rx_ubuf[LWS_PRE],
- wsi->u.ws.rx_ubuf_head);
-
- wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head;
- wsi->u.ws.ping_pending_flag = 1;
-
- /* get it sent as soon as possible */
- lws_callback_on_writable(wsi);
-ping_drop:
- wsi->u.ws.rx_ubuf_head = 0;
- return 0;
-
- case LWSWSOPC_PONG:
- lwsl_info("received pong\n");
- lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE],
- wsi->u.ws.rx_ubuf_head);
-
- if (wsi->pending_timeout == PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
- lwsl_info("received expected PONG on wsi %p\n", wsi);
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
- }
-
- /* issue it */
- callback_action = LWS_CALLBACK_RECEIVE_PONG;
- break;
-
- case LWSWSOPC_TEXT_FRAME:
- case LWSWSOPC_BINARY_FRAME:
- case LWSWSOPC_CONTINUATION:
- break;
-
- default:
- lwsl_parser("passing opc %x up to exts\n",
- wsi->u.ws.opcode);
- /*
- * It's something special we can't understand here.
- * Pass the payload up to the extension's parsing
- * state machine.
- */
-
- eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
- eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
-
- if (lws_ext_cb_active(wsi, LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
- &eff_buf, 0) <= 0)
- /* not handle or fail */
- lwsl_ext("ext opc opcode 0x%x unknown\n",
- wsi->u.ws.opcode);
-
- wsi->u.ws.rx_ubuf_head = 0;
- return 0;
- }
-
- /*
- * No it's real payload, pass it up to the user callback.
- * It's nicely buffered with the pre-padding taken care of
- * so it can be sent straight out again using lws_write
- */
-
- eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
- eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
-
- if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len)
- goto already_done;
-
-drain_extension:
- lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len);
-
- if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
- wsi->state == LWSS_AWAITING_CLOSE_ACK)
- goto already_done;
-
- n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
- /*
- * eff_buf may be pointing somewhere completely different now,
- * it's the output
- */
- wsi->u.ws.first_fragment = 0;
- if (n < 0) {
- /*
- * we may rely on this to get RX, just drop connection
- */
- wsi->socket_is_permanently_unusable = 1;
- return -1;
- }
-
- if (rx_draining_ext && eff_buf.token_len == 0)
- goto already_done;
-
- if (n && eff_buf.token_len)
- /* extension had more... main loop will come back */
- lws_add_wsi_to_draining_ext_list(wsi);
- else
- lws_remove_wsi_from_draining_ext_list(wsi);
-
- if (eff_buf.token_len > 0 ||
- callback_action == LWS_CALLBACK_RECEIVE_PONG) {
- eff_buf.token[eff_buf.token_len] = '\0';
-
- if (wsi->protocol->callback) {
-
- if (callback_action == LWS_CALLBACK_RECEIVE_PONG)
- lwsl_info("Doing pong callback\n");
-
- ret = user_callback_handle_rxflow(
- wsi->protocol->callback,
- wsi,
- (enum lws_callback_reasons)callback_action,
- wsi->user_space,
- eff_buf.token,
- eff_buf.token_len);
- }
- else
- lwsl_err("No callback on payload spill!\n");
- }
-
-already_done:
- wsi->u.ws.rx_ubuf_head = 0;
- break;
- }
-
- return ret;
-
-illegal_ctl_length:
-
- lwsl_warn("Control frame with xtended length is illegal\n");
- /* kill the connection */
- return -1;
-}
-
-LWS_VISIBLE size_t
-lws_remaining_packet_payload(struct lws *wsi)
-{
- return wsi->u.ws.rx_packet_length;
-}
-
-/* Once we reach LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED, we know how much
- * to expect in that state and can deal with it in bulk more efficiently.
- */
-
-int
-lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf,
- size_t *len)
-{
- unsigned char *buffer = *buf, mask[4];
- int buffer_size, n;
- unsigned int avail;
- char *rx_ubuf;
-
- if (wsi->protocol->rx_buffer_size)
- buffer_size = wsi->protocol->rx_buffer_size;
- else
- buffer_size = wsi->context->pt_serv_buf_size;
- avail = buffer_size - wsi->u.ws.rx_ubuf_head;
-
- /* do not consume more than we should */
- if (avail > wsi->u.ws.rx_packet_length)
- avail = wsi->u.ws.rx_packet_length;
-
- /* do not consume more than what is in the buffer */
- if (avail > *len)
- avail = *len;
-
- /* we want to leave 1 byte for the parser to handle properly */
- if (avail <= 1)
- return 0;
-
- avail--;
- rx_ubuf = wsi->u.ws.rx_ubuf + LWS_PRE + wsi->u.ws.rx_ubuf_head;
- if (wsi->u.ws.all_zero_nonce)
- memcpy(rx_ubuf, buffer, avail);
- else {
-
- for (n = 0; n < 4; n++)
- mask[n] = wsi->u.ws.mask[(wsi->u.ws.mask_idx + n) & 3];
-
- /* deal with 4-byte chunks using unwrapped loop */
- n = avail >> 2;
- while (n--) {
- *(rx_ubuf++) = *(buffer++) ^ mask[0];
- *(rx_ubuf++) = *(buffer++) ^ mask[1];
- *(rx_ubuf++) = *(buffer++) ^ mask[2];
- *(rx_ubuf++) = *(buffer++) ^ mask[3];
- }
- /* and the remaining bytes bytewise */
- for (n = 0; n < (int)(avail & 3); n++)
- *(rx_ubuf++) = *(buffer++) ^ mask[n];
-
- wsi->u.ws.mask_idx = (wsi->u.ws.mask_idx + avail) & 3;
- }
-
- (*buf) += avail;
- wsi->u.ws.rx_ubuf_head += avail;
- wsi->u.ws.rx_packet_length -= avail;
- *len -= avail;
-
- return avail;
-}
diff --git a/thirdparty/lws/server/ranges.c b/thirdparty/lws/server/ranges.c
deleted file mode 100644
index bc1578d733..0000000000
--- a/thirdparty/lws/server/ranges.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * RFC7233 ranges parser
- *
- * Copyright (C) 2016 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-/*
- * RFC7233 examples
- *
- * o The first 500 bytes (byte offsets 0-499, inclusive):
- *
- * bytes=0-499
- *
- * o The second 500 bytes (byte offsets 500-999, inclusive):
- *
- * bytes=500-999
- *
- * o The final 500 bytes (byte offsets 9500-9999, inclusive):
- *
- * bytes=-500
- *
- * Or:
- *
- * bytes=9500-
- *
- * o The first and last bytes only (bytes 0 and 9999):
- *
- * bytes=0-0,-1
- *
- * o Other valid (but not canonical) specifications of the second 500
- * bytes (byte offsets 500-999, inclusive):
- *
- * bytes=500-600,601-999
- * bytes=500-700,601-999
- */
-
-/*
- * returns 1 if the range struct represents a usable range
- * if no ranges header, you get one of these for the whole
- * file. Otherwise you get one for each valid range in the
- * header.
- *
- * returns 0 if no further valid range forthcoming; rp->state
- * may be LWSRS_SYNTAX or LWSRS_COMPLETED
- */
-
-int
-lws_ranges_next(struct lws_range_parsing *rp)
-{
- static const char * const beq = "bytes=";
- char c;
-
- while (1) {
-
- c = rp->buf[rp->pos];
-
- switch (rp->state) {
- case LWSRS_SYNTAX:
- case LWSRS_COMPLETED:
- return 0;
-
- case LWSRS_NO_ACTIVE_RANGE:
- rp->state = LWSRS_COMPLETED;
- return 0;
-
- case LWSRS_BYTES_EQ: // looking for "bytes="
- if (c != beq[rp->pos]) {
- rp->state = LWSRS_SYNTAX;
- return -1;
- }
- if (rp->pos == 5)
- rp->state = LWSRS_FIRST;
- break;
-
- case LWSRS_FIRST:
- rp->start = 0;
- rp->end = 0;
- rp->start_valid = 0;
- rp->end_valid = 0;
-
- rp->state = LWSRS_STARTING;
-
- // fallthru
-
- case LWSRS_STARTING:
- if (c == '-') {
- rp->state = LWSRS_ENDING;
- break;
- }
-
- if (!(c >= '0' && c <= '9')) {
- rp->state = LWSRS_SYNTAX;
- return 0;
- }
- rp->start = (rp->start * 10) + (c - '0');
- rp->start_valid = 1;
- break;
-
- case LWSRS_ENDING:
- if (c == ',' || c == '\0') {
- rp->state = LWSRS_FIRST;
- if (c == ',')
- rp->pos++;
-
- /*
- * By the end of this, start and end are
- * always valid if the range still is
- */
-
- if (!rp->start_valid) { /* eg, -500 */
- if (rp->end > rp->extent)
- rp->end = rp->extent;
-
- rp->start = rp->extent - rp->end;
- rp->end = rp->extent - 1;
- } else
- if (!rp->end_valid)
- rp->end = rp->extent - 1;
-
- rp->did_try = 1;
-
- /* end must be >= start or ignore it */
- if (rp->end < rp->start) {
- if (c == ',')
- break;
- rp->state = LWSRS_COMPLETED;
- return 0;
- }
-
- return 1; /* issue range */
- }
-
- if (!(c >= '0' && c <= '9')) {
- rp->state = LWSRS_SYNTAX;
- return 0;
- }
- rp->end = (rp->end * 10) + (c - '0');
- rp->end_valid = 1;
- break;
- }
-
- rp->pos++;
- }
-}
-
-void
-lws_ranges_reset(struct lws_range_parsing *rp)
-{
- rp->pos = 0;
- rp->ctr = 0;
- rp->start = 0;
- rp->end = 0;
- rp->start_valid = 0;
- rp->end_valid = 0;
- rp->state = LWSRS_BYTES_EQ;
-}
-
-/*
- * returns count of valid ranges
- */
-int
-lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp,
- unsigned long long extent)
-{
- rp->agg = 0;
- rp->send_ctr = 0;
- rp->inside = 0;
- rp->count_ranges = 0;
- rp->did_try = 0;
- lws_ranges_reset(rp);
- rp->state = LWSRS_COMPLETED;
-
- rp->extent = extent;
-
- if (lws_hdr_copy(wsi, (char *)rp->buf, sizeof(rp->buf),
- WSI_TOKEN_HTTP_RANGE) <= 0)
- return 0;
-
- rp->state = LWSRS_BYTES_EQ;
-
- while (lws_ranges_next(rp)) {
- rp->count_ranges++;
- rp->agg += rp->end - rp->start + 1;
- }
-
- lwsl_debug("%s: count %d\n", __func__, rp->count_ranges);
- lws_ranges_reset(rp);
-
- if (rp->did_try && !rp->count_ranges)
- return -1; /* "not satisfiable */
-
- lws_ranges_next(rp);
-
- return rp->count_ranges;
-}
diff --git a/thirdparty/lws/server/server-handshake.c b/thirdparty/lws/server/server-handshake.c
deleted file mode 100644
index 3d319c35d6..0000000000
--- a/thirdparty/lws/server/server-handshake.c
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
-
-#ifndef LWS_NO_EXTENSIONS
-static int
-lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
-{
- struct lws_context *context = wsi->context;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- char ext_name[64], *args, *end = (*p) + budget - 1;
- const struct lws_ext_options *opts, *po;
- const struct lws_extension *ext;
- struct lws_ext_option_arg oa;
- int n, m, more = 1;
- int ext_count = 0;
- char ignore;
- char *c;
-
- /*
- * Figure out which extensions the client has that we want to
- * enable on this connection, and give him back the list
- */
- if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS))
- return 0;
-
- /*
- * break down the list of client extensions
- * and go through them
- */
-
- if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size,
- WSI_TOKEN_EXTENSIONS) < 0)
- return 1;
-
- c = (char *)pt->serv_buf;
- lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
- wsi->count_act_ext = 0;
- ignore = 0;
- n = 0;
- args = NULL;
-
- /*
- * We may get a simple request
- *
- * Sec-WebSocket-Extensions: permessage-deflate
- *
- * or an elaborated one with requested options
- *
- * Sec-WebSocket-Extensions: permessage-deflate; \
- * server_no_context_takeover; \
- * client_no_context_takeover
- */
-
- while (more) {
-
- if (*c && (*c != ',' && *c != '\t')) {
- if (*c == ';') {
- ignore = 1;
- args = c + 1;
- }
- if (ignore || *c == ' ') {
- c++;
- continue;
- }
- ext_name[n] = *c++;
- if (n < sizeof(ext_name) - 1)
- n++;
- continue;
- }
- ext_name[n] = '\0';
-
- ignore = 0;
- if (!*c)
- more = 0;
- else {
- c++;
- if (!n)
- continue;
- }
-
- while (args && *args && *args == ' ')
- args++;
-
- /* check a client's extension against our support */
-
- ext = wsi->vhost->extensions;
-
- while (ext && ext->callback) {
-
- if (strcmp(ext_name, ext->name)) {
- ext++;
- continue;
- }
-
- /*
- * oh, we do support this one he asked for... but let's
- * confirm he only gave it once
- */
- for (m = 0; m < wsi->count_act_ext; m++)
- if (wsi->active_extensions[m] == ext) {
- lwsl_info("extension mentioned twice\n");
- return 1; /* shenanigans */
- }
-
- /*
- * ask user code if it's OK to apply it on this
- * particular connection + protocol
- */
- m = (wsi->protocol->callback)(wsi,
- LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
- wsi->user_space, ext_name, 0);
-
- /*
- * zero return from callback means go ahead and allow
- * the extension, it's what we get if the callback is
- * unhandled
- */
- if (m) {
- ext++;
- continue;
- }
-
- /* apply it */
-
- ext_count++;
-
- /* instantiate the extension on this conn */
-
- wsi->active_extensions[wsi->count_act_ext] = ext;
-
- /* allow him to construct his context */
-
- if (ext->callback(lws_get_context(wsi), ext, wsi,
- LWS_EXT_CB_CONSTRUCT,
- (void *)&wsi->act_ext_user[
- wsi->count_act_ext],
- (void *)&opts, 0)) {
- lwsl_info("ext %s failed construction\n",
- ext_name);
- ext_count--;
- ext++;
-
- continue;
- }
-
- if (ext_count > 1)
- *(*p)++ = ',';
- else
- LWS_CPYAPP(*p,
- "\x0d\x0aSec-WebSocket-Extensions: ");
- *p += lws_snprintf(*p, (end - *p), "%s", ext_name);
-
- /*
- * go through the options trying to apply the
- * recognized ones
- */
-
- lwsl_debug("ext args %s", args);
-
- while (args && *args && *args != ',') {
- while (*args == ' ')
- args++;
- po = opts;
- while (po->name) {
- lwsl_debug("'%s' '%s'\n", po->name, args);
- /* only support arg-less options... */
- if (po->type == EXTARG_NONE &&
- !strncmp(args, po->name,
- strlen(po->name))) {
- oa.option_name = NULL;
- oa.option_index = po - opts;
- oa.start = NULL;
- lwsl_debug("setting %s\n", po->name);
- if (!ext->callback(
- lws_get_context(wsi), ext, wsi,
- LWS_EXT_CB_OPTION_SET,
- wsi->act_ext_user[
- wsi->count_act_ext],
- &oa, (end - *p))) {
-
- *p += lws_snprintf(*p, (end - *p), "; %s", po->name);
- lwsl_debug("adding option %s\n", po->name);
- }
- }
- po++;
- }
- while (*args && *args != ',' && *args != ';')
- args++;
- }
-
- wsi->count_act_ext++;
- lwsl_parser("count_act_ext <- %d\n",
- wsi->count_act_ext);
-
- ext++;
- }
-
- n = 0;
- args = NULL;
- }
-
- return 0;
-}
-#endif
-int
-handshake_0405(struct lws_context *context, struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- struct lws_process_html_args args;
- unsigned char hash[20];
- int n, accept_len;
- char *response;
- char *p;
-
- if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
- !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
- lwsl_parser("handshake_04 missing pieces\n");
- /* completed header processing, but missing some bits */
- goto bail;
- }
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= MAX_WEBSOCKET_04_KEY_LEN) {
- lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
- goto bail;
- }
-
- /*
- * since key length is restricted above (currently 128), cannot
- * overflow
- */
- n = sprintf((char *)pt->serv_buf,
- "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
- lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
-
- lws_SHA1(pt->serv_buf, n, hash);
-
- accept_len = lws_b64_encode_string((char *)hash, 20,
- (char *)pt->serv_buf, context->pt_serv_buf_size);
- if (accept_len < 0) {
- lwsl_warn("Base64 encoded hash too long\n");
- goto bail;
- }
-
- /* allocate the per-connection user memory (if any) */
- if (lws_ensure_user_space(wsi))
- goto bail;
-
- /* create the response packet */
-
- /* make a buffer big enough for everything */
-
- response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE;
- p = response;
- LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
- "Upgrade: WebSocket\x0d\x0a"
- "Connection: Upgrade\x0d\x0a"
- "Sec-WebSocket-Accept: ");
- strcpy(p, (char *)pt->serv_buf);
- p += accept_len;
-
- /* we can only return the protocol header if:
- * - one came in, and ... */
- if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
- /* - it is not an empty string */
- wsi->protocol->name &&
- wsi->protocol->name[0]) {
- LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
- p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
- }
-
-#ifndef LWS_NO_EXTENSIONS
- /*
- * Figure out which extensions the client has that we want to
- * enable on this connection, and give him back the list.
- *
- * Give him a limited write bugdet
- */
- if (lws_extension_server_handshake(wsi, &p, 192))
- goto bail;
-#endif
- LWS_CPYAPP(p, "\x0d\x0a");
-
- args.p = p;
- args.max_len = ((char *)pt->serv_buf + context->pt_serv_buf_size) - p;
- if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
- LWS_CALLBACK_ADD_HEADERS,
- wsi->user_space, &args, 0))
- goto bail;
-
- p = args.p;
-
- /* end of response packet */
-
- LWS_CPYAPP(p, "\x0d\x0a");
-
- if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX,
- response, p - response)) {
-
- /* okay send the handshake response accepting the connection */
-
- lwsl_parser("issuing resp pkt %d len\n", (int)(p - response));
-#if defined(DEBUG) && ! defined(LWS_WITH_ESP8266)
- fwrite(response, 1, p - response, stderr);
-#endif
- n = lws_write(wsi, (unsigned char *)response,
- p - response, LWS_WRITE_HTTP_HEADERS);
- if (n != (p - response)) {
- lwsl_debug("handshake_0405: ERROR writing to socket\n");
- goto bail;
- }
-
- }
-
- /* alright clean up and set ourselves into established state */
-
- wsi->state = LWSS_ESTABLISHED;
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
-
- {
- const char * uri_ptr =
- lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
- int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
- const struct lws_http_mount *hit =
- lws_find_mount(wsi, uri_ptr, uri_len);
- if (hit && hit->cgienv &&
- wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
- wsi->user_space, (void *)hit->cgienv, 0))
- return 1;
- }
-
- return 0;
-
-
-bail:
- /* caller will free up his parsing allocations */
- return -1;
-}
-
diff --git a/thirdparty/lws/server/ssl-server.c b/thirdparty/lws/server/ssl-server.c
deleted file mode 100644
index c4362824bf..0000000000
--- a/thirdparty/lws/server/ssl-server.c
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-extern int openssl_websocket_private_data_index,
- openssl_SSL_CTX_private_data_index;
-
-extern void
-lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info);
-
-#if !defined(LWS_WITH_MBEDTLS)
-static int
-OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
-{
- SSL *ssl;
- int n;
- struct lws *wsi;
-
- ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
- SSL_get_ex_data_X509_STORE_CTX_idx());
-
- /*
- * !!! nasty openssl requires the index to come as a library-scope
- * static
- */
- wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
-
- n = wsi->vhost->protocols[0].callback(wsi,
- LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION,
- x509_ctx, ssl, preverify_ok);
-
- /* convert return code from 0 = OK to 1 = OK */
- return !n;
-}
-#endif
-
-static int
-lws_context_ssl_init_ecdh(struct lws_vhost *vhost)
-{
-#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT
- EC_KEY *EC_key = NULL;
- EVP_PKEY *pkey;
- int KeyType;
- X509 *x;
-
- if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH))
- return 0;
-
- lwsl_notice(" Using ECDH certificate support\n");
-
- /* Get X509 certificate from ssl context */
- x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0);
- if (!x) {
- lwsl_err("%s: x is NULL\n", __func__);
- return 1;
- }
- /* Get the public key from certificate */
- pkey = X509_get_pubkey(x);
- if (!pkey) {
- lwsl_err("%s: pkey is NULL\n", __func__);
-
- return 1;
- }
- /* Get the key type */
- KeyType = EVP_PKEY_type(pkey->type);
-
- if (EVP_PKEY_EC != KeyType) {
- lwsl_notice("Key type is not EC\n");
- return 0;
- }
- /* Get the key */
- EC_key = EVP_PKEY_get1_EC_KEY(pkey);
- /* Set ECDH parameter */
- if (!EC_key) {
- lwsl_err("%s: ECDH key is NULL \n", __func__);
- return 1;
- }
- SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, EC_key);
- EC_KEY_free(EC_key);
-#endif
- return 0;
-}
-
-static int
-lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info,
- struct lws_vhost *vhost)
-{
-#if defined(LWS_HAVE_OPENSSL_ECDH_H) && !defined(LWS_WITH_MBEDTLS)
- EC_KEY *ecdh;
- int ecdh_nid;
- const char *ecdh_curve = "prime256v1";
-
- if (info->ecdh_curve)
- ecdh_curve = info->ecdh_curve;
-
- ecdh_nid = OBJ_sn2nid(ecdh_curve);
- if (NID_undef == ecdh_nid) {
- lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve);
- return 1;
- }
-
- ecdh = EC_KEY_new_by_curve_name(ecdh_nid);
- if (NULL == ecdh) {
- lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve);
- return 1;
- }
- SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, ecdh);
- EC_KEY_free(ecdh);
-
- SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
-
- lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve);
-#else
-#if !defined(LWS_WITH_MBEDTLS)
- lwsl_notice(" OpenSSL doesn't support ECDH\n");
-#endif
-#endif
- return 0;
-}
-
-#if !defined(LWS_WITH_MBEDTLS) && defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT)
-static int
-lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
-{
- struct lws_context *context = (struct lws_context *)arg;
- struct lws_vhost *vhost, *vh;
- const char *servername;
-
- if (!ssl)
- return SSL_TLSEXT_ERR_NOACK;
-
- /*
- * We can only get ssl accepted connections by using a vhost's ssl_ctx
- * find out which listening one took us and only match vhosts on the
- * same port.
- */
- vh = context->vhost_list;
- while (vh) {
- if (!vh->being_destroyed && ssl && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
- break;
- vh = vh->vhost_next;
- }
-
- if (!vh) {
- assert(vh); /* can't match the incoming vh? */
- return SSL_TLSEXT_ERR_OK;
- }
-
- servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
- if (!servername) {
- /* the client doesn't know what hostname it wants */
- lwsl_info("SNI: Unknown ServerName: %s\n", servername);
-
- return SSL_TLSEXT_ERR_OK;
- }
-
- vhost = lws_select_vhost(context, vh->listen_port, servername);
- if (!vhost) {
- lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port);
-
- return SSL_TLSEXT_ERR_OK;
- }
-
- lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port);
-
- /* select the ssl ctx from the selected vhost for this conn */
- SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
-
- return SSL_TLSEXT_ERR_OK;
-}
-#endif
-
-LWS_VISIBLE int
-lws_context_init_server_ssl(struct lws_context_creation_info *info,
- struct lws_vhost *vhost)
-{
- struct lws_context *context = vhost->context;
- struct lws wsi;
- unsigned long error;
- int n;
-
- if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
- vhost->use_ssl = 0;
- return 0;
- }
-
- /*
- * If he is giving a cert filepath, take it as a sign he wants to use
- * it on this vhost. User code can leave the cert filepath NULL and
- * set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in
- * which case he's expected to set up the cert himself at
- * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
- * provides the vhost SSL_CTX * in the user parameter.
- */
- if (info->ssl_cert_filepath)
- info->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX;
-
- if (info->port != CONTEXT_PORT_NO_LISTEN) {
-
- vhost->use_ssl = lws_check_opt(info->options,
- LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX);
-
- if (vhost->use_ssl && info->ssl_cipher_list)
- lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list);
-
- if (vhost->use_ssl)
- lwsl_notice(" Using SSL mode\n");
- else
- lwsl_notice(" Using non-SSL mode\n");
- }
-
- /*
- * give him a fake wsi with context + vhost set, so he can use
- * lws_get_context() in the callback
- */
- memset(&wsi, 0, sizeof(wsi));
- wsi.vhost = vhost;
- wsi.context = context;
-
- (void)n;
- (void)error;
-
- /*
- * Firefox insists on SSLv23 not SSLv3
- * Konq disables SSLv2 by default now, SSLv23 works
- *
- * SSLv23_server_method() is the openssl method for "allow all TLS
- * versions", compared to e.g. TLSv1_2_server_method() which only allows
- * tlsv1.2. Unwanted versions must be disabled using SSL_CTX_set_options()
- */
-#if !defined(LWS_WITH_MBEDTLS)
- {
- SSL_METHOD *method;
-
- method = (SSL_METHOD *)SSLv23_server_method();
- if (!method) {
- error = ERR_get_error();
- lwsl_err("problem creating ssl method %lu: %s\n",
- error, ERR_error_string(error,
- (char *)context->pt[0].serv_buf));
- return 1;
- }
- vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
- if (!vhost->ssl_ctx) {
- error = ERR_get_error();
- lwsl_err("problem creating ssl context %lu: %s\n",
- error, ERR_error_string(error,
- (char *)context->pt[0].serv_buf));
- return 1;
- }
- }
-#else
- {
- const SSL_METHOD *method = TLSv1_2_server_method();
-
- vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
- if (!vhost->ssl_ctx) {
- lwsl_err("problem creating ssl context\n");
- return 1;
- }
-
- }
-#endif
-#if !defined(LWS_WITH_MBEDTLS)
-
- /* associate the lws context with the SSL_CTX */
-
- SSL_CTX_set_ex_data(vhost->ssl_ctx,
- openssl_SSL_CTX_private_data_index, (char *)vhost->context);
- /* Disable SSLv2 and SSLv3 */
- SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-#ifdef SSL_OP_NO_COMPRESSION
- SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION);
-#endif
- SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE);
- SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- if (info->ssl_cipher_list)
- SSL_CTX_set_cipher_list(vhost->ssl_ctx,
- info->ssl_cipher_list);
-#endif
-
- /* as a server, are we requiring clients to identify themselves? */
-
- if (lws_check_opt(info->options,
- LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) {
- int verify_options = SSL_VERIFY_PEER;
-
- if (!lws_check_opt(info->options,
- LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED))
- verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
-
-#if !defined(LWS_WITH_MBEDTLS)
- SSL_CTX_set_session_id_context(vhost->ssl_ctx,
- (unsigned char *)context, sizeof(void *));
-
- /* absolutely require the client cert */
-
- SSL_CTX_set_verify(vhost->ssl_ctx,
- verify_options, OpenSSL_verify_callback);
-#endif
- }
-
-#if !defined(LWS_WITH_MBEDTLS) && !defined(OPENSSL_NO_TLSEXT)
- SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx,
- lws_ssl_server_name_cb);
- SSL_CTX_set_tlsext_servername_arg(vhost->ssl_ctx, context);
-#endif
-
- /*
- * give user code a chance to load certs into the server
- * allowing it to verify incoming client certs
- */
-#if !defined(LWS_WITH_MBEDTLS)
- if (info->ssl_ca_filepath &&
- !SSL_CTX_load_verify_locations(vhost->ssl_ctx,
- info->ssl_ca_filepath, NULL)) {
- lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__);
- }
-#endif
- if (vhost->use_ssl) {
- if (lws_context_ssl_init_ecdh_curve(info, vhost))
- return -1;
-
- vhost->protocols[0].callback(&wsi,
- LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
- vhost->ssl_ctx, NULL, 0);
- }
-
- if (lws_check_opt(info->options, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT))
- /* Normally SSL listener rejects non-ssl, optionally allow */
- vhost->allow_non_ssl_on_ssl_port = 1;
-
- if (info->ssl_options_set)
- SSL_CTX_set_options(vhost->ssl_ctx, info->ssl_options_set);
-
-/* SSL_clear_options introduced in 0.9.8m */
-#if !defined(LWS_WITH_MBEDTLS)
-#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL)
- if (info->ssl_options_clear)
- SSL_CTX_clear_options(vhost->ssl_ctx, info->ssl_options_clear);
-#endif
-#endif
-
- lwsl_info(" SSL options 0x%lX\n", SSL_CTX_get_options(vhost->ssl_ctx));
-
- if (vhost->use_ssl && info->ssl_cert_filepath) {
- /*
- * The user code can choose to either pass the cert and
- * key filepaths using the info members like this, or it can
- * leave them NULL; force the vhost SSL_CTX init using the info
- * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
- * set up the cert himself using the user callback
- * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
- * happened just above and has the vhost SSL_CTX * in the user
- * parameter.
- */
-#if !defined(LWS_WITH_MBEDTLS)
- /* set the local certificate from CertFile */
- n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx,
- info->ssl_cert_filepath);
- if (n != 1) {
- error = ERR_get_error();
- lwsl_err("problem getting cert '%s' %lu: %s\n",
- info->ssl_cert_filepath,
- error,
- ERR_error_string(error,
- (char *)context->pt[0].serv_buf));
- return 1;
- }
- lws_ssl_bind_passphrase(vhost->ssl_ctx, info);
-#else
- uint8_t *p;
- lws_filepos_t flen;
- int err;
-
- if (alloc_pem_to_der_file(vhost->context, info->ssl_cert_filepath, &p,
- &flen)) {
- lwsl_err("couldn't find cert file %s\n",
- info->ssl_cert_filepath);
-
- return 1;
- }
- err = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, flen, p);
- if (!err) {
- lwsl_err("Problem loading cert\n");
- return 1;
- }
-#if !defined(LWS_WITH_ESP32)
- free(p);
- p = NULL;
-#endif
-
- if (info->ssl_private_key_filepath) {
- if (alloc_pem_to_der_file(vhost->context,
- info->ssl_private_key_filepath, &p, &flen)) {
- lwsl_err("couldn't find cert file %s\n",
- info->ssl_cert_filepath);
-
- return 1;
- }
- err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen);
- if (!err) {
- lwsl_err("Problem loading key\n");
-
- return 1;
- }
- }
-
-#if !defined(LWS_WITH_ESP32)
- free(p);
- p = NULL;
-#endif
-#endif
- if (info->ssl_private_key_filepath != NULL) {
-#if !defined(LWS_WITH_MBEDTLS)
- /* set the private key from KeyFile */
- if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx,
- info->ssl_private_key_filepath,
- SSL_FILETYPE_PEM) != 1) {
- error = ERR_get_error();
- lwsl_err("ssl problem getting key '%s' %lu: %s\n",
- info->ssl_private_key_filepath, error,
- ERR_error_string(error,
- (char *)context->pt[0].serv_buf));
- return 1;
- }
-#endif
- } else
- if (vhost->protocols[0].callback(&wsi,
- LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
- vhost->ssl_ctx, NULL, 0)) {
- lwsl_err("ssl private key not set\n");
-
- return 1;
- }
-#if !defined(LWS_WITH_MBEDTLS)
- /* verify private key */
- if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) {
- lwsl_err("Private SSL key doesn't match cert\n");
- return 1;
- }
-#endif
- }
- if (vhost->use_ssl) {
- if (lws_context_ssl_init_ecdh(vhost))
- return 1;
-
- /*
- * SSL is happy and has a cert it's content with
- * If we're supporting HTTP2, initialize that
- */
- lws_context_init_http2_ssl(vhost);
- }
-
- return 0;
-}
-
diff --git a/thirdparty/lws/service.c b/thirdparty/lws/service.c
deleted file mode 100644
index 8cf455e2c9..0000000000
--- a/thirdparty/lws/service.c
+++ /dev/null
@@ -1,1714 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-
-static int
-lws_calllback_as_writeable(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- int n;
-
- lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB, 1);
-#if defined(LWS_WITH_STATS)
- if (wsi->active_writable_req_us) {
- uint64_t ul = time_in_microseconds() -
- wsi->active_writable_req_us;
-
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_MS_WRITABLE_DELAY, ul);
- lws_stats_atomic_max(wsi->context, pt,
- LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
- wsi->active_writable_req_us = 0;
- }
-#endif
-
- switch (wsi->mode) {
- case LWSCM_RAW:
- n = LWS_CALLBACK_RAW_WRITEABLE;
- break;
- case LWSCM_RAW_FILEDESC:
- n = LWS_CALLBACK_RAW_WRITEABLE_FILE;
- break;
- case LWSCM_WS_CLIENT:
- n = LWS_CALLBACK_CLIENT_WRITEABLE;
- break;
- case LWSCM_WSCL_ISSUE_HTTP_BODY:
- n = LWS_CALLBACK_CLIENT_HTTP_WRITEABLE;
- break;
- case LWSCM_WS_SERVING:
- n = LWS_CALLBACK_SERVER_WRITEABLE;
- break;
- default:
- n = LWS_CALLBACK_HTTP_WRITEABLE;
- break;
- }
-
- return user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, (enum lws_callback_reasons) n,
- wsi->user_space, NULL, 0);
-}
-
-LWS_VISIBLE int
-lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
-{
- int write_type = LWS_WRITE_PONG;
- struct lws_tokens eff_buf;
-#ifdef LWS_WITH_HTTP2
- struct lws **wsi2, *wsi2a;
-#endif
- int ret, m, n;
-
- wsi->leave_pollout_active = 0;
- wsi->handling_pollout = 1;
- /*
- * if another thread wants POLLOUT on us, from here on while
- * handling_pollout is set, he will only set leave_pollout_active.
- * If we are going to disable POLLOUT, we will check that first.
- */
-
- /*
- * user callback is lowest priority to get these notifications
- * actually, since other pending things cannot be disordered
- */
-
- /* Priority 1: pending truncated sends are incomplete ws fragments
- * If anything else sent first the protocol would be
- * corrupted.
- */
- if (wsi->trunc_len) {
- //lwsl_notice("%s: completing partial\n", __func__);
- if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
- wsi->trunc_len) < 0) {
- lwsl_info("%s signalling to close\n", __func__);
- goto bail_die;
- }
- /* leave POLLOUT active either way */
- goto bail_ok;
- } else
- if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
- wsi->socket_is_permanently_unusable = 1;
- goto bail_die; /* retry closing now */
- }
-
- if (wsi->mode == LWSCM_WSCL_ISSUE_HTTP_BODY)
- goto user_service;
-
-#ifdef LWS_WITH_HTTP2
- /*
- * Priority 2: protocol packets
- */
- if (wsi->upgraded_to_http2 && wsi->u.h2.h2n->pps) {
- lwsl_info("servicing pps\n");
- if (lws_h2_do_pps_send(wsi)) {
- wsi->socket_is_permanently_unusable = 1;
- goto bail_die;
- }
- if (wsi->u.h2.h2n->pps)
- goto bail_ok;
-
- /* we can resume whatever we were doing */
- lws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_ENABLE |
- LWS_RXFLOW_REASON_H2_PPS_PENDING);
-
- goto bail_ok; /* leave POLLOUT active */
- }
-#endif
-
-#ifdef LWS_WITH_CGI
- if (wsi->cgi) {
- /* also one shot */
- if (pollfd)
- if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
- lwsl_info("failed at set pollfd\n");
- return 1;
- }
- goto user_service_go_again;
- }
-#endif
-
- /* Priority 3: pending control packets (pong or close)
- *
- * 3a: close notification packet requested from close api
- */
-
- if (wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION) {
- lwsl_debug("sending close packet\n");
- wsi->waiting_to_send_close_frame = 0;
- n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
- wsi->u.ws.close_in_ping_buffer_len,
- LWS_WRITE_CLOSE);
- if (n >= 0) {
- wsi->state = LWSS_AWAITING_CLOSE_ACK;
- lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1);
- lwsl_debug("sent close indication, awaiting ack\n");
-
- goto bail_ok;
- }
-
- goto bail_die;
- }
-
- /* else, the send failed and we should just hang up */
-
- if ((wsi->state == LWSS_ESTABLISHED &&
- wsi->u.ws.ping_pending_flag) ||
- (wsi->state == LWSS_RETURNED_CLOSE_ALREADY &&
- wsi->u.ws.payload_is_close)) {
-
- if (wsi->u.ws.payload_is_close)
- write_type = LWS_WRITE_CLOSE;
-
- n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
- wsi->u.ws.ping_payload_len, write_type);
- if (n < 0)
- goto bail_die;
-
- /* well he is sent, mark him done */
- wsi->u.ws.ping_pending_flag = 0;
- if (wsi->u.ws.payload_is_close)
- /* oh... a close frame was it... then we are done */
- goto bail_die;
-
- /* otherwise for PING, leave POLLOUT active either way */
- goto bail_ok;
- }
-
- if (wsi->state == LWSS_ESTABLISHED &&
- !wsi->socket_is_permanently_unusable &&
- wsi->u.ws.send_check_ping) {
-
- lwsl_info("issuing ping on wsi %p\n", wsi);
- wsi->u.ws.send_check_ping = 0;
- n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
- 0, LWS_WRITE_PING);
- if (n < 0)
- goto bail_die;
-
- /*
- * we apparently were able to send the PING in a reasonable time
- * now reset the clock on our peer to be able to send the
- * PONG in a reasonable time.
- */
-
- lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG,
- wsi->context->timeout_secs);
-
- goto bail_ok;
- }
-
- /* Priority 4: if we are closing, not allowed to send more data frags
- * which means user callback or tx ext flush banned now
- */
- if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
- goto user_service;
-
- /* Priority 5: Tx path extension with more to send
- *
- * These are handled as new fragments each time around
- * So while we must block new writeable callback to enforce
- * payload ordering, but since they are always complete
- * fragments control packets can interleave OK.
- */
- if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) {
- lwsl_ext("SERVICING TX EXT DRAINING\n");
- if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0)
- goto bail_die;
- /* leave POLLOUT active */
- goto bail_ok;
- }
-
- /* Priority 6: user can get the callback
- */
- m = lws_ext_cb_active(wsi, LWS_EXT_CB_IS_WRITEABLE, NULL, 0);
- if (m)
- goto bail_die;
-#ifndef LWS_NO_EXTENSIONS
- if (!wsi->extension_data_pending)
- goto user_service;
-#endif
- /*
- * check in on the active extensions, see if they
- * had pending stuff to spill... they need to get the
- * first look-in otherwise sequence will be disordered
- *
- * NULL, zero-length eff_buf means just spill pending
- */
-
- ret = 1;
- if (wsi->mode == LWSCM_RAW || wsi->mode == LWSCM_RAW_FILEDESC)
- ret = 0;
-
- while (ret == 1) {
-
- /* default to nobody has more to spill */
-
- ret = 0;
- eff_buf.token = NULL;
- eff_buf.token_len = 0;
-
- /* give every extension a chance to spill */
-
- m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND,
- &eff_buf, 0);
- if (m < 0) {
- lwsl_err("ext reports fatal error\n");
- goto bail_die;
- }
- if (m)
- /*
- * at least one extension told us he has more
- * to spill, so we will go around again after
- */
- ret = 1;
-
- /* assuming they gave us something to send, send it */
-
- if (eff_buf.token_len) {
- n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
- eff_buf.token_len);
- if (n < 0) {
- lwsl_info("closing from POLLOUT spill\n");
- goto bail_die;
- }
- /*
- * Keep amount spilled small to minimize chance of this
- */
- if (n != eff_buf.token_len) {
- lwsl_err("Unable to spill ext %d vs %d\n",
- eff_buf.token_len, n);
- goto bail_die;
- }
- } else
- continue;
-
- /* no extension has more to spill */
-
- if (!ret)
- continue;
-
- /*
- * There's more to spill from an extension, but we just sent
- * something... did that leave the pipe choked?
- */
-
- if (!lws_send_pipe_choked(wsi))
- /* no we could add more */
- continue;
-
- lwsl_info("choked in POLLOUT service\n");
-
- /*
- * Yes, he's choked. Leave the POLLOUT masked on so we will
- * come back here when he is unchoked. Don't call the user
- * callback to enforce ordering of spilling, he'll get called
- * when we come back here and there's nothing more to spill.
- */
-
- goto bail_ok;
- }
-#ifndef LWS_NO_EXTENSIONS
- wsi->extension_data_pending = 0;
-#endif
-user_service:
- /* one shot */
-
- if (wsi->parent_carries_io) {
- wsi->handling_pollout = 0;
- wsi->leave_pollout_active = 0;
-
- return lws_calllback_as_writeable(wsi);
- }
-
- if (pollfd) {
- int eff = wsi->leave_pollout_active;
-
- if (!eff)
- if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
- lwsl_info("failed at set pollfd\n");
- goto bail_die;
- }
-
- wsi->handling_pollout = 0;
-
- /* cannot get leave_pollout_active set after the above */
- if (!eff && wsi->leave_pollout_active)
- /* got set inbetween sampling eff and clearing
- * handling_pollout, force POLLOUT on */
- lws_calllback_as_writeable(wsi);
-
- wsi->leave_pollout_active = 0;
- }
-
- if (wsi->mode != LWSCM_WSCL_ISSUE_HTTP_BODY &&
- !wsi->hdr_parsing_completed)
- goto bail_ok;
-
-
-#ifdef LWS_WITH_CGI
-user_service_go_again:
-#endif
-
-#ifdef LWS_WITH_HTTP2
- /*
- * we are the 'network wsi' for potentially many muxed child wsi with
- * no network connection of their own, who have to use us for all their
- * network actions. So we use a round-robin scheme to share out the
- * POLLOUT notifications to our children.
- *
- * But because any child could exhaust the socket's ability to take
- * writes, we can only let one child get notified each time.
- *
- * In addition children may be closed / deleted / added between POLLOUT
- * notifications, so we can't hold pointers
- */
-
- if (wsi->mode != LWSCM_HTTP2_SERVING) {
- lwsl_info("%s: non http2\n", __func__);
- goto notify;
- }
-
- wsi->u.h2.requested_POLLOUT = 0;
- if (!wsi->u.h2.initialized) {
- lwsl_info("pollout on uninitialized http2 conn\n");
- goto bail_ok;
- }
-
-// if (SSL_want_read(wsi->ssl) || SSL_want_write(wsi->ssl)) {
-// lws_callback_on_writable(wsi);
-// goto bail_ok;
-// }
-
- lwsl_info("%s: %p: children waiting for POLLOUT service:\n", __func__, wsi);
- wsi2a = wsi->u.h2.child_list;
- while (wsi2a) {
- if (wsi2a->u.h2.requested_POLLOUT)
- lwsl_debug(" * %p\n", wsi2a);
- else
- lwsl_debug(" %p\n", wsi2a);
-
- wsi2a = wsi2a->u.h2.sibling_list;
- }
-
- wsi2 = &wsi->u.h2.child_list;
- if (!*wsi2)
- goto bail_ok;
-
- do {
- struct lws *w, **wa;
-
- wa = &(*wsi2)->u.h2.sibling_list;
- if (!(*wsi2)->u.h2.requested_POLLOUT) {
- lwsl_debug(" child %p doesn't want POLLOUT\n", *wsi2);
- goto next_child;
- }
-
- /*
- * we're going to do writable callback for this child.
- * move him to be the last child
- */
-
- lwsl_debug("servicing child %p\n", *wsi2);
-
- w = *wsi2;
- while (w) {
- if (!w->u.h2.sibling_list) { /* w is the current last */
- lwsl_debug("w=%p, *wsi2 = %p\n", w, *wsi2);
- if (w == *wsi2) /* we are already last */
- break;
- w->u.h2.sibling_list = *wsi2; /* last points to us as new last */
- *wsi2 = (*wsi2)->u.h2.sibling_list; /* guy pointing to us until now points to our old next */
- w->u.h2.sibling_list->u.h2.sibling_list = NULL; /* we point to nothing because we are last */
- w = w->u.h2.sibling_list; /* w becomes us */
- break;
- }
- w = w->u.h2.sibling_list;
- }
-
- w->u.h2.requested_POLLOUT = 0;
- lwsl_info("%s: child %p (state %d)\n", __func__, (*wsi2), (*wsi2)->state);
-
- if (w->u.h2.pending_status_body) {
- w->u.h2.send_END_STREAM = 1;
- n = lws_write(w,
- (uint8_t *)w->u.h2.pending_status_body + LWS_PRE,
- strlen(w->u.h2.pending_status_body + LWS_PRE),
- LWS_WRITE_HTTP_FINAL);
- lws_free_set_NULL(w->u.h2.pending_status_body);
- lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
- wa = &wsi->u.h2.child_list;
- goto next_child;
- }
-
- if (w->state == LWSS_HTTP_ISSUING_FILE) {
-
- w->leave_pollout_active = 0;
-
- /* >0 == completion, <0 == error
- *
- * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
- * it's done. That's the case even if we just completed the
- * send, so wait for that.
- */
- n = lws_serve_http_file_fragment(w);
- lwsl_debug("lws_serve_http_file_fragment says %d\n", n);
-
- /*
- * We will often hear about out having sent the final
- * DATA here... if so close the actual wsi
- */
- if (n < 0 || w->u.h2.send_END_STREAM) {
- lwsl_debug("Closing POLLOUT child %p\n", w);
- lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
- wa = &wsi->u.h2.child_list;
- goto next_child;
- }
- if (n > 0)
- if (lws_http_transaction_completed(w))
- goto bail_die;
- if (!n) {
- lws_callback_on_writable(w);
- (w)->u.h2.requested_POLLOUT = 1;
- }
-
- goto next_child;
- }
-
- if (lws_calllback_as_writeable(w) || w->u.h2.send_END_STREAM) {
- lwsl_debug("Closing POLLOUT child\n");
- lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
- wa = &wsi->u.h2.child_list;
- }
-
-next_child:
- wsi2 = wa;
- } while (wsi2 && *wsi2 && !lws_send_pipe_choked(wsi));
-
- lwsl_info("%s: %p: children waiting for POLLOUT service: %p\n", __func__, wsi, wsi->u.h2.child_list);
- wsi2a = wsi->u.h2.child_list;
- while (wsi2a) {
- if (wsi2a->u.h2.requested_POLLOUT)
- lwsl_debug(" * %p\n", wsi2a);
- else
- lwsl_debug(" %p\n", wsi2a);
-
- wsi2a = wsi2a->u.h2.sibling_list;
- }
-
-
- wsi2a = wsi->u.h2.child_list;
- while (wsi2a) {
- if (wsi2a->u.h2.requested_POLLOUT) {
- lws_change_pollfd(wsi, 0, LWS_POLLOUT);
- break;
- }
- wsi2a = wsi2a->u.h2.sibling_list;
- }
-
- goto bail_ok;
-
-
-notify:
-#endif
- wsi->leave_pollout_active = 0;
-
- n = lws_calllback_as_writeable(wsi);
- wsi->handling_pollout = 0;
-
- if (wsi->leave_pollout_active)
- lws_change_pollfd(wsi, 0, LWS_POLLOUT);
-
- return n;
-
- /*
- * since these don't disable the POLLOUT, they are always doing the
- * right thing for leave_pollout_active whether it was set or not.
- */
-
-bail_ok:
- wsi->handling_pollout = 0;
- wsi->leave_pollout_active = 0;
-
- return 0;
-
-bail_die:
- wsi->handling_pollout = 0;
- wsi->leave_pollout_active = 0;
-
- return -1;
-}
-
-int
-lws_service_timeout_check(struct lws *wsi, unsigned int sec)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- int n = 0;
-
- (void)n;
-
- /*
- * if extensions want in on it (eg, we are a mux parent)
- * give them a chance to service child timeouts
- */
- if (lws_ext_cb_active(wsi, LWS_EXT_CB_1HZ, NULL, sec) < 0)
- return 0;
-
- if (!wsi->pending_timeout)
- return 0;
-
- /*
- * if we went beyond the allowed time, kill the
- * connection
- */
- if ((time_t)sec > wsi->pending_timeout_limit) {
-
- if (wsi->desc.sockfd != LWS_SOCK_INVALID &&
- wsi->position_in_fds_table >= 0)
- n = pt->fds[wsi->position_in_fds_table].events;
-
- lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_TIMEOUTS, 1);
-
- /* no need to log normal idle keepalive timeout */
- if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE)
- lwsl_info("wsi %p: TIMEDOUT WAITING on %d "
- "(did hdr %d, ah %p, wl %d, pfd "
- "events %d) %llu vs %llu\n",
- (void *)wsi, wsi->pending_timeout,
- wsi->hdr_parsing_completed, wsi->u.hdr.ah,
- pt->ah_wait_list_length, n,
- (unsigned long long)sec,
- (unsigned long long)wsi->pending_timeout_limit);
-
- /*
- * Since he failed a timeout, he already had a chance to do
- * something and was unable to... that includes situations like
- * half closed connections. So process this "failed timeout"
- * close as a violent death and don't try to do protocol
- * cleanup like flush partials.
- */
- wsi->socket_is_permanently_unusable = 1;
- if (wsi->mode == LWSCM_WSCL_WAITING_SSL)
- wsi->vhost->protocols[0].callback(wsi,
- LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
- wsi->user_space,
- (void *)"Timed out waiting SSL", 21);
-
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-
- return 1;
- }
-
- return 0;
-}
-
-int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len)
-{
-#if defined(LWS_WITH_HTTP2)
- if (wsi->upgraded_to_http2) {
- struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
-
- assert(h2n->rx_scratch);
- buf += n;
- len -= n;
- assert ((char *)buf >= (char *)h2n->rx_scratch &&
- (char *)&buf[len] <= (char *)&h2n->rx_scratch[LWS_H2_RX_SCRATCH_SIZE]);
-
- h2n->rx_scratch_pos = ((char *)buf - (char *)h2n->rx_scratch);
- h2n->rx_scratch_len = len;
-
- lwsl_info("%s: %p: pausing h2 rx_scratch\n", __func__, wsi);
-
- return 0;
- }
-#endif
- /* his RX is flowcontrolled, don't send remaining now */
- if (wsi->rxflow_buffer) {
- if (buf >= wsi->rxflow_buffer &&
- &buf[len - 1] < &wsi->rxflow_buffer[wsi->rxflow_len]) {
- /* rxflow while we were spilling prev rxflow */
- lwsl_info("%s: staying in rxflow buf\n", __func__);
- return 1;
- } else {
- lwsl_err("%s: conflicting rxflow buf, "
- "current %p len %d, new %p len %d\n", __func__,
- wsi->rxflow_buffer, wsi->rxflow_len, buf, len);
- assert(0);
- return 1;
- }
- }
-
- /* a new rxflow, buffer it and warn caller */
- lwsl_info("%s: new rxflow input buffer len %d\n", __func__, len - n);
- wsi->rxflow_buffer = lws_malloc(len - n, "rxflow buf");
- if (!wsi->rxflow_buffer)
- return -1;
-
- wsi->rxflow_len = len - n;
- wsi->rxflow_pos = 0;
- memcpy(wsi->rxflow_buffer, buf + n, len - n);
-
- return 0;
-}
-
-/* this is used by the platform service code to stop us waiting for network
- * activity in poll() when we have something that already needs service
- */
-
-LWS_VISIBLE LWS_EXTERN int
-lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
-{
- struct lws_context_per_thread *pt = &context->pt[tsi];
- struct allocated_headers *ah;
-
- /* Figure out if we really want to wait in poll()
- * We only need to wait if really nothing already to do and we have
- * to wait for something from network
- */
-
- /* 1) if we know we are draining rx ext, do not wait in poll */
- if (pt->rx_draining_ext_list)
- return 0;
-
-#ifdef LWS_OPENSSL_SUPPORT
- /* 2) if we know we have non-network pending data, do not wait in poll */
- if (lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) {
- lwsl_info("ssl buffered read\n");
- return 0;
- }
-#endif
-
- /* 3) if any ah has pending rx, do not wait in poll */
- ah = pt->ah_list;
- while (ah) {
- if (ah->rxpos != ah->rxlen) {
- if (!ah->wsi) {
- assert(0);
- }
- return 0;
- }
- ah = ah->next;
- }
-
- return timeout_ms;
-}
-
-/*
- * guys that need POLLIN service again without waiting for network action
- * can force POLLIN here if not flowcontrolled, so they will get service.
- *
- * Return nonzero if anybody got their POLLIN faked
- */
-int
-lws_service_flag_pending(struct lws_context *context, int tsi)
-{
- struct lws_context_per_thread *pt = &context->pt[tsi];
- struct allocated_headers *ah;
-#ifdef LWS_OPENSSL_SUPPORT
- struct lws *wsi_next;
-#endif
- struct lws *wsi;
- int forced = 0;
-
- /* POLLIN faking */
-
- /*
- * 1) For all guys with already-available ext data to drain, if they are
- * not flowcontrolled, fake their POLLIN status
- */
- wsi = pt->rx_draining_ext_list;
- while (wsi) {
- pt->fds[wsi->position_in_fds_table].revents |=
- pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
- if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) {
- forced = 1;
- break;
- }
- wsi = wsi->u.ws.rx_draining_ext_list;
- }
-
-#ifdef LWS_OPENSSL_SUPPORT
- /*
- * 2) For all guys with buffered SSL read data already saved up, if they
- * are not flowcontrolled, fake their POLLIN status so they'll get
- * service to use up the buffered incoming data, even though their
- * network socket may have nothing
- */
- wsi = pt->pending_read_list;
- while (wsi) {
- wsi_next = wsi->pending_read_list_next;
- pt->fds[wsi->position_in_fds_table].revents |=
- pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
- if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) {
- forced = 1;
- /*
- * he's going to get serviced now, take him off the
- * list of guys with buffered SSL. If he still has some
- * at the end of the service, he'll get put back on the
- * list then.
- */
- lws_ssl_remove_wsi_from_buffered_list(wsi);
- }
-
- wsi = wsi_next;
- }
-#endif
- /*
- * 3) For any wsi who have an ah with pending RX who did not
- * complete their current headers, and are not flowcontrolled,
- * fake their POLLIN status so they will be able to drain the
- * rx buffered in the ah
- */
- ah = pt->ah_list;
- while (ah) {
- if (ah->rxpos != ah->rxlen && !ah->wsi->hdr_parsing_completed) {
- pt->fds[ah->wsi->position_in_fds_table].revents |=
- pt->fds[ah->wsi->position_in_fds_table].events &
- LWS_POLLIN;
- if (pt->fds[ah->wsi->position_in_fds_table].revents &
- LWS_POLLIN) {
- forced = 1;
- break;
- }
- }
- ah = ah->next;
- }
-
- return forced;
-}
-
-#ifndef LWS_NO_CLIENT
-
-LWS_VISIBLE int
-lws_http_client_read(struct lws *wsi, char **buf, int *len)
-{
- int rlen, n;
-
- rlen = lws_ssl_capable_read(wsi, (unsigned char *)*buf, *len);
- *len = 0;
-
- /* allow the source to signal he has data again next time */
- lws_change_pollfd(wsi, 0, LWS_POLLIN);
-
- if (rlen == LWS_SSL_CAPABLE_ERROR) {
- lwsl_notice("%s: SSL capable error\n", __func__);
- return -1;
- }
-
- if (rlen == 0)
- return -1;
-
- if (rlen < 0)
- return 0;
-
- *len = rlen;
- wsi->client_rx_avail = 0;
-
- /*
- * server may insist on transfer-encoding: chunked,
- * so http client must deal with it
- */
-spin_chunks:
- while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) {
- switch (wsi->chunk_parser) {
- case ELCP_HEX:
- if ((*buf)[0] == '\x0d') {
- wsi->chunk_parser = ELCP_CR;
- break;
- }
- n = char_to_hex((*buf)[0]);
- if (n < 0) {
- lwsl_debug("chunking failure\n");
- return -1;
- }
- wsi->chunk_remaining <<= 4;
- wsi->chunk_remaining |= n;
- break;
- case ELCP_CR:
- if ((*buf)[0] != '\x0a') {
- lwsl_debug("chunking failure\n");
- return -1;
- }
- wsi->chunk_parser = ELCP_CONTENT;
- lwsl_info("chunk %d\n", wsi->chunk_remaining);
- if (wsi->chunk_remaining)
- break;
- lwsl_info("final chunk\n");
- goto completed;
-
- case ELCP_CONTENT:
- break;
-
- case ELCP_POST_CR:
- if ((*buf)[0] != '\x0d') {
- lwsl_debug("chunking failure\n");
-
- return -1;
- }
-
- wsi->chunk_parser = ELCP_POST_LF;
- break;
-
- case ELCP_POST_LF:
- if ((*buf)[0] != '\x0a')
- return -1;
-
- wsi->chunk_parser = ELCP_HEX;
- wsi->chunk_remaining = 0;
- break;
- }
- (*buf)++;
- (*len)--;
- }
-
- if (wsi->chunked && !wsi->chunk_remaining)
- return 0;
-
- if (wsi->u.http.rx_content_remain &&
- wsi->u.http.rx_content_remain < *len)
- n = (int)wsi->u.http.rx_content_remain;
- else
- n = *len;
-
- if (wsi->chunked && wsi->chunk_remaining &&
- wsi->chunk_remaining < n)
- n = wsi->chunk_remaining;
-
-#ifdef LWS_WITH_HTTP_PROXY
- /* hubbub */
- if (wsi->perform_rewrite)
- lws_rewrite_parse(wsi->rw, (unsigned char *)*buf, n);
- else
-#endif
- if (user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
- wsi->user_space, *buf, n)) {
- lwsl_debug("%s: LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ returned -1\n", __func__);
-
- return -1;
- }
-
- if (wsi->chunked && wsi->chunk_remaining) {
- (*buf) += n;
- wsi->chunk_remaining -= n;
- *len -= n;
- }
-
- if (wsi->chunked && !wsi->chunk_remaining)
- wsi->chunk_parser = ELCP_POST_CR;
-
- if (wsi->chunked && *len)
- goto spin_chunks;
-
- if (wsi->chunked)
- return 0;
-
- /* if we know the content length, decrement the content remaining */
- if (wsi->u.http.rx_content_length > 0)
- wsi->u.http.rx_content_remain -= n;
-
- if (wsi->u.http.rx_content_remain || !wsi->u.http.rx_content_length)
- return 0;
-
-completed:
- if (user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
- wsi->user_space, NULL, 0)) {
- lwsl_debug("Completed call returned -1\n");
- return -1;
- }
-
- if (lws_http_transaction_completed_client(wsi)) {
- lwsl_notice("%s: transaction completed says -1\n", __func__);
- return -1;
- }
-
- return 0;
-}
-#endif
-
-static int
-lws_is_ws_with_ext(struct lws *wsi)
-{
-#if defined(LWS_NO_EXTENSIONS)
- return 0;
-#else
- return wsi->state == LWSS_ESTABLISHED &&
- !!wsi->count_act_ext;
-#endif
-}
-
-LWS_VISIBLE int
-lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int tsi)
-{
- struct lws_context_per_thread *pt = &context->pt[tsi];
- lws_sockfd_type our_fd = 0, tmp_fd;
- struct allocated_headers *ah;
- struct lws_tokens eff_buf;
- unsigned int pending = 0;
- struct lws *wsi, *wsi1;
- char draining_flow = 0;
- int timed_out = 0;
- time_t now;
- int n = 0, m;
- int more;
-
- if (!context->protocol_init_done)
- lws_protocol_init(context);
-
- time(&now);
-
- /*
- * handle case that system time was uninitialized when lws started
- * at boot, and got initialized a little later
- */
- if (context->time_up < 1464083026 && now > 1464083026)
- context->time_up = now;
-
- /* TODO: if using libev, we should probably use timeout watchers... */
- if (context->last_timeout_check_s != now) {
- context->last_timeout_check_s = now;
-
-#if defined(LWS_WITH_STATS)
- if (!tsi && now - context->last_dump > 10) {
- lws_stats_log_dump(context);
- context->last_dump = now;
- }
-#endif
-
- lws_plat_service_periodic(context);
-
- lws_check_deferred_free(context, 0);
-
-#if defined(LWS_WITH_PEER_LIMITS)
- lws_peer_cull_peer_wait_list(context);
-#endif
-
- /* retire unused deprecated context */
-#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_ESP32)
-#if LWS_POSIX && !defined(_WIN32)
- if (context->deprecated && !context->count_wsi_allocated) {
- lwsl_notice("%s: ending deprecated context\n", __func__);
- kill(getpid(), SIGINT);
- return 0;
- }
-#endif
-#endif
- /* global timeout check once per second */
-
- if (pollfd)
- our_fd = pollfd->fd;
-
- /*
- * Phase 1: check every wsi on the timeout check list
- */
-
- wsi = context->pt[tsi].timeout_list;
- while (wsi) {
- /* we have to take copies, because he may be deleted */
- wsi1 = wsi->timeout_list;
- tmp_fd = wsi->desc.sockfd;
- if (lws_service_timeout_check(wsi, (unsigned int)now)) {
- /* he did time out... */
- if (tmp_fd == our_fd)
- /* it was the guy we came to service! */
- timed_out = 1;
- /* he's gone, no need to mark as handled */
- }
- wsi = wsi1;
- }
-
- /*
- * Phase 2: double-check active ah timeouts independent of wsi
- * timeout status
- */
-
- ah = pt->ah_list;
- while (ah) {
- int len;
- char buf[256];
- const unsigned char *c;
-
- if (!ah->in_use || !ah->wsi || !ah->assigned ||
- (ah->wsi->vhost && now - ah->assigned <
- ah->wsi->vhost->timeout_secs_ah_idle + 60)) {
- ah = ah->next;
- continue;
- }
-
- /*
- * a single ah session somehow got held for
- * an unreasonable amount of time.
- *
- * Dump info on the connection...
- */
- wsi = ah->wsi;
- buf[0] = '\0';
- lws_get_peer_simple(wsi, buf, sizeof(buf));
- lwsl_notice("ah excessive hold: wsi %p\n"
- " peer address: %s\n"
- " ah rxpos %u, rxlen %u, pos %u\n",
- wsi, buf, ah->rxpos, ah->rxlen,
- ah->pos);
- buf[0] = '\0';
- m = 0;
- do {
- c = lws_token_to_string(m);
- if (!c)
- break;
- if (!(*c))
- break;
-
- len = lws_hdr_total_length(wsi, m);
- if (!len || len > sizeof(buf) - 1) {
- m++;
- continue;
- }
-
- if (lws_hdr_copy(wsi, buf,
- sizeof buf, m) > 0) {
- buf[sizeof(buf) - 1] = '\0';
-
- lwsl_notice(" %s = %s\n",
- (const char *)c, buf);
- }
- m++;
- } while (1);
-
- /* explicitly detach the ah */
-
- lws_header_table_force_to_detachable_state(wsi);
- lws_header_table_detach(wsi, 0);
-
- /* ... and then drop the connection */
-
- if (wsi->desc.sockfd == our_fd)
- /* it was the guy we came to service! */
- timed_out = 1;
-
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-
- ah = pt->ah_list;
- }
-
-#ifdef LWS_WITH_CGI
- /*
- * Phase 3: handle cgi timeouts
- */
- lws_cgi_kill_terminated(pt);
-#endif
-#if 0
- {
- char s[300], *p = s;
-
- for (n = 0; n < context->count_threads; n++)
- p += sprintf(p, " %7lu (%5d), ",
- context->pt[n].count_conns,
- context->pt[n].fds_count);
-
- lwsl_notice("load: %s\n", s);
- }
-#endif
- }
-
- /*
- * at intervals, check for ws connections needing ping-pong checks
- */
-
- if (context->ws_ping_pong_interval &&
- context->last_ws_ping_pong_check_s < now + 10) {
- struct lws_vhost *vh = context->vhost_list;
- context->last_ws_ping_pong_check_s = now;
-
- while (vh) {
- for (n = 0; n < vh->count_protocols; n++) {
- wsi = vh->same_vh_protocol_list[n];
-
- while (wsi) {
- if (wsi->state == LWSS_ESTABLISHED &&
- !wsi->socket_is_permanently_unusable &&
- !wsi->u.ws.send_check_ping &&
- wsi->u.ws.time_next_ping_check &&
- wsi->u.ws.time_next_ping_check < now) {
-
- lwsl_info("requesting ping-pong on wsi %p\n", wsi);
- wsi->u.ws.send_check_ping = 1;
- lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING,
- context->timeout_secs);
- lws_callback_on_writable(wsi);
- wsi->u.ws.time_next_ping_check = now +
- wsi->context->ws_ping_pong_interval;
- }
- wsi = wsi->same_vh_protocol_next;
- }
- }
- vh = vh->vhost_next;
- }
- }
-
-
- /* the socket we came to service timed out, nothing to do */
- if (timed_out)
- return 0;
-
- /* just here for timeout management? */
- if (!pollfd)
- return 0;
-
- /* no, here to service a socket descriptor */
- wsi = wsi_from_fd(context, pollfd->fd);
- if (!wsi)
- /* not lws connection ... leave revents alone and return */
- return 0;
-
- /*
- * so that caller can tell we handled, past here we need to
- * zero down pollfd->revents after handling
- */
-
-#if LWS_POSIX
- /* handle session socket closed */
-
- if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
- (pollfd->revents & LWS_POLLHUP)) {
- wsi->socket_is_permanently_unusable = 1;
- lwsl_debug("Session Socket %p (fd=%d) dead\n",
- (void *)wsi, pollfd->fd);
-
- goto close_and_handled;
- }
-
-#ifdef _WIN32
- if (pollfd->revents & LWS_POLLOUT)
- wsi->sock_send_blocking = FALSE;
-#endif
-
-#endif
-
- if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
- (pollfd->revents & LWS_POLLHUP)) {
- lwsl_debug("pollhup\n");
- wsi->socket_is_permanently_unusable = 1;
- goto close_and_handled;
- }
-
-#ifdef LWS_OPENSSL_SUPPORT
- if ((wsi->state == LWSS_SHUTDOWN) && lws_is_ssl(wsi) && wsi->ssl) {
- n = SSL_shutdown(wsi->ssl);
- lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd);
- switch (n) {
- case 1:
- n = shutdown(wsi->desc.sockfd, SHUT_WR);
- goto close_and_handled;
-
- case 0:
- lws_change_pollfd(wsi, 0, LWS_POLLIN);
- n = 0;
- goto handled;
-
- default:
- n = SSL_get_error(wsi->ssl, n);
- if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
- if (SSL_want_read(wsi->ssl)) {
- lwsl_debug("(wants read)\n");
- lws_change_pollfd(wsi, 0, LWS_POLLIN);
- n = 0;
- goto handled;
- }
- if (SSL_want_write(wsi->ssl)) {
- lwsl_debug("(wants write)\n");
- lws_change_pollfd(wsi, 0, LWS_POLLOUT);
- n = 0;
- goto handled;
- }
- }
-
- /* actual error occurred, just close the connection */
- n = shutdown(wsi->desc.sockfd, SHUT_WR);
- goto close_and_handled;
- }
- }
-#endif
-
- /* okay, what we came here to do... */
-
- switch (wsi->mode) {
- case LWSCM_HTTP_SERVING:
- case LWSCM_HTTP_CLIENT:
- case LWSCM_HTTP_SERVING_ACCEPTED:
- case LWSCM_SERVER_LISTENER:
- case LWSCM_SSL_ACK_PENDING:
- case LWSCM_SSL_ACK_PENDING_RAW:
- if (wsi->state == LWSS_CLIENT_HTTP_ESTABLISHED)
- goto handled;
-
-#ifdef LWS_WITH_CGI
- if (wsi->cgi && (pollfd->revents & LWS_POLLOUT)) {
- n = lws_handle_POLLOUT_event(wsi, pollfd);
- if (n)
- goto close_and_handled;
- goto handled;
- }
-#endif
- /* fallthru */
- case LWSCM_RAW:
- n = lws_server_socket_service(context, wsi, pollfd);
- if (n) /* closed by above */
- return 1;
- goto handled;
-
- case LWSCM_RAW_FILEDESC:
-
- if (pollfd->revents & LWS_POLLOUT) {
- n = lws_calllback_as_writeable(wsi);
- if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
- lwsl_info("failed at set pollfd\n");
- return 1;
- }
- if (n)
- goto close_and_handled;
- }
- n = LWS_CALLBACK_RAW_RX;
- if (wsi->mode == LWSCM_RAW_FILEDESC)
- n = LWS_CALLBACK_RAW_RX_FILE;
-
- if (pollfd->revents & LWS_POLLIN) {
- if (user_callback_handle_rxflow(
- wsi->protocol->callback,
- wsi, n,
- wsi->user_space, NULL, 0)) {
- lwsl_debug("raw rx callback closed it\n");
- goto close_and_handled;
- }
- }
-
- if (pollfd->revents & LWS_POLLHUP)
- goto close_and_handled;
- n = 0;
- goto handled;
-
- case LWSCM_WS_SERVING:
- case LWSCM_WS_CLIENT:
- case LWSCM_HTTP2_SERVING:
- case LWSCM_HTTP_CLIENT_ACCEPTED:
-
- /* 1: something requested a callback when it was OK to write */
-
- if ((pollfd->revents & LWS_POLLOUT) &&
- ((wsi->state == LWSS_ESTABLISHED ||
- wsi->state == LWSS_HTTP2_ESTABLISHED ||
- wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS ||
- wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
- wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
- wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE)) &&
- lws_handle_POLLOUT_event(wsi, pollfd)) {
- if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
- wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE;
- lwsl_info("lws_service_fd: closing\n");
- goto close_and_handled;
- }
-
- if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
- wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
- wsi->state == LWSS_AWAITING_CLOSE_ACK) {
- /*
- * we stopped caring about anything except control
- * packets. Force flow control off, defeat tx
- * draining.
- */
- lws_rx_flow_control(wsi, 1);
- wsi->u.ws.tx_draining_ext = 0;
- }
-
- if (wsi->u.ws.tx_draining_ext)
- /* we cannot deal with new RX until the TX ext
- * path has been drained. It's because new
- * rx will, eg, crap on the wsi rx buf that
- * may be needed to retain state.
- *
- * TX ext drain path MUST go through event loop
- * to avoid blocking.
- */
- break;
-
- if (lws_is_flowcontrolled(wsi))
- /* We cannot deal with any kind of new RX
- * because we are RX-flowcontrolled.
- */
- break;
-
-#if defined(LWS_WITH_HTTP2)
- if (wsi->http2_substream || wsi->upgraded_to_http2) {
- wsi1 = lws_get_network_wsi(wsi);
- if (wsi1 && wsi1->trunc_len)
- /* We cannot deal with any kind of new RX
- * because we are dealing with a partial send
- * (new RX may trigger new http_action() that
- * expect to be able to send)
- */
- break;
- }
-#endif
-
- /* 2: RX Extension needs to be drained
- */
-
- if (wsi->state == LWSS_ESTABLISHED &&
- wsi->u.ws.rx_draining_ext) {
-
- lwsl_ext("%s: RX EXT DRAINING: Service\n", __func__);
-#ifndef LWS_NO_CLIENT
- if (wsi->mode == LWSCM_WS_CLIENT) {
- n = lws_client_rx_sm(wsi, 0);
- if (n < 0)
- /* we closed wsi */
- n = 0;
- } else
-#endif
- n = lws_rx_sm(wsi, 0);
-
- goto handled;
- }
-
- if (wsi->u.ws.rx_draining_ext)
- /*
- * We have RX EXT content to drain, but can't do it
- * right now. That means we cannot do anything lower
- * priority either.
- */
- break;
-
- /* 3: RX Flowcontrol buffer / h2 rx scratch needs to be drained
- */
-
- if (wsi->rxflow_buffer) {
- lwsl_info("draining rxflow (len %d)\n",
- wsi->rxflow_len - wsi->rxflow_pos);
- assert(wsi->rxflow_pos < wsi->rxflow_len);
- /* well, drain it */
- eff_buf.token = (char *)wsi->rxflow_buffer +
- wsi->rxflow_pos;
- eff_buf.token_len = wsi->rxflow_len - wsi->rxflow_pos;
- draining_flow = 1;
- goto drain;
- }
-
-#if defined(LWS_WITH_HTTP2)
- if (wsi->upgraded_to_http2) {
- struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
-
- if (h2n->rx_scratch_len) {
- lwsl_info("%s: %p: resuming h2 rx_scratch pos = %d len = %d\n",
- __func__, wsi, h2n->rx_scratch_pos, h2n->rx_scratch_len);
- eff_buf.token = (char *)h2n->rx_scratch +
- h2n->rx_scratch_pos;
- eff_buf.token_len = h2n->rx_scratch_len;
-
- h2n->rx_scratch_len = 0;
- goto drain;
- }
- }
-#endif
-
- /* 4: any incoming (or ah-stashed incoming rx) data ready?
- * notice if rx flow going off raced poll(), rx flow wins
- */
-
- if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
- break;
-read:
- if (lws_is_flowcontrolled(wsi)) {
- lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n",
- __func__, wsi, wsi->rxflow_bitmap);
- break;
- }
-
- /* all the union members start with hdr, so even in ws mode
- * we can deal with the ah via u.hdr
- */
- if (wsi->u.hdr.ah) {
- lwsl_info("%s: %p: inherited ah rx\n", __func__, wsi);
- eff_buf.token_len = wsi->u.hdr.ah->rxlen -
- wsi->u.hdr.ah->rxpos;
- eff_buf.token = (char *)wsi->u.hdr.ah->rx +
- wsi->u.hdr.ah->rxpos;
- } else {
- if (wsi->mode != LWSCM_HTTP_CLIENT_ACCEPTED) {
- /*
- * extension may not consume everything (eg, pmd may be constrained
- * as to what it can output...) has to go in per-wsi rx buf area.
- * Otherwise in large temp serv_buf area.
- */
-
-#if defined(LWS_WITH_HTTP2)
- if (wsi->upgraded_to_http2) {
- if (!wsi->u.h2.h2n->rx_scratch) {
- wsi->u.h2.h2n->rx_scratch = lws_malloc(LWS_H2_RX_SCRATCH_SIZE, "h2 rx scratch");
- if (!wsi->u.h2.h2n->rx_scratch)
- goto close_and_handled;
- }
- eff_buf.token = wsi->u.h2.h2n->rx_scratch;
- eff_buf.token_len = LWS_H2_RX_SCRATCH_SIZE;
- } else
-#endif
- {
- eff_buf.token = (char *)pt->serv_buf;
- if (lws_is_ws_with_ext(wsi)) {
- eff_buf.token_len = wsi->u.ws.rx_ubuf_alloc;
- } else {
- eff_buf.token_len = context->pt_serv_buf_size;
- }
-
- if ((unsigned int)eff_buf.token_len > context->pt_serv_buf_size)
- eff_buf.token_len = context->pt_serv_buf_size;
- }
-
- if ((int)pending > eff_buf.token_len)
- pending = eff_buf.token_len;
-
- eff_buf.token_len = lws_ssl_capable_read(wsi,
- (unsigned char *)eff_buf.token, pending ? pending :
- eff_buf.token_len);
- switch (eff_buf.token_len) {
- case 0:
- lwsl_info("%s: zero length read\n", __func__);
- goto close_and_handled;
- case LWS_SSL_CAPABLE_MORE_SERVICE:
- lwsl_info("SSL Capable more service\n");
- n = 0;
- goto handled;
- case LWS_SSL_CAPABLE_ERROR:
- lwsl_info("Closing when error\n");
- goto close_and_handled;
- }
- // lwsl_notice("Actual RX %d\n", eff_buf.token_len);
- }
- }
-
-drain:
-#ifndef LWS_NO_CLIENT
- if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED &&
- !wsi->told_user_closed) {
-
- /*
- * In SSL mode we get POLLIN notification about
- * encrypted data in.
- *
- * But that is not necessarily related to decrypted
- * data out becoming available; in may need to perform
- * other in or out before that happens.
- *
- * simply mark ourselves as having readable data
- * and turn off our POLLIN
- */
- wsi->client_rx_avail = 1;
- lws_change_pollfd(wsi, LWS_POLLIN, 0);
-
- /* let user code know, he'll usually ask for writeable
- * callback and drain / re-enable it there
- */
- if (user_callback_handle_rxflow(
- wsi->protocol->callback,
- wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
- wsi->user_space, NULL, 0)) {
- lwsl_info("RECEIVE_CLIENT_HTTP closed it\n");
- goto close_and_handled;
- }
-
- n = 0;
- goto handled;
- }
-#endif
- /*
- * give any active extensions a chance to munge the buffer
- * before parse. We pass in a pointer to an lws_tokens struct
- * prepared with the default buffer and content length that's in
- * there. Rather than rewrite the default buffer, extensions
- * that expect to grow the buffer can adapt .token to
- * point to their own per-connection buffer in the extension
- * user allocation. By default with no extensions or no
- * extension callback handling, just the normal input buffer is
- * used then so it is efficient.
- */
- do {
- more = 0;
-
- m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_RX_PREPARSE,
- &eff_buf, 0);
- if (m < 0)
- goto close_and_handled;
- if (m)
- more = 1;
-
- /* service incoming data */
-
- if (eff_buf.token_len) {
- /*
- * if draining from rxflow buffer, not
- * critical to track what was used since at the
- * use it bumps wsi->rxflow_pos. If we come
- * around again it will pick up from where it
- * left off.
- */
- n = lws_read(wsi, (unsigned char *)eff_buf.token,
- eff_buf.token_len);
- if (n < 0) {
- /* we closed wsi */
- n = 0;
- goto handled;
- }
- }
-
- eff_buf.token = NULL;
- eff_buf.token_len = 0;
- } while (more);
-
- if (wsi->u.hdr.ah) {
- lwsl_debug("%s: %p: detaching\n", __func__, wsi);
- lws_header_table_force_to_detachable_state(wsi);
- /* we can run the normal ah detach flow despite
- * being in ws union mode, since all union members
- * start with hdr */
- lws_header_table_detach(wsi, 0);
- }
-
- pending = lws_ssl_pending(wsi);
- if (pending) {
- if (lws_is_ws_with_ext(wsi))
- pending = pending > wsi->u.ws.rx_ubuf_alloc ?
- wsi->u.ws.rx_ubuf_alloc : pending;
- else
- pending = pending > context->pt_serv_buf_size ?
- context->pt_serv_buf_size : pending;
- goto read;
- }
-
- if (draining_flow && wsi->rxflow_buffer &&
- wsi->rxflow_pos == wsi->rxflow_len) {
- lwsl_info("%s: %p flow buf: drained\n", __func__, wsi);
- lws_free_set_NULL(wsi->rxflow_buffer);
- /* having drained the rxflow buffer, can rearm POLLIN */
-#ifdef LWS_NO_SERVER
- n =
-#endif
- _lws_rx_flow_control(wsi);
- /* n ignored, needed for NO_SERVER case */
- }
-
- break;
-#ifdef LWS_WITH_CGI
- case LWSCM_CGI: /* we exist to handle a cgi's stdin/out/err data...
- * do the callback on our master wsi
- */
- {
- struct lws_cgi_args args;
-
- if (wsi->cgi_channel >= LWS_STDOUT &&
- !(pollfd->revents & pollfd->events & LWS_POLLIN))
- break;
- if (wsi->cgi_channel == LWS_STDIN &&
- !(pollfd->revents & pollfd->events & LWS_POLLOUT))
- break;
-
- if (wsi->cgi_channel == LWS_STDIN)
- if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
- lwsl_info("failed at set pollfd\n");
- return 1;
- }
-
- args.ch = wsi->cgi_channel;
- args.stdwsi = &wsi->parent->cgi->stdwsi[0];
- args.hdr_state = wsi->hdr_state;
-
- lwsl_debug("CGI LWS_STDOUT %p mode %d state %d\n",
- wsi->parent, wsi->parent->mode,
- wsi->parent->state);
-
- if (user_callback_handle_rxflow(
- wsi->parent->protocol->callback,
- wsi->parent, LWS_CALLBACK_CGI,
- wsi->parent->user_space,
- (void *)&args, 0))
- return 1;
-
- break;
- }
-#endif
- /*
- * something went wrong with parsing the handshake, and
- * we ended up back in the event loop without completing it
- */
- case LWSCM_PRE_WS_SERVING_ACCEPT:
- wsi->socket_is_permanently_unusable = 1;
- goto close_and_handled;
-
- default:
-#ifdef LWS_NO_CLIENT
- break;
-#else
- if ((pollfd->revents & LWS_POLLOUT) &&
- lws_handle_POLLOUT_event(wsi, pollfd)) {
- lwsl_debug("POLLOUT event closed it\n");
- goto close_and_handled;
- }
-
- n = lws_client_socket_service(context, wsi, pollfd);
- if (n)
- return 1;
- goto handled;
-#endif
- }
-
- n = 0;
- goto handled;
-
-close_and_handled:
- lwsl_debug("%p: Close and handled\n", wsi);
- lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
- /*
- * pollfd may point to something else after the close
- * due to pollfd swapping scheme on delete on some platforms
- * we can't clear revents now because it'd be the wrong guy's revents
- */
- return 1;
-
-handled:
- pollfd->revents = 0;
- return n;
-}
-
-LWS_VISIBLE int
-lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd)
-{
- return lws_service_fd_tsi(context, pollfd, 0);
-}
-
-LWS_VISIBLE int
-lws_service(struct lws_context *context, int timeout_ms)
-{
- return lws_plat_service(context, timeout_ms);
-}
-
-LWS_VISIBLE int
-lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
-{
- return _lws_plat_service_tsi(context, timeout_ms, tsi);
-}
-
diff --git a/thirdparty/lws/ssl.c b/thirdparty/lws/ssl.c
deleted file mode 100644
index 4ff3088ab3..0000000000
--- a/thirdparty/lws/ssl.c
+++ /dev/null
@@ -1,973 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "private-libwebsockets.h"
-#include <errno.h>
-
-int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf,
- lws_filepos_t *amount)
-{
- lws_filepos_t len;
- lws_fop_flags_t flags = LWS_O_RDONLY;
- lws_fop_fd_t fops_fd = lws_vfs_file_open(
- lws_get_fops(context), filename, &flags);
- int ret = 1;
-
- if (!fops_fd)
- return 1;
-
- len = lws_vfs_get_length(fops_fd);
-
- *buf = lws_malloc((size_t)len, "lws_alloc_vfs_file");
- if (!*buf)
- goto bail;
-
- if (lws_vfs_file_read(fops_fd, amount, *buf, len))
- goto bail;
-
- ret = 0;
-bail:
- lws_vfs_file_close(&fops_fd);
-
- return ret;
-}
-
-#if defined(LWS_WITH_MBEDTLS)
-#if defined(LWS_WITH_ESP32)
-int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
- lws_filepos_t *amount)
-{
- nvs_handle nvh;
- size_t s;
- int n = 0;
-
- ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
- if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
- n = 1;
- goto bail;
- }
- *buf = lws_malloc(s, "alloc_file");
- if (!*buf) {
- n = 2;
- goto bail;
- }
- if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
- lws_free(*buf);
- n = 1;
- goto bail;
- }
-
- *amount = s;
-
-bail:
- nvs_close(nvh);
-
- return n;
-}
-#else
-int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
- lws_filepos_t *amount)
-{
- FILE *f;
- size_t s;
- int n = 0;
-
- f = fopen(filename, "rb");
- if (f == NULL) {
- n = 1;
- goto bail;
- }
-
- if (fseek(f, 0, SEEK_END) != 0) {
- n = 1;
- goto bail;
- }
-
- s = ftell(f);
- if (s == -1) {
- n = 1;
- goto bail;
- }
-
- if (fseek(f, 0, SEEK_SET) != 0) {
- n = 1;
- goto bail;
- }
-
- *buf = lws_malloc(s, "alloc_file");
- if (!*buf) {
- n = 2;
- goto bail;
- }
-
- if (fread(*buf, s, 1, f) != 1) {
- lws_free(*buf);
- n = 1;
- goto bail;
- }
-
- *amount = s;
-
-bail:
- if (f)
- fclose(f);
-
- return n;
-
-}
-#endif
-int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf,
- lws_filepos_t *amount)
-{
- uint8_t *pem, *p, *q, *end;
- lws_filepos_t len;
- int n;
-
- n = alloc_file(context, filename, &pem, &len);
- if (n)
- return n;
-
- /* trim the first line */
-
- p = pem;
- end = p + len;
- if (strncmp((char *)p, "-----", 5))
- goto bail;
- p += 5;
- while (p < end && *p != '\n' && *p != '-')
- p++;
-
- if (*p != '-')
- goto bail;
-
- while (p < end && *p != '\n')
- p++;
-
- if (p >= end)
- goto bail;
-
- p++;
-
- /* trim the last line */
-
- q = end - 2;
-
- while (q > pem && *q != '\n')
- q--;
-
- if (*q != '\n')
- goto bail;
-
- *q = '\0';
-
- *amount = lws_b64_decode_string((char *)p, (char *)pem, len);
- *buf = pem;
-
- return 0;
-
-bail:
- lws_free(pem);
-
- return 4;
-}
-#endif
-
-int openssl_websocket_private_data_index,
- openssl_SSL_CTX_private_data_index;
-
-int lws_ssl_get_error(struct lws *wsi, int n)
-{
- int m;
-
- if (!wsi->ssl)
- return 99;
-
- m = SSL_get_error(wsi->ssl, n);
- lwsl_debug("%s: %p %d -> %d\n", __func__, wsi->ssl, n, m);
-
- return m;
-}
-
-/* Copies a string describing the code returned by lws_ssl_get_error(),
- * which may also contain system error information in the case of SSL_ERROR_SYSCALL,
- * into buf up to len.
- * Returns a pointer to buf.
- *
- * Note: the lws_ssl_get_error() code is *not* an error code that can be passed
- * to ERR_error_string(),
- *
- * ret is the return value originally passed to lws_ssl_get_error(), needed to disambiguate
- * SYS_ERROR_SYSCALL.
- *
- * See man page for SSL_get_error().
- *
- * Not thread safe, uses strerror()
- */
-char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) {
- switch (status) {
- case SSL_ERROR_NONE: return strncpy(buf, "SSL_ERROR_NONE", len);
- case SSL_ERROR_ZERO_RETURN: return strncpy(buf, "SSL_ERROR_ZERO_RETURN", len);
- case SSL_ERROR_WANT_READ: return strncpy(buf, "SSL_ERROR_WANT_READ", len);
- case SSL_ERROR_WANT_WRITE: return strncpy(buf, "SSL_ERROR_WANT_WRITE", len);
- case SSL_ERROR_WANT_CONNECT: return strncpy(buf, "SSL_ERROR_WANT_CONNECT", len);
- case SSL_ERROR_WANT_ACCEPT: return strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len);
- case SSL_ERROR_WANT_X509_LOOKUP: return strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len);
- case SSL_ERROR_SYSCALL:
- switch (ret) {
- case 0:
- lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: EOF");
- return buf;
- case -1:
-#ifndef LWS_PLAT_OPTEE
- lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %s", strerror(errno));
-#else
- lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %d", errno);
-#endif
- return buf;
- default:
- return strncpy(buf, "SSL_ERROR_SYSCALL", len);
- }
- case SSL_ERROR_SSL: return "SSL_ERROR_SSL";
- default: return "SSL_ERROR_UNKNOWN";
- }
-}
-
-void
-lws_ssl_elaborate_error(void)
-{
-#if defined(LWS_WITH_MBEDTLS)
-#else
- char buf[256];
- u_long err;
-
- while ((err = ERR_get_error()) != 0) {
- ERR_error_string_n(err, buf, sizeof(buf));
- lwsl_info("*** %s\n", buf);
- }
-#endif
-}
-
-#if !defined(LWS_WITH_MBEDTLS)
-
-static int
-lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, void *userdata)
-{
- struct lws_context_creation_info * info =
- (struct lws_context_creation_info *)userdata;
-
- strncpy(buf, info->ssl_private_key_password, size);
- buf[size - 1] = '\0';
-
- return strlen(buf);
-}
-
-void
-lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info)
-{
- if (!info->ssl_private_key_password)
- return;
- /*
- * password provided, set ssl callback and user data
- * for checking password which will be trigered during
- * SSL_CTX_use_PrivateKey_file function
- */
- SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info);
- SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb);
-}
-#endif
-
-int
-lws_context_init_ssl_library(struct lws_context_creation_info *info)
-{
-#ifdef USE_WOLFSSL
-#ifdef USE_OLD_CYASSL
- lwsl_info(" Compiled with CyaSSL support\n");
-#else
- lwsl_info(" Compiled with wolfSSL support\n");
-#endif
-#else
-#if defined(LWS_WITH_BORINGSSL)
- lwsl_info(" Compiled with BoringSSL support\n");
-#else
-#if defined(LWS_WITH_MBEDTLS)
- lwsl_info(" Compiled with MbedTLS support\n");
-#else
- lwsl_info(" Compiled with OpenSSL support\n");
-#endif
-#endif
-#endif
- if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
- lwsl_info(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
- return 0;
- }
-
- /* basic openssl init */
-
- lwsl_info("Doing SSL library init\n");
-
-#if !defined(LWS_WITH_MBEDTLS)
- SSL_library_init();
- OpenSSL_add_all_algorithms();
- SSL_load_error_strings();
-
- openssl_websocket_private_data_index =
- SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL);
-
- openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0,
- NULL, NULL, NULL, NULL);
-#endif
-
- return 0;
-}
-
-LWS_VISIBLE void
-lws_ssl_destroy(struct lws_vhost *vhost)
-{
- if (!lws_check_opt(vhost->context->options,
- LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
- return;
-
- if (vhost->ssl_ctx)
- SSL_CTX_free(vhost->ssl_ctx);
- if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
- SSL_CTX_free(vhost->ssl_client_ctx);
-
-#if defined(LWS_WITH_MBEDTLS)
- if (vhost->x509_client_CA)
- X509_free(vhost->x509_client_CA);
-#else
-// after 1.1.0 no need
-#if (OPENSSL_VERSION_NUMBER < 0x10100000)
-// <= 1.0.1f = old api, 1.0.1g+ = new api
-#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
- ERR_remove_state(0);
-#else
-#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
- !defined(LIBRESSL_VERSION_NUMBER) && \
- !defined(OPENSSL_IS_BORINGSSL)
- ERR_remove_thread_state();
-#else
- ERR_remove_thread_state(NULL);
-#endif
-#endif
- // after 1.1.0 no need
-#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
- SSL_COMP_free_compression_methods();
-#endif
- ERR_free_strings();
- EVP_cleanup();
- CRYPTO_cleanup_all_ex_data();
-#endif
-#endif
-}
-
-int
-lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi)
-{
- struct lws_context_per_thread *pt = &context->pt[tsi];
- struct lws *wsi, *wsi_next;
-
- wsi = pt->pending_read_list;
- while (wsi) {
- wsi_next = wsi->pending_read_list_next;
- pt->fds[wsi->position_in_fds_table].revents |=
- pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
- if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN)
- return 1;
-
- wsi = wsi_next;
- }
-
- return 0;
-}
-
-LWS_VISIBLE void
-lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
-{
- struct lws_context *context = wsi->context;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-
- if (!wsi->pending_read_list_prev &&
- !wsi->pending_read_list_next &&
- pt->pending_read_list != wsi)
- /* we are not on the list */
- return;
-
- /* point previous guy's next to our next */
- if (!wsi->pending_read_list_prev)
- pt->pending_read_list = wsi->pending_read_list_next;
- else
- wsi->pending_read_list_prev->pending_read_list_next =
- wsi->pending_read_list_next;
-
- /* point next guy's previous to our previous */
- if (wsi->pending_read_list_next)
- wsi->pending_read_list_next->pending_read_list_prev =
- wsi->pending_read_list_prev;
-
- wsi->pending_read_list_prev = NULL;
- wsi->pending_read_list_next = NULL;
-}
-
-LWS_VISIBLE int
-lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
-{
- struct lws_context *context = wsi->context;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- int n = 0, m;
-
- if (!wsi->ssl)
- return lws_ssl_capable_read_no_ssl(wsi, buf, len);
-
- lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
-
- errno = 0;
- n = SSL_read(wsi->ssl, buf, len);
-#if defined(LWS_WITH_ESP32)
- if (!n && errno == ENOTCONN) {
- lwsl_debug("%p: SSL_read ENOTCONN\n", wsi);
- return LWS_SSL_CAPABLE_ERROR;
- }
-#endif
-#if defined(LWS_WITH_STATS)
- if (!wsi->seen_rx) {
- lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_MS_SSL_RX_DELAY,
- time_in_microseconds() - wsi->accept_start_us);
- lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNS_HAD_RX, 1);
- wsi->seen_rx = 1;
- }
-#endif
-
-
- lwsl_debug("%p: SSL_read says %d\n", wsi, n);
- /* manpage: returning 0 means connection shut down */
- if (!n || (n == -1 && errno == ENOTCONN)) {
- wsi->socket_is_permanently_unusable = 1;
-
- return LWS_SSL_CAPABLE_ERROR;
- }
-
- if (n < 0) {
- m = lws_ssl_get_error(wsi, n);
- lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
- if (m == SSL_ERROR_ZERO_RETURN ||
- m == SSL_ERROR_SYSCALL)
- return LWS_SSL_CAPABLE_ERROR;
-
- if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
- lwsl_debug("%s: WANT_READ\n", __func__);
- lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
- return LWS_SSL_CAPABLE_MORE_SERVICE;
- }
- if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
- lwsl_debug("%s: WANT_WRITE\n", __func__);
- lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
- return LWS_SSL_CAPABLE_MORE_SERVICE;
- }
- wsi->socket_is_permanently_unusable = 1;
-
- return LWS_SSL_CAPABLE_ERROR;
- }
-
- lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
-
- if (wsi->vhost)
- wsi->vhost->conn_stats.rx += n;
-
- lws_restart_ws_ping_pong_timer(wsi);
-
- /*
- * if it was our buffer that limited what we read,
- * check if SSL has additional data pending inside SSL buffers.
- *
- * Because these won't signal at the network layer with POLLIN
- * and if we don't realize, this data will sit there forever
- */
- if (n != len)
- goto bail;
- if (!wsi->ssl)
- goto bail;
-
- if (!SSL_pending(wsi->ssl))
- goto bail;
-
- if (wsi->pending_read_list_next)
- return n;
- if (wsi->pending_read_list_prev)
- return n;
- if (pt->pending_read_list == wsi)
- return n;
-
- /* add us to the linked list of guys with pending ssl */
- if (pt->pending_read_list)
- pt->pending_read_list->pending_read_list_prev = wsi;
-
- wsi->pending_read_list_next = pt->pending_read_list;
- wsi->pending_read_list_prev = NULL;
- pt->pending_read_list = wsi;
-
- return n;
-bail:
- lws_ssl_remove_wsi_from_buffered_list(wsi);
-
- return n;
-}
-
-LWS_VISIBLE int
-lws_ssl_pending(struct lws *wsi)
-{
- if (!wsi->ssl)
- return 0;
-
- return SSL_pending(wsi->ssl);
-}
-
-LWS_VISIBLE int
-lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
-{
- int n, m;
-
- if (!wsi->ssl)
- return lws_ssl_capable_write_no_ssl(wsi, buf, len);
-
- n = SSL_write(wsi->ssl, buf, len);
- if (n > 0)
- return n;
-
- m = lws_ssl_get_error(wsi, n);
- if (m != SSL_ERROR_SYSCALL) {
-
- if (SSL_want_read(wsi->ssl)) {
- lwsl_notice("%s: want read\n", __func__);
-
- return LWS_SSL_CAPABLE_MORE_SERVICE;
- }
-
- if (SSL_want_write(wsi->ssl)) {
- lws_set_blocking_send(wsi);
-
- lwsl_notice("%s: want write\n", __func__);
-
- return LWS_SSL_CAPABLE_MORE_SERVICE;
- }
- }
-
- lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL));
- lws_ssl_elaborate_error();
-
- wsi->socket_is_permanently_unusable = 1;
-
- return LWS_SSL_CAPABLE_ERROR;
-}
-
-static int
-lws_gate_accepts(struct lws_context *context, int on)
-{
- struct lws_vhost *v = context->vhost_list;
-
- lwsl_info("gating accepts %d\n", on);
- context->ssl_gate_accepts = !on;
-#if defined(LWS_WITH_STATS)
- context->updated = 1;
-#endif
-
- while (v) {
- if (v->use_ssl && v->lserv_wsi) /* gate ability to accept incoming connections */
- if (lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on,
- (LWS_POLLIN) * on))
- lwsl_info("Unable to set accept POLLIN %d\n", on);
-
- v = v->vhost_next;
- }
-
- return 0;
-}
-
-void
-lws_ssl_info_callback(const SSL *ssl, int where, int ret)
-{
- struct lws *wsi;
- struct lws_context *context;
- struct lws_ssl_info si;
-
- context = (struct lws_context *)SSL_CTX_get_ex_data(
- SSL_get_SSL_CTX(ssl),
- openssl_SSL_CTX_private_data_index);
- if (!context)
- return;
- wsi = wsi_from_fd(context, SSL_get_fd(ssl));
- if (!wsi)
- return;
-
- if (!(where & wsi->vhost->ssl_info_event_mask))
- return;
-
- si.where = where;
- si.ret = ret;
-
- if (user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_SSL_INFO,
- wsi->user_space, &si, 0))
- lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1);
-}
-
-
-LWS_VISIBLE int
-lws_ssl_close(struct lws *wsi)
-{
- lws_sockfd_type n;
-
- if (!wsi->ssl)
- return 0; /* not handled */
-
-#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
- /* kill ssl callbacks, becausse we will remove the fd from the
- * table linking it to the wsi
- */
- if (wsi->vhost->ssl_info_event_mask)
- SSL_set_info_callback(wsi->ssl, NULL);
-#endif
-
- n = SSL_get_fd(wsi->ssl);
- if (!wsi->socket_is_permanently_unusable)
- SSL_shutdown(wsi->ssl);
- compatible_close(n);
- SSL_free(wsi->ssl);
- wsi->ssl = NULL;
-
- if (wsi->context->simultaneous_ssl_restriction &&
- wsi->context->simultaneous_ssl-- ==
- wsi->context->simultaneous_ssl_restriction)
- /* we made space and can do an accept */
- lws_gate_accepts(wsi->context, 1);
-#if defined(LWS_WITH_STATS)
- wsi->context->updated = 1;
-#endif
-
- return 1; /* handled */
-}
-
-/* leave all wsi close processing to the caller */
-
-LWS_VISIBLE int
-lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
-{
- struct lws_context *context = wsi->context;
- struct lws_vhost *vh;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- int n, m;
-#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
- BIO *bio;
-#endif
- char buf[256];
-
- (void)buf;
-
- if (!LWS_SSL_ENABLED(wsi->vhost))
- return 0;
-
- switch (wsi->mode) {
- case LWSCM_SSL_INIT:
- case LWSCM_SSL_INIT_RAW:
- if (wsi->ssl)
- lwsl_err("%s: leaking ssl\n", __func__);
- if (accept_fd == LWS_SOCK_INVALID)
- assert(0);
- if (context->simultaneous_ssl_restriction &&
- context->simultaneous_ssl >= context->simultaneous_ssl_restriction) {
- lwsl_notice("unable to deal with SSL connection\n");
- return 1;
- }
- errno = 0;
- wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
- if (wsi->ssl == NULL) {
- lwsl_err("SSL_new failed: %d (errno %d)\n",
- lws_ssl_get_error(wsi, 0), errno);
-
- lws_ssl_elaborate_error();
- if (accept_fd != LWS_SOCK_INVALID)
- compatible_close(accept_fd);
- goto fail;
- }
-#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
- if (wsi->vhost->ssl_info_event_mask)
- SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
-#endif
- if (context->simultaneous_ssl_restriction &&
- ++context->simultaneous_ssl == context->simultaneous_ssl_restriction)
- /* that was the last allowed SSL connection */
- lws_gate_accepts(context, 0);
-#if defined(LWS_WITH_STATS)
- context->updated = 1;
-#endif
-
-#if !defined(LWS_WITH_MBEDTLS)
- SSL_set_ex_data(wsi->ssl,
- openssl_websocket_private_data_index, wsi);
-#endif
- SSL_set_fd(wsi->ssl, accept_fd);
-
-#ifdef USE_WOLFSSL
-#ifdef USE_OLD_CYASSL
- CyaSSL_set_using_nonblock(wsi->ssl, 1);
-#else
- wolfSSL_set_using_nonblock(wsi->ssl, 1);
-#endif
-#else
-#if defined(LWS_WITH_MBEDTLS)
- lws_plat_set_socket_options(wsi->vhost, accept_fd);
-#else
- SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- bio = SSL_get_rbio(wsi->ssl);
- if (bio)
- BIO_set_nbio(bio, 1); /* nonblocking */
- else
- lwsl_notice("NULL rbio\n");
- bio = SSL_get_wbio(wsi->ssl);
- if (bio)
- BIO_set_nbio(bio, 1); /* nonblocking */
- else
- lwsl_notice("NULL rbio\n");
-#endif
-#endif
-
- /*
- * we are not accepted yet, but we need to enter ourselves
- * as a live connection. That way we can retry when more
- * pieces come if we're not sorted yet
- */
-
- if (wsi->mode == LWSCM_SSL_INIT)
- wsi->mode = LWSCM_SSL_ACK_PENDING;
- else
- wsi->mode = LWSCM_SSL_ACK_PENDING_RAW;
-
- if (insert_wsi_socket_into_fds(context, wsi)) {
- lwsl_err("%s: failed to insert into fds\n", __func__);
- goto fail;
- }
-
- lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
- context->timeout_secs);
-
- lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n");
-
- /* fallthru */
-
- case LWSCM_SSL_ACK_PENDING:
- case LWSCM_SSL_ACK_PENDING_RAW:
- if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
- lwsl_err("%s: lws_change_pollfd failed\n", __func__);
- goto fail;
- }
-
- lws_latency_pre(context, wsi);
-
- if (wsi->vhost->allow_non_ssl_on_ssl_port) {
-
- n = recv(wsi->desc.sockfd, (char *)pt->serv_buf,
- context->pt_serv_buf_size, MSG_PEEK);
-
- /*
- * optionally allow non-SSL connect on SSL listening socket
- * This is disabled by default, if enabled it goes around any
- * SSL-level access control (eg, client-side certs) so leave
- * it disabled unless you know it's not a problem for you
- */
-
- if (n >= 1 && pt->serv_buf[0] >= ' ') {
- /*
- * TLS content-type for Handshake is 0x16, and
- * for ChangeCipherSpec Record, it's 0x14
- *
- * A non-ssl session will start with the HTTP
- * method in ASCII. If we see it's not a legit
- * SSL handshake kill the SSL for this
- * connection and try to handle as a HTTP
- * connection upgrade directly.
- */
- wsi->use_ssl = 0;
-
- SSL_shutdown(wsi->ssl);
- SSL_free(wsi->ssl);
- wsi->ssl = NULL;
- if (lws_check_opt(context->options,
- LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
- wsi->redirect_to_https = 1;
- goto accepted;
- }
- if (!n) /*
- * connection is gone, or nothing to read
- * if it's gone, we will timeout on
- * PENDING_TIMEOUT_SSL_ACCEPT
- */
- break;
- if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
- LWS_ERRNO == LWS_EWOULDBLOCK)) {
- /*
- * well, we get no way to know ssl or not
- * so go around again waiting for something
- * to come and give us a hint, or timeout the
- * connection.
- */
- m = SSL_ERROR_WANT_READ;
- goto go_again;
- }
- }
-
- /* normal SSL connection processing path */
-
-#if defined(LWS_WITH_STATS)
- if (!wsi->accept_start_us)
- wsi->accept_start_us = time_in_microseconds();
-#endif
- errno = 0;
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
- n = SSL_accept(wsi->ssl);
- lws_latency(context, wsi,
- "SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1);
- lwsl_info("SSL_accept says %d\n", n);
- if (n == 1)
- goto accepted;
-
- m = lws_ssl_get_error(wsi, n);
-
-#if defined(LWS_WITH_MBEDTLS)
- if (m == SSL_ERROR_SYSCALL && errno == 11)
- m = SSL_ERROR_WANT_READ;
-#endif
- if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
- goto failed;
-
-go_again:
- if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
- if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
- lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__);
- goto fail;
- }
-
- lwsl_info("SSL_ERROR_WANT_READ\n");
- break;
- }
- if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
- lwsl_debug("%s: WANT_WRITE\n", __func__);
-
- if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
- lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__);
- goto fail;
- }
-
- break;
- }
-failed:
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
- wsi->socket_is_permanently_unusable = 1;
- lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd,
- lws_ssl_get_error_string(m, n, buf, sizeof(buf)));
- lws_ssl_elaborate_error();
- goto fail;
-
-accepted:
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1);
-#if defined(LWS_WITH_STATS)
- lws_stats_atomic_bump(wsi->context, pt,
- LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY,
- time_in_microseconds() - wsi->accept_start_us);
- wsi->accept_start_us = time_in_microseconds();
-#endif
-
- /* adapt our vhost to match the SNI SSL_CTX that was chosen */
- vh = context->vhost_list;
- while (vh) {
- if (!vh->being_destroyed && wsi->ssl &&
- vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) {
- lwsl_info("setting wsi to vh %s\n", vh->name);
- wsi->vhost = vh;
- break;
- }
- vh = vh->vhost_next;
- }
-
- /* OK, we are accepted... give him some time to negotiate */
- lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
- context->timeout_secs);
-
- if (wsi->mode == LWSCM_SSL_ACK_PENDING_RAW)
- wsi->mode = LWSCM_RAW;
- else
- wsi->mode = LWSCM_HTTP_SERVING;
-#if defined(LWS_WITH_HTTP2)
- if (lws_h2_configure_if_upgraded(wsi))
- goto fail;
-#endif
- lwsl_debug("accepted new SSL conn\n");
- break;
- }
-
- return 0;
-
-fail:
- return 1;
-}
-
-void
-lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
-{
- if (vhost->ssl_ctx)
- SSL_CTX_free(vhost->ssl_ctx);
-
- if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
- SSL_CTX_free(vhost->ssl_client_ctx);
-}
-
-void
-lws_ssl_context_destroy(struct lws_context *context)
-{
-
-#if !defined(LWS_WITH_MBEDTLS)
-
-// after 1.1.0 no need
-#if (OPENSSL_VERSION_NUMBER < 0x10100000)
-// <= 1.0.1f = old api, 1.0.1g+ = new api
-#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
- ERR_remove_state(0);
-#else
-#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
- !defined(LIBRESSL_VERSION_NUMBER) && \
- !defined(OPENSSL_IS_BORINGSSL)
- ERR_remove_thread_state();
-#else
- ERR_remove_thread_state(NULL);
-#endif
-#endif
- // after 1.1.0 no need
-#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
- SSL_COMP_free_compression_methods();
-#endif
- ERR_free_strings();
- EVP_cleanup();
- CRYPTO_cleanup_all_ex_data();
-#endif
-#endif
-}
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/LICENSE b/thirdparty/mbedtls/LICENSE
new file mode 100644
index 0000000000..546a8e631f
--- /dev/null
+++ b/thirdparty/mbedtls/LICENSE
@@ -0,0 +1,2 @@
+Unless specifically indicated otherwise in a file, files are licensed
+under the Apache 2.0 license, as can be found in: apache-2.0.txt
diff --git a/thirdparty/mbedtls/apache-2.0.txt b/thirdparty/mbedtls/apache-2.0.txt
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/thirdparty/mbedtls/apache-2.0.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
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/miniupnpc/LICENSE b/thirdparty/miniupnpc/LICENSE
new file mode 100644
index 0000000000..0816733704
--- /dev/null
+++ b/thirdparty/miniupnpc/LICENSE
@@ -0,0 +1,27 @@
+MiniUPnPc
+Copyright (c) 2005-2016, Thomas BERNARD
+All rights reserved.
+
+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.
+ * The name of the author may not 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.
+
diff --git a/thirdparty/miniupnpc/codelength.h b/thirdparty/miniupnpc/codelength.h
new file mode 100644
index 0000000000..ea0b005ffe
--- /dev/null
+++ b/thirdparty/miniupnpc/codelength.h
@@ -0,0 +1,54 @@
+/* $Id: codelength.h,v 1.3 2011/07/30 13:10:05 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef CODELENGTH_H_INCLUDED
+#define CODELENGTH_H_INCLUDED
+
+/* Encode length by using 7bit per Byte :
+ * Most significant bit of each byte specifies that the
+ * following byte is part of the code */
+
+/* n : unsigned
+ * p : unsigned char *
+ */
+#define DECODELENGTH(n, p) n = 0; \
+ do { n = (n << 7) | (*p & 0x7f); } \
+ while((*(p++)&0x80) && (n<(1<<25)));
+
+/* n : unsigned
+ * READ : function/macro to read one byte (unsigned char)
+ */
+#define DECODELENGTH_READ(n, READ) \
+ n = 0; \
+ do { \
+ unsigned char c; \
+ READ(c); \
+ n = (n << 7) | (c & 0x07f); \
+ if(!(c&0x80)) break; \
+ } while(n<(1<<25));
+
+/* n : unsigned
+ * p : unsigned char *
+ * p_limit : unsigned char *
+ */
+#define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \
+ n = 0; \
+ do { \
+ if((p) >= (p_limit)) break; \
+ n = (n << 7) | (*(p) & 0x7f); \
+ } while((*((p)++)&0x80) && (n<(1<<25)));
+
+
+/* n : unsigned
+ * p : unsigned char *
+ */
+#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
+ if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
+ if(n>=16384) *(p++) = (n >> 14) | 0x80; \
+ if(n>=128) *(p++) = (n >> 7) | 0x80; \
+ *(p++) = n & 0x7f;
+
+#endif /* CODELENGTH_H_INCLUDED */
diff --git a/thirdparty/miniupnpc/connecthostport.c b/thirdparty/miniupnpc/connecthostport.c
new file mode 100644
index 0000000000..ea6e4e5943
--- /dev/null
+++ b/thirdparty/miniupnpc/connecthostport.c
@@ -0,0 +1,264 @@
+/* $Id: connecthostport.c,v 1.15 2015/10/09 16:26:19 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2010-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+/* use getaddrinfo() or gethostbyname()
+ * uncomment the following line in order to use gethostbyname() */
+#ifdef NO_GETADDRINFO
+#define USE_GETHOSTBYNAME
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define MAXHOSTNAMELEN 64
+#define snprintf _snprintf
+#define herror
+#define socklen_t int
+#else /* #ifdef _WIN32 */
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+#include <sys/time.h>
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+#include <sys/param.h>
+#include <sys/select.h>
+#include <errno.h>
+#define closesocket close
+#include <netdb.h>
+#include <netinet/in.h>
+/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
+ * during the connect() call */
+#define MINIUPNPC_IGNORE_EINTR
+#include <sys/socket.h>
+#include <sys/select.h>
+#endif /* #else _WIN32 */
+
+/* definition of PRINT_SOCKET_ERROR */
+#ifdef _WIN32
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#if defined(__amigaos__) || defined(__amigaos4__)
+#define herror(A) printf("%s\n", A)
+#endif
+
+#include "connecthostport.h"
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+/* connecthostport()
+ * return a socket connected (TCP) to the host and port
+ * or -1 in case of error */
+SOCKET connecthostport(const char * host, unsigned short port,
+ unsigned int scope_id)
+{
+ SOCKET s;
+ int n;
+#ifdef USE_GETHOSTBYNAME
+ struct sockaddr_in dest;
+ struct hostent *hp;
+#else /* #ifdef USE_GETHOSTBYNAME */
+ char tmp_host[MAXHOSTNAMELEN+1];
+ char port_str[8];
+ struct addrinfo *ai, *p;
+ struct addrinfo hints;
+#endif /* #ifdef USE_GETHOSTBYNAME */
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ struct timeval timeout;
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+
+#ifdef USE_GETHOSTBYNAME
+ hp = gethostbyname(host);
+ if(hp == NULL)
+ {
+ herror(host);
+ return INVALID_SOCKET;
+ }
+ memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
+ memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if(ISINVALID(s))
+ {
+ PRINT_SOCKET_ERROR("socket");
+ return INVALID_SOCKET;
+ }
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ /* setting a 3 seconds timeout for the connect() call */
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO");
+ }
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO");
+ }
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+ dest.sin_family = AF_INET;
+ dest.sin_port = htons(port);
+ n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
+#ifdef MINIUPNPC_IGNORE_EINTR
+ /* EINTR The system call was interrupted by a signal that was caught
+ * EINPROGRESS The socket is nonblocking and the connection cannot
+ * be completed immediately. */
+ while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
+ {
+ socklen_t len;
+ fd_set wset;
+ int err;
+ FD_ZERO(&wset);
+ FD_SET(s, &wset);
+ if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
+ continue;
+ /*len = 0;*/
+ /*n = getpeername(s, NULL, &len);*/
+ len = sizeof(err);
+ if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+ PRINT_SOCKET_ERROR("getsockopt");
+ closesocket(s);
+ return INVALID_SOCKET;
+ }
+ if(err != 0) {
+ errno = err;
+ n = -1;
+ }
+ }
+#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
+ if(n<0)
+ {
+ PRINT_SOCKET_ERROR("connect");
+ closesocket(s);
+ return INVALID_SOCKET;
+ }
+#else /* #ifdef USE_GETHOSTBYNAME */
+ /* use getaddrinfo() instead of gethostbyname() */
+ memset(&hints, 0, sizeof(hints));
+ /* hints.ai_flags = AI_ADDRCONFIG; */
+#ifdef AI_NUMERICSERV
+ hints.ai_flags = AI_NUMERICSERV;
+#endif
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
+ /* hints.ai_protocol = IPPROTO_TCP; */
+ snprintf(port_str, sizeof(port_str), "%hu", port);
+ if(host[0] == '[')
+ {
+ /* literal ip v6 address */
+ int i, j;
+ for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++)
+ {
+ tmp_host[i] = host[j];
+ if(0 == memcmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */
+ j+=2; /* skip "25" */
+ }
+ tmp_host[i] = '\0';
+ }
+ else
+ {
+ strncpy(tmp_host, host, MAXHOSTNAMELEN);
+ }
+ tmp_host[MAXHOSTNAMELEN] = '\0';
+ n = getaddrinfo(tmp_host, port_str, &hints, &ai);
+ if(n != 0)
+ {
+#ifdef _WIN32
+ fprintf(stderr, "getaddrinfo() error : %d\n", n);
+#else
+ fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
+#endif
+ return INVALID_SOCKET;
+ }
+ s = -1;
+ for(p = ai; p; p = p->ai_next)
+ {
+ s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if(ISINVALID(s))
+ continue;
+ if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) {
+ struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr;
+ addr6->sin6_scope_id = scope_id;
+ }
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ /* setting a 3 seconds timeout for the connect() call */
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+ n = connect(s, p->ai_addr, p->ai_addrlen);
+#ifdef MINIUPNPC_IGNORE_EINTR
+ /* EINTR The system call was interrupted by a signal that was caught
+ * EINPROGRESS The socket is nonblocking and the connection cannot
+ * be completed immediately. */
+ while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
+ {
+ socklen_t len;
+ fd_set wset;
+ int err;
+ FD_ZERO(&wset);
+ FD_SET(s, &wset);
+ if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
+ continue;
+ /*len = 0;*/
+ /*n = getpeername(s, NULL, &len);*/
+ len = sizeof(err);
+ if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+ PRINT_SOCKET_ERROR("getsockopt");
+ closesocket(s);
+ freeaddrinfo(ai);
+ return INVALID_SOCKET;
+ }
+ if(err != 0) {
+ errno = err;
+ n = -1;
+ }
+ }
+#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
+ if(n < 0)
+ {
+ closesocket(s);
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+ freeaddrinfo(ai);
+ if(ISINVALID(s))
+ {
+ PRINT_SOCKET_ERROR("socket");
+ return INVALID_SOCKET;
+ }
+ if(n < 0)
+ {
+ PRINT_SOCKET_ERROR("connect");
+ return INVALID_SOCKET;
+ }
+#endif /* #ifdef USE_GETHOSTBYNAME */
+ return s;
+}
+
diff --git a/thirdparty/miniupnpc/connecthostport.h b/thirdparty/miniupnpc/connecthostport.h
new file mode 100644
index 0000000000..701816b5b6
--- /dev/null
+++ b/thirdparty/miniupnpc/connecthostport.h
@@ -0,0 +1,20 @@
+/* $Id: connecthostport.h,v 1.2 2012/06/23 22:32:33 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2010-2018 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef CONNECTHOSTPORT_H_INCLUDED
+#define CONNECTHOSTPORT_H_INCLUDED
+
+#include "miniupnpc_socketdef.h"
+
+/* connecthostport()
+ * return a socket connected (TCP) to the host and port
+ * or INVALID_SOCKET in case of error */
+SOCKET connecthostport(const char * host, unsigned short port,
+ unsigned int scope_id);
+
+#endif
+
diff --git a/thirdparty/miniupnpc/igd_desc_parse.c b/thirdparty/miniupnpc/igd_desc_parse.c
new file mode 100644
index 0000000000..d2999ad011
--- /dev/null
+++ b/thirdparty/miniupnpc/igd_desc_parse.c
@@ -0,0 +1,123 @@
+/* $Id: igd_desc_parse.c,v 1.17 2015/09/15 13:30:04 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include "igd_desc_parse.h"
+#include <stdio.h>
+#include <string.h>
+
+/* Start element handler :
+ * update nesting level counter and copy element name */
+void IGDstartelt(void * d, const char * name, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ if(l >= MINIUPNPC_URL_MAXSIZE)
+ l = MINIUPNPC_URL_MAXSIZE-1;
+ memcpy(datas->cureltname, name, l);
+ datas->cureltname[l] = '\0';
+ datas->level++;
+ if( (l==7) && !memcmp(name, "service", l) ) {
+ datas->tmp.controlurl[0] = '\0';
+ datas->tmp.eventsuburl[0] = '\0';
+ datas->tmp.scpdurl[0] = '\0';
+ datas->tmp.servicetype[0] = '\0';
+ }
+}
+
+#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
+
+/* End element handler :
+ * update nesting level counter and update parser state if
+ * service element is parsed */
+void IGDendelt(void * d, const char * name, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ datas->level--;
+ /*printf("endelt %2d %.*s\n", datas->level, l, name);*/
+ if( (l==7) && !memcmp(name, "service", l) )
+ {
+ if(COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) {
+ memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else if(COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANIPv6FirewallControl:")) {
+ memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else if(COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANIPConnection:")
+ || COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANPPPConnection:") ) {
+ if(datas->first.servicetype[0] == '\0') {
+ memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else {
+ memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
+ }
+ }
+ }
+}
+
+/* Data handler :
+ * copy data depending on the current element name and state */
+void IGDdata(void * d, const char * data, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ char * dstmember = 0;
+ /*printf("%2d %s : %.*s\n",
+ datas->level, datas->cureltname, l, data); */
+ if( !strcmp(datas->cureltname, "URLBase") )
+ dstmember = datas->urlbase;
+ else if( !strcmp(datas->cureltname, "presentationURL") )
+ dstmember = datas->presentationurl;
+ else if( !strcmp(datas->cureltname, "serviceType") )
+ dstmember = datas->tmp.servicetype;
+ else if( !strcmp(datas->cureltname, "controlURL") )
+ dstmember = datas->tmp.controlurl;
+ else if( !strcmp(datas->cureltname, "eventSubURL") )
+ dstmember = datas->tmp.eventsuburl;
+ else if( !strcmp(datas->cureltname, "SCPDURL") )
+ dstmember = datas->tmp.scpdurl;
+/* else if( !strcmp(datas->cureltname, "deviceType") )
+ dstmember = datas->devicetype_tmp;*/
+ if(dstmember)
+ {
+ if(l>=MINIUPNPC_URL_MAXSIZE)
+ l = MINIUPNPC_URL_MAXSIZE-1;
+ memcpy(dstmember, data, l);
+ dstmember[l] = '\0';
+ }
+}
+
+#ifdef DEBUG
+void printIGD(struct IGDdatas * d)
+{
+ printf("urlbase = '%s'\n", d->urlbase);
+ printf("WAN Device (Common interface config) :\n");
+ /*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/
+ printf(" serviceType = '%s'\n", d->CIF.servicetype);
+ printf(" controlURL = '%s'\n", d->CIF.controlurl);
+ printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
+ printf("primary WAN Connection Device (IP or PPP Connection):\n");
+ /*printf(" deviceType = '%s'\n", d->first.devicetype);*/
+ printf(" servicetype = '%s'\n", d->first.servicetype);
+ printf(" controlURL = '%s'\n", d->first.controlurl);
+ printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->first.scpdurl);
+ printf("secondary WAN Connection Device (IP or PPP Connection):\n");
+ /*printf(" deviceType = '%s'\n", d->second.devicetype);*/
+ printf(" servicetype = '%s'\n", d->second.servicetype);
+ printf(" controlURL = '%s'\n", d->second.controlurl);
+ printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->second.scpdurl);
+ printf("WAN IPv6 Firewall Control :\n");
+ /*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
+ printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
+ printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
+ printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
+}
+#endif /* DEBUG */
+
diff --git a/thirdparty/miniupnpc/igd_desc_parse.h b/thirdparty/miniupnpc/igd_desc_parse.h
new file mode 100644
index 0000000000..0de546b697
--- /dev/null
+++ b/thirdparty/miniupnpc/igd_desc_parse.h
@@ -0,0 +1,49 @@
+/* $Id: igd_desc_parse.h,v 1.12 2014/11/17 17:19:13 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2014 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef IGD_DESC_PARSE_H_INCLUDED
+#define IGD_DESC_PARSE_H_INCLUDED
+
+/* Structure to store the result of the parsing of UPnP
+ * descriptions of Internet Gateway Devices */
+#define MINIUPNPC_URL_MAXSIZE (128)
+struct IGDdatas_service {
+ char controlurl[MINIUPNPC_URL_MAXSIZE];
+ char eventsuburl[MINIUPNPC_URL_MAXSIZE];
+ char scpdurl[MINIUPNPC_URL_MAXSIZE];
+ char servicetype[MINIUPNPC_URL_MAXSIZE];
+ /*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
+};
+
+struct IGDdatas {
+ char cureltname[MINIUPNPC_URL_MAXSIZE];
+ char urlbase[MINIUPNPC_URL_MAXSIZE];
+ char presentationurl[MINIUPNPC_URL_MAXSIZE];
+ int level;
+ /*int state;*/
+ /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
+ struct IGDdatas_service CIF;
+ /* "urn:schemas-upnp-org:service:WANIPConnection:1"
+ * "urn:schemas-upnp-org:service:WANPPPConnection:1" */
+ struct IGDdatas_service first;
+ /* if both WANIPConnection and WANPPPConnection are present */
+ struct IGDdatas_service second;
+ /* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
+ struct IGDdatas_service IPv6FC;
+ /* tmp */
+ struct IGDdatas_service tmp;
+};
+
+void IGDstartelt(void *, const char *, int);
+void IGDendelt(void *, const char *, int);
+void IGDdata(void *, const char *, int);
+#ifdef DEBUG
+void printIGD(struct IGDdatas *);
+#endif /* DEBUG */
+
+#endif /* IGD_DESC_PARSE_H_INCLUDED */
diff --git a/thirdparty/miniupnpc/listdevices.c b/thirdparty/miniupnpc/listdevices.c
new file mode 100644
index 0000000000..bd9ba57efc
--- /dev/null
+++ b/thirdparty/miniupnpc/listdevices.c
@@ -0,0 +1,197 @@
+/* $Id: listdevices.c,v 1.6 2015/07/23 20:40:08 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2013-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#endif /* _WIN32 */
+#include "miniupnpc.h"
+
+struct upnp_dev_list {
+ struct upnp_dev_list * next;
+ char * descURL;
+ struct UPNPDev * * array;
+ size_t count;
+ size_t allocated_count;
+};
+
+#define ADD_DEVICE_COUNT_STEP 16
+
+void add_device(struct upnp_dev_list * * list_head, struct UPNPDev * dev)
+{
+ struct upnp_dev_list * elt;
+ size_t i;
+
+ if(dev == NULL)
+ return;
+ for(elt = *list_head; elt != NULL; elt = elt->next) {
+ if(strcmp(elt->descURL, dev->descURL) == 0) {
+ for(i = 0; i < elt->count; i++) {
+ if (strcmp(elt->array[i]->st, dev->st) == 0 && strcmp(elt->array[i]->usn, dev->usn) == 0) {
+ return; /* already found */
+ }
+ }
+ if(elt->count >= elt->allocated_count) {
+ struct UPNPDev * * tmp;
+ elt->allocated_count += ADD_DEVICE_COUNT_STEP;
+ tmp = realloc(elt->array, elt->allocated_count * sizeof(struct UPNPDev *));
+ if(tmp == NULL) {
+ fprintf(stderr, "Failed to realloc(%p, %lu)\n", elt->array, (unsigned long)(elt->allocated_count * sizeof(struct UPNPDev *)));
+ return;
+ }
+ elt->array = tmp;
+ }
+ elt->array[elt->count++] = dev;
+ return;
+ }
+ }
+ elt = malloc(sizeof(struct upnp_dev_list));
+ if(elt == NULL) {
+ fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)sizeof(struct upnp_dev_list));
+ return;
+ }
+ elt->next = *list_head;
+ elt->descURL = strdup(dev->descURL);
+ if(elt->descURL == NULL) {
+ fprintf(stderr, "Failed to strdup(%s)\n", dev->descURL);
+ free(elt);
+ return;
+ }
+ elt->allocated_count = ADD_DEVICE_COUNT_STEP;
+ elt->array = malloc(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *));
+ if(elt->array == NULL) {
+ fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *)));
+ free(elt->descURL);
+ free(elt);
+ return;
+ }
+ elt->array[0] = dev;
+ elt->count = 1;
+ *list_head = elt;
+}
+
+void free_device(struct upnp_dev_list * elt)
+{
+ free(elt->descURL);
+ free(elt->array);
+ free(elt);
+}
+
+int main(int argc, char * * argv)
+{
+ const char * searched_device = NULL;
+ const char * * searched_devices = NULL;
+ const char * multicastif = 0;
+ const char * minissdpdpath = 0;
+ int ipv6 = 0;
+ unsigned char ttl = 2;
+ int error = 0;
+ struct UPNPDev * devlist = 0;
+ struct UPNPDev * dev;
+ struct upnp_dev_list * sorted_list = NULL;
+ struct upnp_dev_list * dev_array;
+ int i;
+
+#ifdef _WIN32
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+ if(nResult != NO_ERROR)
+ {
+ fprintf(stderr, "WSAStartup() failed.\n");
+ return -1;
+ }
+#endif
+
+ for(i = 1; i < argc; i++) {
+ if(strcmp(argv[i], "-6") == 0)
+ ipv6 = 1;
+ else if(strcmp(argv[i], "-d") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "%s option needs one argument\n", "-d");
+ return 1;
+ }
+ searched_device = argv[i];
+ } else if(strcmp(argv[i], "-t") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "%s option needs one argument\n", "-t");
+ return 1;
+ }
+ ttl = (unsigned char)atoi(argv[i]);
+ } else if(strcmp(argv[i], "-l") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "-l option needs at least one argument\n");
+ return 1;
+ }
+ searched_devices = (const char * *)(argv + i);
+ break;
+ } else if(strcmp(argv[i], "-m") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "-m option needs one argument\n");
+ return 1;
+ }
+ multicastif = argv[i];
+ } else {
+ printf("usage : %s [options] [-l <device1> <device2> ...]\n", argv[0]);
+ printf("options :\n");
+ printf(" -6 : use IPv6\n");
+ printf(" -m address/ifname : network interface to use for multicast\n");
+ printf(" -d <device string> : search only for this type of device\n");
+ printf(" -l <device1> <device2> ... : search only for theses types of device\n");
+ printf(" -t ttl : set multicast TTL. Default value is 2.\n");
+ printf(" -h : this help\n");
+ return 1;
+ }
+ }
+
+ if(searched_device) {
+ printf("searching UPnP device type %s\n", searched_device);
+ devlist = upnpDiscoverDevice(searched_device,
+ 2000, multicastif, minissdpdpath,
+ 0/*localport*/, ipv6, ttl, &error);
+ } else if(searched_devices) {
+ printf("searching UPnP device types :\n");
+ for(i = 0; searched_devices[i]; i++)
+ printf("\t%s\n", searched_devices[i]);
+ devlist = upnpDiscoverDevices(searched_devices,
+ 2000, multicastif, minissdpdpath,
+ 0/*localport*/, ipv6, ttl, &error, 1);
+ } else {
+ printf("searching all UPnP devices\n");
+ devlist = upnpDiscoverAll(2000, multicastif, minissdpdpath,
+ 0/*localport*/, ipv6, ttl, &error);
+ }
+ if(devlist) {
+ for(dev = devlist, i = 1; dev != NULL; dev = dev->pNext, i++) {
+ printf("%3d: %-48s\n", i, dev->st);
+ printf(" %s\n", dev->descURL);
+ printf(" %s\n", dev->usn);
+ add_device(&sorted_list, dev);
+ }
+ putchar('\n');
+ for (dev_array = sorted_list; dev_array != NULL ; dev_array = dev_array->next) {
+ printf("%s :\n", dev_array->descURL);
+ for(i = 0; (unsigned)i < dev_array->count; i++) {
+ printf("%2d: %s\n", i+1, dev_array->array[i]->st);
+ printf(" %s\n", dev_array->array[i]->usn);
+ }
+ putchar('\n');
+ }
+ freeUPNPDevlist(devlist);
+ while(sorted_list != NULL) {
+ dev_array = sorted_list;
+ sorted_list = sorted_list->next;
+ free_device(dev_array);
+ }
+ } else {
+ printf("no device found.\n");
+ }
+
+ return 0;
+}
+
diff --git a/thirdparty/miniupnpc/minisoap.c b/thirdparty/miniupnpc/minisoap.c
new file mode 100644
index 0000000000..520c9302e8
--- /dev/null
+++ b/thirdparty/miniupnpc/minisoap.c
@@ -0,0 +1,124 @@
+/* $Id: minisoap.c,v 1.25 2017/04/21 10:03:24 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ *
+ * Minimal SOAP implementation for UPnP protocol.
+ */
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <io.h>
+#include <winsock2.h>
+#define snprintf _snprintf
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+#include "minisoap.h"
+#include "miniupnpcstrings.h"
+
+/* only for malloc */
+#include <stdlib.h>
+
+#ifdef _WIN32
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+/* httpWrite sends the headers and the body to the socket
+ * and returns the number of bytes sent */
+static int
+httpWrite(SOCKET fd, const char * body, int bodysize,
+ const char * headers, int headerssize)
+{
+ int n = 0;
+ /*n = write(fd, headers, headerssize);*/
+ /*if(bodysize>0)
+ n += write(fd, body, bodysize);*/
+ /* Note : my old linksys router only took into account
+ * soap request that are sent into only one packet */
+ char * p;
+ /* TODO: AVOID MALLOC, we could use writev() for that */
+ p = malloc(headerssize+bodysize);
+ if(!p)
+ return -1;
+ memcpy(p, headers, headerssize);
+ memcpy(p+headerssize, body, bodysize);
+ /*n = write(fd, p, headerssize+bodysize);*/
+ n = send(fd, p, headerssize+bodysize, 0);
+ if(n<0) {
+ PRINT_SOCKET_ERROR("send");
+ }
+ /* disable send on the socket */
+ /* draytek routers don't seem to like that... */
+#if 0
+#ifdef _WIN32
+ if(shutdown(fd, SD_SEND)<0) {
+#else
+ if(shutdown(fd, SHUT_WR)<0) { /*SD_SEND*/
+#endif
+ PRINT_SOCKET_ERROR("shutdown");
+ }
+#endif
+ free(p);
+ return n;
+}
+
+/* self explanatory */
+int soapPostSubmit(SOCKET fd,
+ const char * url,
+ const char * host,
+ unsigned short port,
+ const char * action,
+ const char * body,
+ const char * httpversion)
+{
+ int bodysize;
+ char headerbuf[512];
+ int headerssize;
+ char portstr[8];
+ bodysize = (int)strlen(body);
+ /* We are not using keep-alive HTTP connections.
+ * HTTP/1.1 needs the header Connection: close to do that.
+ * This is the default with HTTP/1.0
+ * Using HTTP/1.1 means we need to support chunked transfer-encoding :
+ * When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked
+ * transfer encoding. */
+ /* Connection: Close is normally there only in HTTP/1.1 but who knows */
+ portstr[0] = '\0';
+ if(port != 80)
+ snprintf(portstr, sizeof(portstr), ":%hu", port);
+ headerssize = snprintf(headerbuf, sizeof(headerbuf),
+ "POST %s HTTP/%s\r\n"
+ "Host: %s%s\r\n"
+ "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
+ "Content-Length: %d\r\n"
+ "Content-Type: text/xml\r\n"
+ "SOAPAction: \"%s\"\r\n"
+ "Connection: Close\r\n"
+ "Cache-Control: no-cache\r\n" /* ??? */
+ "Pragma: no-cache\r\n"
+ "\r\n",
+ url, httpversion, host, portstr, bodysize, action);
+ if ((unsigned int)headerssize >= sizeof(headerbuf))
+ return -1;
+#ifdef DEBUG
+ /*printf("SOAP request : headersize=%d bodysize=%d\n",
+ headerssize, bodysize);
+ */
+ printf("SOAP request : POST %s HTTP/%s - Host: %s%s\n",
+ url, httpversion, host, portstr);
+ printf("SOAPAction: \"%s\" - Content-Length: %d\n", action, bodysize);
+ printf("Headers :\n%s", headerbuf);
+ printf("Body :\n%s\n", body);
+#endif
+ return httpWrite(fd, body, bodysize, headerbuf, headerssize);
+}
+
+
diff --git a/thirdparty/miniupnpc/minisoap.h b/thirdparty/miniupnpc/minisoap.h
new file mode 100644
index 0000000000..d6a45d03ba
--- /dev/null
+++ b/thirdparty/miniupnpc/minisoap.h
@@ -0,0 +1,17 @@
+/* $Id: minisoap.h,v 1.4 2010/04/12 20:39:41 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+#ifndef MINISOAP_H_INCLUDED
+#define MINISOAP_H_INCLUDED
+
+#include "miniupnpc_socketdef.h"
+
+/*int httpWrite(int, const char *, int, const char *);*/
+int soapPostSubmit(SOCKET, const char *, const char *, unsigned short,
+ const char *, const char *, const char *);
+
+#endif
+
diff --git a/thirdparty/miniupnpc/minissdpc.c b/thirdparty/miniupnpc/minissdpc.c
new file mode 100644
index 0000000000..d76b242ad0
--- /dev/null
+++ b/thirdparty/miniupnpc/minissdpc.c
@@ -0,0 +1,888 @@
+/* $Id: minissdpc.c,v 1.32 2016/10/07 09:04:36 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2018 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+/*#include <syslog.h>*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#if defined (__NetBSD__)
+#include <net/if.h>
+#endif
+#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#include <iphlpapi.h>
+#define snprintf _snprintf
+#if !defined(_MSC_VER)
+#include <stdint.h>
+#else /* !defined(_MSC_VER) */
+typedef unsigned short uint16_t;
+#endif /* !defined(_MSC_VER) */
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#endif /* _WIN32 */
+#if defined(__amigaos__) || defined(__amigaos4__)
+#include <sys/socket.h>
+#endif /* defined(__amigaos__) || defined(__amigaos4__) */
+#if defined(__amigaos__)
+#define uint16_t unsigned short
+#endif /* defined(__amigaos__) */
+/* Hack */
+#define UNIX_PATH_LEN 108
+struct sockaddr_un {
+ uint16_t sun_family;
+ char sun_path[UNIX_PATH_LEN];
+};
+#else /* defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) */
+#include <strings.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+#define closesocket close
+#endif
+
+#include "miniupnpc_socketdef.h"
+
+#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__)
+#define HAS_IP_MREQN
+#endif
+
+#if !defined(HAS_IP_MREQN) && !defined(_WIN32)
+#include <sys/ioctl.h>
+#if defined(__sun)
+#include <sys/sockio.h>
+#endif
+#endif
+
+#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
+/* Several versions of glibc don't define this structure,
+ * define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
+struct ip_mreqn
+{
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_address; /* local IP address of interface */
+ int imr_ifindex; /* Interface index */
+};
+#endif
+
+#if defined(__amigaos__) || defined(__amigaos4__)
+/* Amiga OS specific stuff */
+#define TIMEVAL struct timeval
+#endif
+
+#include "minissdpc.h"
+#include "miniupnpc.h"
+#include "receivedata.h"
+
+#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
+
+#include "codelength.h"
+
+struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error)
+{
+ struct UPNPDev * devlist = NULL;
+ int s;
+ int res;
+
+ s = connectToMiniSSDPD(socketpath);
+ if (s < 0) {
+ if (error)
+ *error = s;
+ return NULL;
+ }
+ res = requestDevicesFromMiniSSDPD(s, devtype);
+ if (res < 0) {
+ if (error)
+ *error = res;
+ } else {
+ devlist = receiveDevicesFromMiniSSDPD(s, error);
+ }
+ disconnectFromMiniSSDPD(s);
+ return devlist;
+}
+
+/* macros used to read from unix socket */
+#define READ_BYTE_BUFFER(c) \
+ if((int)bufferindex >= n) { \
+ n = read(s, buffer, sizeof(buffer)); \
+ if(n<=0) break; \
+ bufferindex = 0; \
+ } \
+ c = buffer[bufferindex++];
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+
+#define READ_COPY_BUFFER(dst, len) \
+ for(l = len, p = (unsigned char *)dst; l > 0; ) { \
+ unsigned int lcopy; \
+ if((int)bufferindex >= n) { \
+ n = read(s, buffer, sizeof(buffer)); \
+ if(n<=0) break; \
+ bufferindex = 0; \
+ } \
+ lcopy = MIN(l, (n - bufferindex)); \
+ memcpy(p, buffer + bufferindex, lcopy); \
+ l -= lcopy; \
+ p += lcopy; \
+ bufferindex += lcopy; \
+ }
+
+#define READ_DISCARD_BUFFER(len) \
+ for(l = len; l > 0; ) { \
+ unsigned int lcopy; \
+ if(bufferindex >= n) { \
+ n = read(s, buffer, sizeof(buffer)); \
+ if(n<=0) break; \
+ bufferindex = 0; \
+ } \
+ lcopy = MIN(l, (n - bufferindex)); \
+ l -= lcopy; \
+ bufferindex += lcopy; \
+ }
+
+int
+connectToMiniSSDPD(const char * socketpath)
+{
+ int s;
+ struct sockaddr_un addr;
+#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
+ struct timeval timeout;
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(s < 0)
+ {
+ /*syslog(LOG_ERR, "socket(unix): %m");*/
+ perror("socket(unix)");
+ return MINISSDPC_SOCKET_ERROR;
+ }
+#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
+ /* setting a 3 seconds timeout */
+ /* not supported for AF_UNIX sockets under Solaris */
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ perror("setsockopt SO_RCVTIMEO unix");
+ }
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ perror("setsockopt SO_SNDTIMEO unix");
+ }
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+ if(!socketpath)
+ socketpath = "/var/run/minissdpd.sock";
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
+ /* TODO : check if we need to handle the EINTR */
+ if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
+ {
+ /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/
+ close(s);
+ return MINISSDPC_SOCKET_ERROR;
+ }
+ return s;
+}
+
+int
+disconnectFromMiniSSDPD(int s)
+{
+ if (close(s) < 0)
+ return MINISSDPC_SOCKET_ERROR;
+ return MINISSDPC_SUCCESS;
+}
+
+int
+requestDevicesFromMiniSSDPD(int s, const char * devtype)
+{
+ unsigned char buffer[256];
+ unsigned char * p;
+ unsigned int stsize, l;
+
+ stsize = strlen(devtype);
+ if(stsize == 8 && 0 == memcmp(devtype, "ssdp:all", 8))
+ {
+ buffer[0] = 3; /* request type 3 : everything */
+ }
+ else
+ {
+ buffer[0] = 1; /* request type 1 : request devices/services by type */
+ }
+ p = buffer + 1;
+ l = stsize; CODELENGTH(l, p);
+ if(p + stsize > buffer + sizeof(buffer))
+ {
+ /* devtype is too long ! */
+#ifdef DEBUG
+ fprintf(stderr, "devtype is too long ! stsize=%u sizeof(buffer)=%u\n",
+ stsize, (unsigned)sizeof(buffer));
+#endif /* DEBUG */
+ return MINISSDPC_INVALID_INPUT;
+ }
+ memcpy(p, devtype, stsize);
+ p += stsize;
+ if(write(s, buffer, p - buffer) < 0)
+ {
+ /*syslog(LOG_ERR, "write(): %m");*/
+ perror("minissdpc.c: write()");
+ return MINISSDPC_SOCKET_ERROR;
+ }
+ return MINISSDPC_SUCCESS;
+}
+
+struct UPNPDev *
+receiveDevicesFromMiniSSDPD(int s, int * error)
+{
+ struct UPNPDev * tmp;
+ struct UPNPDev * devlist = NULL;
+ unsigned char buffer[256];
+ ssize_t n;
+ unsigned char * p;
+ unsigned char * url;
+ unsigned char * st;
+ unsigned int bufferindex;
+ unsigned int i, ndev;
+ unsigned int urlsize, stsize, usnsize, l;
+
+ n = read(s, buffer, sizeof(buffer));
+ if(n<=0)
+ {
+ perror("minissdpc.c: read()");
+ if (error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ return NULL;
+ }
+ ndev = buffer[0];
+ bufferindex = 1;
+ for(i = 0; i < ndev; i++)
+ {
+ DECODELENGTH_READ(urlsize, READ_BYTE_BUFFER);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ return devlist;
+ }
+#ifdef DEBUG
+ printf(" urlsize=%u", urlsize);
+#endif /* DEBUG */
+ url = malloc(urlsize);
+ if(url == NULL) {
+ if (error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ return devlist;
+ }
+ READ_COPY_BUFFER(url, urlsize);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_return;
+ }
+ DECODELENGTH_READ(stsize, READ_BYTE_BUFFER);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_return;
+ }
+#ifdef DEBUG
+ printf(" stsize=%u", stsize);
+#endif /* DEBUG */
+ st = malloc(stsize);
+ if (st == NULL) {
+ if (error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto free_url_and_return;
+ }
+ READ_COPY_BUFFER(st, stsize);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_st_and_return;
+ }
+ DECODELENGTH_READ(usnsize, READ_BYTE_BUFFER);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_st_and_return;
+ }
+#ifdef DEBUG
+ printf(" usnsize=%u\n", usnsize);
+#endif /* DEBUG */
+ tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
+ if(tmp == NULL) {
+ if (error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto free_url_and_st_and_return;
+ }
+ tmp->pNext = devlist;
+ tmp->descURL = tmp->buffer;
+ tmp->st = tmp->buffer + 1 + urlsize;
+ memcpy(tmp->buffer, url, urlsize);
+ tmp->buffer[urlsize] = '\0';
+ memcpy(tmp->st, st, stsize);
+ tmp->buffer[urlsize+1+stsize] = '\0';
+ free(url);
+ free(st);
+ url = NULL;
+ st = NULL;
+ tmp->usn = tmp->buffer + 1 + urlsize + 1 + stsize;
+ READ_COPY_BUFFER(tmp->usn, usnsize);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_tmp_and_return;
+ }
+ tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
+ tmp->scope_id = 0; /* default value. scope_id is not available with MiniSSDPd */
+ devlist = tmp;
+ }
+ if (error)
+ *error = MINISSDPC_SUCCESS;
+ return devlist;
+
+free_url_and_st_and_return:
+ free(st);
+free_url_and_return:
+ free(url);
+ return devlist;
+
+free_tmp_and_return:
+ free(tmp);
+ return devlist;
+}
+
+#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
+
+/* parseMSEARCHReply()
+ * the last 4 arguments are filled during the parsing :
+ * - location/locationsize : "location:" field of the SSDP reply packet
+ * - st/stsize : "st:" field of the SSDP reply packet.
+ * The strings are NOT null terminated */
+static void
+parseMSEARCHReply(const char * reply, int size,
+ const char * * location, int * locationsize,
+ const char * * st, int * stsize,
+ const char * * usn, int * usnsize)
+{
+ int a, b, i;
+ i = 0;
+ a = i; /* start of the line */
+ b = 0; /* end of the "header" (position of the colon) */
+ while(i<size)
+ {
+ switch(reply[i])
+ {
+ case ':':
+ if(b==0)
+ {
+ b = i; /* end of the "header" */
+ /*for(j=a; j<b; j++)
+ {
+ putchar(reply[j]);
+ }
+ */
+ }
+ break;
+ case '\x0a':
+ case '\x0d':
+ if(b!=0)
+ {
+ /*for(j=b+1; j<i; j++)
+ {
+ putchar(reply[j]);
+ }
+ putchar('\n');*/
+ /* skip the colon and white spaces */
+ do { b++; } while(reply[b]==' ');
+ if(0==strncasecmp(reply+a, "location", 8))
+ {
+ *location = reply+b;
+ *locationsize = i-b;
+ }
+ else if(0==strncasecmp(reply+a, "st", 2))
+ {
+ *st = reply+b;
+ *stsize = i-b;
+ }
+ else if(0==strncasecmp(reply+a, "usn", 3))
+ {
+ *usn = reply+b;
+ *usnsize = i-b;
+ }
+ b = 0;
+ }
+ a = i+1;
+ break;
+ default:
+ break;
+ }
+ i++;
+ }
+}
+
+/* port upnp discover : SSDP protocol */
+#define SSDP_PORT 1900
+#define XSTR(s) STR(s)
+#define STR(s) #s
+#define UPNP_MCAST_ADDR "239.255.255.250"
+/* for IPv6 */
+#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
+#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
+
+/* direct discovery if minissdpd responses are not sufficient */
+/* ssdpDiscoverDevices() :
+ * return a chained list of all devices found or NULL if
+ * no devices was found.
+ * It is up to the caller to free the chained list
+ * delay is in millisecond (poll).
+ * UDA v1.1 says :
+ * The TTL for the IP packet SHOULD default to 2 and
+ * SHOULD be configurable. */
+struct UPNPDev *
+ssdpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes)
+{
+ struct UPNPDev * tmp;
+ struct UPNPDev * devlist = 0;
+ unsigned int scope_id = 0;
+ int opt = 1;
+ static const char MSearchMsgFmt[] =
+ "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: %s:" XSTR(SSDP_PORT) "\r\n"
+ "ST: %s\r\n"
+ "MAN: \"ssdp:discover\"\r\n"
+ "MX: %u\r\n"
+ "\r\n";
+ int deviceIndex;
+ char bufr[1536]; /* reception and emission buffer */
+ SOCKET sudp;
+ int n;
+ struct sockaddr_storage sockudp_r;
+ unsigned int mx;
+#ifdef NO_GETADDRINFO
+ struct sockaddr_storage sockudp_w;
+#else
+ int rv;
+ struct addrinfo hints, *servinfo, *p;
+#endif
+#ifdef _WIN32
+ MIB_IPFORWARDROW ip_forward;
+ unsigned long _ttl = (unsigned long)ttl;
+#endif
+ int linklocal = 1;
+ int sentok;
+
+ if(error)
+ *error = MINISSDPC_UNKNOWN_ERROR;
+
+ if(localport==UPNP_LOCAL_PORT_SAME)
+ localport = SSDP_PORT;
+
+#ifdef _WIN32
+ sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+#else
+ sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
+#endif
+ if(ISINVALID(sudp))
+ {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("socket");
+ return NULL;
+ }
+ /* reception */
+ memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
+ if(ipv6) {
+ struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
+ p->sin6_family = AF_INET6;
+ if(localport > 0 && localport < 65536)
+ p->sin6_port = htons((unsigned short)localport);
+ p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
+ } else {
+ struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
+ p->sin_family = AF_INET;
+ if(localport > 0 && localport < 65536)
+ p->sin_port = htons((unsigned short)localport);
+ p->sin_addr.s_addr = INADDR_ANY;
+ }
+#ifdef _WIN32
+/* This code could help us to use the right Network interface for
+ * SSDP multicast traffic */
+/* Get IP associated with the index given in the ip_forward struct
+ * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
+ if(!ipv6
+ && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
+ DWORD dwRetVal = 0;
+ PMIB_IPADDRTABLE pIPAddrTable;
+ DWORD dwSize = 0;
+#ifdef DEBUG
+ IN_ADDR IPAddr;
+#endif
+ int i;
+#ifdef DEBUG
+ printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
+#endif
+ pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
+ if(pIPAddrTable) {
+ if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
+ free(pIPAddrTable);
+ pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
+ }
+ }
+ if(pIPAddrTable) {
+ dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
+ if (dwRetVal == NO_ERROR) {
+#ifdef DEBUG
+ printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
+#endif
+ for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
+#ifdef DEBUG
+ printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
+ printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
+ printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
+ printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
+ printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
+ printf("\tType and State[%d]:", i);
+ printf("\n");
+#endif
+ if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
+ /* Set the address of this interface to be used */
+ struct in_addr mc_if;
+ memset(&mc_if, 0, sizeof(mc_if));
+ mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
+#ifndef DEBUG
+ break;
+#endif
+ }
+ }
+ }
+ free(pIPAddrTable);
+ pIPAddrTable = NULL;
+ }
+ }
+#endif /* _WIN32 */
+
+#ifdef _WIN32
+ if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
+#else
+ if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
+#endif
+ {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("setsockopt(SO_REUSEADDR,...)");
+ return NULL;
+ }
+
+ if(ipv6) {
+#ifdef _WIN32
+ DWORD mcastHops = ttl;
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&mcastHops, sizeof(mcastHops)) < 0)
+#else /* _WIN32 */
+ int mcastHops = ttl;
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastHops, sizeof(mcastHops)) < 0)
+#endif /* _WIN32 */
+ {
+ PRINT_SOCKET_ERROR("setsockopt(IPV6_MULTICAST_HOPS,...)");
+ }
+ } else {
+#ifdef _WIN32
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
+#else /* _WIN32 */
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
+#endif /* _WIN32 */
+ {
+ /* not a fatal error */
+ PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
+ }
+ }
+
+ if(multicastif)
+ {
+ if(ipv6) {
+#if !defined(_WIN32)
+ /* according to MSDN, if_nametoindex() is supported since
+ * MS Windows Vista and MS Windows Server 2008.
+ * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
+ unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF");
+ }
+#else
+#ifdef DEBUG
+ printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
+#endif
+#endif
+ } else {
+ struct in_addr mc_if;
+ mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
+ if(mc_if.s_addr != INADDR_NONE)
+ {
+ ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
+ }
+ } else {
+#ifdef HAS_IP_MREQN
+ /* was not an ip address, try with an interface name */
+ struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
+ memset(&reqn, 0, sizeof(struct ip_mreqn));
+ reqn.imr_ifindex = if_nametoindex(multicastif);
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
+ }
+#elif !defined(_WIN32)
+ struct ifreq ifr;
+ int ifrlen = sizeof(ifr);
+ strncpy(ifr.ifr_name, multicastif, IFNAMSIZ);
+ ifr.ifr_name[IFNAMSIZ-1] = '\0';
+ if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0)
+ {
+ PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)");
+ }
+ mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
+ }
+#else /* _WIN32 */
+#ifdef DEBUG
+ printf("Setting of multicast interface not supported with interface name.\n");
+#endif
+#endif /* #ifdef HAS_IP_MREQN / !defined(_WIN32) */
+ }
+ }
+ }
+
+ /* Before sending the packed, we first "bind" in order to be able
+ * to receive the response */
+ if (bind(sudp, (const struct sockaddr *)&sockudp_r,
+ ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
+ {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("bind");
+ closesocket(sudp);
+ return NULL;
+ }
+
+ if(error)
+ *error = MINISSDPC_SUCCESS;
+ /* Calculating maximum response time in seconds */
+ mx = ((unsigned int)delay) / 1000u;
+ if(mx == 0) {
+ mx = 1;
+ delay = 1000;
+ }
+ /* receiving SSDP response packet */
+ for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
+ sentok = 0;
+ /* sending the SSDP M-SEARCH packet */
+ n = snprintf(bufr, sizeof(bufr),
+ MSearchMsgFmt,
+ ipv6 ?
+ (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
+ : UPNP_MCAST_ADDR,
+ deviceTypes[deviceIndex], mx);
+ if ((unsigned int)n >= sizeof(bufr)) {
+ if(error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto error;
+ }
+#ifdef DEBUG
+ /*printf("Sending %s", bufr);*/
+ printf("Sending M-SEARCH request to %s with ST: %s\n",
+ ipv6 ?
+ (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
+ : UPNP_MCAST_ADDR,
+ deviceTypes[deviceIndex]);
+#endif
+#ifdef NO_GETADDRINFO
+ /* the following code is not using getaddrinfo */
+ /* emission */
+ memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
+ if(ipv6) {
+ struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
+ p->sin6_family = AF_INET6;
+ p->sin6_port = htons(SSDP_PORT);
+ inet_pton(AF_INET6,
+ linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
+ &(p->sin6_addr));
+ } else {
+ struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
+ p->sin_family = AF_INET;
+ p->sin_port = htons(SSDP_PORT);
+ p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
+ }
+ n = sendto(sudp, bufr, n, 0, &sockudp_w,
+ ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
+ if (n < 0) {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("sendto");
+ } else {
+ sentok = 1;
+ }
+#else /* #ifdef NO_GETADDRINFO */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */
+ hints.ai_socktype = SOCK_DGRAM;
+ /*hints.ai_flags = */
+ if ((rv = getaddrinfo(ipv6
+ ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
+ : UPNP_MCAST_ADDR,
+ XSTR(SSDP_PORT), &hints, &servinfo)) != 0) {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+#ifdef _WIN32
+ fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
+#else
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+#endif
+ break;
+ }
+ for(p = servinfo; p; p = p->ai_next) {
+ n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
+ if (n < 0) {
+#ifdef DEBUG
+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+ if (getnameinfo(p->ai_addr, p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
+ sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
+ fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
+ }
+#endif
+ PRINT_SOCKET_ERROR("sendto");
+ continue;
+ } else {
+ sentok = 1;
+ }
+ }
+ freeaddrinfo(servinfo);
+ if(!sentok) {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ }
+#endif /* #ifdef NO_GETADDRINFO */
+ /* Waiting for SSDP REPLY packet to M-SEARCH
+ * if searchalltypes is set, enter the loop only
+ * when the last deviceType is reached */
+ if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) do {
+ n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
+ if (n < 0) {
+ /* error */
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ goto error;
+ } else if (n == 0) {
+ /* no data or Time Out */
+#ifdef DEBUG
+ printf("NODATA or TIMEOUT\n");
+#endif /* DEBUG */
+ if (devlist && !searchalltypes) {
+ /* found some devices, stop now*/
+ if(error)
+ *error = MINISSDPC_SUCCESS;
+ goto error;
+ }
+ } else {
+ const char * descURL=NULL;
+ int urlsize=0;
+ const char * st=NULL;
+ int stsize=0;
+ const char * usn=NULL;
+ int usnsize=0;
+ parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize);
+ if(st&&descURL) {
+#ifdef DEBUG
+ printf("M-SEARCH Reply:\n ST: %.*s\n USN: %.*s\n Location: %.*s\n",
+ stsize, st, usnsize, (usn?usn:""), urlsize, descURL);
+#endif /* DEBUG */
+ for(tmp=devlist; tmp; tmp = tmp->pNext) {
+ if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
+ tmp->descURL[urlsize] == '\0' &&
+ memcmp(tmp->st, st, stsize) == 0 &&
+ tmp->st[stsize] == '\0' &&
+ (usnsize == 0 || memcmp(tmp->usn, usn, usnsize) == 0) &&
+ tmp->usn[usnsize] == '\0')
+ break;
+ }
+ /* at the exit of the loop above, tmp is null if
+ * no duplicate device was found */
+ if(tmp)
+ continue;
+ tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
+ if(!tmp) {
+ /* memory allocation error */
+ if(error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto error;
+ }
+ tmp->pNext = devlist;
+ tmp->descURL = tmp->buffer;
+ tmp->st = tmp->buffer + 1 + urlsize;
+ tmp->usn = tmp->st + 1 + stsize;
+ memcpy(tmp->buffer, descURL, urlsize);
+ tmp->buffer[urlsize] = '\0';
+ memcpy(tmp->st, st, stsize);
+ tmp->buffer[urlsize+1+stsize] = '\0';
+ if(usn != NULL)
+ memcpy(tmp->usn, usn, usnsize);
+ tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
+ tmp->scope_id = scope_id;
+ devlist = tmp;
+ }
+ }
+ } while(n > 0);
+ if(ipv6) {
+ /* switch linklocal flag */
+ if(linklocal) {
+ linklocal = 0;
+ --deviceIndex;
+ } else {
+ linklocal = 1;
+ }
+ }
+ }
+error:
+ closesocket(sudp);
+ return devlist;
+}
+
diff --git a/thirdparty/miniupnpc/minissdpc.h b/thirdparty/miniupnpc/minissdpc.h
new file mode 100644
index 0000000000..167d897cb6
--- /dev/null
+++ b/thirdparty/miniupnpc/minissdpc.h
@@ -0,0 +1,58 @@
+/* $Id: minissdpc.h,v 1.6 2015/09/18 12:45:16 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef MINISSDPC_H_INCLUDED
+#define MINISSDPC_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "upnpdev.h"
+
+/* error codes : */
+#define MINISSDPC_SUCCESS (0)
+#define MINISSDPC_UNKNOWN_ERROR (-1)
+#define MINISSDPC_SOCKET_ERROR (-101)
+#define MINISSDPC_MEMORY_ERROR (-102)
+#define MINISSDPC_INVALID_INPUT (-103)
+#define MINISSDPC_INVALID_SERVER_REPLY (-104)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error);
+
+MINIUPNP_LIBSPEC int
+connectToMiniSSDPD(const char * socketpath);
+
+MINIUPNP_LIBSPEC int
+disconnectFromMiniSSDPD(int fd);
+
+MINIUPNP_LIBSPEC int
+requestDevicesFromMiniSSDPD(int fd, const char * devtype);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+receiveDevicesFromMiniSSDPD(int fd, int * error);
+
+#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+ssdpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/miniupnpc.c b/thirdparty/miniupnpc/miniupnpc.c
new file mode 100644
index 0000000000..5d93ef9933
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc.c
@@ -0,0 +1,727 @@
+/* $Id: miniupnpc.c,v 1.149 2016/02/09 09:50:46 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2018 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENSE file. */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+/* Win32 Specific includes and defines */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#include <iphlpapi.h>
+#define snprintf _snprintf
+#define strdup _strdup
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#define MAXHOSTNAMELEN 64
+#else /* #ifdef _WIN32 */
+/* Standard POSIX includes */
+#include <unistd.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+/* Amiga OS 3 specific stuff */
+#define socklen_t int
+#else
+#include <sys/select.h>
+#endif
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+#if !defined(__amigaos__) && !defined(__amigaos4__)
+#include <poll.h>
+#endif
+#include <strings.h>
+#include <errno.h>
+#define closesocket close
+#endif /* #else _WIN32 */
+#ifdef __GNU__
+#define MAXHOSTNAMELEN 64
+#endif
+
+
+#include "miniupnpc.h"
+#include "minissdpc.h"
+#include "miniwget.h"
+#include "miniwget_private.h"
+#include "minisoap.h"
+#include "minixml.h"
+#include "upnpcommands.h"
+#include "connecthostport.h"
+
+/* compare the beginning of a string with a constant string */
+#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define SOAPPREFIX "s"
+#define SERVICEPREFIX "u"
+#define SERVICEPREFIX2 'u'
+
+/* check if an ip address is a private (LAN) address
+ * see https://tools.ietf.org/html/rfc1918 */
+static int is_rfc1918addr(const char * addr)
+{
+ /* 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) */
+ if(COMPARE(addr, "192.168."))
+ return 1;
+ /* 10.0.0.0 - 10.255.255.255 (10/8 prefix) */
+ if(COMPARE(addr, "10."))
+ return 1;
+ /* 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) */
+ if(COMPARE(addr, "172.")) {
+ int i = atoi(addr + 4);
+ if((16 <= i) && (i <= 31))
+ return 1;
+ }
+ return 0;
+}
+
+/* root description parsing */
+MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
+{
+ struct xmlparser parser;
+ /* xmlparser object */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = data;
+ parser.starteltfunc = IGDstartelt;
+ parser.endeltfunc = IGDendelt;
+ parser.datafunc = IGDdata;
+ parser.attfunc = 0;
+ parsexml(&parser);
+#ifdef DEBUG
+ printIGD(data);
+#endif
+}
+
+/* simpleUPnPcommand2 :
+ * not so simple !
+ * return values :
+ * pointer - OK
+ * NULL - error */
+static char *
+simpleUPnPcommand2(SOCKET s, const char * url, const char * service,
+ const char * action, struct UPNParg * args,
+ int * bufsize, const char * httpversion)
+{
+ char hostname[MAXHOSTNAMELEN+1];
+ unsigned short port = 0;
+ char * path;
+ char soapact[128];
+ char soapbody[2048];
+ int soapbodylen;
+ char * buf;
+ int n;
+ int status_code;
+
+ *bufsize = 0;
+ snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
+ if(args==NULL)
+ {
+ soapbodylen = snprintf(soapbody, sizeof(soapbody),
+ "<?xml version=\"1.0\"?>\r\n"
+ "<" SOAPPREFIX ":Envelope "
+ "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<" SOAPPREFIX ":Body>"
+ "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
+ "</" SERVICEPREFIX ":%s>"
+ "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
+ "\r\n", action, service, action);
+ if ((unsigned int)soapbodylen >= sizeof(soapbody))
+ return NULL;
+ }
+ else
+ {
+ char * p;
+ const char * pe, * pv;
+ const char * const pend = soapbody + sizeof(soapbody);
+ soapbodylen = snprintf(soapbody, sizeof(soapbody),
+ "<?xml version=\"1.0\"?>\r\n"
+ "<" SOAPPREFIX ":Envelope "
+ "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<" SOAPPREFIX ":Body>"
+ "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
+ action, service);
+ if ((unsigned int)soapbodylen >= sizeof(soapbody))
+ return NULL;
+ p = soapbody + soapbodylen;
+ while(args->elt)
+ {
+ if(p >= pend) /* check for space to write next byte */
+ return NULL;
+ *(p++) = '<';
+
+ pe = args->elt;
+ while(p < pend && *pe)
+ *(p++) = *(pe++);
+
+ if(p >= pend) /* check for space to write next byte */
+ return NULL;
+ *(p++) = '>';
+
+ if((pv = args->val))
+ {
+ while(p < pend && *pv)
+ *(p++) = *(pv++);
+ }
+
+ if((p+2) > pend) /* check for space to write next 2 bytes */
+ return NULL;
+ *(p++) = '<';
+ *(p++) = '/';
+
+ pe = args->elt;
+ while(p < pend && *pe)
+ *(p++) = *(pe++);
+
+ if(p >= pend) /* check for space to write next byte */
+ return NULL;
+ *(p++) = '>';
+
+ args++;
+ }
+ if((p+4) > pend) /* check for space to write next 4 bytes */
+ return NULL;
+ *(p++) = '<';
+ *(p++) = '/';
+ *(p++) = SERVICEPREFIX2;
+ *(p++) = ':';
+
+ pe = action;
+ while(p < pend && *pe)
+ *(p++) = *(pe++);
+
+ strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
+ pend - p);
+ if(soapbody[sizeof(soapbody)-1]) /* strncpy pads buffer with 0s, so if it doesn't end in 0, could not fit full string */
+ return NULL;
+ }
+ if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
+ if(ISINVALID(s)) {
+ s = connecthostport(hostname, port, 0);
+ if(ISINVALID(s)) {
+ /* failed to connect */
+ return NULL;
+ }
+ }
+
+ n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
+ if(n<=0) {
+#ifdef DEBUG
+ printf("Error sending SOAP request\n");
+#endif
+ closesocket(s);
+ return NULL;
+ }
+
+ buf = getHTTPResponse(s, bufsize, &status_code);
+#ifdef DEBUG
+ if(*bufsize > 0 && buf)
+ {
+ printf("HTTP %d SOAP Response :\n%.*s\n", status_code, *bufsize, buf);
+ }
+ else
+ {
+ printf("HTTP %d, empty SOAP response. size=%d\n", status_code, *bufsize);
+ }
+#endif
+ closesocket(s);
+ return buf;
+}
+
+/* simpleUPnPcommand :
+ * not so simple !
+ * return values :
+ * pointer - OK
+ * NULL - error */
+char *
+simpleUPnPcommand(int s, const char * url, const char * service,
+ const char * action, struct UPNParg * args,
+ int * bufsize)
+{
+ char * buf;
+
+#if 1
+ buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.1");
+#else
+ buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.0");
+ if (!buf || *bufsize == 0)
+ {
+#if DEBUG
+ printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
+#endif
+ buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.1");
+ }
+#endif
+ return buf;
+}
+
+/* upnpDiscoverDevices() :
+ * return a chained list of all devices found or NULL if
+ * no devices was found.
+ * It is up to the caller to free the chained list
+ * delay is in millisecond (poll).
+ * UDA v1.1 says :
+ * The TTL for the IP packet SHOULD default to 2 and
+ * SHOULD be configurable. */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes)
+{
+ struct UPNPDev * tmp;
+ struct UPNPDev * devlist = 0;
+#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+ int deviceIndex;
+#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+
+ if(error)
+ *error = UPNPDISCOVER_UNKNOWN_ERROR;
+#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+ /* first try to get infos from minissdpd ! */
+ if(!minissdpdsock)
+ minissdpdsock = "/var/run/minissdpd.sock";
+ if(minissdpdsock[0] != '\0') {
+ for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
+ struct UPNPDev * minissdpd_devlist;
+ int only_rootdevice = 1;
+ minissdpd_devlist = getDevicesFromMiniSSDPD(deviceTypes[deviceIndex],
+ minissdpdsock, 0);
+ if(minissdpd_devlist) {
+#ifdef DEBUG
+ printf("returned by MiniSSDPD: %s\t%s\n",
+ minissdpd_devlist->st, minissdpd_devlist->descURL);
+#endif /* DEBUG */
+ if(!strstr(minissdpd_devlist->st, "rootdevice"))
+ only_rootdevice = 0;
+ for(tmp = minissdpd_devlist; tmp->pNext != NULL; tmp = tmp->pNext) {
+#ifdef DEBUG
+ printf("returned by MiniSSDPD: %s\t%s\n",
+ tmp->pNext->st, tmp->pNext->descURL);
+#endif /* DEBUG */
+ if(!strstr(tmp->st, "rootdevice"))
+ only_rootdevice = 0;
+ }
+ tmp->pNext = devlist;
+ devlist = minissdpd_devlist;
+ if(!searchalltypes && !only_rootdevice)
+ break;
+ }
+ }
+ }
+ for(tmp = devlist; tmp != NULL; tmp = tmp->pNext) {
+ /* We return what we have found if it was not only a rootdevice */
+ if(!strstr(tmp->st, "rootdevice")) {
+ if(error)
+ *error = UPNPDISCOVER_SUCCESS;
+ return devlist;
+ }
+ }
+#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+
+ /* direct discovery if minissdpd responses are not sufficient */
+ {
+ struct UPNPDev * discovered_devlist;
+ discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, localport,
+ ipv6, ttl, error, searchalltypes);
+ if(devlist == NULL)
+ devlist = discovered_devlist;
+ else {
+ for(tmp = devlist; tmp->pNext != NULL; tmp = tmp->pNext);
+ tmp->pNext = discovered_devlist;
+ }
+ }
+ return devlist;
+}
+
+/* upnpDiscover() Discover IGD device */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error)
+{
+ static const char * const deviceList[] = {
+#if 0
+ "urn:schemas-upnp-org:device:InternetGatewayDevice:2",
+ "urn:schemas-upnp-org:service:WANIPConnection:2",
+#endif
+ "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
+ "urn:schemas-upnp-org:service:WANIPConnection:1",
+ "urn:schemas-upnp-org:service:WANPPPConnection:1",
+ "upnp:rootdevice",
+ /*"ssdp:all",*/
+ 0
+ };
+ return upnpDiscoverDevices(deviceList,
+ delay, multicastif, minissdpdsock, localport,
+ ipv6, ttl, error, 0);
+}
+
+/* upnpDiscoverAll() Discover all UPnP devices */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverAll(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error)
+{
+ static const char * const deviceList[] = {
+ /*"upnp:rootdevice",*/
+ "ssdp:all",
+ 0
+ };
+ return upnpDiscoverDevices(deviceList,
+ delay, multicastif, minissdpdsock, localport,
+ ipv6, ttl, error, 0);
+}
+
+/* upnpDiscoverDevice() Discover a specific device */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error)
+{
+ const char * const deviceList[] = {
+ device,
+ 0
+ };
+ return upnpDiscoverDevices(deviceList,
+ delay, multicastif, minissdpdsock, localport,
+ ipv6, ttl, error, 0);
+}
+
+static char *
+build_absolute_url(const char * baseurl, const char * descURL,
+ const char * url, unsigned int scope_id)
+{
+ int l, n;
+ char * s;
+ const char * base;
+ char * p;
+#if defined(IF_NAMESIZE) && !defined(_WIN32)
+ char ifname[IF_NAMESIZE];
+#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ char scope_str[8];
+#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+
+ if( (url[0] == 'h')
+ &&(url[1] == 't')
+ &&(url[2] == 't')
+ &&(url[3] == 'p')
+ &&(url[4] == ':')
+ &&(url[5] == '/')
+ &&(url[6] == '/'))
+ return strdup(url);
+ base = (baseurl[0] == '\0') ? descURL : baseurl;
+ n = strlen(base);
+ if(n > 7) {
+ p = strchr(base + 7, '/');
+ if(p)
+ n = p - base;
+ }
+ l = n + strlen(url) + 1;
+ if(url[0] != '/')
+ l++;
+ if(scope_id != 0) {
+#if defined(IF_NAMESIZE) && !defined(_WIN32)
+ if(if_indextoname(scope_id, ifname)) {
+ l += 3 + strlen(ifname); /* 3 == strlen(%25) */
+ }
+#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ /* under windows, scope is numerical */
+ l += 3 + snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
+#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ }
+ s = malloc(l);
+ if(s == NULL) return NULL;
+ memcpy(s, base, n);
+ if(scope_id != 0) {
+ s[n] = '\0';
+ if(0 == memcmp(s, "http://[fe80:", 13)) {
+ /* this is a linklocal IPv6 address */
+ p = strchr(s, ']');
+ if(p) {
+ /* insert %25<scope> into URL */
+#if defined(IF_NAMESIZE) && !defined(_WIN32)
+ memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
+ memcpy(p, "%25", 3);
+ memcpy(p + 3, ifname, strlen(ifname));
+ n += 3 + strlen(ifname);
+#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
+ memcpy(p, "%25", 3);
+ memcpy(p + 3, scope_str, strlen(scope_str));
+ n += 3 + strlen(scope_str);
+#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ }
+ }
+ }
+ if(url[0] != '/')
+ s[n++] = '/';
+ memcpy(s + n, url, l - n);
+ return s;
+}
+
+/* Prepare the Urls for usage...
+ */
+MINIUPNP_LIBSPEC void
+GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * descURL, unsigned int scope_id)
+{
+ /* strdup descURL */
+ urls->rootdescURL = strdup(descURL);
+
+ /* get description of WANIPConnection */
+ urls->ipcondescURL = build_absolute_url(data->urlbase, descURL,
+ data->first.scpdurl, scope_id);
+ urls->controlURL = build_absolute_url(data->urlbase, descURL,
+ data->first.controlurl, scope_id);
+ urls->controlURL_CIF = build_absolute_url(data->urlbase, descURL,
+ data->CIF.controlurl, scope_id);
+ urls->controlURL_6FC = build_absolute_url(data->urlbase, descURL,
+ data->IPv6FC.controlurl, scope_id);
+
+#ifdef DEBUG
+ printf("urls->ipcondescURL='%s'\n", urls->ipcondescURL);
+ printf("urls->controlURL='%s'\n", urls->controlURL);
+ printf("urls->controlURL_CIF='%s'\n", urls->controlURL_CIF);
+ printf("urls->controlURL_6FC='%s'\n", urls->controlURL_6FC);
+#endif
+}
+
+MINIUPNP_LIBSPEC void
+FreeUPNPUrls(struct UPNPUrls * urls)
+{
+ if(!urls)
+ return;
+ free(urls->controlURL);
+ urls->controlURL = 0;
+ free(urls->ipcondescURL);
+ urls->ipcondescURL = 0;
+ free(urls->controlURL_CIF);
+ urls->controlURL_CIF = 0;
+ free(urls->controlURL_6FC);
+ urls->controlURL_6FC = 0;
+ free(urls->rootdescURL);
+ urls->rootdescURL = 0;
+}
+
+int
+UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
+{
+ char status[64];
+ unsigned int uptime;
+ status[0] = '\0';
+ UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
+ status, &uptime, NULL);
+ if(0 == strcmp("Connected", status))
+ return 1;
+ else if(0 == strcmp("Up", status)) /* Also accept "Up" */
+ return 1;
+ else
+ return 0;
+}
+
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ * -1 = Internal error
+ * 0 = NO IGD found
+ * 1 = A valid connected IGD has been found
+ * 2 = A valid IGD has been found but it reported as
+ * not connected
+ * 3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any positive non zero return case, the urls and data structures
+ * passed as parameters are set. Don't forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen)
+{
+ struct xml_desc {
+ char * xml;
+ int size;
+ int is_igd;
+ } * desc = NULL;
+ struct UPNPDev * dev;
+ int ndev = 0;
+ int i;
+ int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
+ int n_igd = 0;
+ char extIpAddr[16];
+ char myLanAddr[40];
+ int status_code = -1;
+
+ if(!devlist)
+ {
+#ifdef DEBUG
+ printf("Empty devlist\n");
+#endif
+ return 0;
+ }
+ /* counting total number of devices in the list */
+ for(dev = devlist; dev; dev = dev->pNext)
+ ndev++;
+ if(ndev > 0)
+ {
+ desc = calloc(ndev, sizeof(struct xml_desc));
+ if(!desc)
+ return -1; /* memory allocation error */
+ }
+ /* Step 1 : downloading descriptions and testing type */
+ for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
+ {
+ /* we should choose an internet gateway device.
+ * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
+ desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
+ myLanAddr, sizeof(myLanAddr),
+ dev->scope_id, &status_code);
+#ifdef DEBUG
+ if(!desc[i].xml)
+ {
+ printf("error getting XML description %s\n", dev->descURL);
+ }
+#endif
+ if(desc[i].xml)
+ {
+ memset(data, 0, sizeof(struct IGDdatas));
+ memset(urls, 0, sizeof(struct UPNPUrls));
+ parserootdesc(desc[i].xml, desc[i].size, data);
+ if(COMPARE(data->CIF.servicetype,
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:"))
+ {
+ desc[i].is_igd = 1;
+ n_igd++;
+ if(lanaddr)
+ strncpy(lanaddr, myLanAddr, lanaddrlen);
+ }
+ }
+ }
+ /* iterate the list to find a device depending on state */
+ for(state = 1; state <= 3; state++)
+ {
+ for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
+ {
+ if(desc[i].xml)
+ {
+ memset(data, 0, sizeof(struct IGDdatas));
+ memset(urls, 0, sizeof(struct UPNPUrls));
+ parserootdesc(desc[i].xml, desc[i].size, data);
+ if(desc[i].is_igd || state >= 3 )
+ {
+ int is_connected;
+
+ GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
+
+ /* in state 2 and 3 we don't test if device is connected ! */
+ if(state >= 2)
+ goto free_and_return;
+ is_connected = UPNPIGD_IsConnected(urls, data);
+#ifdef DEBUG
+ printf("UPNPIGD_IsConnected(%s) = %d\n",
+ urls->controlURL, is_connected);
+#endif
+ /* checks that status is connected AND there is a external IP address assigned */
+ if(is_connected &&
+ (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
+ if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
+ && (0 != strcmp(extIpAddr, "0.0.0.0")))
+ goto free_and_return;
+ }
+ FreeUPNPUrls(urls);
+ if(data->second.servicetype[0] != '\0') {
+#ifdef DEBUG
+ printf("We tried %s, now we try %s !\n",
+ data->first.servicetype, data->second.servicetype);
+#endif
+ /* swaping WANPPPConnection and WANIPConnection ! */
+ memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
+ memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
+ memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
+ GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
+ is_connected = UPNPIGD_IsConnected(urls, data);
+#ifdef DEBUG
+ printf("UPNPIGD_IsConnected(%s) = %d\n",
+ urls->controlURL, is_connected);
+#endif
+ if(is_connected &&
+ (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
+ if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
+ && (0 != strcmp(extIpAddr, "0.0.0.0")))
+ goto free_and_return;
+ }
+ FreeUPNPUrls(urls);
+ }
+ }
+ memset(data, 0, sizeof(struct IGDdatas));
+ }
+ }
+ }
+ state = 0;
+free_and_return:
+ if(desc) {
+ for(i = 0; i < ndev; i++) {
+ if(desc[i].xml) {
+ free(desc[i].xml);
+ }
+ }
+ free(desc);
+ }
+ return state;
+}
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * return value :
+ * 0 - Not ok
+ * 1 - OK */
+int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen)
+{
+ char * descXML;
+ int descXMLsize = 0;
+
+ descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
+ lanaddr, lanaddrlen, 0, NULL);
+ if(descXML) {
+ memset(data, 0, sizeof(struct IGDdatas));
+ memset(urls, 0, sizeof(struct UPNPUrls));
+ parserootdesc(descXML, descXMLsize, data);
+ free(descXML);
+ descXML = NULL;
+ GetUPNPUrls(urls, data, rootdescurl, 0);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
diff --git a/thirdparty/miniupnpc/miniupnpc.def b/thirdparty/miniupnpc/miniupnpc.def
new file mode 100644
index 0000000000..60e0bbe423
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc.def
@@ -0,0 +1,45 @@
+LIBRARY
+; miniupnpc library
+ miniupnpc
+
+EXPORTS
+; miniupnpc
+ upnpDiscover
+ freeUPNPDevlist
+ parserootdesc
+ UPNP_GetValidIGD
+ UPNP_GetIGDFromUrl
+ GetUPNPUrls
+ FreeUPNPUrls
+; miniwget
+ miniwget
+ miniwget_getaddr
+; upnpcommands
+ UPNP_GetTotalBytesSent
+ UPNP_GetTotalBytesReceived
+ UPNP_GetTotalPacketsSent
+ UPNP_GetTotalPacketsReceived
+ UPNP_GetStatusInfo
+ UPNP_GetConnectionTypeInfo
+ UPNP_GetExternalIPAddress
+ UPNP_GetLinkLayerMaxBitRates
+ UPNP_AddPortMapping
+ UPNP_AddAnyPortMapping
+ UPNP_DeletePortMapping
+ UPNP_DeletePortMappingRange
+ UPNP_GetPortMappingNumberOfEntries
+ UPNP_GetSpecificPortMappingEntry
+ UPNP_GetGenericPortMappingEntry
+ UPNP_GetListOfPortMappings
+ UPNP_AddPinhole
+ UPNP_CheckPinholeWorking
+ UPNP_UpdatePinhole
+ UPNP_GetPinholePackets
+ UPNP_DeletePinhole
+ UPNP_GetFirewallStatus
+ UPNP_GetOutboundPinholeTimeout
+; upnperrors
+ strupnperror
+; portlistingparse
+ ParsePortListing
+ FreePortListing
diff --git a/thirdparty/miniupnpc/miniupnpc.h b/thirdparty/miniupnpc/miniupnpc.h
new file mode 100644
index 0000000000..8ddc282bd1
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc.h
@@ -0,0 +1,153 @@
+/* $Id: miniupnpc.h,v 1.53 2018/05/07 11:05:16 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef MINIUPNPC_H_INCLUDED
+#define MINIUPNPC_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "igd_desc_parse.h"
+#include "upnpdev.h"
+
+/* error codes : */
+#define UPNPDISCOVER_SUCCESS (0)
+#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
+#define UPNPDISCOVER_SOCKET_ERROR (-101)
+#define UPNPDISCOVER_MEMORY_ERROR (-102)
+
+/* versions : */
+#define MINIUPNPC_VERSION "2.1"
+#define MINIUPNPC_API_VERSION 17
+
+/* Source port:
+ Using "1" as an alias for 1900 for backwards compatibility
+ (presuming one would have used that for the "sameport" parameter) */
+#define UPNP_LOCAL_PORT_ANY 0
+#define UPNP_LOCAL_PORT_SAME 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structures definitions : */
+struct UPNParg { const char * elt; const char * val; };
+
+char *
+simpleUPnPcommand(int, const char *, const char *,
+ const char *, struct UPNParg *,
+ int *);
+
+/* upnpDiscover()
+ * discover UPnP devices on the network.
+ * The discovered devices are returned as a chained list.
+ * It is up to the caller to free the list with freeUPNPDevlist().
+ * delay (in millisecond) is the maximum time for waiting any device
+ * response.
+ * If available, device list will be obtained from MiniSSDPd.
+ * Default path for minissdpd socket will be used if minissdpdsock argument
+ * is NULL.
+ * If multicastif is not NULL, it will be used instead of the default
+ * multicast interface for sending SSDP discover packets.
+ * If localport is set to UPNP_LOCAL_PORT_SAME(1) SSDP packets will be sent
+ * from the source port 1900 (same as destination port), if set to
+ * UPNP_LOCAL_PORT_ANY(0) system assign a source port, any other value will
+ * be attempted as the source port.
+ * "searchalltypes" parameter is useful when searching several types,
+ * if 0, the discovery will stop with the first type returning results.
+ * TTL should default to 2. */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverAll(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes);
+
+/* parserootdesc() :
+ * parse root XML description of a UPnP device and fill the IGDdatas
+ * structure. */
+MINIUPNP_LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *);
+
+/* structure used to get fast access to urls
+ * controlURL: controlURL of the WANIPConnection
+ * ipcondescURL: url of the description of the WANIPConnection
+ * controlURL_CIF: controlURL of the WANCommonInterfaceConfig
+ * controlURL_6FC: controlURL of the WANIPv6FirewallControl
+ */
+struct UPNPUrls {
+ char * controlURL;
+ char * ipcondescURL;
+ char * controlURL_CIF;
+ char * controlURL_6FC;
+ char * rootdescURL;
+};
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ * 0 = NO IGD found
+ * 1 = A valid connected IGD has been found
+ * 2 = A valid IGD has been found but it reported as
+ * not connected
+ * 3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any non zero return case, the urls and data structures
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * When succeding, urls, data, and lanaddr arguments are set.
+ * return value :
+ * 0 - Not ok
+ * 1 - OK */
+MINIUPNP_LIBSPEC int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+MINIUPNP_LIBSPEC void
+GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *,
+ const char *, unsigned int);
+
+MINIUPNP_LIBSPEC void
+FreeUPNPUrls(struct UPNPUrls *);
+
+/* return 0 or 1 */
+MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/miniupnpc/miniupnpc.h b/thirdparty/miniupnpc/miniupnpc/miniupnpc.h
new file mode 100644
index 0000000000..8ddc282bd1
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc/miniupnpc.h
@@ -0,0 +1,153 @@
+/* $Id: miniupnpc.h,v 1.53 2018/05/07 11:05:16 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef MINIUPNPC_H_INCLUDED
+#define MINIUPNPC_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "igd_desc_parse.h"
+#include "upnpdev.h"
+
+/* error codes : */
+#define UPNPDISCOVER_SUCCESS (0)
+#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
+#define UPNPDISCOVER_SOCKET_ERROR (-101)
+#define UPNPDISCOVER_MEMORY_ERROR (-102)
+
+/* versions : */
+#define MINIUPNPC_VERSION "2.1"
+#define MINIUPNPC_API_VERSION 17
+
+/* Source port:
+ Using "1" as an alias for 1900 for backwards compatibility
+ (presuming one would have used that for the "sameport" parameter) */
+#define UPNP_LOCAL_PORT_ANY 0
+#define UPNP_LOCAL_PORT_SAME 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structures definitions : */
+struct UPNParg { const char * elt; const char * val; };
+
+char *
+simpleUPnPcommand(int, const char *, const char *,
+ const char *, struct UPNParg *,
+ int *);
+
+/* upnpDiscover()
+ * discover UPnP devices on the network.
+ * The discovered devices are returned as a chained list.
+ * It is up to the caller to free the list with freeUPNPDevlist().
+ * delay (in millisecond) is the maximum time for waiting any device
+ * response.
+ * If available, device list will be obtained from MiniSSDPd.
+ * Default path for minissdpd socket will be used if minissdpdsock argument
+ * is NULL.
+ * If multicastif is not NULL, it will be used instead of the default
+ * multicast interface for sending SSDP discover packets.
+ * If localport is set to UPNP_LOCAL_PORT_SAME(1) SSDP packets will be sent
+ * from the source port 1900 (same as destination port), if set to
+ * UPNP_LOCAL_PORT_ANY(0) system assign a source port, any other value will
+ * be attempted as the source port.
+ * "searchalltypes" parameter is useful when searching several types,
+ * if 0, the discovery will stop with the first type returning results.
+ * TTL should default to 2. */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverAll(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes);
+
+/* parserootdesc() :
+ * parse root XML description of a UPnP device and fill the IGDdatas
+ * structure. */
+MINIUPNP_LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *);
+
+/* structure used to get fast access to urls
+ * controlURL: controlURL of the WANIPConnection
+ * ipcondescURL: url of the description of the WANIPConnection
+ * controlURL_CIF: controlURL of the WANCommonInterfaceConfig
+ * controlURL_6FC: controlURL of the WANIPv6FirewallControl
+ */
+struct UPNPUrls {
+ char * controlURL;
+ char * ipcondescURL;
+ char * controlURL_CIF;
+ char * controlURL_6FC;
+ char * rootdescURL;
+};
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ * 0 = NO IGD found
+ * 1 = A valid connected IGD has been found
+ * 2 = A valid IGD has been found but it reported as
+ * not connected
+ * 3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any non zero return case, the urls and data structures
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * When succeding, urls, data, and lanaddr arguments are set.
+ * return value :
+ * 0 - Not ok
+ * 1 - OK */
+MINIUPNP_LIBSPEC int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+MINIUPNP_LIBSPEC void
+GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *,
+ const char *, unsigned int);
+
+MINIUPNP_LIBSPEC void
+FreeUPNPUrls(struct UPNPUrls *);
+
+/* return 0 or 1 */
+MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/miniupnpc/miniwget.h b/thirdparty/miniupnpc/miniupnpc/miniwget.h
new file mode 100644
index 0000000000..f5572c2544
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc/miniwget.h
@@ -0,0 +1,27 @@
+/* $Id: miniwget.h,v 1.12 2016/01/24 17:24:36 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2016 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef MINIWGET_H_INCLUDED
+#define MINIWGET_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int, int *);
+
+MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int, int *);
+
+int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/thirdparty/miniupnpc/miniupnpc/upnpcommands.h b/thirdparty/miniupnpc/miniupnpc/upnpcommands.h
new file mode 100644
index 0000000000..0c6d501666
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc/upnpcommands.h
@@ -0,0 +1,348 @@
+/* $Id: upnpcommands.h,v 1.32 2018/03/13 23:34:47 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef UPNPCOMMANDS_H_INCLUDED
+#define UPNPCOMMANDS_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "miniupnpctypes.h"
+
+/* MiniUPnPc return codes : */
+#define UPNPCOMMAND_SUCCESS (0)
+#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
+#define UPNPCOMMAND_INVALID_ARGS (-2)
+#define UPNPCOMMAND_HTTP_ERROR (-3)
+#define UPNPCOMMAND_INVALID_RESPONSE (-4)
+#define UPNPCOMMAND_MEM_ALLOC_ERROR (-5)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct PortMappingParserData;
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+ const char * servicetype);
+
+/* UPNP_GetStatusInfo()
+ * status and lastconnerror are 64 byte buffers
+ * Return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+MINIUPNP_LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+ const char * servicetype,
+ char * status,
+ unsigned int * uptime,
+ char * lastconnerror);
+
+/* UPNP_GetConnectionTypeInfo()
+ * argument connectionType is a 64 character buffer
+ * Return Values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+MINIUPNP_LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+ const char * servicetype,
+ char * connectionType);
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ *
+ * possible UPnP Errors :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control. */
+MINIUPNP_LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+ const char * servicetype,
+ char * extIpAdd);
+
+/* UPNP_GetLinkLayerMaxBitRates()
+ * call WANCommonInterfaceConfig:1#GetCommonLinkProperties
+ *
+ * return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code. */
+MINIUPNP_LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
+ const char* servicetype,
+ unsigned int * bitrateDown,
+ unsigned int * bitrateUp);
+
+/* UPNP_AddPortMapping()
+ * if desc is NULL, it will be defaulted to "libminiupnpc"
+ * remoteHost is usually NULL because IGD don't support it.
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
+ *
+ * List of possible UPnP errors for AddPortMapping :
+ * errorCode errorDescription (short) - Description (long)
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization and
+ * the sender was not authorized.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ * wild-carded
+ * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
+ * 718 ConflictInMappingEntry - The port mapping entry specified conflicts
+ * with a mapping assigned previously to another client
+ * 724 SamePortValuesRequired - Internal and External port values
+ * must be the same
+ * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
+ * permanent lease times on port mappings
+ * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
+ * and cannot be a specific IP address or DNS name
+ * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
+ * cannot be a specific port value
+ * 728 NoPortMapsAvailable - There are not enough free ports available to
+ * complete port mapping.
+ * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
+ * due to conflict with other mechanisms.
+ * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
+ */
+MINIUPNP_LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration);
+
+/* UPNP_AddAnyPortMapping()
+ * if desc is NULL, it will be defaulted to "libminiupnpc"
+ * remoteHost is usually NULL because IGD don't support it.
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
+ *
+ * List of possible UPnP errors for AddPortMapping :
+ * errorCode errorDescription (short) - Description (long)
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization and
+ * the sender was not authorized.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ * wild-carded
+ * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
+ * 728 NoPortMapsAvailable - There are not enough free ports available to
+ * complete port mapping.
+ * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
+ * due to conflict with other mechanisms.
+ * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
+ */
+MINIUPNP_LIBSPEC int
+UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration,
+ char * reservedPort);
+
+/* UPNP_DeletePortMapping()
+ * Use same argument values as what was used for AddPortMapping().
+ * remoteHost is usually NULL because IGD don't support it.
+ * Return Values :
+ * 0 : SUCCESS
+ * NON ZERO : error. Either an UPnP error code or an undefined error.
+ *
+ * List of possible UPnP errors for DeletePortMapping :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array */
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort, const char * proto,
+ const char * remoteHost);
+
+/* UPNP_DeletePortRangeMapping()
+ * Use same argument values as what was used for AddPortMapping().
+ * remoteHost is usually NULL because IGD don't support it.
+ * Return Values :
+ * 0 : SUCCESS
+ * NON ZERO : error. Either an UPnP error code or an undefined error.
+ *
+ * List of possible UPnP errors for DeletePortMapping :
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 730 PortMappingNotFound - This error message is returned if no port
+ * mapping is found in the specified range.
+ * 733 InconsistentParameters - NewStartPort and NewEndPort values are not consistent. */
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
+ const char * extPortStart, const char * extPortEnd,
+ const char * proto,
+ const char * manage);
+
+/* UPNP_GetPortMappingNumberOfEntries()
+ * not supported by all routers */
+MINIUPNP_LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char* controlURL,
+ const char* servicetype,
+ unsigned int * num);
+
+/* UPNP_GetSpecificPortMappingEntry()
+ * retrieves an existing port mapping
+ * params :
+ * in extPort
+ * in proto
+ * in remoteHost
+ * out intClient (16 bytes)
+ * out intPort (6 bytes)
+ * out desc (80 bytes)
+ * out enabled (4 bytes)
+ * out leaseDuration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code.
+ *
+ * List of possible UPnP errors for _GetSpecificPortMappingEntry :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * extPort,
+ const char * proto,
+ const char * remoteHost,
+ char * intClient,
+ char * intPort,
+ char * desc,
+ char * enabled,
+ char * leaseDuration);
+
+/* UPNP_GetGenericPortMappingEntry()
+ * params :
+ * in index
+ * out extPort (6 bytes)
+ * out intClient (16 bytes)
+ * out intPort (6 bytes)
+ * out protocol (4 bytes)
+ * out desc (80 bytes)
+ * out enabled (4 bytes)
+ * out rHost (64 bytes)
+ * out duration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code.
+ *
+ * Possible UPNP Error codes :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * index,
+ char * extPort,
+ char * intClient,
+ char * intPort,
+ char * protocol,
+ char * desc,
+ char * enabled,
+ char * rHost,
+ char * duration);
+
+/* UPNP_GetListOfPortMappings() Available in IGD v2
+ *
+ *
+ * Possible UPNP Error codes :
+ * 606 Action not Authorized
+ * 730 PortMappingNotFound - no port mapping is found in the specified range.
+ * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
+ * consistent.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+ const char * servicetype,
+ const char * startPort,
+ const char * endPort,
+ const char * protocol,
+ const char * numberOfPorts,
+ struct PortMappingParserData * data);
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+MINIUPNP_LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+ const char * servicetype,
+ int * firewallEnabled,
+ int * inboundPinholeAllowed);
+
+MINIUPNP_LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ int * opTimeout);
+
+MINIUPNP_LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ const char * leaseTime,
+ char * uniqueID);
+
+MINIUPNP_LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+ const char * uniqueID,
+ const char * leaseTime);
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
+
+MINIUPNP_LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * isWorking);
+
+MINIUPNP_LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * packets);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/miniupnpc_declspec.h b/thirdparty/miniupnpc/miniupnpc_declspec.h
new file mode 100644
index 0000000000..40adb922ec
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc_declspec.h
@@ -0,0 +1,21 @@
+#ifndef MINIUPNPC_DECLSPEC_H_INCLUDED
+#define MINIUPNPC_DECLSPEC_H_INCLUDED
+
+#if defined(_WIN32) && !defined(MINIUPNP_STATICLIB)
+ /* for windows dll */
+ #ifdef MINIUPNP_EXPORTS
+ #define MINIUPNP_LIBSPEC __declspec(dllexport)
+ #else
+ #define MINIUPNP_LIBSPEC __declspec(dllimport)
+ #endif
+#else
+ #if defined(__GNUC__) && __GNUC__ >= 4
+ /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */
+ #define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default")))
+ #else
+ #define MINIUPNP_LIBSPEC
+ #endif
+#endif
+
+#endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */
+
diff --git a/thirdparty/miniupnpc/miniupnpc_socketdef.h b/thirdparty/miniupnpc/miniupnpc_socketdef.h
new file mode 100644
index 0000000000..965d9151b9
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc_socketdef.h
@@ -0,0 +1,37 @@
+/* $Id: miniupnpc_socketdef.h,v 1.1 2018/03/13 23:44:10 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
+ * Author : Thomas Bernard
+ * Copyright (c) 2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef MINIUPNPC_SOCKETDEF_H_INCLUDED
+#define MINIUPNPC_SOCKETDEF_H_INCLUDED
+
+#ifdef _MSC_VER
+
+#define ISINVALID(s) (INVALID_SOCKET==(s))
+
+#else
+
+#ifndef SOCKET
+#define SOCKET int
+#endif
+#ifndef SSIZE_T
+#define SSIZE_T ssize_t
+#endif
+#ifndef INVALID_SOCKET
+#define INVALID_SOCKET (-1)
+#endif
+#ifndef ISINVALID
+#define ISINVALID(s) ((s)<0)
+#endif
+
+#endif
+
+#ifdef _WIN32
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#endif /* MINIUPNPC_SOCKETDEF_H_INCLUDED */
diff --git a/thirdparty/miniupnpc/miniupnpcmodule.c b/thirdparty/miniupnpc/miniupnpcmodule.c
new file mode 100644
index 0000000000..8657a0e002
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpcmodule.c
@@ -0,0 +1,703 @@
+/* $Id: miniupnpcmodule.c,v 1.24 2014/06/10 09:48:11 nanard Exp $*/
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * website : https://miniupnp.tuxfamily.org/
+ * copyright (c) 2007-2018 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#include <Python.h>
+#define MINIUPNP_STATICLIB
+#include "structmember.h"
+#include "miniupnpc.h"
+#include "upnpcommands.h"
+#include "upnperrors.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
+/* for compatibility with Python < 2.4 */
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+#ifndef Py_RETURN_TRUE
+#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
+#endif
+
+#ifndef Py_RETURN_FALSE
+#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
+#endif
+
+/* for compatibility with Python < 3.0 */
+#ifndef PyVarObject_HEAD_INIT
+#define PyVarObject_HEAD_INIT(type, size) \
+ PyObject_HEAD_INIT(type) size,
+#endif
+
+#ifndef Py_TYPE
+#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
+
+typedef struct {
+ PyObject_HEAD
+ /* Type-specific fields go here. */
+ struct UPNPDev * devlist;
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ unsigned int discoverdelay; /* value passed to upnpDiscover() */
+ unsigned int localport; /* value passed to upnpDiscover() */
+ char lanaddr[40]; /* our ip address on the LAN */
+ char * multicastif;
+ char * minissdpdsocket;
+} UPnPObject;
+
+static PyMemberDef UPnP_members[] = {
+ {"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr),
+ READONLY, "ip address on the LAN"
+ },
+ {"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay),
+ 0/*READWRITE*/, "value in ms used to wait for SSDP responses"
+ },
+ {"localport", T_UINT, offsetof(UPnPObject, localport),
+ 0/*READWRITE*/,
+ "If localport is set to UPNP_LOCAL_PORT_SAME(1) "
+ "SSDP packets will be sent from the source port "
+ "1900 (same as destination port), if set to "
+ "UPNP_LOCAL_PORT_ANY(0) system assign a source "
+ "port, any other value will be attempted as the "
+ "source port"
+ },
+ /* T_STRING is allways readonly :( */
+ {"multicastif", T_STRING, offsetof(UPnPObject, multicastif),
+ 0, "IP of the network interface to be used for multicast operations"
+ },
+ {"minissdpdsocket", T_STRING, offsetof(UPnPObject, minissdpdsocket),
+ 0, "path of the MiniSSDPd unix socket"
+ },
+ {NULL}
+};
+
+
+static int UPnP_init(UPnPObject *self, PyObject *args, PyObject *kwds)
+{
+ char* multicastif = NULL;
+ char* minissdpdsocket = NULL;
+ static char *kwlist[] = {
+ "multicastif", "minissdpdsocket", "discoverdelay",
+ "localport", NULL
+ };
+
+ if(!PyArg_ParseTupleAndKeywords(args, kwds, "|zzII", kwlist,
+ &multicastif,
+ &minissdpdsocket,
+ &self->discoverdelay,
+ &self->localport))
+ return -1;
+
+ if(self->localport>1 &&
+ (self->localport>65534||self->localport<1024)) {
+ PyErr_SetString(PyExc_Exception, "Invalid localport value");
+ return -1;
+ }
+ if(multicastif)
+ self->multicastif = strdup(multicastif);
+ if(minissdpdsocket)
+ self->minissdpdsocket = strdup(minissdpdsocket);
+
+ return 0;
+}
+
+static void
+UPnPObject_dealloc(UPnPObject *self)
+{
+ freeUPNPDevlist(self->devlist);
+ FreeUPNPUrls(&self->urls);
+ free(self->multicastif);
+ free(self->minissdpdsocket);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static PyObject *
+UPnP_discover(UPnPObject *self)
+{
+ struct UPNPDev * dev;
+ int i;
+ PyObject *res = NULL;
+ if(self->devlist)
+ {
+ freeUPNPDevlist(self->devlist);
+ self->devlist = 0;
+ }
+ Py_BEGIN_ALLOW_THREADS
+ self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/,
+ self->multicastif,
+ self->minissdpdsocket,
+ (int)self->localport,
+ 0/*ip v6*/,
+ 2/* TTL */,
+ 0/*error */);
+ Py_END_ALLOW_THREADS
+ /* Py_RETURN_NONE ??? */
+ for(dev = self->devlist, i = 0; dev; dev = dev->pNext)
+ i++;
+ res = Py_BuildValue("i", i);
+ return res;
+}
+
+static PyObject *
+UPnP_selectigd(UPnPObject *self)
+{
+ int r;
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data,
+ self->lanaddr, sizeof(self->lanaddr));
+Py_END_ALLOW_THREADS
+ if(r)
+ {
+ return Py_BuildValue("s", self->urls.controlURL);
+ }
+ else
+ {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, "No UPnP device discovered");
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_totalbytesent(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_totalbytereceived(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_totalpacketsent(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_totalpacketreceived(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_statusinfo(UPnPObject *self)
+{
+ char status[64];
+ char lastconnerror[64];
+ unsigned int uptime = 0;
+ int r;
+ status[0] = '\0';
+ lastconnerror[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetStatusInfo(self->urls.controlURL, self->data.first.servicetype,
+ status, &uptime, lastconnerror);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror);
+#else
+ return Py_BuildValue("(s,i,s)", status, (int)uptime, lastconnerror);
+#endif
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_connectiontype(UPnPObject *self)
+{
+ char connectionType[64];
+ int r;
+ connectionType[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetConnectionTypeInfo(self->urls.controlURL,
+ self->data.first.servicetype,
+ connectionType);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ return Py_BuildValue("s", connectionType);
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_externalipaddress(UPnPObject *self)
+{
+ char externalIPAddress[40];
+ int r;
+ externalIPAddress[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetExternalIPAddress(self->urls.controlURL,
+ self->data.first.servicetype,
+ externalIPAddress);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ return Py_BuildValue("s", externalIPAddress);
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc,
+ * remoteHost)
+ * protocol is 'UDP' or 'TCP' */
+static PyObject *
+UPnP_addportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ char inPort[6];
+ unsigned short iPort;
+ const char * proto;
+ const char * host;
+ const char * desc;
+ const char * remoteHost;
+ const char * leaseDuration = "0";
+ int r;
+ if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto,
+ &host, &iPort, &desc, &remoteHost))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ sprintf(inPort, "%hu", iPort);
+ r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype,
+ extPort, inPort, host, desc, proto,
+ remoteHost, leaseDuration);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS)
+ {
+ Py_RETURN_TRUE;
+ }
+ else
+ {
+ // TODO: RAISE an Exception. See upnpcommands.h for errors codes.
+ // upnperrors.c
+ //Py_RETURN_FALSE;
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* AddAnyPortMapping(externalPort, protocol, internalHost, internalPort, desc,
+ * remoteHost)
+ * protocol is 'UDP' or 'TCP' */
+static PyObject *
+UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ char inPort[6];
+ unsigned short iPort;
+ char reservedPort[6];
+ const char * proto;
+ const char * host;
+ const char * desc;
+ const char * remoteHost;
+ const char * leaseDuration = "0";
+ int r;
+ if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ sprintf(inPort, "%hu", iPort);
+ r = UPNP_AddAnyPortMapping(self->urls.controlURL, self->data.first.servicetype,
+ extPort, inPort, host, desc, proto,
+ remoteHost, leaseDuration, reservedPort);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ return Py_BuildValue("i", atoi(reservedPort));
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+
+/* DeletePortMapping(extPort, proto, removeHost='')
+ * proto = 'UDP', 'TCP' */
+static PyObject *
+UPnP_deleteportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ const char * proto;
+ const char * remoteHost = "";
+ int r;
+ if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ r = UPNP_DeletePortMapping(self->urls.controlURL, self->data.first.servicetype,
+ extPort, proto, remoteHost);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ Py_RETURN_TRUE;
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* DeletePortMappingRange(extPort, proto, removeHost='')
+ * proto = 'UDP', 'TCP' */
+static PyObject *
+UPnP_deleteportmappingrange(UPnPObject *self, PyObject *args)
+{
+ char extPortStart[6];
+ unsigned short ePortStart;
+ char extPortEnd[6];
+ unsigned short ePortEnd;
+ const char * proto;
+ unsigned char manage;
+ char manageStr[6];
+ int r;
+ if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPortStart, "%hu", ePortStart);
+ sprintf(extPortEnd, "%hu", ePortEnd);
+ sprintf(manageStr, "%hu", (unsigned short)manage);
+ r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype,
+ extPortStart, extPortEnd, proto, manageStr);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ Py_RETURN_TRUE;
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_getportmappingnumberofentries(UPnPObject *self)
+{
+ unsigned int n = 0;
+ int r;
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetPortMappingNumberOfEntries(self->urls.controlURL,
+ self->data.first.servicetype,
+ &n);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", n);
+#else
+ return Py_BuildValue("i", (int)n);
+#endif
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* GetSpecificPortMapping(ePort, proto, remoteHost='')
+ * proto = 'UDP' or 'TCP' */
+static PyObject *
+UPnP_getspecificportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ const char * proto;
+ const char * remoteHost = "";
+ char intClient[40];
+ char intPort[6];
+ unsigned short iPort;
+ char desc[80];
+ char enabled[4];
+ char leaseDuration[16];
+ if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
+ return NULL;
+ extPort[0] = '\0'; intClient[0] = '\0'; intPort[0] = '\0';
+ desc[0] = '\0'; enabled[0] = '\0'; leaseDuration[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ UPNP_GetSpecificPortMappingEntry(self->urls.controlURL,
+ self->data.first.servicetype,
+ extPort, proto, remoteHost,
+ intClient, intPort,
+ desc, enabled, leaseDuration);
+Py_END_ALLOW_THREADS
+ if(intClient[0])
+ {
+ iPort = (unsigned short)atoi(intPort);
+ return Py_BuildValue("(s,H,s,O,i)",
+ intClient, iPort, desc,
+ PyBool_FromLong(atoi(enabled)),
+ atoi(leaseDuration));
+ }
+ else
+ {
+ Py_RETURN_NONE;
+ }
+}
+
+/* GetGenericPortMapping(index) */
+static PyObject *
+UPnP_getgenericportmapping(UPnPObject *self, PyObject *args)
+{
+ int i, r;
+ char index[8];
+ char intClient[40];
+ char intPort[6];
+ unsigned short iPort;
+ char extPort[6];
+ unsigned short ePort;
+ char protocol[4];
+ char desc[80];
+ char enabled[6];
+ char rHost[64];
+ char duration[16]; /* lease duration */
+ unsigned int dur;
+ if(!PyArg_ParseTuple(args, "i", &i))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ snprintf(index, sizeof(index), "%d", i);
+ rHost[0] = '\0'; enabled[0] = '\0';
+ duration[0] = '\0'; desc[0] = '\0';
+ extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
+ r = UPNP_GetGenericPortMappingEntry(self->urls.controlURL,
+ self->data.first.servicetype,
+ index,
+ extPort, intClient, intPort,
+ protocol, desc, enabled, rHost,
+ duration);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS)
+ {
+ ePort = (unsigned short)atoi(extPort);
+ iPort = (unsigned short)atoi(intPort);
+ dur = (unsigned int)strtoul(duration, 0, 0);
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("(H,s,(s,H),s,s,s,I)",
+ ePort, protocol, intClient, iPort,
+ desc, enabled, rHost, dur);
+#else
+ return Py_BuildValue("(i,s,(s,i),s,s,s,i)",
+ (int)ePort, protocol, intClient, (int)iPort,
+ desc, enabled, rHost, (int)dur);
+#endif
+ }
+ else
+ {
+ Py_RETURN_NONE;
+ }
+}
+
+/* miniupnpc.UPnP object Method Table */
+static PyMethodDef UPnP_methods[] = {
+ {"discover", (PyCFunction)UPnP_discover, METH_NOARGS,
+ "discover UPnP IGD devices on the network"
+ },
+ {"selectigd", (PyCFunction)UPnP_selectigd, METH_NOARGS,
+ "select a valid UPnP IGD among discovered devices"
+ },
+ {"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS,
+ "return the total number of bytes sent by UPnP IGD"
+ },
+ {"totalbytereceived", (PyCFunction)UPnP_totalbytereceived, METH_NOARGS,
+ "return the total number of bytes received by UPnP IGD"
+ },
+ {"totalpacketsent", (PyCFunction)UPnP_totalpacketsent, METH_NOARGS,
+ "return the total number of packets sent by UPnP IGD"
+ },
+ {"totalpacketreceived", (PyCFunction)UPnP_totalpacketreceived, METH_NOARGS,
+ "return the total number of packets received by UPnP IGD"
+ },
+ {"statusinfo", (PyCFunction)UPnP_statusinfo, METH_NOARGS,
+ "return status and uptime"
+ },
+ {"connectiontype", (PyCFunction)UPnP_connectiontype, METH_NOARGS,
+ "return IGD WAN connection type"
+ },
+ {"externalipaddress", (PyCFunction)UPnP_externalipaddress, METH_NOARGS,
+ "return external IP address"
+ },
+ {"addportmapping", (PyCFunction)UPnP_addportmapping, METH_VARARGS,
+ "add a port mapping"
+ },
+ {"addanyportmapping", (PyCFunction)UPnP_addanyportmapping, METH_VARARGS,
+ "add a port mapping, IGD to select alternative if necessary"
+ },
+ {"deleteportmapping", (PyCFunction)UPnP_deleteportmapping, METH_VARARGS,
+ "delete a port mapping"
+ },
+ {"deleteportmappingrange", (PyCFunction)UPnP_deleteportmappingrange, METH_VARARGS,
+ "delete a range of port mappings"
+ },
+ {"getportmappingnumberofentries", (PyCFunction)UPnP_getportmappingnumberofentries, METH_NOARGS,
+ "-- non standard --"
+ },
+ {"getspecificportmapping", (PyCFunction)UPnP_getspecificportmapping, METH_VARARGS,
+ "get details about a specific port mapping entry"
+ },
+ {"getgenericportmapping", (PyCFunction)UPnP_getgenericportmapping, METH_VARARGS,
+ "get all details about the port mapping at index"
+ },
+ {NULL} /* Sentinel */
+};
+
+static PyTypeObject UPnPType = {
+ PyVarObject_HEAD_INIT(NULL,
+ 0) /*ob_size*/
+ "miniupnpc.UPnP", /*tp_name*/
+ sizeof(UPnPObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)UPnPObject_dealloc,/*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "UPnP objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ UPnP_methods, /* tp_methods */
+ UPnP_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)UPnP_init, /* tp_init */
+ 0, /* tp_alloc */
+#ifndef _WIN32
+ PyType_GenericNew,/*UPnP_new,*/ /* tp_new */
+#else
+ 0,
+#endif
+};
+
+/* module methods */
+static PyMethodDef miniupnpc_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "miniupnpc", /* m_name */
+ "miniupnpc module.", /* m_doc */
+ -1, /* m_size */
+ miniupnpc_methods, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+#endif
+
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+
+PyMODINIT_FUNC
+#if PY_MAJOR_VERSION >= 3
+PyInit_miniupnpc(void)
+#else
+initminiupnpc(void)
+#endif
+{
+ PyObject* m;
+
+#ifdef _WIN32
+ /* initialize Winsock. */
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+
+ UPnPType.tp_new = PyType_GenericNew;
+#endif
+ if (PyType_Ready(&UPnPType) < 0)
+#if PY_MAJOR_VERSION >= 3
+ return 0;
+#else
+ return;
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+ m = PyModule_Create(&moduledef);
+#else
+ m = Py_InitModule3("miniupnpc", miniupnpc_methods,
+ "miniupnpc module.");
+#endif
+
+ Py_INCREF(&UPnPType);
+ PyModule_AddObject(m, "UPnP", (PyObject *)&UPnPType);
+
+#if PY_MAJOR_VERSION >= 3
+ return m;
+#endif
+}
+
diff --git a/thirdparty/miniupnpc/miniupnpcstrings.h b/thirdparty/miniupnpc/miniupnpcstrings.h
new file mode 100644
index 0000000000..1d5c5882fd
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpcstrings.h
@@ -0,0 +1,17 @@
+#ifndef MINIUPNPCSTRINGS_H_INCLUDED
+#define MINIUPNPCSTRINGS_H_INCLUDED
+
+#include <version.h>
+
+#define OS_STRING VERSION_NAME "/1.0"
+#define MINIUPNPC_VERSION_STRING "2.1"
+
+#if 0
+/* according to "UPnP Device Architecture 1.0" */
+#define UPNP_VERSION_STRING "UPnP/1.0"
+#else
+/* according to "UPnP Device Architecture 1.1" */
+#define UPNP_VERSION_STRING "UPnP/1.1"
+#endif
+
+#endif
diff --git a/thirdparty/miniupnpc/miniupnpctypes.h b/thirdparty/miniupnpc/miniupnpctypes.h
new file mode 100644
index 0000000000..307ce39699
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpctypes.h
@@ -0,0 +1,19 @@
+/* $Id: miniupnpctypes.h,v 1.1 2011/02/15 11:10:40 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org
+ * Author : Thomas Bernard
+ * Copyright (c) 2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef MINIUPNPCTYPES_H_INCLUDED
+#define MINIUPNPCTYPES_H_INCLUDED
+
+#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+#define UNSIGNED_INTEGER unsigned long long
+#define STRTOUI strtoull
+#else
+#define UNSIGNED_INTEGER unsigned int
+#define STRTOUI strtoul
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/miniwget.c b/thirdparty/miniupnpc/miniwget.c
new file mode 100644
index 0000000000..a46ba76022
--- /dev/null
+++ b/thirdparty/miniupnpc/miniwget.c
@@ -0,0 +1,662 @@
+/* $Id: miniwget.c,v 1.78 2018/03/13 23:22:18 nanard Exp $ */
+/* Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define MAXHOSTNAMELEN 64
+#define snprintf _snprintf
+#define socklen_t int
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#else /* #ifdef _WIN32 */
+#include <unistd.h>
+#include <sys/param.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+#define socklen_t int
+#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/select.h>
+#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netdb.h>
+#define closesocket close
+#include <strings.h>
+#endif /* #else _WIN32 */
+#ifdef __GNU__
+#define MAXHOSTNAMELEN 64
+#endif /* __GNU__ */
+
+#ifndef MIN
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#endif /* MIN */
+
+
+#include "miniupnpcstrings.h"
+#include "miniwget.h"
+#include "connecthostport.h"
+#include "receivedata.h"
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+/*
+ * Read a HTTP response from a socket.
+ * Process Content-Length and Transfer-encoding headers.
+ * return a pointer to the content buffer, which length is saved
+ * to the length parameter.
+ */
+void *
+getHTTPResponse(SOCKET s, int * size, int * status_code)
+{
+ char buf[2048];
+ int n;
+ int endofheaders = 0;
+ int chunked = 0;
+ int content_length = -1;
+ unsigned int chunksize = 0;
+ unsigned int bytestocopy = 0;
+ /* buffers : */
+ char * header_buf;
+ unsigned int header_buf_len = 2048;
+ unsigned int header_buf_used = 0;
+ char * content_buf;
+ unsigned int content_buf_len = 2048;
+ unsigned int content_buf_used = 0;
+ char chunksize_buf[32];
+ unsigned int chunksize_buf_index;
+#ifdef DEBUG
+ char * reason_phrase = NULL;
+ int reason_phrase_len = 0;
+#endif
+
+ if(status_code) *status_code = -1;
+ header_buf = malloc(header_buf_len);
+ if(header_buf == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
+#endif /* DEBUG */
+ *size = -1;
+ return NULL;
+ }
+ content_buf = malloc(content_buf_len);
+ if(content_buf == NULL)
+ {
+ free(header_buf);
+#ifdef DEBUG
+ fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
+#endif /* DEBUG */
+ *size = -1;
+ return NULL;
+ }
+ chunksize_buf[0] = '\0';
+ chunksize_buf_index = 0;
+
+ while((n = receivedata(s, buf, sizeof(buf), 5000, NULL)) > 0)
+ {
+ if(endofheaders == 0)
+ {
+ int i;
+ int linestart=0;
+ int colon=0;
+ int valuestart=0;
+ if(header_buf_used + n > header_buf_len) {
+ char * tmp = realloc(header_buf, header_buf_used + n);
+ if(tmp == NULL) {
+ /* memory allocation error */
+ free(header_buf);
+ free(content_buf);
+ *size = -1;
+ return NULL;
+ }
+ header_buf = tmp;
+ header_buf_len = header_buf_used + n;
+ }
+ memcpy(header_buf + header_buf_used, buf, n);
+ header_buf_used += n;
+ /* search for CR LF CR LF (end of headers)
+ * recognize also LF LF */
+ i = 0;
+ while(i < ((int)header_buf_used-1) && (endofheaders == 0)) {
+ if(header_buf[i] == '\r') {
+ i++;
+ if(header_buf[i] == '\n') {
+ i++;
+ if(i < (int)header_buf_used && header_buf[i] == '\r') {
+ i++;
+ if(i < (int)header_buf_used && header_buf[i] == '\n') {
+ endofheaders = i+1;
+ }
+ }
+ }
+ } else if(header_buf[i] == '\n') {
+ i++;
+ if(header_buf[i] == '\n') {
+ endofheaders = i+1;
+ }
+ }
+ i++;
+ }
+ if(endofheaders == 0)
+ continue;
+ /* parse header lines */
+ for(i = 0; i < endofheaders - 1; i++) {
+ if(linestart > 0 && colon <= linestart && header_buf[i]==':')
+ {
+ colon = i;
+ while(i < (endofheaders-1)
+ && (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
+ i++;
+ valuestart = i + 1;
+ }
+ /* detecting end of line */
+ else if(header_buf[i]=='\r' || header_buf[i]=='\n')
+ {
+ if(linestart == 0 && status_code)
+ {
+ /* Status line
+ * HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
+ int sp;
+ for(sp = 0; sp < i; sp++)
+ if(header_buf[sp] == ' ')
+ {
+ if(*status_code < 0)
+ *status_code = atoi(header_buf + sp + 1);
+ else
+ {
+#ifdef DEBUG
+ reason_phrase = header_buf + sp + 1;
+ reason_phrase_len = i - sp - 1;
+#endif
+ break;
+ }
+ }
+#ifdef DEBUG
+ printf("HTTP status code = %d, Reason phrase = %.*s\n",
+ *status_code, reason_phrase_len, reason_phrase);
+#endif
+ }
+ else if(colon > linestart && valuestart > colon)
+ {
+#ifdef DEBUG
+ printf("header='%.*s', value='%.*s'\n",
+ colon-linestart, header_buf+linestart,
+ i-valuestart, header_buf+valuestart);
+#endif
+ if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
+ {
+ content_length = atoi(header_buf+valuestart);
+#ifdef DEBUG
+ printf("Content-Length: %d\n", content_length);
+#endif
+ }
+ else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
+ && 0==strncasecmp(header_buf+valuestart, "chunked", 7))
+ {
+#ifdef DEBUG
+ printf("chunked transfer-encoding!\n");
+#endif
+ chunked = 1;
+ }
+ }
+ while((i < (int)header_buf_used) && (header_buf[i]=='\r' || header_buf[i] == '\n'))
+ i++;
+ linestart = i;
+ colon = linestart;
+ valuestart = 0;
+ }
+ }
+ /* copy the remaining of the received data back to buf */
+ n = header_buf_used - endofheaders;
+ memcpy(buf, header_buf + endofheaders, n);
+ /* if(headers) */
+ }
+ /* if we get there, endofheaders != 0.
+ * In the other case, there was a continue above */
+ /* content */
+ if(chunked)
+ {
+ int i = 0;
+ while(i < n)
+ {
+ if(chunksize == 0)
+ {
+ /* reading chunk size */
+ if(chunksize_buf_index == 0) {
+ /* skipping any leading CR LF */
+ if(i<n && buf[i] == '\r') i++;
+ if(i<n && buf[i] == '\n') i++;
+ }
+ while(i<n && isxdigit(buf[i])
+ && chunksize_buf_index < (sizeof(chunksize_buf)-1))
+ {
+ chunksize_buf[chunksize_buf_index++] = buf[i];
+ chunksize_buf[chunksize_buf_index] = '\0';
+ i++;
+ }
+ while(i<n && buf[i] != '\r' && buf[i] != '\n')
+ i++; /* discarding chunk-extension */
+ if(i<n && buf[i] == '\r') i++;
+ if(i<n && buf[i] == '\n') {
+ unsigned int j;
+ for(j = 0; j < chunksize_buf_index; j++) {
+ if(chunksize_buf[j] >= '0'
+ && chunksize_buf[j] <= '9')
+ chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
+ else
+ chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
+ }
+ chunksize_buf[0] = '\0';
+ chunksize_buf_index = 0;
+ i++;
+ } else {
+ /* not finished to get chunksize */
+ continue;
+ }
+#ifdef DEBUG
+ printf("chunksize = %u (%x)\n", chunksize, chunksize);
+#endif
+ if(chunksize == 0)
+ {
+#ifdef DEBUG
+ printf("end of HTTP content - %d %d\n", i, n);
+ /*printf("'%.*s'\n", n-i, buf+i);*/
+#endif
+ goto end_of_stream;
+ }
+ }
+ /* it is guaranteed that (n >= i) */
+ bytestocopy = (chunksize < (unsigned int)(n - i))?chunksize:(unsigned int)(n - i);
+ if((content_buf_used + bytestocopy) > content_buf_len)
+ {
+ char * tmp;
+ if((content_length >= 0) && ((unsigned int)content_length >= (content_buf_used + bytestocopy))) {
+ content_buf_len = content_length;
+ } else {
+ content_buf_len = content_buf_used + bytestocopy;
+ }
+ tmp = realloc(content_buf, content_buf_len);
+ if(tmp == NULL) {
+ /* memory allocation error */
+ free(content_buf);
+ free(header_buf);
+ *size = -1;
+ return NULL;
+ }
+ content_buf = tmp;
+ }
+ memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
+ content_buf_used += bytestocopy;
+ i += bytestocopy;
+ chunksize -= bytestocopy;
+ }
+ }
+ else
+ {
+ /* not chunked */
+ if(content_length > 0
+ && (content_buf_used + n) > (unsigned int)content_length) {
+ /* skipping additional bytes */
+ n = content_length - content_buf_used;
+ }
+ if(content_buf_used + n > content_buf_len)
+ {
+ char * tmp;
+ if(content_length >= 0
+ && (unsigned int)content_length >= (content_buf_used + n)) {
+ content_buf_len = content_length;
+ } else {
+ content_buf_len = content_buf_used + n;
+ }
+ tmp = realloc(content_buf, content_buf_len);
+ if(tmp == NULL) {
+ /* memory allocation error */
+ free(content_buf);
+ free(header_buf);
+ *size = -1;
+ return NULL;
+ }
+ content_buf = tmp;
+ }
+ memcpy(content_buf + content_buf_used, buf, n);
+ content_buf_used += n;
+ }
+ /* use the Content-Length header value if available */
+ if(content_length > 0 && content_buf_used >= (unsigned int)content_length)
+ {
+#ifdef DEBUG
+ printf("End of HTTP content\n");
+#endif
+ break;
+ }
+ }
+end_of_stream:
+ free(header_buf); header_buf = NULL;
+ *size = content_buf_used;
+ if(content_buf_used == 0)
+ {
+ free(content_buf);
+ content_buf = NULL;
+ }
+ return content_buf;
+}
+
+/* miniwget3() :
+ * do all the work.
+ * Return NULL if something failed. */
+static void *
+miniwget3(const char * host,
+ unsigned short port, const char * path,
+ int * size, char * addr_str, int addr_str_len,
+ const char * httpversion, unsigned int scope_id,
+ int * status_code)
+{
+ char buf[2048];
+ SOCKET s;
+ int n;
+ int len;
+ int sent;
+ void * content;
+
+ *size = 0;
+ s = connecthostport(host, port, scope_id);
+ if(ISINVALID(s))
+ return NULL;
+
+ /* get address for caller ! */
+ if(addr_str)
+ {
+ struct sockaddr_storage saddr;
+ socklen_t saddrlen;
+
+ saddrlen = sizeof(saddr);
+ if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
+ {
+ perror("getsockname");
+ }
+ else
+ {
+#if defined(__amigaos__) && !defined(__amigaos4__)
+ /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
+ * But his function make a string with the port : nn.nn.nn.nn:port */
+/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
+ NULL, addr_str, (DWORD *)&addr_str_len))
+ {
+ printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
+ }*/
+ /* the following code is only compatible with ip v4 addresses */
+ strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
+#else
+#if 0
+ if(saddr.sa_family == AF_INET6) {
+ inet_ntop(AF_INET6,
+ &(((struct sockaddr_in6 *)&saddr)->sin6_addr),
+ addr_str, addr_str_len);
+ } else {
+ inet_ntop(AF_INET,
+ &(((struct sockaddr_in *)&saddr)->sin_addr),
+ addr_str, addr_str_len);
+ }
+#endif
+ /* getnameinfo return ip v6 address with the scope identifier
+ * such as : 2a01:e35:8b2b:7330::%4281128194 */
+ n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
+ addr_str, addr_str_len,
+ NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if(n != 0) {
+#ifdef _WIN32
+ fprintf(stderr, "getnameinfo() failed : %d\n", n);
+#else
+ fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
+#endif
+ }
+#endif
+ }
+#ifdef DEBUG
+ printf("address miniwget : %s\n", addr_str);
+#endif
+ }
+
+ len = snprintf(buf, sizeof(buf),
+ "GET %s HTTP/%s\r\n"
+ "Host: %s:%d\r\n"
+ "Connection: Close\r\n"
+ "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
+
+ "\r\n",
+ path, httpversion, host, port);
+ if ((unsigned int)len >= sizeof(buf))
+ {
+ closesocket(s);
+ return NULL;
+ }
+ sent = 0;
+ /* sending the HTTP request */
+ while(sent < len)
+ {
+ n = send(s, buf+sent, len-sent, 0);
+ if(n < 0)
+ {
+ perror("send");
+ closesocket(s);
+ return NULL;
+ }
+ else
+ {
+ sent += n;
+ }
+ }
+ content = getHTTPResponse(s, size, status_code);
+ closesocket(s);
+ return content;
+}
+
+/* miniwget2() :
+ * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
+static void *
+miniwget2(const char * host,
+ unsigned short port, const char * path,
+ int * size, char * addr_str, int addr_str_len,
+ unsigned int scope_id, int * status_code)
+{
+ char * respbuffer;
+
+#if 1
+ respbuffer = miniwget3(host, port, path, size,
+ addr_str, addr_str_len, "1.1",
+ scope_id, status_code);
+#else
+ respbuffer = miniwget3(host, port, path, size,
+ addr_str, addr_str_len, "1.0",
+ scope_id, status_code);
+ if (*size == 0)
+ {
+#ifdef DEBUG
+ printf("Retrying with HTTP/1.1\n");
+#endif
+ free(respbuffer);
+ respbuffer = miniwget3(host, port, path, size,
+ addr_str, addr_str_len, "1.1",
+ scope_id, status_code);
+ }
+#endif
+ return respbuffer;
+}
+
+
+
+
+/* parseURL()
+ * arguments :
+ * url : source string not modified
+ * hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
+ * port : port (destination)
+ * path : pointer to the path part of the URL
+ *
+ * Return values :
+ * 0 - Failure
+ * 1 - Success */
+int
+parseURL(const char * url,
+ char * hostname, unsigned short * port,
+ char * * path, unsigned int * scope_id)
+{
+ char * p1, *p2, *p3;
+ if(!url)
+ return 0;
+ p1 = strstr(url, "://");
+ if(!p1)
+ return 0;
+ p1 += 3;
+ if( (url[0]!='h') || (url[1]!='t')
+ ||(url[2]!='t') || (url[3]!='p'))
+ return 0;
+ memset(hostname, 0, MAXHOSTNAMELEN + 1);
+ if(*p1 == '[')
+ {
+ /* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
+ char * scope;
+ scope = strchr(p1, '%');
+ p2 = strchr(p1, ']');
+ if(p2 && scope && scope < p2 && scope_id) {
+ /* parse scope */
+#ifdef IF_NAMESIZE
+ char tmp[IF_NAMESIZE];
+ int l;
+ scope++;
+ /* "%25" is just '%' in URL encoding */
+ if(scope[0] == '2' && scope[1] == '5')
+ scope += 2; /* skip "25" */
+ l = p2 - scope;
+ if(l >= IF_NAMESIZE)
+ l = IF_NAMESIZE - 1;
+ memcpy(tmp, scope, l);
+ tmp[l] = '\0';
+ *scope_id = if_nametoindex(tmp);
+ if(*scope_id == 0) {
+ *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
+ }
+#else
+ /* under windows, scope is numerical */
+ char tmp[8];
+ int l;
+ scope++;
+ /* "%25" is just '%' in URL encoding */
+ if(scope[0] == '2' && scope[1] == '5')
+ scope += 2; /* skip "25" */
+ l = p2 - scope;
+ if(l >= sizeof(tmp))
+ l = sizeof(tmp) - 1;
+ memcpy(tmp, scope, l);
+ tmp[l] = '\0';
+ *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
+#endif
+ }
+ p3 = strchr(p1, '/');
+ if(p2 && p3)
+ {
+ p2++;
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
+ if(*p2 == ':')
+ {
+ *port = 0;
+ p2++;
+ while( (*p2 >= '0') && (*p2 <= '9'))
+ {
+ *port *= 10;
+ *port += (unsigned short)(*p2 - '0');
+ p2++;
+ }
+ }
+ else
+ {
+ *port = 80;
+ }
+ *path = p3;
+ return 1;
+ }
+ }
+ p2 = strchr(p1, ':');
+ p3 = strchr(p1, '/');
+ if(!p3)
+ return 0;
+ if(!p2 || (p2>p3))
+ {
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
+ *port = 80;
+ }
+ else
+ {
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
+ *port = 0;
+ p2++;
+ while( (*p2 >= '0') && (*p2 <= '9'))
+ {
+ *port *= 10;
+ *port += (unsigned short)(*p2 - '0');
+ p2++;
+ }
+ }
+ *path = p3;
+ return 1;
+}
+
+void *
+miniwget(const char * url, int * size,
+ unsigned int scope_id, int * status_code)
+{
+ unsigned short port;
+ char * path;
+ /* protocol://host:port/chemin */
+ char hostname[MAXHOSTNAMELEN+1];
+ *size = 0;
+ if(!parseURL(url, hostname, &port, &path, &scope_id))
+ return NULL;
+#ifdef DEBUG
+ printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
+ hostname, port, path, scope_id);
+#endif
+ return miniwget2(hostname, port, path, size, 0, 0, scope_id, status_code);
+}
+
+void *
+miniwget_getaddr(const char * url, int * size,
+ char * addr, int addrlen, unsigned int scope_id,
+ int * status_code)
+{
+ unsigned short port;
+ char * path;
+ /* protocol://host:port/path */
+ char hostname[MAXHOSTNAMELEN+1];
+ *size = 0;
+ if(addr)
+ addr[0] = '\0';
+ if(!parseURL(url, hostname, &port, &path, &scope_id))
+ return NULL;
+#ifdef DEBUG
+ printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
+ hostname, port, path, scope_id);
+#endif
+ return miniwget2(hostname, port, path, size, addr, addrlen, scope_id, status_code);
+}
+
diff --git a/thirdparty/miniupnpc/miniwget.h b/thirdparty/miniupnpc/miniwget.h
new file mode 100644
index 0000000000..f5572c2544
--- /dev/null
+++ b/thirdparty/miniupnpc/miniwget.h
@@ -0,0 +1,27 @@
+/* $Id: miniwget.h,v 1.12 2016/01/24 17:24:36 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2016 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef MINIWGET_H_INCLUDED
+#define MINIWGET_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int, int *);
+
+MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int, int *);
+
+int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/thirdparty/miniupnpc/miniwget_private.h b/thirdparty/miniupnpc/miniwget_private.h
new file mode 100644
index 0000000000..e4eaac8085
--- /dev/null
+++ b/thirdparty/miniupnpc/miniwget_private.h
@@ -0,0 +1,15 @@
+/* $Id: miniwget_private.h,v 1.1 2018/04/06 10:17:58 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef MINIWGET_INTERNAL_H_INCLUDED
+#define MINIWGET_INTERNAL_H_INCLUDED
+
+#include "miniupnpc_socketdef.h"
+
+void * getHTTPResponse(SOCKET s, int * size, int * status_code);
+
+#endif
diff --git a/thirdparty/miniupnpc/minixml.c b/thirdparty/miniupnpc/minixml.c
new file mode 100644
index 0000000000..ed2d3c759c
--- /dev/null
+++ b/thirdparty/miniupnpc/minixml.c
@@ -0,0 +1,231 @@
+/* $Id: minixml.c,v 1.10 2012/03/05 19:42:47 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * minixml.c : the minimum size a xml parser can be ! */
+/* Project : miniupnp
+ * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author : Thomas Bernard
+
+Copyright (c) 2005-2017, Thomas BERNARD
+All rights reserved.
+
+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.
+ * The name of the author may not 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.
+*/
+#include <string.h>
+#include "minixml.h"
+
+/* parseatt : used to parse the argument list
+ * return 0 (false) in case of success and -1 (true) if the end
+ * of the xmlbuffer is reached. */
+static int parseatt(struct xmlparser * p)
+{
+ const char * attname;
+ int attnamelen;
+ const char * attvalue;
+ int attvaluelen;
+ while(p->xml < p->xmlend)
+ {
+ if(*p->xml=='/' || *p->xml=='>')
+ return 0;
+ if( !IS_WHITE_SPACE(*p->xml) )
+ {
+ char sep;
+ attname = p->xml;
+ attnamelen = 0;
+ while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
+ {
+ attnamelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ while(*(p->xml++) != '=')
+ {
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ while(IS_WHITE_SPACE(*p->xml))
+ {
+ p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ sep = *p->xml;
+ if(sep=='\'' || sep=='\"')
+ {
+ p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ attvalue = p->xml;
+ attvaluelen = 0;
+ while(*p->xml != sep)
+ {
+ attvaluelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ }
+ else
+ {
+ attvalue = p->xml;
+ attvaluelen = 0;
+ while( !IS_WHITE_SPACE(*p->xml)
+ && *p->xml != '>' && *p->xml != '/')
+ {
+ attvaluelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ }
+ /*printf("%.*s='%.*s'\n",
+ attnamelen, attname, attvaluelen, attvalue);*/
+ if(p->attfunc)
+ p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
+ }
+ p->xml++;
+ }
+ return -1;
+}
+
+/* parseelt parse the xml stream and
+ * call the callback functions when needed... */
+static void parseelt(struct xmlparser * p)
+{
+ int i;
+ const char * elementname;
+ while(p->xml < (p->xmlend - 1))
+ {
+ if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "<!--", 4)))
+ {
+ p->xml += 3;
+ /* ignore comments */
+ do
+ {
+ p->xml++;
+ if ((p->xml + 3) >= p->xmlend)
+ return;
+ }
+ while(memcmp(p->xml, "-->", 3) != 0);
+ p->xml += 3;
+ }
+ else if((p->xml)[0]=='<' && (p->xml)[1]!='?')
+ {
+ i = 0; elementname = ++p->xml;
+ while( !IS_WHITE_SPACE(*p->xml)
+ && (*p->xml!='>') && (*p->xml!='/')
+ )
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ /* to ignore namespace : */
+ if(*p->xml==':')
+ {
+ i = 0;
+ elementname = ++p->xml;
+ }
+ }
+ if(i>0)
+ {
+ if(p->starteltfunc)
+ p->starteltfunc(p->data, elementname, i);
+ if(parseatt(p))
+ return;
+ if(*p->xml!='/')
+ {
+ const char * data;
+ i = 0; data = ++p->xml;
+ if (p->xml >= p->xmlend)
+ return;
+ while( IS_WHITE_SPACE(*p->xml) )
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ /* CDATA are at least 9 + 3 characters long : <![CDATA[ ]]> */
+ if((p->xmlend >= (p->xml + (9 + 3))) && (memcmp(p->xml, "<![CDATA[", 9) == 0))
+ {
+ /* CDATA handling */
+ p->xml += 9;
+ data = p->xml;
+ i = 0;
+ while(memcmp(p->xml, "]]>", 3) != 0)
+ {
+ i++; p->xml++;
+ if ((p->xml + 3) >= p->xmlend)
+ return;
+ }
+ if(i>0 && p->datafunc)
+ p->datafunc(p->data, data, i);
+ while(*p->xml!='<')
+ {
+ p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ }
+ else
+ {
+ while(*p->xml!='<')
+ {
+ i++; p->xml++;
+ if ((p->xml + 1) >= p->xmlend)
+ return;
+ }
+ if(i>0 && p->datafunc && *(p->xml + 1) == '/')
+ p->datafunc(p->data, data, i);
+ }
+ }
+ }
+ else if(*p->xml == '/')
+ {
+ i = 0; elementname = ++p->xml;
+ if (p->xml >= p->xmlend)
+ return;
+ while((*p->xml != '>'))
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ if(p->endeltfunc)
+ p->endeltfunc(p->data, elementname, i);
+ p->xml++;
+ }
+ }
+ else
+ {
+ p->xml++;
+ }
+ }
+}
+
+/* the parser must be initialized before calling this function */
+void parsexml(struct xmlparser * parser)
+{
+ parser->xml = parser->xmlstart;
+ parser->xmlend = parser->xmlstart + parser->xmlsize;
+ parseelt(parser);
+}
+
+
diff --git a/thirdparty/miniupnpc/minixml.h b/thirdparty/miniupnpc/minixml.h
new file mode 100644
index 0000000000..19e6f513bf
--- /dev/null
+++ b/thirdparty/miniupnpc/minixml.h
@@ -0,0 +1,37 @@
+/* $Id: minixml.h,v 1.6 2006/11/30 11:47:21 nanard Exp $ */
+/* minimal xml parser
+ *
+ * Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef MINIXML_H_INCLUDED
+#define MINIXML_H_INCLUDED
+#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
+
+/* if a callback function pointer is set to NULL,
+ * the function is not called */
+struct xmlparser {
+ const char *xmlstart;
+ const char *xmlend;
+ const char *xml; /* pointer to current character */
+ int xmlsize;
+ void * data;
+ void (*starteltfunc) (void *, const char *, int);
+ void (*endeltfunc) (void *, const char *, int);
+ void (*datafunc) (void *, const char *, int);
+ void (*attfunc) (void *, const char *, int, const char *, int);
+};
+
+/* parsexml()
+ * the xmlparser structure must be initialized before the call
+ * the following structure members have to be initialized :
+ * xmlstart, xmlsize, data, *func
+ * xml is for internal usage, xmlend is computed automatically */
+void parsexml(struct xmlparser *);
+
+#endif
+
diff --git a/thirdparty/miniupnpc/minixmlvalid.c b/thirdparty/miniupnpc/minixmlvalid.c
new file mode 100644
index 0000000000..dad1488122
--- /dev/null
+++ b/thirdparty/miniupnpc/minixmlvalid.c
@@ -0,0 +1,163 @@
+/* $Id: minixmlvalid.c,v 1.7 2015/07/15 12:41:15 nanard Exp $ */
+/* MiniUPnP Project
+ * http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
+ * minixmlvalid.c :
+ * validation program for the minixml parser
+ *
+ * (c) 2006-2011 Thomas Bernard */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "minixml.h"
+
+/* xml event structure */
+struct event {
+ enum { ELTSTART, ELTEND, ATT, CHARDATA } type;
+ const char * data;
+ int len;
+};
+
+struct eventlist {
+ int n;
+ struct event * events;
+};
+
+/* compare 2 xml event lists
+ * return 0 if the two lists are equals */
+int evtlistcmp(struct eventlist * a, struct eventlist * b)
+{
+ int i;
+ struct event * ae, * be;
+ if(a->n != b->n)
+ {
+ printf("event number not matching : %d != %d\n", a->n, b->n);
+ /*return 1;*/
+ }
+ for(i=0; i<a->n; i++)
+ {
+ ae = a->events + i;
+ be = b->events + i;
+ if( (ae->type != be->type)
+ ||(ae->len != be->len)
+ ||memcmp(ae->data, be->data, ae->len))
+ {
+ printf("Found a difference : %d '%.*s' != %d '%.*s'\n",
+ ae->type, ae->len, ae->data,
+ be->type, be->len, be->data);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Test data */
+static const char xmldata[] =
+"<xmlroot>\n"
+" <elt1 att1=\"attvalue1\" att2=\"attvalue2\">"
+"character data"
+"</elt1> \n \t"
+"<elt1b/>"
+"<elt1>\n<![CDATA[ <html>stuff !\n ]]> \n</elt1>\n"
+"<elt2a> \t<elt2b>chardata1</elt2b><elt2b> chardata2 </elt2b></elt2a>"
+"</xmlroot>";
+
+static const struct event evtref[] =
+{
+ {ELTSTART, "xmlroot", 7},
+ {ELTSTART, "elt1", 4},
+ /* attributes */
+ {CHARDATA, "character data", 14},
+ {ELTEND, "elt1", 4},
+ {ELTSTART, "elt1b", 5},
+ {ELTSTART, "elt1", 4},
+ {CHARDATA, " <html>stuff !\n ", 16},
+ {ELTEND, "elt1", 4},
+ {ELTSTART, "elt2a", 5},
+ {ELTSTART, "elt2b", 5},
+ {CHARDATA, "chardata1", 9},
+ {ELTEND, "elt2b", 5},
+ {ELTSTART, "elt2b", 5},
+ {CHARDATA, " chardata2 ", 11},
+ {ELTEND, "elt2b", 5},
+ {ELTEND, "elt2a", 5},
+ {ELTEND, "xmlroot", 7}
+};
+
+void startelt(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("startelt : %.*s\n", l, p);*/
+ evt->type = ELTSTART;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+void endelt(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("endelt : %.*s\n", l, p);*/
+ evt->type = ELTEND;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+void chardata(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("chardata : '%.*s'\n", l, p);*/
+ evt->type = CHARDATA;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+int testxmlparser(const char * xml, int size)
+{
+ int r;
+ struct eventlist evtlist;
+ struct eventlist evtlistref;
+ struct xmlparser parser;
+ evtlist.n = 0;
+ evtlist.events = malloc(sizeof(struct event)*100);
+ if(evtlist.events == NULL)
+ {
+ fprintf(stderr, "Memory allocation error.\n");
+ return -1;
+ }
+ memset(&parser, 0, sizeof(parser));
+ parser.xmlstart = xml;
+ parser.xmlsize = size;
+ parser.data = &evtlist;
+ parser.starteltfunc = startelt;
+ parser.endeltfunc = endelt;
+ parser.datafunc = chardata;
+ parsexml(&parser);
+ printf("%d events\n", evtlist.n);
+ /* compare */
+ evtlistref.n = sizeof(evtref)/sizeof(struct event);
+ evtlistref.events = (struct event *)evtref;
+ r = evtlistcmp(&evtlistref, &evtlist);
+ free(evtlist.events);
+ return r;
+}
+
+int main(int argc, char * * argv)
+{
+ int r;
+ (void)argc; (void)argv;
+
+ r = testxmlparser(xmldata, sizeof(xmldata)-1);
+ if(r)
+ printf("minixml validation test failed\n");
+ return r;
+}
+
diff --git a/thirdparty/miniupnpc/portlistingparse.c b/thirdparty/miniupnpc/portlistingparse.c
new file mode 100644
index 0000000000..55859f2714
--- /dev/null
+++ b/thirdparty/miniupnpc/portlistingparse.c
@@ -0,0 +1,172 @@
+/* $Id: portlistingparse.c,v 1.9 2015/07/15 12:41:13 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2011-2016 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#include <string.h>
+#include <stdlib.h>
+#ifdef DEBUG
+#include <stdio.h>
+#endif /* DEBUG */
+#include "portlistingparse.h"
+#include "minixml.h"
+
+/* list of the elements */
+static const struct {
+ const portMappingElt code;
+ const char * const str;
+} elements[] = {
+ { PortMappingEntry, "PortMappingEntry"},
+ { NewRemoteHost, "NewRemoteHost"},
+ { NewExternalPort, "NewExternalPort"},
+ { NewProtocol, "NewProtocol"},
+ { NewInternalPort, "NewInternalPort"},
+ { NewInternalClient, "NewInternalClient"},
+ { NewEnabled, "NewEnabled"},
+ { NewDescription, "NewDescription"},
+ { NewLeaseTime, "NewLeaseTime"},
+ { PortMappingEltNone, NULL}
+};
+
+/* Helper function */
+static UNSIGNED_INTEGER
+atoui(const char * p, int l)
+{
+ UNSIGNED_INTEGER r = 0;
+ while(l > 0 && *p)
+ {
+ if(*p >= '0' && *p <= '9')
+ r = r*10 + (*p - '0');
+ else
+ break;
+ p++;
+ l--;
+ }
+ return r;
+}
+
+/* Start element handler */
+static void
+startelt(void * d, const char * name, int l)
+{
+ int i;
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ pdata->curelt = PortMappingEltNone;
+ for(i = 0; elements[i].str; i++)
+ {
+ if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0)
+ {
+ pdata->curelt = elements[i].code;
+ break;
+ }
+ }
+ if(pdata->curelt == PortMappingEntry)
+ {
+ struct PortMapping * pm;
+ pm = calloc(1, sizeof(struct PortMapping));
+ if(pm == NULL)
+ {
+ /* malloc error */
+#ifdef DEBUG
+ fprintf(stderr, "%s: error allocating memory",
+ "startelt");
+#endif /* DEBUG */
+ return;
+ }
+ pm->l_next = pdata->l_head; /* insert in list */
+ pdata->l_head = pm;
+ }
+}
+
+/* End element handler */
+static void
+endelt(void * d, const char * name, int l)
+{
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ (void)name;
+ (void)l;
+ pdata->curelt = PortMappingEltNone;
+}
+
+/* Data handler */
+static void
+data(void * d, const char * data, int l)
+{
+ struct PortMapping * pm;
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ pm = pdata->l_head;
+ if(!pm)
+ return;
+ if(l > 63)
+ l = 63;
+ switch(pdata->curelt)
+ {
+ case NewRemoteHost:
+ memcpy(pm->remoteHost, data, l);
+ pm->remoteHost[l] = '\0';
+ break;
+ case NewExternalPort:
+ pm->externalPort = (unsigned short)atoui(data, l);
+ break;
+ case NewProtocol:
+ if(l > 3)
+ l = 3;
+ memcpy(pm->protocol, data, l);
+ pm->protocol[l] = '\0';
+ break;
+ case NewInternalPort:
+ pm->internalPort = (unsigned short)atoui(data, l);
+ break;
+ case NewInternalClient:
+ memcpy(pm->internalClient, data, l);
+ pm->internalClient[l] = '\0';
+ break;
+ case NewEnabled:
+ pm->enabled = (unsigned char)atoui(data, l);
+ break;
+ case NewDescription:
+ memcpy(pm->description, data, l);
+ pm->description[l] = '\0';
+ break;
+ case NewLeaseTime:
+ pm->leaseTime = atoui(data, l);
+ break;
+ default:
+ break;
+ }
+}
+
+
+/* Parse the PortMappingList XML document for IGD version 2
+ */
+void
+ParsePortListing(const char * buffer, int bufsize,
+ struct PortMappingParserData * pdata)
+{
+ struct xmlparser parser;
+
+ memset(pdata, 0, sizeof(struct PortMappingParserData));
+ /* init xmlparser */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = pdata;
+ parser.starteltfunc = startelt;
+ parser.endeltfunc = endelt;
+ parser.datafunc = data;
+ parser.attfunc = 0;
+ parsexml(&parser);
+}
+
+void
+FreePortListing(struct PortMappingParserData * pdata)
+{
+ struct PortMapping * pm;
+ while((pm = pdata->l_head) != NULL)
+ {
+ /* remove from list */
+ pdata->l_head = pm->l_next;
+ free(pm);
+ }
+}
+
diff --git a/thirdparty/miniupnpc/portlistingparse.h b/thirdparty/miniupnpc/portlistingparse.h
new file mode 100644
index 0000000000..e3957a3f4c
--- /dev/null
+++ b/thirdparty/miniupnpc/portlistingparse.h
@@ -0,0 +1,65 @@
+/* $Id: portlistingparse.h,v 1.10 2014/11/01 10:37:32 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2011-2015 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#ifndef PORTLISTINGPARSE_H_INCLUDED
+#define PORTLISTINGPARSE_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+/* for the definition of UNSIGNED_INTEGER */
+#include "miniupnpctypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* sample of PortMappingEntry :
+ <p:PortMappingEntry>
+ <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
+ <p:NewExternalPort>2345</p:NewExternalPort>
+ <p:NewProtocol>TCP</p:NewProtocol>
+ <p:NewInternalPort>2345</p:NewInternalPort>
+ <p:NewInternalClient>192.168.1.137</p:NewInternalClient>
+ <p:NewEnabled>1</p:NewEnabled>
+ <p:NewDescription>dooom</p:NewDescription>
+ <p:NewLeaseTime>345</p:NewLeaseTime>
+ </p:PortMappingEntry>
+ */
+typedef enum { PortMappingEltNone,
+ PortMappingEntry, NewRemoteHost,
+ NewExternalPort, NewProtocol,
+ NewInternalPort, NewInternalClient,
+ NewEnabled, NewDescription,
+ NewLeaseTime } portMappingElt;
+
+struct PortMapping {
+ struct PortMapping * l_next; /* list next element */
+ UNSIGNED_INTEGER leaseTime;
+ unsigned short externalPort;
+ unsigned short internalPort;
+ char remoteHost[64];
+ char internalClient[64];
+ char description[64];
+ char protocol[4];
+ unsigned char enabled;
+};
+
+struct PortMappingParserData {
+ struct PortMapping * l_head; /* list head */
+ portMappingElt curelt;
+};
+
+MINIUPNP_LIBSPEC void
+ParsePortListing(const char * buffer, int bufsize,
+ struct PortMappingParserData * pdata);
+
+MINIUPNP_LIBSPEC void
+FreePortListing(struct PortMappingParserData * pdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/thirdparty/miniupnpc/receivedata.c b/thirdparty/miniupnpc/receivedata.c
new file mode 100644
index 0000000000..7b9cc5b778
--- /dev/null
+++ b/thirdparty/miniupnpc/receivedata.c
@@ -0,0 +1,99 @@
+/* $Id: receivedata.c,v 1.7 2015/11/09 21:51:41 nanard Exp $ */
+/* Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2011-2014 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else /* _WIN32 */
+#include <unistd.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+#define socklen_t int
+#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/select.h>
+#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#if !defined(__amigaos__) && !defined(__amigaos4__)
+#include <poll.h>
+#endif /* !defined(__amigaos__) && !defined(__amigaos4__) */
+#include <errno.h>
+#define MINIUPNPC_IGNORE_EINTR
+#endif /* _WIN32 */
+
+#include "receivedata.h"
+
+int
+receivedata(SOCKET socket,
+ char * data, int length,
+ int timeout, unsigned int * scope_id)
+{
+#ifdef MINIUPNPC_GET_SRC_ADDR
+ struct sockaddr_storage src_addr;
+ socklen_t src_addr_len = sizeof(src_addr);
+#endif /* MINIUPNPC_GET_SRC_ADDR */
+ int n;
+#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+ /* using poll */
+ struct pollfd fds[1]; /* for the poll */
+#ifdef MINIUPNPC_IGNORE_EINTR
+ do {
+#endif /* MINIUPNPC_IGNORE_EINTR */
+ fds[0].fd = socket;
+ fds[0].events = POLLIN;
+ n = poll(fds, 1, timeout);
+#ifdef MINIUPNPC_IGNORE_EINTR
+ } while(n < 0 && errno == EINTR);
+#endif /* MINIUPNPC_IGNORE_EINTR */
+ if(n < 0) {
+ PRINT_SOCKET_ERROR("poll");
+ return -1;
+ } else if(n == 0) {
+ /* timeout */
+ return 0;
+ }
+#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+ /* using select under _WIN32 and amigaos */
+ fd_set socketSet;
+ TIMEVAL timeval;
+ FD_ZERO(&socketSet);
+ FD_SET(socket, &socketSet);
+ timeval.tv_sec = timeout / 1000;
+ timeval.tv_usec = (timeout % 1000) * 1000;
+ n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
+ if(n < 0) {
+ PRINT_SOCKET_ERROR("select");
+ return -1;
+ } else if(n == 0) {
+ return 0;
+ }
+#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+#ifdef MINIUPNPC_GET_SRC_ADDR
+ memset(&src_addr, 0, sizeof(src_addr));
+ n = recvfrom(socket, data, length, 0,
+ (struct sockaddr *)&src_addr, &src_addr_len);
+#else /* MINIUPNPC_GET_SRC_ADDR */
+ n = recv(socket, data, length, 0);
+#endif /* MINIUPNPC_GET_SRC_ADDR */
+ if(n<0) {
+ PRINT_SOCKET_ERROR("recv");
+ }
+#ifdef MINIUPNPC_GET_SRC_ADDR
+ if (src_addr.ss_family == AF_INET6) {
+ const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr;
+#ifdef DEBUG
+ printf("scope_id=%u\n", src_addr6->sin6_scope_id);
+#endif /* DEBUG */
+ if(scope_id)
+ *scope_id = src_addr6->sin6_scope_id;
+ }
+#endif /* MINIUPNPC_GET_SRC_ADDR */
+ return n;
+}
+
diff --git a/thirdparty/miniupnpc/receivedata.h b/thirdparty/miniupnpc/receivedata.h
new file mode 100644
index 0000000000..c9fdc561f8
--- /dev/null
+++ b/thirdparty/miniupnpc/receivedata.h
@@ -0,0 +1,21 @@
+/* $Id: receivedata.h,v 1.3 2012/06/23 22:34:47 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2011-2018 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef RECEIVEDATA_H_INCLUDED
+#define RECEIVEDATA_H_INCLUDED
+
+#include "miniupnpc_socketdef.h"
+
+/* Reads data from the specified socket.
+ * Returns the number of bytes read if successful, zero if no bytes were
+ * read or if we timed out. Returns negative if there was an error. */
+int receivedata(SOCKET socket,
+ char * data, int length,
+ int timeout, unsigned int * scope_id);
+
+#endif
+
diff --git a/thirdparty/miniupnpc/upnpc.c b/thirdparty/miniupnpc/upnpc.c
new file mode 100644
index 0000000000..0c65cbe8c0
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpc.c
@@ -0,0 +1,861 @@
+/* $Id: upnpc.c,v 1.119 2018/03/13 23:34:46 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#define snprintf _snprintf
+#else
+/* for IPPROTO_TCP / IPPROTO_UDP */
+#include <netinet/in.h>
+#endif
+#include <ctype.h>
+#include "miniwget.h"
+#include "miniupnpc.h"
+#include "upnpcommands.h"
+#include "portlistingparse.h"
+#include "upnperrors.h"
+#include "miniupnpcstrings.h"
+
+/* protofix() checks if protocol is "UDP" or "TCP"
+ * returns NULL if not */
+const char * protofix(const char * proto)
+{
+ static const char proto_tcp[4] = { 'T', 'C', 'P', 0};
+ static const char proto_udp[4] = { 'U', 'D', 'P', 0};
+ int i, b;
+ for(i=0, b=1; i<4; i++)
+ b = b && ( (proto[i] == proto_tcp[i])
+ || (proto[i] == (proto_tcp[i] | 32)) );
+ if(b)
+ return proto_tcp;
+ for(i=0, b=1; i<4; i++)
+ b = b && ( (proto[i] == proto_udp[i])
+ || (proto[i] == (proto_udp[i] | 32)) );
+ if(b)
+ return proto_udp;
+ return 0;
+}
+
+/* is_int() checks if parameter is an integer or not
+ * 1 for integer
+ * 0 for not an integer */
+int is_int(char const* s)
+{
+ if(s == NULL)
+ return 0;
+ while(*s) {
+ /* #define isdigit(c) ((c) >= '0' && (c) <= '9') */
+ if(!isdigit(*s))
+ return 0;
+ s++;
+ }
+ return 1;
+}
+
+static void DisplayInfos(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ char externalIPAddress[40];
+ char connectionType[64];
+ char status[64];
+ char lastconnerr[64];
+ unsigned int uptime = 0;
+ unsigned int brUp, brDown;
+ time_t timenow, timestarted;
+ int r;
+ if(UPNP_GetConnectionTypeInfo(urls->controlURL,
+ data->first.servicetype,
+ connectionType) != UPNPCOMMAND_SUCCESS)
+ printf("GetConnectionTypeInfo failed.\n");
+ else
+ printf("Connection Type : %s\n", connectionType);
+ if(UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
+ status, &uptime, lastconnerr) != UPNPCOMMAND_SUCCESS)
+ printf("GetStatusInfo failed.\n");
+ else
+ printf("Status : %s, uptime=%us, LastConnectionError : %s\n",
+ status, uptime, lastconnerr);
+ if(uptime > 0) {
+ timenow = time(NULL);
+ timestarted = timenow - uptime;
+ printf(" Time started : %s", ctime(&timestarted));
+ }
+ if(UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype,
+ &brDown, &brUp) != UPNPCOMMAND_SUCCESS) {
+ printf("GetLinkLayerMaxBitRates failed.\n");
+ } else {
+ printf("MaxBitRateDown : %u bps", brDown);
+ if(brDown >= 1000000) {
+ printf(" (%u.%u Mbps)", brDown / 1000000, (brDown / 100000) % 10);
+ } else if(brDown >= 1000) {
+ printf(" (%u Kbps)", brDown / 1000);
+ }
+ printf(" MaxBitRateUp %u bps", brUp);
+ if(brUp >= 1000000) {
+ printf(" (%u.%u Mbps)", brUp / 1000000, (brUp / 100000) % 10);
+ } else if(brUp >= 1000) {
+ printf(" (%u Kbps)", brUp / 1000);
+ }
+ printf("\n");
+ }
+ r = UPNP_GetExternalIPAddress(urls->controlURL,
+ data->first.servicetype,
+ externalIPAddress);
+ if(r != UPNPCOMMAND_SUCCESS) {
+ printf("GetExternalIPAddress failed. (errorcode=%d)\n", r);
+ } else {
+ printf("ExternalIPAddress = %s\n", externalIPAddress);
+ }
+}
+
+static void GetConnectionStatus(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
+ DisplayInfos(urls, data);
+ bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
+ bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
+ packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
+ printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
+}
+
+static void ListRedirections(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ int r;
+ int i = 0;
+ char index[6];
+ char intClient[40];
+ char intPort[6];
+ char extPort[6];
+ char protocol[4];
+ char desc[80];
+ char enabled[6];
+ char rHost[64];
+ char duration[16];
+ /*unsigned int num=0;
+ UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num);
+ printf("PortMappingNumberOfEntries : %u\n", num);*/
+ printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n");
+ do {
+ snprintf(index, 6, "%d", i);
+ rHost[0] = '\0'; enabled[0] = '\0';
+ duration[0] = '\0'; desc[0] = '\0';
+ extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
+ r = UPNP_GetGenericPortMappingEntry(urls->controlURL,
+ data->first.servicetype,
+ index,
+ extPort, intClient, intPort,
+ protocol, desc, enabled,
+ rHost, duration);
+ if(r==0)
+ /*
+ printf("%02d - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n"
+ " desc='%s' rHost='%s'\n",
+ i, protocol, extPort, intClient, intPort,
+ enabled, duration,
+ desc, rHost);
+ */
+ printf("%2d %s %5s->%s:%-5s '%s' '%s' %s\n",
+ i, protocol, extPort, intClient, intPort,
+ desc, rHost, duration);
+ else
+ printf("GetGenericPortMappingEntry() returned %d (%s)\n",
+ r, strupnperror(r));
+ i++;
+ } while(r==0);
+}
+
+static void NewListRedirections(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ int r;
+ int i = 0;
+ struct PortMappingParserData pdata;
+ struct PortMapping * pm;
+
+ memset(&pdata, 0, sizeof(struct PortMappingParserData));
+ r = UPNP_GetListOfPortMappings(urls->controlURL,
+ data->first.servicetype,
+ "0",
+ "65535",
+ "TCP",
+ "1000",
+ &pdata);
+ if(r == UPNPCOMMAND_SUCCESS)
+ {
+ printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n");
+ for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost,
+ (unsigned)pm->leaseTime);
+ i++;
+ }
+ FreePortListing(&pdata);
+ }
+ else
+ {
+ printf("GetListOfPortMappings() returned %d (%s)\n",
+ r, strupnperror(r));
+ }
+ r = UPNP_GetListOfPortMappings(urls->controlURL,
+ data->first.servicetype,
+ "0",
+ "65535",
+ "UDP",
+ "1000",
+ &pdata);
+ if(r == UPNPCOMMAND_SUCCESS)
+ {
+ for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost,
+ (unsigned)pm->leaseTime);
+ i++;
+ }
+ FreePortListing(&pdata);
+ }
+ else
+ {
+ printf("GetListOfPortMappings() returned %d (%s)\n",
+ r, strupnperror(r));
+ }
+}
+
+/* Test function
+ * 1 - get connection type
+ * 2 - get extenal ip address
+ * 3 - Add port mapping
+ * 4 - get this port mapping from the IGD */
+static int SetRedirectAndTest(struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ const char * iaddr,
+ const char * iport,
+ const char * eport,
+ const char * proto,
+ const char * leaseDuration,
+ const char * description,
+ int addAny)
+{
+ char externalIPAddress[40];
+ char intClient[40];
+ char intPort[6];
+ char reservedPort[6];
+ char duration[16];
+ int r;
+
+ if(!iaddr || !iport || !eport || !proto)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return -1;
+ }
+ proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "invalid protocol\n");
+ return -1;
+ }
+
+ r = UPNP_GetExternalIPAddress(urls->controlURL,
+ data->first.servicetype,
+ externalIPAddress);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetExternalIPAddress failed.\n");
+ else
+ printf("ExternalIPAddress = %s\n", externalIPAddress);
+
+ if (addAny) {
+ r = UPNP_AddAnyPortMapping(urls->controlURL, data->first.servicetype,
+ eport, iport, iaddr, description,
+ proto, 0, leaseDuration, reservedPort);
+ if(r==UPNPCOMMAND_SUCCESS)
+ eport = reservedPort;
+ else
+ printf("AddAnyPortMapping(%s, %s, %s) failed with code %d (%s)\n",
+ eport, iport, iaddr, r, strupnperror(r));
+ } else {
+ r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
+ eport, iport, iaddr, description,
+ proto, 0, leaseDuration);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
+ eport, iport, iaddr, r, strupnperror(r));
+ return -2;
+ }
+ }
+
+ r = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
+ data->first.servicetype,
+ eport, proto, NULL/*remoteHost*/,
+ intClient, intPort, NULL/*desc*/,
+ NULL/*enabled*/, duration);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
+ r, strupnperror(r));
+ return -2;
+ } else {
+ printf("InternalIP:Port = %s:%s\n", intClient, intPort);
+ printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
+ externalIPAddress, eport, proto, intClient, intPort, duration);
+ }
+ return 0;
+}
+
+static int
+RemoveRedirect(struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ const char * eport,
+ const char * proto,
+ const char * remoteHost)
+{
+ int r;
+ if(!proto || !eport)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return -1;
+ }
+ proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "protocol invalid\n");
+ return -1;
+ }
+ r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("UPNP_DeletePortMapping() failed with code : %d\n", r);
+ return -2;
+ }else {
+ printf("UPNP_DeletePortMapping() returned : %d\n", r);
+ }
+ return 0;
+}
+
+static int
+RemoveRedirectRange(struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ const char * ePortStart, char const * ePortEnd,
+ const char * proto, const char * manage)
+{
+ int r;
+
+ if (!manage)
+ manage = "0";
+
+ if(!proto || !ePortStart || !ePortEnd)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return -1;
+ }
+ proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "protocol invalid\n");
+ return -1;
+ }
+ r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("UPNP_DeletePortMappingRange() failed with code : %d\n", r);
+ return -2;
+ }else {
+ printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
+ }
+ return 0;
+}
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data)
+{
+ unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
+ int firewallEnabled = 0, inboundPinholeAllowed = 0;
+
+ UPNP_GetFirewallStatus(urls->controlURL_6FC, data->IPv6FC.servicetype, &firewallEnabled, &inboundPinholeAllowed);
+ printf("FirewallEnabled: %d & Inbound Pinhole Allowed: %d\n", firewallEnabled, inboundPinholeAllowed);
+ printf("GetFirewallStatus:\n Firewall Enabled: %s\n Inbound Pinhole Allowed: %s\n", (firewallEnabled)? "Yes":"No", (inboundPinholeAllowed)? "Yes":"No");
+
+ bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
+ bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
+ packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
+ printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
+}
+
+/* Test function
+ * 1 - Add pinhole
+ * 2 - Check if pinhole is working from the IGD side */
+static void SetPinholeAndTest(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * remoteaddr, const char * eport,
+ const char * intaddr, const char * iport,
+ const char * proto, const char * lease_time)
+{
+ char uniqueID[8];
+ /*int isWorking = 0;*/
+ int r;
+ char proto_tmp[8];
+
+ if(!intaddr || !remoteaddr || !iport || !eport || !proto || !lease_time)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+ if(atoi(proto) == 0)
+ {
+ const char * protocol;
+ protocol = protofix(proto);
+ if(protocol && (strcmp("TCP", protocol) == 0))
+ {
+ snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_TCP);
+ proto = proto_tmp;
+ }
+ else if(protocol && (strcmp("UDP", protocol) == 0))
+ {
+ snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_UDP);
+ proto = proto_tmp;
+ }
+ else
+ {
+ fprintf(stderr, "invalid protocol\n");
+ return;
+ }
+ }
+ r = UPNP_AddPinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, lease_time, uniqueID);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("AddPinhole([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
+ remoteaddr, eport, intaddr, iport, r, strupnperror(r));
+ else
+ {
+ printf("AddPinhole: ([%s]:%s -> [%s]:%s) / Pinhole ID = %s\n",
+ remoteaddr, eport, intaddr, iport, uniqueID);
+ /*r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->servicetype_6FC, uniqueID, &isWorking);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");*/
+ }
+}
+
+/* Test function
+ * 1 - Check if pinhole is working from the IGD side
+ * 2 - Update pinhole */
+static void GetPinholeAndUpdate(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * uniqueID, const char * lease_time)
+{
+ int isWorking = 0;
+ int r;
+
+ if(!uniqueID || !lease_time)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+ r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ if(isWorking || r==709)
+ {
+ r = UPNP_UpdatePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, lease_time);
+ printf("UpdatePinhole: Pinhole ID = %s with Lease Time: %s\n", uniqueID, lease_time);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("UpdatePinhole: ID (%s) failed with code %d (%s)\n", uniqueID, r, strupnperror(r));
+ }
+}
+
+/* Test function
+ * Get pinhole timeout
+ */
+static void GetPinholeOutboundTimeout(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * remoteaddr, const char * eport,
+ const char * intaddr, const char * iport,
+ const char * proto)
+{
+ int timeout = 0;
+ int r;
+
+ if(!intaddr || !remoteaddr || !iport || !eport || !proto)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+
+ r = UPNP_GetOutboundPinholeTimeout(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, &timeout);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetOutboundPinholeTimeout([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
+ intaddr, iport, remoteaddr, eport, r, strupnperror(r));
+ else
+ printf("GetOutboundPinholeTimeout: ([%s]:%s -> [%s]:%s) / Timeout = %d\n", intaddr, iport, remoteaddr, eport, timeout);
+}
+
+static void
+GetPinholePackets(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r, pinholePackets = 0;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_GetPinholePackets(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &pinholePackets);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetPinholePackets() failed with code %d (%s)\n", r, strupnperror(r));
+ else
+ printf("GetPinholePackets: Pinhole ID = %s / PinholePackets = %d\n", uniqueID, pinholePackets);
+}
+
+static void
+CheckPinhole(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r, isWorking = 0;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ else
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
+}
+
+static void
+RemovePinhole(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_DeletePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID);
+ printf("UPNP_DeletePinhole() returned : %d\n", r);
+}
+
+
+/* sample upnp client program */
+int main(int argc, char ** argv)
+{
+ char command = 0;
+ char ** commandargv = 0;
+ int commandargc = 0;
+ struct UPNPDev * devlist = 0;
+ char lanaddr[64] = "unset"; /* my ip address on the LAN */
+ int i;
+ const char * rootdescurl = 0;
+ const char * multicastif = 0;
+ const char * minissdpdpath = 0;
+ int localport = UPNP_LOCAL_PORT_ANY;
+ int retcode = 0;
+ int error = 0;
+ int ipv6 = 0;
+ unsigned char ttl = 2; /* defaulting to 2 */
+ const char * description = 0;
+
+#ifdef _WIN32
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+ if(nResult != NO_ERROR)
+ {
+ fprintf(stderr, "WSAStartup() failed.\n");
+ return -1;
+ }
+#endif
+ printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING);
+ printf(" (c) 2005-2018 Thomas Bernard.\n");
+ printf("Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/\n"
+ "for more information.\n");
+ /* command line processing */
+ for(i=1; i<argc; i++)
+ {
+ if(0 == strcmp(argv[i], "--help") || 0 == strcmp(argv[i], "-h"))
+ {
+ command = 0;
+ break;
+ }
+ if(argv[i][0] == '-')
+ {
+ if(argv[i][1] == 'u')
+ rootdescurl = argv[++i];
+ else if(argv[i][1] == 'm')
+ {
+ multicastif = argv[++i];
+ minissdpdpath = ""; /* Disable usage of minissdpd */
+ }
+ else if(argv[i][1] == 'z')
+ {
+ char junk;
+ if(sscanf(argv[++i], "%d%c", &localport, &junk)!=1 ||
+ localport<0 || localport>65535 ||
+ (localport >1 && localport < 1024))
+ {
+ fprintf(stderr, "Invalid localport '%s'\n", argv[i]);
+ localport = UPNP_LOCAL_PORT_ANY;
+ break;
+ }
+ }
+ else if(argv[i][1] == 'p')
+ minissdpdpath = argv[++i];
+ else if(argv[i][1] == '6')
+ ipv6 = 1;
+ else if(argv[i][1] == 'e')
+ description = argv[++i];
+ else if(argv[i][1] == 't')
+ ttl = (unsigned char)atoi(argv[++i]);
+ else
+ {
+ command = argv[i][1];
+ i++;
+ commandargv = argv + i;
+ commandargc = argc - i;
+ break;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "option '%s' invalid\n", argv[i]);
+ }
+ }
+
+ if(!command
+ || (command == 'a' && commandargc<4)
+ || (command == 'd' && argc<2)
+ || (command == 'r' && argc<2)
+ || (command == 'A' && commandargc<6)
+ || (command == 'U' && commandargc<2)
+ || (command == 'D' && commandargc<1))
+ {
+ fprintf(stderr, "Usage :\t%s [options] -a ip port external_port protocol [duration]\n\t\tAdd port redirection\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -d external_port protocol <remote host>\n\t\tDelete port redirection\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -s\n\t\tGet Connection status\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -l\n\t\tList redirections\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -L\n\t\tList redirections (using GetListOfPortMappings (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -n ip port external_port protocol [duration]\n\t\tAdd (any) port redirection allowing IGD to use alternative external_port (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -N external_port_start external_port_end protocol [manage]\n\t\tDelete range of port redirections (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -r port1 [external_port1] protocol1 [port2 [external_port2] protocol2] [...]\n\t\tAdd all redirections to the current host\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -A remote_ip remote_port internal_ip internal_port protocol lease_time\n\t\tAdd Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -U uniqueID new_lease_time\n\t\tUpdate Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -C uniqueID\n\t\tCheck if Pinhole is Working (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -K uniqueID\n\t\tGet Number of packets going through the rule (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -D uniqueID\n\t\tDelete Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -S\n\t\tGet Firewall status (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -G remote_ip remote_port internal_ip internal_port protocol\n\t\tGet Outbound Pinhole Timeout (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -P\n\t\tGet Presentation url\n", argv[0]);
+ fprintf(stderr, "\nprotocol is UDP or TCP\n");
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -e description : set description for port mapping.\n");
+ fprintf(stderr, " -6 : use ip v6 instead of ip v4.\n");
+ fprintf(stderr, " -u url : bypass discovery process by providing the XML root description url.\n");
+ fprintf(stderr, " -m address/interface : provide ip address (ip v4) or interface name (ip v4 or v6) to use for sending SSDP multicast packets.\n");
+ fprintf(stderr, " -z localport : SSDP packets local (source) port (1024-65535).\n");
+ fprintf(stderr, " -p path : use this path for MiniSSDPd socket.\n");
+ fprintf(stderr, " -t ttl : set multicast TTL. Default value is 2.\n");
+ return 1;
+ }
+
+ if( rootdescurl
+ || (devlist = upnpDiscover(2000, multicastif, minissdpdpath,
+ localport, ipv6, ttl, &error)))
+ {
+ struct UPNPDev * device;
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ if(devlist)
+ {
+ printf("List of UPNP devices found on the network :\n");
+ for(device = devlist; device; device = device->pNext)
+ {
+ printf(" desc: %s\n st: %s\n\n",
+ device->descURL, device->st);
+ }
+ }
+ else if(!rootdescurl)
+ {
+ printf("upnpDiscover() error code=%d\n", error);
+ }
+ i = 1;
+ if( (rootdescurl && UPNP_GetIGDFromUrl(rootdescurl, &urls, &data, lanaddr, sizeof(lanaddr)))
+ || (i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr))))
+ {
+ switch(i) {
+ case 1:
+ printf("Found valid IGD : %s\n", urls.controlURL);
+ break;
+ case 2:
+ printf("Found a (not connected?) IGD : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ break;
+ case 3:
+ printf("UPnP device found. Is it an IGD ? : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ break;
+ default:
+ printf("Found device (igd ?) : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ }
+ printf("Local LAN ip address : %s\n", lanaddr);
+ #if 0
+ printf("getting \"%s\"\n", urls.ipcondescURL);
+ descXML = miniwget(urls.ipcondescURL, &descXMLsize);
+ if(descXML)
+ {
+ /*fwrite(descXML, 1, descXMLsize, stdout);*/
+ free(descXML); descXML = NULL;
+ }
+ #endif
+
+ switch(command)
+ {
+ case 'l':
+ DisplayInfos(&urls, &data);
+ ListRedirections(&urls, &data);
+ break;
+ case 'L':
+ NewListRedirections(&urls, &data);
+ break;
+ case 'a':
+ if (SetRedirectAndTest(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ (commandargc > 4)?commandargv[4]:"0",
+ description, 0) < 0)
+ retcode = 2;
+ break;
+ case 'd':
+ if (RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
+ commandargc > 2 ? commandargv[2] : NULL) < 0)
+ retcode = 2;
+ break;
+ case 'n': /* aNy */
+ if (SetRedirectAndTest(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ (commandargc > 4)?commandargv[4]:"0",
+ description, 1) < 0)
+ retcode = 2;
+ break;
+ case 'N':
+ if (commandargc < 3)
+ fprintf(stderr, "too few arguments\n");
+
+ if (RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
+ commandargc > 3 ? commandargv[3] : NULL) < 0)
+ retcode = 2;
+ break;
+ case 's':
+ GetConnectionStatus(&urls, &data);
+ break;
+ case 'r':
+ i = 0;
+ while(i<commandargc)
+ {
+ if(!is_int(commandargv[i])) {
+ /* 1st parameter not an integer : error */
+ fprintf(stderr, "command -r : %s is not an port number\n", commandargv[i]);
+ retcode = 1;
+ break;
+ } else if(is_int(commandargv[i+1])){
+ /* 2nd parameter is an integer : <port> <external_port> <protocol> */
+ if (SetRedirectAndTest(&urls, &data,
+ lanaddr, commandargv[i],
+ commandargv[i+1], commandargv[i+2], "0",
+ description, 0) < 0)
+ retcode = 2;
+ i+=3; /* 3 parameters parsed */
+ } else {
+ /* 2nd parameter not an integer : <port> <protocol> */
+ if (SetRedirectAndTest(&urls, &data,
+ lanaddr, commandargv[i],
+ commandargv[i], commandargv[i+1], "0",
+ description, 0) < 0)
+ retcode = 2;
+ i+=2; /* 2 parameters parsed */
+ }
+ }
+ break;
+ case 'A':
+ SetPinholeAndTest(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ commandargv[4], commandargv[5]);
+ break;
+ case 'U':
+ GetPinholeAndUpdate(&urls, &data,
+ commandargv[0], commandargv[1]);
+ break;
+ case 'C':
+ for(i=0; i<commandargc; i++)
+ {
+ CheckPinhole(&urls, &data, commandargv[i]);
+ }
+ break;
+ case 'K':
+ for(i=0; i<commandargc; i++)
+ {
+ GetPinholePackets(&urls, &data, commandargv[i]);
+ }
+ break;
+ case 'D':
+ for(i=0; i<commandargc; i++)
+ {
+ RemovePinhole(&urls, &data, commandargv[i]);
+ }
+ break;
+ case 'S':
+ GetFirewallStatus(&urls, &data);
+ break;
+ case 'G':
+ GetPinholeOutboundTimeout(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ commandargv[4]);
+ break;
+ case 'P':
+ printf("Presentation URL found:\n");
+ printf(" %s\n", data.presentationurl);
+ break;
+ default:
+ fprintf(stderr, "Unknown switch -%c\n", command);
+ retcode = 1;
+ }
+
+ FreeUPNPUrls(&urls);
+ }
+ else
+ {
+ fprintf(stderr, "No valid UPNP Internet Gateway Device found.\n");
+ retcode = 1;
+ }
+ freeUPNPDevlist(devlist); devlist = 0;
+ }
+ else
+ {
+ fprintf(stderr, "No IGD UPnP Device found on the network !\n");
+ retcode = 1;
+ }
+#ifdef _WIN32
+ nResult = WSACleanup();
+ if(nResult != NO_ERROR) {
+ fprintf(stderr, "WSACleanup() failed.\n");
+ }
+#endif /* _WIN32 */
+ return retcode;
+}
+
diff --git a/thirdparty/miniupnpc/upnpcommands.c b/thirdparty/miniupnpc/upnpcommands.c
new file mode 100644
index 0000000000..b6a693a93a
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpcommands.c
@@ -0,0 +1,1241 @@
+/* $Id: upnpcommands.c,v 1.49 2018/03/13 23:34:47 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "upnpcommands.h"
+#include "miniupnpc.h"
+#include "portlistingparse.h"
+#include "upnpreplyparse.h"
+
+static UNSIGNED_INTEGER
+my_atoui(const char * s)
+{
+ return s ? ((UNSIGNED_INTEGER)STRTOUI(s, NULL, 0)) : 0;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalBytesSent", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalBytesReceived", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalPacketsSent", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalPacketsReceived", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/* UPNP_GetStatusInfo() call the corresponding UPNP method
+ * returns the current status and uptime */
+MINIUPNP_LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+ const char * servicetype,
+ char * status,
+ unsigned int * uptime,
+ char * lastconnerror)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ char * up;
+ char * err;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!status && !uptime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetStatusInfo", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ up = GetValueFromNameValueList(&pdata, "NewUptime");
+ p = GetValueFromNameValueList(&pdata, "NewConnectionStatus");
+ err = GetValueFromNameValueList(&pdata, "NewLastConnectionError");
+ if(p && up)
+ ret = UPNPCOMMAND_SUCCESS;
+
+ if(status) {
+ if(p){
+ strncpy(status, p, 64 );
+ status[63] = '\0';
+ }else
+ status[0]= '\0';
+ }
+
+ if(uptime) {
+ if(up)
+ sscanf(up,"%u",uptime);
+ else
+ *uptime = 0;
+ }
+
+ if(lastconnerror) {
+ if(err) {
+ strncpy(lastconnerror, err, 64 );
+ lastconnerror[63] = '\0';
+ } else
+ lastconnerror[0] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method
+ * returns the connection type */
+MINIUPNP_LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+ const char * servicetype,
+ char * connectionType)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!connectionType)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetConnectionTypeInfo", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewConnectionType");
+ /*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/
+ /* PossibleConnectionTypes will have several values.... */
+ if(p) {
+ strncpy(connectionType, p, 64 );
+ connectionType[63] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ connectionType[0] = '\0';
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetLinkLayerMaxBitRate() call the corresponding UPNP method.
+ * Returns 2 values: Downloadlink bandwidth and Uplink bandwidth.
+ * One of the values can be null
+ * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only
+ * We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */
+MINIUPNP_LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char * controlURL,
+ const char * servicetype,
+ unsigned int * bitrateDown,
+ unsigned int * bitrateUp)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ char * down;
+ char * up;
+ char * p;
+
+ if(!bitrateDown && !bitrateUp)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ /* shouldn't we use GetCommonLinkProperties ? */
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetCommonLinkProperties", 0, &bufsize))) {
+ /*"GetLinkLayerMaxBitRates", 0, &bufsize);*/
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ /*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/
+ /*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate");*/
+ down = GetValueFromNameValueList(&pdata, "NewLayer1DownstreamMaxBitRate");
+ up = GetValueFromNameValueList(&pdata, "NewLayer1UpstreamMaxBitRate");
+ /*GetValueFromNameValueList(&pdata, "NewWANAccessType");*/
+ /*GetValueFromNameValueList(&pdata, "NewPhysicalLinkStatus");*/
+ if(down && up)
+ ret = UPNPCOMMAND_SUCCESS;
+
+ if(bitrateDown) {
+ if(down)
+ sscanf(down,"%u",bitrateDown);
+ else
+ *bitrateDown = 0;
+ }
+
+ if(bitrateUp) {
+ if(up)
+ sscanf(up,"%u",bitrateUp);
+ else
+ *bitrateUp = 0;
+ }
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ *
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+ const char * servicetype,
+ char * extIpAdd)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!extIpAdd || !controlURL || !servicetype)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetExternalIPAddress", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ /*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/
+ p = GetValueFromNameValueList(&pdata, "NewExternalIPAddress");
+ if(p) {
+ strncpy(extIpAdd, p, 16 );
+ extIpAdd[15] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ extIpAdd[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration)
+{
+ struct UPNParg * AddPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!inPort || !inClient || !proto || !extPort)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ AddPortMappingArgs = calloc(9, sizeof(struct UPNParg));
+ if(AddPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ AddPortMappingArgs[0].elt = "NewRemoteHost";
+ AddPortMappingArgs[0].val = remoteHost;
+ AddPortMappingArgs[1].elt = "NewExternalPort";
+ AddPortMappingArgs[1].val = extPort;
+ AddPortMappingArgs[2].elt = "NewProtocol";
+ AddPortMappingArgs[2].val = proto;
+ AddPortMappingArgs[3].elt = "NewInternalPort";
+ AddPortMappingArgs[3].val = inPort;
+ AddPortMappingArgs[4].elt = "NewInternalClient";
+ AddPortMappingArgs[4].val = inClient;
+ AddPortMappingArgs[5].elt = "NewEnabled";
+ AddPortMappingArgs[5].val = "1";
+ AddPortMappingArgs[6].elt = "NewPortMappingDescription";
+ AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
+ AddPortMappingArgs[7].elt = "NewLeaseDuration";
+ AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddPortMapping", AddPortMappingArgs,
+ &bufsize);
+ free(AddPortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ /*buffer[bufsize] = '\0';*/
+ /*puts(buffer);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ /*printf("AddPortMapping errorCode = '%s'\n", resVal); */
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration,
+ char * reservedPort)
+{
+ struct UPNParg * AddPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!inPort || !inClient || !proto || !extPort)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ AddPortMappingArgs = calloc(9, sizeof(struct UPNParg));
+ if(AddPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ AddPortMappingArgs[0].elt = "NewRemoteHost";
+ AddPortMappingArgs[0].val = remoteHost;
+ AddPortMappingArgs[1].elt = "NewExternalPort";
+ AddPortMappingArgs[1].val = extPort;
+ AddPortMappingArgs[2].elt = "NewProtocol";
+ AddPortMappingArgs[2].val = proto;
+ AddPortMappingArgs[3].elt = "NewInternalPort";
+ AddPortMappingArgs[3].val = inPort;
+ AddPortMappingArgs[4].elt = "NewInternalClient";
+ AddPortMappingArgs[4].val = inClient;
+ AddPortMappingArgs[5].elt = "NewEnabled";
+ AddPortMappingArgs[5].val = "1";
+ AddPortMappingArgs[6].elt = "NewPortMappingDescription";
+ AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
+ AddPortMappingArgs[7].elt = "NewLeaseDuration";
+ AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddAnyPortMapping", AddPortMappingArgs,
+ &bufsize);
+ free(AddPortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ char *p;
+
+ p = GetValueFromNameValueList(&pdata, "NewReservedPort");
+ if(p) {
+ strncpy(reservedPort, p, 6);
+ reservedPort[5] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else {
+ ret = UPNPCOMMAND_INVALID_RESPONSE;
+ }
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort, const char * proto,
+ const char * remoteHost)
+{
+ /*struct NameValueParserData pdata;*/
+ struct UPNParg * DeletePortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!extPort || !proto)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ DeletePortMappingArgs = calloc(4, sizeof(struct UPNParg));
+ if(DeletePortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ DeletePortMappingArgs[0].elt = "NewRemoteHost";
+ DeletePortMappingArgs[0].val = remoteHost;
+ DeletePortMappingArgs[1].elt = "NewExternalPort";
+ DeletePortMappingArgs[1].val = extPort;
+ DeletePortMappingArgs[2].elt = "NewProtocol";
+ DeletePortMappingArgs[2].val = proto;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMapping",
+ DeletePortMappingArgs, &bufsize);
+ free(DeletePortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
+ const char * extPortStart, const char * extPortEnd,
+ const char * proto,
+ const char * manage)
+{
+ struct UPNParg * DeletePortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!extPortStart || !extPortEnd || !proto || !manage)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ DeletePortMappingArgs = calloc(5, sizeof(struct UPNParg));
+ if(DeletePortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ DeletePortMappingArgs[0].elt = "NewStartPort";
+ DeletePortMappingArgs[0].val = extPortStart;
+ DeletePortMappingArgs[1].elt = "NewEndPort";
+ DeletePortMappingArgs[1].val = extPortEnd;
+ DeletePortMappingArgs[2].elt = "NewProtocol";
+ DeletePortMappingArgs[2].val = proto;
+ DeletePortMappingArgs[3].elt = "NewManage";
+ DeletePortMappingArgs[3].val = manage;
+
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMappingRange",
+ DeletePortMappingArgs, &bufsize);
+ free(DeletePortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * index,
+ char * extPort,
+ char * intClient,
+ char * intPort,
+ char * protocol,
+ char * desc,
+ char * enabled,
+ char * rHost,
+ char * duration)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int r = UPNPCOMMAND_UNKNOWN_ERROR;
+ if(!index)
+ return UPNPCOMMAND_INVALID_ARGS;
+ intClient[0] = '\0';
+ intPort[0] = '\0';
+ GetPortMappingArgs = calloc(2, sizeof(struct UPNParg));
+ if(GetPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetPortMappingArgs[0].elt = "NewPortMappingIndex";
+ GetPortMappingArgs[0].val = index;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetGenericPortMappingEntry",
+ GetPortMappingArgs, &bufsize);
+ free(GetPortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewRemoteHost");
+ if(p && rHost)
+ {
+ strncpy(rHost, p, 64);
+ rHost[63] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewExternalPort");
+ if(p && extPort)
+ {
+ strncpy(extPort, p, 6);
+ extPort[5] = '\0';
+ r = UPNPCOMMAND_SUCCESS;
+ }
+ p = GetValueFromNameValueList(&pdata, "NewProtocol");
+ if(p && protocol)
+ {
+ strncpy(protocol, p, 4);
+ protocol[3] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+ if(p)
+ {
+ strncpy(intClient, p, 16);
+ intClient[15] = '\0';
+ r = 0;
+ }
+ p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+ if(p)
+ {
+ strncpy(intPort, p, 6);
+ intPort[5] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewEnabled");
+ if(p && enabled)
+ {
+ strncpy(enabled, p, 4);
+ enabled[3] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
+ if(p && desc)
+ {
+ strncpy(desc, p, 80);
+ desc[79] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
+ if(p && duration)
+ {
+ strncpy(duration, p, 16);
+ duration[15] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ r = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &r);
+ }
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char * controlURL,
+ const char * servicetype,
+ unsigned int * numEntries)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char* p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetPortMappingNumberOfEntries", 0,
+ &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+#ifdef DEBUG
+ DisplayNameValueList(buffer, bufsize);
+#endif
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingNumberOfEntries");
+ if(numEntries && p) {
+ *numEntries = 0;
+ sscanf(p, "%u", numEntries);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
+ * the result is returned in the intClient and intPort strings
+ * please provide 16 and 6 bytes of data */
+MINIUPNP_LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * extPort,
+ const char * proto,
+ const char * remoteHost,
+ char * intClient,
+ char * intPort,
+ char * desc,
+ char * enabled,
+ char * leaseDuration)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!intPort || !intClient || !extPort || !proto)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetPortMappingArgs = calloc(4, sizeof(struct UPNParg));
+ if(GetPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetPortMappingArgs[0].elt = "NewRemoteHost";
+ GetPortMappingArgs[0].val = remoteHost;
+ GetPortMappingArgs[1].elt = "NewExternalPort";
+ GetPortMappingArgs[1].val = extPort;
+ GetPortMappingArgs[2].elt = "NewProtocol";
+ GetPortMappingArgs[2].val = proto;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetSpecificPortMappingEntry",
+ GetPortMappingArgs, &bufsize);
+ free(GetPortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+ if(p) {
+ strncpy(intClient, p, 16);
+ intClient[15] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ intClient[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+ if(p) {
+ strncpy(intPort, p, 6);
+ intPort[5] = '\0';
+ } else
+ intPort[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "NewEnabled");
+ if(p && enabled) {
+ strncpy(enabled, p, 4);
+ enabled[3] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
+ if(p && desc) {
+ strncpy(desc, p, 80);
+ desc[79] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
+ if(p && leaseDuration)
+ {
+ strncpy(leaseDuration, p, 16);
+ leaseDuration[15] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetListOfPortMappings()
+ *
+ * Possible UPNP Error codes :
+ * 606 Action not Authorized
+ * 730 PortMappingNotFound - no port mapping is found in the specified range.
+ * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
+ * consistent.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+ const char * servicetype,
+ const char * startPort,
+ const char * endPort,
+ const char * protocol,
+ const char * numberOfPorts,
+ struct PortMappingParserData * data)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetListOfPortMappingsArgs;
+ const char * p;
+ char * buffer;
+ int bufsize;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!startPort || !endPort || !protocol)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetListOfPortMappingsArgs = calloc(6, sizeof(struct UPNParg));
+ if(GetListOfPortMappingsArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetListOfPortMappingsArgs[0].elt = "NewStartPort";
+ GetListOfPortMappingsArgs[0].val = startPort;
+ GetListOfPortMappingsArgs[1].elt = "NewEndPort";
+ GetListOfPortMappingsArgs[1].val = endPort;
+ GetListOfPortMappingsArgs[2].elt = "NewProtocol";
+ GetListOfPortMappingsArgs[2].val = protocol;
+ GetListOfPortMappingsArgs[3].elt = "NewManage";
+ GetListOfPortMappingsArgs[3].val = "1";
+ GetListOfPortMappingsArgs[4].elt = "NewNumberOfPorts";
+ GetListOfPortMappingsArgs[4].val = numberOfPorts?numberOfPorts:"1000";
+
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetListOfPortMappings",
+ GetListOfPortMappingsArgs, &bufsize);
+ free(GetListOfPortMappingsArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ /*p = GetValueFromNameValueList(&pdata, "NewPortListing");*/
+ /*if(p) {
+ printf("NewPortListing : %s\n", p);
+ }*/
+ /*printf("NewPortListing(%d chars) : %s\n",
+ pdata.portListingLength, pdata.portListing);*/
+ if(pdata.portListing)
+ {
+ /*struct PortMapping * pm;
+ int i = 0;*/
+ ParsePortListing(pdata.portListing, pdata.portListingLength,
+ data);
+ ret = UPNPCOMMAND_SUCCESS;
+ /*
+ for(pm = data->head.lh_first; pm != NULL; pm = pm->entries.le_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s'\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost);
+ i++;
+ }
+ */
+ /*FreePortListing(&data);*/
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+
+ /*printf("%.*s", bufsize, buffer);*/
+
+ return ret;
+}
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+MINIUPNP_LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+ const char * servicetype,
+ int * firewallEnabled,
+ int * inboundPinholeAllowed)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * fe, *ipa, *p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!firewallEnabled || !inboundPinholeAllowed)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetFirewallStatus", 0, &bufsize);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ fe = GetValueFromNameValueList(&pdata, "FirewallEnabled");
+ ipa = GetValueFromNameValueList(&pdata, "InboundPinholeAllowed");
+ if(ipa && fe)
+ ret = UPNPCOMMAND_SUCCESS;
+ if(fe)
+ *firewallEnabled = my_atoui(fe);
+ /*else
+ *firewallEnabled = 0;*/
+ if(ipa)
+ *inboundPinholeAllowed = my_atoui(ipa);
+ /*else
+ *inboundPinholeAllowed = 0;*/
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ int * opTimeout)
+{
+ struct UPNParg * GetOutboundPinholeTimeoutArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ char * p;
+ int ret;
+
+ if(!intPort || !intClient || !proto || !remotePort || !remoteHost)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetOutboundPinholeTimeoutArgs = calloc(6, sizeof(struct UPNParg));
+ if(GetOutboundPinholeTimeoutArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetOutboundPinholeTimeoutArgs[0].elt = "RemoteHost";
+ GetOutboundPinholeTimeoutArgs[0].val = remoteHost;
+ GetOutboundPinholeTimeoutArgs[1].elt = "RemotePort";
+ GetOutboundPinholeTimeoutArgs[1].val = remotePort;
+ GetOutboundPinholeTimeoutArgs[2].elt = "Protocol";
+ GetOutboundPinholeTimeoutArgs[2].val = proto;
+ GetOutboundPinholeTimeoutArgs[3].elt = "InternalPort";
+ GetOutboundPinholeTimeoutArgs[3].val = intPort;
+ GetOutboundPinholeTimeoutArgs[4].elt = "InternalClient";
+ GetOutboundPinholeTimeoutArgs[4].val = intClient;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs, &bufsize);
+ free(GetOutboundPinholeTimeoutArgs);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ p = GetValueFromNameValueList(&pdata, "OutboundPinholeTimeout");
+ if(p)
+ *opTimeout = my_atoui(p);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ const char * leaseTime,
+ char * uniqueID)
+{
+ struct UPNParg * AddPinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ char * p;
+ int ret;
+
+ if(!intPort || !intClient || !proto || !remoteHost || !remotePort || !leaseTime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ AddPinholeArgs = calloc(7, sizeof(struct UPNParg));
+ if(AddPinholeArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ /* RemoteHost can be wilcarded */
+ if(strncmp(remoteHost, "empty", 5)==0)
+ {
+ AddPinholeArgs[0].elt = "RemoteHost";
+ AddPinholeArgs[0].val = "";
+ }
+ else
+ {
+ AddPinholeArgs[0].elt = "RemoteHost";
+ AddPinholeArgs[0].val = remoteHost;
+ }
+ AddPinholeArgs[1].elt = "RemotePort";
+ AddPinholeArgs[1].val = remotePort;
+ AddPinholeArgs[2].elt = "Protocol";
+ AddPinholeArgs[2].val = proto;
+ AddPinholeArgs[3].elt = "InternalPort";
+ AddPinholeArgs[3].val = intPort;
+ if(strncmp(intClient, "empty", 5)==0)
+ {
+ AddPinholeArgs[4].elt = "InternalClient";
+ AddPinholeArgs[4].val = "";
+ }
+ else
+ {
+ AddPinholeArgs[4].elt = "InternalClient";
+ AddPinholeArgs[4].val = intClient;
+ }
+ AddPinholeArgs[5].elt = "LeaseTime";
+ AddPinholeArgs[5].val = leaseTime;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddPinhole", AddPinholeArgs, &bufsize);
+ free(AddPinholeArgs);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "UniqueID");
+ if(p)
+ {
+ strncpy(uniqueID, p, 8);
+ uniqueID[7] = '\0';
+ }
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ /*printf("AddPortMapping errorCode = '%s'\n", resVal);*/
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+ const char * uniqueID,
+ const char * leaseTime)
+{
+ struct UPNParg * UpdatePinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!uniqueID || !leaseTime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ UpdatePinholeArgs = calloc(3, sizeof(struct UPNParg));
+ if(UpdatePinholeArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ UpdatePinholeArgs[0].elt = "UniqueID";
+ UpdatePinholeArgs[0].val = uniqueID;
+ UpdatePinholeArgs[1].elt = "NewLeaseTime";
+ UpdatePinholeArgs[1].val = leaseTime;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "UpdatePinhole", UpdatePinholeArgs, &bufsize);
+ free(UpdatePinholeArgs);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ /*printf("AddPortMapping errorCode = '%s'\n", resVal); */
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID)
+{
+ /*struct NameValueParserData pdata;*/
+ struct UPNParg * DeletePinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ DeletePinholeArgs = calloc(2, sizeof(struct UPNParg));
+ if(DeletePinholeArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ DeletePinholeArgs[0].elt = "UniqueID";
+ DeletePinholeArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePinhole", DeletePinholeArgs, &bufsize);
+ free(DeletePinholeArgs);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * isWorking)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * CheckPinholeWorkingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ CheckPinholeWorkingArgs = calloc(4, sizeof(struct UPNParg));
+ if(CheckPinholeWorkingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ CheckPinholeWorkingArgs[0].elt = "UniqueID";
+ CheckPinholeWorkingArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "CheckPinholeWorking", CheckPinholeWorkingArgs, &bufsize);
+ free(CheckPinholeWorkingArgs);
+ if(!buffer)
+ {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "IsWorking");
+ if(p)
+ {
+ *isWorking=my_atoui(p);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ else
+ *isWorking = 0;
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * packets)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPinholePacketsArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetPinholePacketsArgs = calloc(4, sizeof(struct UPNParg));
+ if(GetPinholePacketsArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetPinholePacketsArgs[0].elt = "UniqueID";
+ GetPinholePacketsArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetPinholePackets", GetPinholePacketsArgs, &bufsize);
+ free(GetPinholePacketsArgs);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "PinholePackets");
+ if(p)
+ {
+ *packets=my_atoui(p);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+
diff --git a/thirdparty/miniupnpc/upnpcommands.h b/thirdparty/miniupnpc/upnpcommands.h
new file mode 100644
index 0000000000..0c6d501666
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpcommands.h
@@ -0,0 +1,348 @@
+/* $Id: upnpcommands.h,v 1.32 2018/03/13 23:34:47 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef UPNPCOMMANDS_H_INCLUDED
+#define UPNPCOMMANDS_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "miniupnpctypes.h"
+
+/* MiniUPnPc return codes : */
+#define UPNPCOMMAND_SUCCESS (0)
+#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
+#define UPNPCOMMAND_INVALID_ARGS (-2)
+#define UPNPCOMMAND_HTTP_ERROR (-3)
+#define UPNPCOMMAND_INVALID_RESPONSE (-4)
+#define UPNPCOMMAND_MEM_ALLOC_ERROR (-5)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct PortMappingParserData;
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+ const char * servicetype);
+
+/* UPNP_GetStatusInfo()
+ * status and lastconnerror are 64 byte buffers
+ * Return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+MINIUPNP_LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+ const char * servicetype,
+ char * status,
+ unsigned int * uptime,
+ char * lastconnerror);
+
+/* UPNP_GetConnectionTypeInfo()
+ * argument connectionType is a 64 character buffer
+ * Return Values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+MINIUPNP_LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+ const char * servicetype,
+ char * connectionType);
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ *
+ * possible UPnP Errors :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control. */
+MINIUPNP_LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+ const char * servicetype,
+ char * extIpAdd);
+
+/* UPNP_GetLinkLayerMaxBitRates()
+ * call WANCommonInterfaceConfig:1#GetCommonLinkProperties
+ *
+ * return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code. */
+MINIUPNP_LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
+ const char* servicetype,
+ unsigned int * bitrateDown,
+ unsigned int * bitrateUp);
+
+/* UPNP_AddPortMapping()
+ * if desc is NULL, it will be defaulted to "libminiupnpc"
+ * remoteHost is usually NULL because IGD don't support it.
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
+ *
+ * List of possible UPnP errors for AddPortMapping :
+ * errorCode errorDescription (short) - Description (long)
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization and
+ * the sender was not authorized.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ * wild-carded
+ * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
+ * 718 ConflictInMappingEntry - The port mapping entry specified conflicts
+ * with a mapping assigned previously to another client
+ * 724 SamePortValuesRequired - Internal and External port values
+ * must be the same
+ * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
+ * permanent lease times on port mappings
+ * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
+ * and cannot be a specific IP address or DNS name
+ * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
+ * cannot be a specific port value
+ * 728 NoPortMapsAvailable - There are not enough free ports available to
+ * complete port mapping.
+ * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
+ * due to conflict with other mechanisms.
+ * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
+ */
+MINIUPNP_LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration);
+
+/* UPNP_AddAnyPortMapping()
+ * if desc is NULL, it will be defaulted to "libminiupnpc"
+ * remoteHost is usually NULL because IGD don't support it.
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
+ *
+ * List of possible UPnP errors for AddPortMapping :
+ * errorCode errorDescription (short) - Description (long)
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization and
+ * the sender was not authorized.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ * wild-carded
+ * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
+ * 728 NoPortMapsAvailable - There are not enough free ports available to
+ * complete port mapping.
+ * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
+ * due to conflict with other mechanisms.
+ * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
+ */
+MINIUPNP_LIBSPEC int
+UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration,
+ char * reservedPort);
+
+/* UPNP_DeletePortMapping()
+ * Use same argument values as what was used for AddPortMapping().
+ * remoteHost is usually NULL because IGD don't support it.
+ * Return Values :
+ * 0 : SUCCESS
+ * NON ZERO : error. Either an UPnP error code or an undefined error.
+ *
+ * List of possible UPnP errors for DeletePortMapping :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array */
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort, const char * proto,
+ const char * remoteHost);
+
+/* UPNP_DeletePortRangeMapping()
+ * Use same argument values as what was used for AddPortMapping().
+ * remoteHost is usually NULL because IGD don't support it.
+ * Return Values :
+ * 0 : SUCCESS
+ * NON ZERO : error. Either an UPnP error code or an undefined error.
+ *
+ * List of possible UPnP errors for DeletePortMapping :
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 730 PortMappingNotFound - This error message is returned if no port
+ * mapping is found in the specified range.
+ * 733 InconsistentParameters - NewStartPort and NewEndPort values are not consistent. */
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
+ const char * extPortStart, const char * extPortEnd,
+ const char * proto,
+ const char * manage);
+
+/* UPNP_GetPortMappingNumberOfEntries()
+ * not supported by all routers */
+MINIUPNP_LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char* controlURL,
+ const char* servicetype,
+ unsigned int * num);
+
+/* UPNP_GetSpecificPortMappingEntry()
+ * retrieves an existing port mapping
+ * params :
+ * in extPort
+ * in proto
+ * in remoteHost
+ * out intClient (16 bytes)
+ * out intPort (6 bytes)
+ * out desc (80 bytes)
+ * out enabled (4 bytes)
+ * out leaseDuration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code.
+ *
+ * List of possible UPnP errors for _GetSpecificPortMappingEntry :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * extPort,
+ const char * proto,
+ const char * remoteHost,
+ char * intClient,
+ char * intPort,
+ char * desc,
+ char * enabled,
+ char * leaseDuration);
+
+/* UPNP_GetGenericPortMappingEntry()
+ * params :
+ * in index
+ * out extPort (6 bytes)
+ * out intClient (16 bytes)
+ * out intPort (6 bytes)
+ * out protocol (4 bytes)
+ * out desc (80 bytes)
+ * out enabled (4 bytes)
+ * out rHost (64 bytes)
+ * out duration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code.
+ *
+ * Possible UPNP Error codes :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * index,
+ char * extPort,
+ char * intClient,
+ char * intPort,
+ char * protocol,
+ char * desc,
+ char * enabled,
+ char * rHost,
+ char * duration);
+
+/* UPNP_GetListOfPortMappings() Available in IGD v2
+ *
+ *
+ * Possible UPNP Error codes :
+ * 606 Action not Authorized
+ * 730 PortMappingNotFound - no port mapping is found in the specified range.
+ * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
+ * consistent.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+ const char * servicetype,
+ const char * startPort,
+ const char * endPort,
+ const char * protocol,
+ const char * numberOfPorts,
+ struct PortMappingParserData * data);
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+MINIUPNP_LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+ const char * servicetype,
+ int * firewallEnabled,
+ int * inboundPinholeAllowed);
+
+MINIUPNP_LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ int * opTimeout);
+
+MINIUPNP_LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ const char * leaseTime,
+ char * uniqueID);
+
+MINIUPNP_LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+ const char * uniqueID,
+ const char * leaseTime);
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
+
+MINIUPNP_LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * isWorking);
+
+MINIUPNP_LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * packets);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/upnpdev.c b/thirdparty/miniupnpc/upnpdev.c
new file mode 100644
index 0000000000..d89a9934c3
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpdev.c
@@ -0,0 +1,23 @@
+/* $Id: upnpdev.c,v 1.1 2015/08/28 12:14:19 nanard Exp $ */
+/* Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENSE file. */
+#include <stdlib.h>
+#include "upnpdev.h"
+
+/* freeUPNPDevlist() should be used to
+ * free the chained list returned by upnpDiscover() */
+void freeUPNPDevlist(struct UPNPDev * devlist)
+{
+ struct UPNPDev * next;
+ while(devlist)
+ {
+ next = devlist->pNext;
+ free(devlist);
+ devlist = next;
+ }
+}
+
diff --git a/thirdparty/miniupnpc/upnpdev.h b/thirdparty/miniupnpc/upnpdev.h
new file mode 100644
index 0000000000..f4ae174426
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpdev.h
@@ -0,0 +1,36 @@
+/* $Id: upnpdev.h,v 1.1 2015/08/28 12:14:19 nanard Exp $ */
+/* Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2018 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENSE file. */
+#ifndef UPNPDEV_H_INCLUDED
+#define UPNPDEV_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct UPNPDev {
+ struct UPNPDev * pNext;
+ char * descURL;
+ char * st;
+ char * usn;
+ unsigned int scope_id;
+ char buffer[3];
+};
+
+/* freeUPNPDevlist()
+ * free list returned by upnpDiscover() */
+MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* UPNPDEV_H_INCLUDED */
diff --git a/thirdparty/miniupnpc/upnperrors.c b/thirdparty/miniupnpc/upnperrors.c
new file mode 100644
index 0000000000..40a2e7857f
--- /dev/null
+++ b/thirdparty/miniupnpc/upnperrors.c
@@ -0,0 +1,107 @@
+/* $Id: upnperrors.c,v 1.5 2011/04/10 11:19:36 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2007 Thomas Bernard
+ * All Right reserved.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#include <string.h>
+#include "upnperrors.h"
+#include "upnpcommands.h"
+#include "miniupnpc.h"
+
+const char * strupnperror(int err)
+{
+ const char * s = NULL;
+ switch(err) {
+ case UPNPCOMMAND_SUCCESS:
+ s = "Success";
+ break;
+ case UPNPCOMMAND_UNKNOWN_ERROR:
+ s = "Miniupnpc Unknown Error";
+ break;
+ case UPNPCOMMAND_INVALID_ARGS:
+ s = "Miniupnpc Invalid Arguments";
+ break;
+ case UPNPCOMMAND_INVALID_RESPONSE:
+ s = "Miniupnpc Invalid response";
+ break;
+ case UPNPDISCOVER_SOCKET_ERROR:
+ s = "Miniupnpc Socket error";
+ break;
+ case UPNPDISCOVER_MEMORY_ERROR:
+ s = "Miniupnpc Memory allocation error";
+ break;
+ case 401:
+ s = "Invalid Action";
+ break;
+ case 402:
+ s = "Invalid Args";
+ break;
+ case 501:
+ s = "Action Failed";
+ break;
+ case 606:
+ s = "Action not authorized";
+ break;
+ case 701:
+ s = "PinholeSpaceExhausted";
+ break;
+ case 702:
+ s = "FirewallDisabled";
+ break;
+ case 703:
+ s = "InboundPinholeNotAllowed";
+ break;
+ case 704:
+ s = "NoSuchEntry";
+ break;
+ case 705:
+ s = "ProtocolNotSupported";
+ break;
+ case 706:
+ s = "InternalPortWildcardingNotAllowed";
+ break;
+ case 707:
+ s = "ProtocolWildcardingNotAllowed";
+ break;
+ case 708:
+ s = "WildcardNotPermittedInSrcIP";
+ break;
+ case 709:
+ s = "NoPacketSent";
+ break;
+ case 713:
+ s = "SpecifiedArrayIndexInvalid";
+ break;
+ case 714:
+ s = "NoSuchEntryInArray";
+ break;
+ case 715:
+ s = "WildCardNotPermittedInSrcIP";
+ break;
+ case 716:
+ s = "WildCardNotPermittedInExtPort";
+ break;
+ case 718:
+ s = "ConflictInMappingEntry";
+ break;
+ case 724:
+ s = "SamePortValuesRequired";
+ break;
+ case 725:
+ s = "OnlyPermanentLeasesSupported";
+ break;
+ case 726:
+ s = "RemoteHostOnlySupportsWildcard";
+ break;
+ case 727:
+ s = "ExternalPortOnlySupportsWildcard";
+ break;
+ default:
+ s = "UnknownError";
+ break;
+ }
+ return s;
+}
diff --git a/thirdparty/miniupnpc/upnperrors.h b/thirdparty/miniupnpc/upnperrors.h
new file mode 100644
index 0000000000..8499d9a1c9
--- /dev/null
+++ b/thirdparty/miniupnpc/upnperrors.h
@@ -0,0 +1,26 @@
+/* $Id: upnperrors.h,v 1.2 2008/07/02 23:31:15 nanard Exp $ */
+/* (c) 2007-2015 Thomas Bernard
+ * All rights reserved.
+ * MiniUPnP Project.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef UPNPERRORS_H_INCLUDED
+#define UPNPERRORS_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* strupnperror()
+ * Return a string description of the UPnP error code
+ * or NULL for undefinded errors */
+MINIUPNP_LIBSPEC const char * strupnperror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/thirdparty/miniupnpc/upnpreplyparse.c b/thirdparty/miniupnpc/upnpreplyparse.c
new file mode 100644
index 0000000000..68a47c0278
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpreplyparse.c
@@ -0,0 +1,196 @@
+/* $Id: upnpreplyparse.c,v 1.19 2015/07/15 10:29:11 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2017 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "upnpreplyparse.h"
+#include "minixml.h"
+
+static void
+NameValueParserStartElt(void * d, const char * name, int l)
+{
+ struct NameValueParserData * data = (struct NameValueParserData *)d;
+ data->topelt = 1;
+ if(l>63)
+ l = 63;
+ memcpy(data->curelt, name, l);
+ data->curelt[l] = '\0';
+ data->cdata = NULL;
+ data->cdatalen = 0;
+}
+
+static void
+NameValueParserEndElt(void * d, const char * name, int namelen)
+{
+ struct NameValueParserData * data = (struct NameValueParserData *)d;
+ struct NameValue * nv;
+ (void)name;
+ (void)namelen;
+ if(!data->topelt)
+ return;
+ if(strcmp(data->curelt, "NewPortListing") != 0)
+ {
+ int l;
+ /* standard case. Limited to n chars strings */
+ l = data->cdatalen;
+ nv = malloc(sizeof(struct NameValue));
+ if(nv == NULL)
+ {
+ /* malloc error */
+#ifdef DEBUG
+ fprintf(stderr, "%s: error allocating memory",
+ "NameValueParserEndElt");
+#endif /* DEBUG */
+ return;
+ }
+ if(l>=(int)sizeof(nv->value))
+ l = sizeof(nv->value) - 1;
+ strncpy(nv->name, data->curelt, 64);
+ nv->name[63] = '\0';
+ if(data->cdata != NULL)
+ {
+ memcpy(nv->value, data->cdata, l);
+ nv->value[l] = '\0';
+ }
+ else
+ {
+ nv->value[0] = '\0';
+ }
+ nv->l_next = data->l_head; /* insert in list */
+ data->l_head = nv;
+ }
+ data->cdata = NULL;
+ data->cdatalen = 0;
+ data->topelt = 0;
+}
+
+static void
+NameValueParserGetData(void * d, const char * datas, int l)
+{
+ struct NameValueParserData * data = (struct NameValueParserData *)d;
+ if(strcmp(data->curelt, "NewPortListing") == 0)
+ {
+ /* specific case for NewPortListing which is a XML Document */
+ data->portListing = malloc(l + 1);
+ if(!data->portListing)
+ {
+ /* malloc error */
+#ifdef DEBUG
+ fprintf(stderr, "%s: error allocating memory",
+ "NameValueParserGetData");
+#endif /* DEBUG */
+ return;
+ }
+ memcpy(data->portListing, datas, l);
+ data->portListing[l] = '\0';
+ data->portListingLength = l;
+ }
+ else
+ {
+ /* standard case. */
+ data->cdata = datas;
+ data->cdatalen = l;
+ }
+}
+
+void
+ParseNameValue(const char * buffer, int bufsize,
+ struct NameValueParserData * data)
+{
+ struct xmlparser parser;
+ memset(data, 0, sizeof(struct NameValueParserData));
+ /* init xmlparser object */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = data;
+ parser.starteltfunc = NameValueParserStartElt;
+ parser.endeltfunc = NameValueParserEndElt;
+ parser.datafunc = NameValueParserGetData;
+ parser.attfunc = 0;
+ parsexml(&parser);
+}
+
+void
+ClearNameValueList(struct NameValueParserData * pdata)
+{
+ struct NameValue * nv;
+ if(pdata->portListing)
+ {
+ free(pdata->portListing);
+ pdata->portListing = NULL;
+ pdata->portListingLength = 0;
+ }
+ while((nv = pdata->l_head) != NULL)
+ {
+ pdata->l_head = nv->l_next;
+ free(nv);
+ }
+}
+
+char *
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+ const char * Name)
+{
+ struct NameValue * nv;
+ char * p = NULL;
+ for(nv = pdata->l_head;
+ (nv != NULL) && (p == NULL);
+ nv = nv->l_next)
+ {
+ if(strcmp(nv->name, Name) == 0)
+ p = nv->value;
+ }
+ return p;
+}
+
+#if 0
+/* useless now that minixml ignores namespaces by itself */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+ const char * Name)
+{
+ struct NameValue * nv;
+ char * p = NULL;
+ char * pname;
+ for(nv = pdata->head.lh_first;
+ (nv != NULL) && (p == NULL);
+ nv = nv->entries.le_next)
+ {
+ pname = strrchr(nv->name, ':');
+ if(pname)
+ pname++;
+ else
+ pname = nv->name;
+ if(strcmp(pname, Name)==0)
+ p = nv->value;
+ }
+ return p;
+}
+#endif
+
+/* debug all-in-one function
+ * do parsing then display to stdout */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize)
+{
+ struct NameValueParserData pdata;
+ struct NameValue * nv;
+ ParseNameValue(buffer, bufsize, &pdata);
+ for(nv = pdata.l_head;
+ nv != NULL;
+ nv = nv->l_next)
+ {
+ printf("%s = %s\n", nv->name, nv->value);
+ }
+ ClearNameValueList(&pdata);
+}
+#endif /* DEBUG */
+
diff --git a/thirdparty/miniupnpc/upnpreplyparse.h b/thirdparty/miniupnpc/upnpreplyparse.h
new file mode 100644
index 0000000000..6badd15b26
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpreplyparse.h
@@ -0,0 +1,63 @@
+/* $Id: upnpreplyparse.h,v 1.19 2014/10/27 16:33:19 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2013 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#ifndef UPNPREPLYPARSE_H_INCLUDED
+#define UPNPREPLYPARSE_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct NameValue {
+ struct NameValue * l_next;
+ char name[64];
+ char value[128];
+};
+
+struct NameValueParserData {
+ struct NameValue * l_head;
+ char curelt[64];
+ char * portListing;
+ int portListingLength;
+ int topelt;
+ const char * cdata;
+ int cdatalen;
+};
+
+/* ParseNameValue() */
+void
+ParseNameValue(const char * buffer, int bufsize,
+ struct NameValueParserData * data);
+
+/* ClearNameValueList() */
+void
+ClearNameValueList(struct NameValueParserData * pdata);
+
+/* GetValueFromNameValueList() */
+char *
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+ const char * Name);
+
+#if 0
+/* GetValueFromNameValueListIgnoreNS() */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+ const char * Name);
+#endif
+
+/* DisplayNameValueList() */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/thirdparty/minizip/crypt.h b/thirdparty/minizip/crypt.h
index a01d08d932..1e9e8200b2 100644
--- a/thirdparty/minizip/crypt.h
+++ b/thirdparty/minizip/crypt.h
@@ -32,7 +32,7 @@
/***********************************************************************
* Return the next byte in the pseudo-random sequence
*/
-static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab)
+static int decrypt_byte(unsigned long* pkeys, const z_crc_t* pcrc_32_tab)
{
unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an
* unpredictable manner on 16-bit systems; not a problem
@@ -45,7 +45,7 @@ static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab)
/***********************************************************************
* Update the encryption keys with the next byte of plain text
*/
-static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c)
+static int update_keys(unsigned long* pkeys,const z_crc_t* pcrc_32_tab,int c)
{
(*(pkeys+0)) = CRC32((*(pkeys+0)), c);
(*(pkeys+1)) += (*(pkeys+0)) & 0xff;
@@ -62,7 +62,7 @@ static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int
* Initialize the encryption keys and the random header according to
* the given password.
*/
-static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab)
+static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t* pcrc_32_tab)
{
*(pkeys+0) = 305419896L;
*(pkeys+1) = 591751049L;
@@ -91,7 +91,7 @@ static int crypthead(const char* passwd, /* password string */
unsigned char* buf, /* where to write header */
int bufSize,
unsigned long* pkeys,
- const unsigned long* pcrc_32_tab,
+ const z_crc_t* pcrc_32_tab,
unsigned long crcForCrypting)
{
int n; /* index in random header */
diff --git a/thirdparty/minizip/godot-zlib-1.2.4-minizip-seek.patch b/thirdparty/minizip/godot-zlib-1.2.4-minizip-seek.patch
index 8e66416a43..2162bafbbc 100644
--- a/thirdparty/minizip/godot-zlib-1.2.4-minizip-seek.patch
+++ b/thirdparty/minizip/godot-zlib-1.2.4-minizip-seek.patch
@@ -96,8 +96,8 @@ index 7617f41f1..32e27bd65 100644
+/* GODOT end */
+
/*
- Close a ZipFile opened with unzipOpen.
- If there is files inside the .Zip opened with unzipOpenCurrentFile (see later),
+ Close a ZipFile opened with unzOpen.
+ If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
@@ -1018,10 +1034,20 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file,
if (lSeek!=0)
@@ -237,7 +237,7 @@ index 3183968b7..54e65ad8a 100644
--- a/thirdparty/minizip/unzip.h
+++ b/thirdparty/minizip/unzip.h
@@ -202,6 +202,10 @@ extern int ZEXPORT unzClose OF((unzFile file));
- these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ these files MUST be closed with unzCloseCurrentFile before call unzClose.
return UNZ_OK if there is no problem. */
+/* GODOT start */
diff --git a/thirdparty/minizip/ioapi.c b/thirdparty/minizip/ioapi.c
index 2b42df4abd..9cb27c16db 100644
--- a/thirdparty/minizip/ioapi.c
+++ b/thirdparty/minizip/ioapi.c
@@ -10,10 +10,22 @@
*/
-#if (defined(_WIN32))
+#if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS)))
#define _CRT_SECURE_NO_WARNINGS
#endif
+#if defined(__APPLE__) || defined(IOAPI_NO_64)
+// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
+#define FOPEN_FUNC(filename, mode) fopen(filename, mode)
+#define FTELLO_FUNC(stream) ftello(stream)
+#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
+#else
+#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
+#define FTELLO_FUNC(stream) ftello64(stream)
+#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
+#endif
+
+
#include "ioapi.h"
voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)
@@ -47,7 +59,7 @@ ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream
else
{
uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream);
- if ((tell_uLong) == ((uLong)-1))
+ if ((tell_uLong) == MAXU32)
return (ZPOS64_T)-1;
else
return tell_uLong;
@@ -119,7 +131,7 @@ static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename,
mode_fopen = "wb";
if ((filename!=NULL) && (mode_fopen != NULL))
- file = fopen64((const char*)filename, mode_fopen);
+ file = FOPEN_FUNC((const char*)filename, mode_fopen);
return file;
}
@@ -149,7 +161,7 @@ static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream)
static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream)
{
ZPOS64_T ret;
- ret = ftello64((FILE *)stream);
+ ret = FTELLO_FUNC((FILE *)stream);
return ret;
}
@@ -195,7 +207,7 @@ static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T
}
ret = 0;
- if(fseeko64((FILE *)stream, offset, fseek_origin) != 0)
+ if(FSEEKO_FUNC((FILE *)stream, offset, fseek_origin) != 0)
ret = -1;
return ret;
diff --git a/thirdparty/minizip/ioapi.h b/thirdparty/minizip/ioapi.h
index 6043d34cea..4011e9cabb 100644
--- a/thirdparty/minizip/ioapi.h
+++ b/thirdparty/minizip/ioapi.h
@@ -21,7 +21,7 @@
#ifndef _ZLIBIOAPI64_H
#define _ZLIBIOAPI64_H
-#if (!defined(_WIN32)) && (!defined(WIN32))
+#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
// Linux needs this to support file operation on files larger then 4+GB
// But might need better if/def to select just the platforms that needs them.
@@ -38,6 +38,7 @@
#ifndef _FILE_OFFSET_BIT
#define _FILE_OFFSET_BIT 64
#endif
+
#endif
#include <stdio.h>
@@ -65,6 +66,11 @@
#define ftello64 ftell
#define fseeko64 fseek
#else
+#ifdef __FreeBSD__
+#define fopen64 fopen
+#define ftello64 ftello
+#define fseeko64 fseeko
+#endif
#ifdef _MSC_VER
#define fopen64 fopen
#if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC)))
@@ -101,6 +107,8 @@ typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T;
typedef uint64_t ZPOS64_T;
#else
+/* Maximum unsigned 32-bit value used as placeholder for zip64 */
+#define MAXU32 0xffffffff
#if defined(_MSC_VER) || defined(__BORLANDC__)
typedef unsigned __int64 ZPOS64_T;
diff --git a/thirdparty/minizip/unzip.c b/thirdparty/minizip/unzip.c
index 32e27bd657..31f8a5ff47 100644
--- a/thirdparty/minizip/unzip.c
+++ b/thirdparty/minizip/unzip.c
@@ -191,7 +191,7 @@ typedef struct
# ifndef NOUNCRYPT
unsigned long keys[3]; /* keys defining the pseudo-random sequence */
- const unsigned long* pcrc_32_tab;
+ const z_crc_t* pcrc_32_tab;
# endif
} unz64_s;
@@ -203,7 +203,7 @@ typedef struct
/* ===========================================================================
Read a byte from a gz_stream; update next_in and avail_in. Return EOF
for end of file.
- IN assertion: the stream s has been sucessfully opened for reading.
+ IN assertion: the stream s has been successfully opened for reading.
*/
@@ -817,9 +817,9 @@ extern void* unzGetOpaque(unzFile file) {
/* GODOT end */
/*
- Close a ZipFile opened with unzipOpen.
- If there is files inside the .Zip opened with unzipOpenCurrentFile (see later),
- these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ Close a ZipFile opened with unzOpen.
+ If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
+ these files MUST be closed with unzCloseCurrentFile before call unzClose.
return UNZ_OK if there is no problem. */
extern int ZEXPORT unzClose (unzFile file)
{
@@ -1066,26 +1066,26 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file,
{
uLong uL;
- if(file_info.uncompressed_size == (ZPOS64_T)(unsigned long)-1)
+ if(file_info.uncompressed_size == MAXU32)
{
if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK)
err=UNZ_ERRNO;
}
- if(file_info.compressed_size == (ZPOS64_T)(unsigned long)-1)
+ if(file_info.compressed_size == MAXU32)
{
if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK)
err=UNZ_ERRNO;
}
- if(file_info_internal.offset_curfile == (ZPOS64_T)(unsigned long)-1)
+ if(file_info_internal.offset_curfile == MAXU32)
{
/* Relative Header offset */
if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK)
err=UNZ_ERRNO;
}
- if(file_info.disk_num_start == (unsigned long)-1)
+ if(file_info.disk_num_start == MAXU32)
{
/* Disk Start Number */
if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK)
@@ -1171,7 +1171,7 @@ extern int ZEXPORT unzGetCurrentFileInfo (unzFile file,
szFileName,fileNameBufferSize,
extraField,extraFieldBufferSize,
szComment,commentBufferSize);
- if (err==UNZ_OK)
+ if ((err==UNZ_OK) && (pfile_info != NULL))
{
pfile_info->version = file_info64.version;
pfile_info->version_needed = file_info64.version_needed;
@@ -1249,7 +1249,7 @@ extern int ZEXPORT unzGoToNextFile (unzFile file)
/*
Try locate the file szFileName in the zipfile.
- For the iCaseSensitivity signification, see unzipStringFileNameCompare
+ For the iCaseSensitivity signification, see unzStringFileNameCompare
return value :
UNZ_OK if the file is found. It becomes the current file.
@@ -1806,7 +1806,7 @@ extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len)
return UNZ_PARAMERROR;
- if ((pfile_in_zip_read_info->read_buffer == NULL))
+ if (pfile_in_zip_read_info->read_buffer == NULL)
return UNZ_END_OF_LIST_OF_FILE;
if (len==0)
return 0;
@@ -2108,7 +2108,7 @@ extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len)
}
/*
- Close the file in zip opened with unzipOpenCurrentFile
+ Close the file in zip opened with unzOpenCurrentFile
Return UNZ_CRCERROR if all the file was read but the CRC is not good
*/
extern int ZEXPORT unzCloseCurrentFile (unzFile file)
diff --git a/thirdparty/minizip/unzip.h b/thirdparty/minizip/unzip.h
index 54e65ad8ab..bab1cb939f 100644
--- a/thirdparty/minizip/unzip.h
+++ b/thirdparty/minizip/unzip.h
@@ -197,9 +197,9 @@ extern unzFile ZEXPORT unzOpen2_64 OF((const void *path,
extern int ZEXPORT unzClose OF((unzFile file));
/*
- Close a ZipFile opened with unzipOpen.
+ Close a ZipFile opened with unzOpen.
If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
- these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ these files MUST be closed with unzCloseCurrentFile before call unzClose.
return UNZ_OK if there is no problem. */
/* GODOT start */
diff --git a/thirdparty/minizip/zip.c b/thirdparty/minizip/zip.c
index d7093e7457..2936e2b5d9 100644
--- a/thirdparty/minizip/zip.c
+++ b/thirdparty/minizip/zip.c
@@ -15,7 +15,7 @@
Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions.
Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data
It is used when recreting zip archive with RAW when deleting items from a zip.
- ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed.
+ ZIP64 data is automatically added to items that needs it, and existing ZIP64 data need to be removed.
Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required)
Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer
@@ -116,7 +116,7 @@ typedef struct linkedlist_datablock_internal_s
struct linkedlist_datablock_internal_s* next_datablock;
uLong avail_in_this_block;
uLong filled_in_this_block;
- uLong unused; /* for future use and alignement */
+ uLong unused; /* for future use and alignment */
unsigned char data[SIZEDATA_INDATABLOCK];
} linkedlist_datablock_internal;
@@ -157,7 +157,7 @@ typedef struct
ZPOS64_T totalUncompressedData;
#ifndef NOCRYPT
unsigned long keys[3]; /* keys defining the pseudo-random sequence */
- const unsigned long* pcrc_32_tab;
+ const z_crc_t* pcrc_32_tab;
int crypt_header_size;
#endif
} curfile64_info;
@@ -171,7 +171,7 @@ typedef struct
curfile64_info ci; /* info on the file curretly writing */
ZPOS64_T begin_pos; /* position of the beginning of the zipfile */
- ZPOS64_T add_position_when_writting_offset;
+ ZPOS64_T add_position_when_writing_offset;
ZPOS64_T number_entry;
#ifndef NO_ADDFILEINEXISTINGZIP
@@ -807,7 +807,7 @@ int LoadCentralDirectoryRecord(zip64_internal* pziinit)
}
byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir);
- pziinit->add_position_when_writting_offset = byte_before_the_zipfile;
+ pziinit->add_position_when_writing_offset = byte_before_the_zipfile;
{
ZPOS64_T size_central_dir_to_read = size_central_dir;
@@ -877,7 +877,7 @@ extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* gl
ziinit.in_opened_file_inzip = 0;
ziinit.ci.stream_initialised = 0;
ziinit.number_entry = 0;
- ziinit.add_position_when_writting_offset = 0;
+ ziinit.add_position_when_writing_offset = 0;
init_linkedlist(&(ziinit.central_dir));
@@ -1069,6 +1069,7 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename,
int err = ZIP_OK;
# ifdef NOCRYPT
+ (crcForCrypting);
if (password != NULL)
return ZIP_PARAMERROR;
# endif
@@ -1116,9 +1117,9 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename,
zi->ci.flag = flagBase;
if ((level==8) || (level==9))
zi->ci.flag |= 2;
- if ((level==2))
+ if (level==2)
zi->ci.flag |= 4;
- if ((level==1))
+ if (level==1)
zi->ci.flag |= 6;
if (password != NULL)
zi->ci.flag |= 1;
@@ -1165,7 +1166,7 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename,
if(zi->ci.pos_local_header >= 0xffffffff)
zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4);
else
- zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4);
+ zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writing_offset,4);
for (i=0;i<size_filename;i++)
*(zi->ci.central_header+SIZECENTRALHEADER+i) = *(filename+i);
@@ -1714,7 +1715,7 @@ extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_s
if (err==ZIP_OK)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */
- if(uncompressed_size >= 0xffffffff)
+ if(uncompressed_size >= 0xffffffff || compressed_size >= 0xffffffff )
{
if(zi->ci.pos_zip64extrainfo > 0)
{
@@ -1728,6 +1729,8 @@ extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_s
if (err==ZIP_OK) /* uncompressed size, unknown */
err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8);
}
+ else
+ err = ZIP_BADZIPFILE; // Caller passed zip64 = 0, so no room for zip64 info -> fatal
}
else
{
@@ -1756,7 +1759,7 @@ extern int ZEXPORT zipCloseFileInZip (zipFile file)
int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip)
{
int err = ZIP_OK;
- ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset;
+ ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writing_offset;
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4);
@@ -1809,7 +1812,7 @@ int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centra
if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */
{
- ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
+ ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset;
err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8);
}
return err;
@@ -1850,13 +1853,13 @@ int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir,
if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */
{
- ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
+ ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset;
if(pos >= 0xffffffff)
{
err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4);
}
else
- err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4);
+ err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writing_offset),4);
}
return err;
@@ -1922,8 +1925,8 @@ extern int ZEXPORT zipClose (zipFile file, const char* global_comment)
}
free_linkedlist(&(zi->central_dir));
- pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
- if(pos >= 0xffffffff)
+ pos = centraldir_pos_inzip - zi->add_position_when_writing_offset;
+ if(pos >= 0xffffffff || zi->number_entry > 0xFFFF)
{
ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream);
Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip);
diff --git a/thirdparty/misc/stb_truetype.h b/thirdparty/misc/stb_truetype.h
index cec2425471..e2efae2285 100644
--- a/thirdparty/misc/stb_truetype.h
+++ b/thirdparty/misc/stb_truetype.h
@@ -1,4 +1,4 @@
-// stb_truetype.h - v1.17 - public domain
+// stb_truetype.h - v1.19 - public domain
// authored from 2009-2016 by Sean Barrett / RAD Game Tools
//
// This library processes TrueType files:
@@ -22,41 +22,35 @@
// Mikko Mononen: compound shape support, more cmap formats
// Tor Andersson: kerning, subpixel rendering
// Dougall Johnson: OpenType / Type 2 font handling
+// Daniel Ribeiro Maciel: basic GPOS-based kerning
//
// Misc other:
// Ryan Gordon
// Simon Glass
// github:IntellectualKitty
// Imanol Celaya
+// Daniel Ribeiro Maciel
//
// Bug/warning reports/fixes:
-// "Zer" on mollyrocket
-// Cass Everitt
-// stoiko (Haemimont Games)
-// Brian Hook
-// Walter van Niftrik
-// David Gow
-// David Given
-// Ivan-Assen Ivanov
-// Anthony Pesch
-// Johan Duparc
-// Hou Qiming
-// Fabian "ryg" Giesen
-// Martins Mozeiko
-// Cap Petschulat
-// Omar Cornut
-// github:aloucks
-// Peter LaValle
-// Sergey Popov
-// Giumo X. Clanjor
-// Higor Euripedes
-// Thomas Fields
-// Derek Vinyard
-// Cort Stratton
-// github:oyvindjam
+// "Zer" on mollyrocket Fabian "ryg" Giesen
+// Cass Everitt Martins Mozeiko
+// stoiko (Haemimont Games) Cap Petschulat
+// Brian Hook Omar Cornut
+// Walter van Niftrik github:aloucks
+// David Gow Peter LaValle
+// David Given Sergey Popov
+// Ivan-Assen Ivanov Giumo X. Clanjor
+// Anthony Pesch Higor Euripedes
+// Johan Duparc Thomas Fields
+// Hou Qiming Derek Vinyard
+// Rob Loach Cort Stratton
+// Kenney Phillis Jr. github:oyvindjam
+// Brian Costabile github:vassvik
//
// VERSION HISTORY
//
+// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod
+// 1.18 (2018-01-29) add missing function
// 1.17 (2017-07-23) make more arguments const; doc fix
// 1.16 (2017-07-12) SDF support
// 1.15 (2017-03-03) make more arguments const
@@ -171,7 +165,7 @@
// measurement for describing font size, defined as 72 points per inch.
// stb_truetype provides a point API for compatibility. However, true
// "per inch" conventions don't make much sense on computer displays
-// since they different monitors have different number of pixels per
+// since different monitors have different number of pixels per
// inch. For example, Windows traditionally uses a convention that
// there are 96 pixels per inch, thus making 'inch' measurements have
// nothing to do with inches, and thus effectively defining a point to
@@ -181,6 +175,39 @@
// for non-commercial fonts, thus making fonts scaled in points
// according to the TrueType spec incoherently sized in practice.
//
+// DETAILED USAGE:
+//
+// Scale:
+// Select how high you want the font to be, in points or pixels.
+// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute
+// a scale factor SF that will be used by all other functions.
+//
+// Baseline:
+// You need to select a y-coordinate that is the baseline of where
+// your text will appear. Call GetFontBoundingBox to get the baseline-relative
+// bounding box for all characters. SF*-y0 will be the distance in pixels
+// that the worst-case character could extend above the baseline, so if
+// you want the top edge of characters to appear at the top of the
+// screen where y=0, then you would set the baseline to SF*-y0.
+//
+// Current point:
+// Set the current point where the first character will appear. The
+// first character could extend left of the current point; this is font
+// dependent. You can either choose a current point that is the leftmost
+// point and hope, or add some padding, or check the bounding box or
+// left-side-bearing of the first character to be displayed and set
+// the current point based on that.
+//
+// Displaying a character:
+// Compute the bounding box of the character. It will contain signed values
+// relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,
+// then the character should be displayed in the rectangle from
+// <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).
+//
+// Advancing for the next character:
+// Call GlyphHMetrics, and compute 'current_point += SF * advance'.
+//
+//
// ADVANCED USAGE
//
// Quality:
@@ -224,7 +251,7 @@
// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation
// Bitmap management 100 LOC /
// Baked bitmap interface 70 LOC /
-// Font name matching & access 150 LOC ---- 150
+// Font name matching & access 150 LOC ---- 150
// C runtime library abstraction 60 LOC ---- 60
//
//
@@ -317,7 +344,7 @@ int main(int argc, char **argv)
}
return 0;
}
-#endif
+#endif
//
// Output:
//
@@ -331,9 +358,9 @@ int main(int argc, char **argv)
// :@@. M@M
// @@@o@@@@
// :M@@V:@@.
-//
+//
//////////////////////////////////////////////////////////////////////////////
-//
+//
// Complete program: print "Hello World!" banner, with bugs
//
#if 0
@@ -387,7 +414,8 @@ int main(int arg, char **argv)
//// INTEGRATION WITH YOUR CODEBASE
////
//// The following sections allow you to supply alternate definitions
-//// of C library functions used by stb_truetype.
+//// of C library functions used by stb_truetype, e.g. if you don't
+//// link with the C runtime library.
#ifdef STB_TRUETYPE_IMPLEMENTATION
// #define your own (u)stbtt_int8/16/32 before including to override this
@@ -403,7 +431,7 @@ int main(int arg, char **argv)
typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
- // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
+ // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
#ifndef STBTT_ifloor
#include <math.h>
#define STBTT_ifloor(x) ((int) floor(x))
@@ -416,15 +444,15 @@ int main(int arg, char **argv)
#define STBTT_pow(x,y) pow(x,y)
#endif
- #ifndef STBTT_cos
+ #ifndef STBTT_fmod
#include <math.h>
- #define STBTT_cos(x) cos(x)
- #define STBTT_acos(x) acos(x)
+ #define STBTT_fmod(x,y) fmod(x,y)
#endif
- #ifndef STBTT_fabs
+ #ifndef STBTT_cos
#include <math.h>
- #define STBTT_fabs(x) fabs(x)
+ #define STBTT_cos(x) cos(x)
+ #define STBTT_acos(x) acos(x)
#endif
#ifndef STBTT_fabs
@@ -625,7 +653,7 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, cons
// Calling these functions in sequence is roughly equivalent to calling
// stbtt_PackFontRanges(). If you more control over the packing of multiple
// fonts, or if you want to pack custom data into a font texture, take a look
-// at the source to of stbtt_PackFontRanges() and create a custom version
+// at the source to of stbtt_PackFontRanges() and create a custom version
// using these functions, e.g. call GatherRects multiple times,
// building up a single array of rects, then call PackRects once,
// then call RenderIntoRects repeatedly. This may result in a
@@ -676,7 +704,7 @@ struct stbtt_fontinfo
int numGlyphs; // number of glyphs, needed for range checking
- int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf
+ int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf
int index_map; // a cmap mapping for our chosen character encoding
int indexToLocFormat; // format needed to map from glyph index to glyph
@@ -931,7 +959,7 @@ STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, floa
// and computing from that can allow drop-out prevention).
//
// The algorithm has not been optimized at all, so expect it to be slow
-// if computing lots of characters or very large sizes.
+// if computing lots of characters or very large sizes.
@@ -1319,6 +1347,7 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in
info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
+ info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required
if (!cmap || !info->head || !info->hhea || !info->hmtx)
return 0;
@@ -1687,7 +1716,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s
if (i != 0)
num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
- // now start the new one
+ // now start the new one
start_off = !(flags & 1);
if (start_off) {
// if we start off with an off-curve point, then when we need to find a point on the curve
@@ -1740,7 +1769,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s
int comp_num_verts = 0, i;
stbtt_vertex *comp_verts = 0, *tmp = 0;
float mtx[6] = {1,0,0,1,0,0}, m, n;
-
+
flags = ttSHORT(comp); comp+=2;
gidx = ttSHORT(comp); comp+=2;
@@ -1770,7 +1799,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s
mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
}
-
+
// Find transformation scales.
m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
@@ -2172,7 +2201,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st
// push immediate
if (b0 == 255) {
- f = (float)stbtt__buf_get32(&b) / 0x10000;
+ f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000;
} else {
stbtt__buf_skip(&b, -1);
f = (float)(stbtt_int16)stbtt__cff_int(&b);
@@ -2210,12 +2239,10 @@ static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, in
{
stbtt__csctx c = STBTT__CSCTX_INIT(1);
int r = stbtt__run_charstring(info, glyph_index, &c);
- if (x0) {
- *x0 = r ? c.min_x : 0;
- *y0 = r ? c.min_y : 0;
- *x1 = r ? c.max_x : 0;
- *y1 = r ? c.max_y : 0;
- }
+ if (x0) *x0 = r ? c.min_x : 0;
+ if (y0) *y0 = r ? c.min_y : 0;
+ if (x1) *x1 = r ? c.max_x : 0;
+ if (y1) *y1 = r ? c.max_y : 0;
return r ? c.num_vertices : 0;
}
@@ -2239,7 +2266,7 @@ STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_inde
}
}
-STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
+static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
{
stbtt_uint8 *data = info->data + info->kern;
stbtt_uint32 needle, straw;
@@ -2269,9 +2296,260 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1,
return 0;
}
+static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
+{
+ stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
+ switch(coverageFormat) {
+ case 1: {
+ stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
+
+ // Binary search.
+ stbtt_int32 l=0, r=glyphCount-1, m;
+ int straw, needle=glyph;
+ while (l <= r) {
+ stbtt_uint8 *glyphArray = coverageTable + 4;
+ stbtt_uint16 glyphID;
+ m = (l + r) >> 1;
+ glyphID = ttUSHORT(glyphArray + 2 * m);
+ straw = glyphID;
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else {
+ return m;
+ }
+ }
+ } break;
+
+ case 2: {
+ stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
+ stbtt_uint8 *rangeArray = coverageTable + 4;
+
+ // Binary search.
+ stbtt_int32 l=0, r=rangeCount-1, m;
+ int strawStart, strawEnd, needle=glyph;
+ while (l <= r) {
+ stbtt_uint8 *rangeRecord;
+ m = (l + r) >> 1;
+ rangeRecord = rangeArray + 6 * m;
+ strawStart = ttUSHORT(rangeRecord);
+ strawEnd = ttUSHORT(rangeRecord + 2);
+ if (needle < strawStart)
+ r = m - 1;
+ else if (needle > strawEnd)
+ l = m + 1;
+ else {
+ stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
+ return startCoverageIndex + glyph - strawStart;
+ }
+ }
+ } break;
+
+ default: {
+ // There are no other cases.
+ STBTT_assert(0);
+ } break;
+ }
+
+ return -1;
+}
+
+static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)
+{
+ stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
+ switch(classDefFormat)
+ {
+ case 1: {
+ stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
+ stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
+ stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
+
+ if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
+ return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
+
+ classDefTable = classDef1ValueArray + 2 * glyphCount;
+ } break;
+
+ case 2: {
+ stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
+ stbtt_uint8 *classRangeRecords = classDefTable + 4;
+
+ // Binary search.
+ stbtt_int32 l=0, r=classRangeCount-1, m;
+ int strawStart, strawEnd, needle=glyph;
+ while (l <= r) {
+ stbtt_uint8 *classRangeRecord;
+ m = (l + r) >> 1;
+ classRangeRecord = classRangeRecords + 6 * m;
+ strawStart = ttUSHORT(classRangeRecord);
+ strawEnd = ttUSHORT(classRangeRecord + 2);
+ if (needle < strawStart)
+ r = m - 1;
+ else if (needle > strawEnd)
+ l = m + 1;
+ else
+ return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
+ }
+
+ classDefTable = classRangeRecords + 6 * classRangeCount;
+ } break;
+
+ default: {
+ // There are no other cases.
+ STBTT_assert(0);
+ } break;
+ }
+
+ return -1;
+}
+
+// Define to STBTT_assert(x) if you want to break on unimplemented formats.
+#define STBTT_GPOS_TODO_assert(x)
+
+static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
+{
+ stbtt_uint16 lookupListOffset;
+ stbtt_uint8 *lookupList;
+ stbtt_uint16 lookupCount;
+ stbtt_uint8 *data;
+ stbtt_int32 i;
+
+ if (!info->gpos) return 0;
+
+ data = info->data + info->gpos;
+
+ if (ttUSHORT(data+0) != 1) return 0; // Major version 1
+ if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
+
+ lookupListOffset = ttUSHORT(data+8);
+ lookupList = data + lookupListOffset;
+ lookupCount = ttUSHORT(lookupList);
+
+ for (i=0; i<lookupCount; ++i) {
+ stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
+ stbtt_uint8 *lookupTable = lookupList + lookupOffset;
+
+ stbtt_uint16 lookupType = ttUSHORT(lookupTable);
+ stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
+ stbtt_uint8 *subTableOffsets = lookupTable + 6;
+ switch(lookupType) {
+ case 2: { // Pair Adjustment Positioning Subtable
+ stbtt_int32 sti;
+ for (sti=0; sti<subTableCount; sti++) {
+ stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
+ stbtt_uint8 *table = lookupTable + subtableOffset;
+ stbtt_uint16 posFormat = ttUSHORT(table);
+ stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
+ stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
+ if (coverageIndex == -1) continue;
+
+ switch (posFormat) {
+ case 1: {
+ stbtt_int32 l, r, m;
+ int straw, needle;
+ stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
+ stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
+ stbtt_int32 valueRecordPairSizeInBytes = 2;
+ stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
+ stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
+ stbtt_uint8 *pairValueTable = table + pairPosOffset;
+ stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
+ stbtt_uint8 *pairValueArray = pairValueTable + 2;
+ // TODO: Support more formats.
+ STBTT_GPOS_TODO_assert(valueFormat1 == 4);
+ if (valueFormat1 != 4) return 0;
+ STBTT_GPOS_TODO_assert(valueFormat2 == 0);
+ if (valueFormat2 != 0) return 0;
+
+ STBTT_assert(coverageIndex < pairSetCount);
+
+ needle=glyph2;
+ r=pairValueCount-1;
+ l=0;
+
+ // Binary search.
+ while (l <= r) {
+ stbtt_uint16 secondGlyph;
+ stbtt_uint8 *pairValue;
+ m = (l + r) >> 1;
+ pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
+ secondGlyph = ttUSHORT(pairValue);
+ straw = secondGlyph;
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else {
+ stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
+ return xAdvance;
+ }
+ }
+ } break;
+
+ case 2: {
+ stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
+ stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
+
+ stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
+ stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
+ int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
+ int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
+
+ stbtt_uint16 class1Count = ttUSHORT(table + 12);
+ stbtt_uint16 class2Count = ttUSHORT(table + 14);
+ STBTT_assert(glyph1class < class1Count);
+ STBTT_assert(glyph2class < class2Count);
+
+ // TODO: Support more formats.
+ STBTT_GPOS_TODO_assert(valueFormat1 == 4);
+ if (valueFormat1 != 4) return 0;
+ STBTT_GPOS_TODO_assert(valueFormat2 == 0);
+ if (valueFormat2 != 0) return 0;
+
+ if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) {
+ stbtt_uint8 *class1Records = table + 16;
+ stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count);
+ stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class);
+ return xAdvance;
+ }
+ } break;
+
+ default: {
+ // There are no other cases.
+ STBTT_assert(0);
+ break;
+ };
+ }
+ }
+ break;
+ };
+
+ default:
+ // TODO: Implement other stuff.
+ break;
+ }
+ }
+
+ return 0;
+}
+
+STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)
+{
+ int xAdvance = 0;
+
+ if (info->gpos)
+ xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);
+
+ if (info->kern)
+ xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);
+
+ return xAdvance;
+}
+
STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
{
- if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs
+ if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs
return 0;
return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
}
@@ -2395,7 +2673,7 @@ static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
hh->num_remaining_in_head_chunk = count;
}
--hh->num_remaining_in_head_chunk;
- return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk;
+ return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk;
}
}
@@ -2449,7 +2727,7 @@ static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, i
float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
STBTT_assert(z != NULL);
if (!z) return z;
-
+
// round dx down to avoid overshooting
if (dxdy < 0)
z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);
@@ -2527,7 +2805,7 @@ static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__ac
}
}
}
-
+
e = e->next;
}
}
@@ -3229,8 +3507,9 @@ error:
STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)
{
- float scale = scale_x > scale_y ? scale_y : scale_x;
- int winding_count, *winding_lengths;
+ float scale = scale_x > scale_y ? scale_y : scale_x;
+ int winding_count = 0;
+ int *winding_lengths = NULL;
stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
if (windings) {
stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);
@@ -3248,7 +3527,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info
{
int ix0,iy0,ix1,iy1;
stbtt__bitmap gbm;
- stbtt_vertex *vertices;
+ stbtt_vertex *vertices;
int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
if (scale_x == 0) scale_x = scale_y;
@@ -3271,7 +3550,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info
if (height) *height = gbm.h;
if (xoff ) *xoff = ix0;
if (yoff ) *yoff = iy0;
-
+
if (gbm.w && gbm.h) {
gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
if (gbm.pixels) {
@@ -3282,7 +3561,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info
}
STBTT_free(vertices, info->userdata);
return gbm.pixels;
-}
+}
STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
{
@@ -3294,7 +3573,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigne
int ix0,iy0;
stbtt_vertex *vertices;
int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
- stbtt__bitmap gbm;
+ stbtt__bitmap gbm;
stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);
gbm.pixels = output;
@@ -3316,7 +3595,12 @@ STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *
STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
{
return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
-}
+}
+
+STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint)
+{
+ stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint));
+}
STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
{
@@ -3326,7 +3610,7 @@ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, uns
STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
{
return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);
-}
+}
STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
{
@@ -3451,7 +3735,7 @@ static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *no
con->y = 0;
con->bottom_y = 0;
STBTT__NOTUSED(nodes);
- STBTT__NOTUSED(num_nodes);
+ STBTT__NOTUSED(num_nodes);
}
static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)
@@ -3826,7 +4110,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char
n = 0;
for (i=0; i < num_ranges; ++i)
n += ranges[i].num_chars;
-
+
rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
if (rects == NULL)
return 0;
@@ -3837,7 +4121,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char
n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);
stbtt_PackFontRangesPackRects(spc, rects, n);
-
+
return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);
STBTT_free(rects, spc->user_allocator_context);
@@ -3909,7 +4193,7 @@ static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2],
float discr = b*b - a*c;
if (discr > 0.0) {
float rcpna = -1 / a;
- float d = (float) sqrt(discr);
+ float d = (float) STBTT_sqrt(discr);
s0 = (b+d) * rcpna;
s1 = (b-d) * rcpna;
if (s0 >= 0.0 && s0 <= 1.0)
@@ -3971,7 +4255,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
orig[1] = y;
// make sure y never passes through a vertex of the shape
- y_frac = (float) fmod(y, 1.0f);
+ y_frac = (float) STBTT_fmod(y, 1.0f);
if (y_frac < 0.01f)
y += 0.01f;
else if (y_frac > 0.99f)
@@ -3985,7 +4269,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y;
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
- if (x_inter < x)
+ if (x_inter < x)
winding += (y0 < y1) ? 1 : -1;
}
}
@@ -4011,7 +4295,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
y1 = (int)verts[i ].y;
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
- if (x_inter < x)
+ if (x_inter < x)
winding += (y0 < y1) ? 1 : -1;
}
} else {
@@ -4023,7 +4307,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
if (hits[1][0] < 0)
winding += (hits[1][1] < 0 ? -1 : 1);
}
- }
+ }
}
}
return winding;
@@ -4104,7 +4388,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
// invert for y-downwards bitmaps
scale_y = -scale_y;
-
+
{
int x,y,i,j;
float *precompute;
@@ -4253,7 +4537,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
STBTT_free(verts, info->userdata);
}
return data;
-}
+}
STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
{
@@ -4271,7 +4555,7 @@ STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata)
//
// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
-static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)
+static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)
{
stbtt_int32 i=0;
@@ -4310,7 +4594,7 @@ static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, s
return i;
}
-static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2)
+static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2)
{
return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);
}
@@ -4439,7 +4723,7 @@ STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,
STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index)
{
- return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index);
+ return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index);
}
STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data)
@@ -4471,6 +4755,9 @@ STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const
// FULL VERSION HISTORY
//
+// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod
+// 1.18 (2018-01-29) add missing function
+// 1.17 (2017-07-23) make more arguments const; doc fix
// 1.16 (2017-07-12) SDF support
// 1.15 (2017-03-03) make more arguments const
// 1.14 (2017-01-16) num-fonts-in-TTC function
@@ -4529,38 +4816,38 @@ This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
-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
+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
+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
+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.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
-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 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
+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 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.
------------------------------------------------------------------------------
*/
diff --git a/thirdparty/misc/stb_vorbis.c b/thirdparty/misc/stb_vorbis.c
index 14cebbf87e..8edcf0f38a 100644
--- a/thirdparty/misc/stb_vorbis.c
+++ b/thirdparty/misc/stb_vorbis.c
@@ -1,11 +1,11 @@
-// Ogg Vorbis audio decoder - v1.11 - public domain
+// Ogg Vorbis audio decoder - v1.14 - public domain
// http://nothings.org/stb_vorbis/
//
// Original version written by Sean Barrett in 2007.
//
-// Originally sponsored by RAD Game Tools. Seeking sponsored
-// by Phillip Bennefall, Marc Andersen, Aaron Baker, Elias Software,
-// Aras Pranckevicius, and Sean Barrett.
+// Originally sponsored by RAD Game Tools. Seeking implementation
+// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker,
+// Elias Software, Aras Pranckevicius, and Sean Barrett.
//
// LICENSE
//
@@ -30,22 +30,26 @@
// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
// Phillip Bennefall Rohit Thiago Goulart
// manxorist@github saga musix github:infatum
+// Timur Gagiev
//
// Partial history:
-// 1.11 - 2017/07/23 - fix MinGW compilation
-// 1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory
-// 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version
-// 1.08 - 2016/04/02 - warnings; setup memory leaks; truncation of last frame
-// 1.07 - 2015/01/16 - fixes for crashes on invalid files; warning fixes; const
-// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson)
+// 1.14 - 2018-02-11 - delete bogus dealloca usage
+// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully)
+// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
+// 1.11 - 2017-07-23 - fix MinGW compilation
+// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
+// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version
+// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame
+// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const
+// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
// some crash fixes when out of memory or with corrupt files
// fix some inappropriately signed shifts
-// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant
-// 1.04 - 2014/08/27 - fix missing const-correct case in API
-// 1.03 - 2014/08/07 - warning fixes
-// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows
-// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct)
-// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
+// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant
+// 1.04 - 2014-08-27 - fix missing const-correct case in API
+// 1.03 - 2014-08-07 - warning fixes
+// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows
+// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct)
+// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
// (API change) report sample rate for decode-full-file funcs
//
// See end of file for full version history.
@@ -816,7 +820,7 @@ struct stb_vorbis
int current_loc_valid;
// per-blocksize precomputed data
-
+
// twiddle factors
float *A[2],*B[2],*C[2];
float *window[2];
@@ -880,11 +884,7 @@ static int error(vorb *f, enum STBVorbisError e)
#define array_size_required(count,size) (count*(sizeof(void *)+(size)))
#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size))
-#ifdef dealloca
-#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : dealloca(size))
-#else
#define temp_free(f,p) 0
-#endif
#define temp_alloc_save(f) ((f)->temp_offset)
#define temp_alloc_restore(f,p) ((f)->temp_offset = (p))
@@ -1142,7 +1142,7 @@ static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values)
if (!c->sparse) {
int k = 0;
for (i=0; i < c->entries; ++i)
- if (include_in_sort(c, lengths[i]))
+ if (include_in_sort(c, lengths[i]))
c->sorted_codewords[k++] = bit_reverse(c->codewords[i]);
assert(k == c->sorted_entries);
} else {
@@ -1323,7 +1323,7 @@ static int getn(vorb *z, uint8 *data, int n)
return 1;
}
- #ifndef STB_VORBIS_NO_STDIO
+ #ifndef STB_VORBIS_NO_STDIO
if (fread(data, n, 1, z->f) == 1)
return 1;
else {
@@ -1403,7 +1403,7 @@ static int start_page_no_capturepattern(vorb *f)
// header flag
f->page_flag = get8(f);
// absolute granule position
- loc0 = get32(f);
+ loc0 = get32(f);
loc1 = get32(f);
// @TODO: validate loc0,loc1 as valid positions?
// stream serial number -- vorbis doesn't interleave, so discard
@@ -1888,69 +1888,69 @@ static int predict_point(int x, int x0, int x1, int y0, int y1)
// the following table is block-copied from the specification
static float inverse_db_table[256] =
{
- 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f,
- 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f,
- 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f,
- 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f,
- 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f,
- 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f,
- 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f,
- 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f,
- 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f,
- 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f,
- 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f,
- 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f,
- 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f,
- 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f,
- 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f,
- 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f,
- 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f,
- 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f,
- 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f,
- 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f,
- 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f,
- 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f,
- 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f,
- 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f,
- 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f,
- 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f,
- 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f,
- 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f,
- 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f,
- 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f,
- 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f,
- 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f,
- 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f,
- 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f,
- 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f,
- 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f,
- 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f,
- 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f,
- 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f,
- 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f,
- 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f,
- 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f,
- 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f,
- 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f,
- 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f,
- 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f,
- 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f,
- 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f,
- 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f,
- 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f,
- 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f,
- 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f,
- 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f,
- 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f,
- 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f,
- 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f,
- 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f,
- 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f,
- 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f,
- 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f,
- 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f,
- 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f,
- 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f,
+ 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f,
+ 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f,
+ 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f,
+ 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f,
+ 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f,
+ 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f,
+ 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f,
+ 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f,
+ 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f,
+ 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f,
+ 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f,
+ 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f,
+ 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f,
+ 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f,
+ 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f,
+ 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f,
+ 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f,
+ 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f,
+ 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f,
+ 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f,
+ 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f,
+ 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f,
+ 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f,
+ 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f,
+ 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f,
+ 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f,
+ 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f,
+ 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f,
+ 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f,
+ 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f,
+ 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f,
+ 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f,
+ 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f,
+ 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f,
+ 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f,
+ 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f,
+ 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f,
+ 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f,
+ 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f,
+ 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f,
+ 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f,
+ 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f,
+ 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f,
+ 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f,
+ 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f,
+ 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f,
+ 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f,
+ 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f,
+ 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f,
+ 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f,
+ 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f,
+ 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f,
+ 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f,
+ 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f,
+ 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f,
+ 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f,
+ 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f,
+ 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f,
+ 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f,
+ 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f,
+ 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f,
+ 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f,
+ 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f,
0.82788260f, 0.88168307f, 0.9389798f, 1.0f
};
@@ -2042,6 +2042,8 @@ static int residue_decode(vorb *f, Codebook *book, float *target, int offset, in
return TRUE;
}
+// n is 1/2 of the blocksize --
+// specification: "Correct per-vector decode length is [n]/2"
static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode)
{
int i,j,pass;
@@ -2049,7 +2051,10 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
int rtype = f->residue_types[rn];
int c = r->classbook;
int classwords = f->codebooks[c].dimensions;
- int n_read = r->end - r->begin;
+ unsigned int actual_size = rtype == 2 ? n*2 : n;
+ unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size);
+ unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size);
+ int n_read = limit_r_end - limit_r_begin;
int part_read = n_read / r->part_size;
int temp_alloc_point = temp_alloc_save(f);
#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
@@ -2346,11 +2351,11 @@ void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype)
#if LIBVORBIS_MDCT
// directly call the vorbis MDCT using an interface documented
// by Jeff Roberts... useful for performance comparison
-typedef struct
+typedef struct
{
int n;
int log2n;
-
+
float *trig;
int *bitrev;
@@ -2369,7 +2374,7 @@ void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
if (M1.n == n) M = &M1;
else if (M2.n == n) M = &M2;
else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; }
- else {
+ else {
if (M2.n) __asm int 3;
mdct_init(&M2, n);
M = &M2;
@@ -2782,7 +2787,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
d1[0] = u[k4+1];
d0[1] = u[k4+2];
d0[0] = u[k4+3];
-
+
d0 -= 4;
d1 -= 4;
bitrev += 2;
@@ -2863,7 +2868,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
float p0,p1,p2,p3;
p3 = e[6]*B[7] - e[7]*B[6];
- p2 = -e[6]*B[6] - e[7]*B[7];
+ p2 = -e[6]*B[6] - e[7]*B[7];
d0[0] = p3;
d1[3] = - p3;
@@ -2871,7 +2876,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
d3[3] = p2;
p1 = e[4]*B[5] - e[5]*B[4];
- p0 = -e[4]*B[4] - e[5]*B[5];
+ p0 = -e[4]*B[4] - e[5]*B[5];
d0[1] = p1;
d1[2] = - p1;
@@ -2879,7 +2884,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
d3[2] = p0;
p3 = e[2]*B[3] - e[3]*B[2];
- p2 = -e[2]*B[2] - e[3]*B[3];
+ p2 = -e[2]*B[2] - e[3]*B[3];
d0[2] = p3;
d1[1] = - p3;
@@ -2887,7 +2892,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
d3[1] = p2;
p1 = e[0]*B[1] - e[1]*B[0];
- p0 = -e[0]*B[0] - e[1]*B[1];
+ p0 = -e[0]*B[0] - e[1]*B[1];
d0[3] = p1;
d1[0] = - p1;
@@ -3391,7 +3396,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
if (f->last_seg_which == f->end_seg_with_known_loc) {
// if we have a valid current loc, and this is final:
if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) {
- uint32 current_end = f->known_loc_for_packet - (n-right_end);
+ uint32 current_end = f->known_loc_for_packet;
// then let's infer the size of the (probably) short final frame
if (current_end < f->current_loc + (right_end-left_start)) {
if (current_end < f->current_loc) {
@@ -3400,7 +3405,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
} else {
*len = current_end - f->current_loc;
}
- *len += left_start;
+ *len += left_start; // this doesn't seem right, but has no ill effect on my test files
if (*len > right_end) *len = right_end; // this should never happen
f->current_loc += *len;
return TRUE;
@@ -3521,7 +3526,7 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page)
first = FALSE;
}
for (; s == -1;) {
- uint8 *q;
+ uint8 *q;
int n;
// check that we have the page header ready
@@ -3874,7 +3879,7 @@ static int start_decoder(vorb *f)
} else {
stbv__floor_ordering p[31*8+2];
Floor1 *g = &f->floor_config[i].floor1;
- int max_class = -1;
+ int max_class = -1;
g->partitions = get_bits(f, 5);
for (j=0; j < g->partitions; ++j) {
g->partition_class_list[j] = get_bits(f, 4);
@@ -3984,7 +3989,7 @@ static int start_decoder(vorb *f)
if (f->mapping == NULL) return error(f, VORBIS_outofmem);
memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping));
for (i=0; i < f->mapping_count; ++i) {
- Mapping *m = f->mapping + i;
+ Mapping *m = f->mapping + i;
int mapping_type = get_bits(f,16);
if (mapping_type != 0) return error(f, VORBIS_invalid_setup);
m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan));
@@ -4050,6 +4055,7 @@ static int start_decoder(vorb *f)
f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist);
if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem);
+ memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1);
#ifdef STB_VORBIS_NO_DEFER_FLOOR
f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem);
@@ -4077,7 +4083,10 @@ static int start_decoder(vorb *f)
int i,max_part_read=0;
for (i=0; i < f->residue_count; ++i) {
Residue *r = f->residue_config + i;
- int n_read = r->end - r->begin;
+ unsigned int actual_size = f->blocksize_1 / 2;
+ unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size;
+ unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size;
+ int n_read = limit_r_end - limit_r_begin;
int part_read = n_read / r->part_size;
if (part_read > max_part_read)
max_part_read = part_read;
@@ -4088,6 +4097,8 @@ static int start_decoder(vorb *f)
classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *));
#endif
+ // maximum reasonable partition size is f->blocksize_1
+
f->temp_memory_required = classify_mem;
if (imdct_mem > f->temp_memory_required)
f->temp_memory_required = imdct_mem;
@@ -4963,7 +4974,7 @@ stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, con
stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc)
{
FILE *f = fopen(filename, "rb");
- if (f)
+ if (f)
return stb_vorbis_open_file(f, TRUE, error, alloc);
if (error) *error = VORBIS_file_open_failure;
return NULL;
@@ -5026,7 +5037,7 @@ static int8 channel_position[7][6] =
#define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT))
#define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22))
#define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s))
- #define check_endianness()
+ #define check_endianness()
#else
#define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s))))
#define check_endianness()
@@ -5351,20 +5362,22 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in
#endif // STB_VORBIS_NO_PULLDATA_API
/* Version history
- 1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory
- 1.09 - 2016/04/04 - back out 'avoid discarding last frame' fix from previous version
- 1.08 - 2016/04/02 - fixed multiple warnings; fix setup memory leaks;
+ 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
+ 1.11 - 2017-07-23 - fix MinGW compilation
+ 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
+ 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version
+ 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks;
avoid discarding last frame of audio data
- 1.07 - 2015/01/16 - fixed some warnings, fix mingw, const-correct API
- some more crash fixes when out of memory or with corrupt files
- 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson)
+ 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API
+ some more crash fixes when out of memory or with corrupt files
+ 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
some crash fixes when out of memory or with corrupt files
- 1.05 - 2015/04/19 - don't define __forceinline if it's redundant
- 1.04 - 2014/08/27 - fix missing const-correct case in API
- 1.03 - 2014/08/07 - Warning fixes
- 1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows
- 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float
- 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in multichannel
+ 1.05 - 2015-04-19 - don't define __forceinline if it's redundant
+ 1.04 - 2014-08-27 - fix missing const-correct case in API
+ 1.03 - 2014-08-07 - Warning fixes
+ 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows
+ 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float
+ 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel
(API change) report sample rate for decode-full-file funcs
0.99996 - bracket #include <malloc.h> for macintosh compilation by Laurent Gomila
0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem
@@ -5412,38 +5425,38 @@ This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
-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
+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
+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
+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.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
-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 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
+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 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.
------------------------------------------------------------------------------
*/
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 */
diff --git a/thirdparty/zstd/common/bitstream.h b/thirdparty/zstd/common/bitstream.h
index fcf3843079..f7f389fe0f 100644
--- a/thirdparty/zstd/common/bitstream.h
+++ b/thirdparty/zstd/common/bitstream.h
@@ -426,7 +426,7 @@ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits)
* Refill `bitD` from buffer previously set in BIT_initDStream() .
* This function is safe, it guarantees it will not read beyond src buffer.
* @return : status of `BIT_DStream_t` internal register.
- * when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */
+ * when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */
MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD)
{
if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* overflow detected, like end of stream */
diff --git a/thirdparty/zstd/common/compiler.h b/thirdparty/zstd/common/compiler.h
index 3a7553c380..e90a3bcde3 100644
--- a/thirdparty/zstd/common/compiler.h
+++ b/thirdparty/zstd/common/compiler.h
@@ -63,6 +63,31 @@
# endif
#endif
+/* target attribute */
+#ifndef __has_attribute
+ #define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
+#endif
+#if defined(__GNUC__)
+# define TARGET_ATTRIBUTE(target) __attribute__((__target__(target)))
+#else
+# define TARGET_ATTRIBUTE(target)
+#endif
+
+/* Enable runtime BMI2 dispatch based on the CPU.
+ * Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default.
+ */
+#ifndef DYNAMIC_BMI2
+ #if (defined(__clang__) && __has_attribute(__target__)) \
+ || (defined(__GNUC__) \
+ && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) \
+ && (defined(__x86_64__) || defined(_M_X86)) \
+ && !defined(__BMI2__)
+ # define DYNAMIC_BMI2 1
+ #else
+ # define DYNAMIC_BMI2 0
+ #endif
+#endif
+
/* prefetch */
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */
# include <mmintrin.h> /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */
diff --git a/thirdparty/zstd/common/cpu.h b/thirdparty/zstd/common/cpu.h
new file mode 100644
index 0000000000..4eb48e39e1
--- /dev/null
+++ b/thirdparty/zstd/common/cpu.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2018-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_COMMON_CPU_H
+#define ZSTD_COMMON_CPU_H
+
+/**
+ * Implementation taken from folly/CpuId.h
+ * https://github.com/facebook/folly/blob/master/folly/CpuId.h
+ */
+
+#include <string.h>
+
+#include "mem.h"
+
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
+typedef struct {
+ U32 f1c;
+ U32 f1d;
+ U32 f7b;
+ U32 f7c;
+} ZSTD_cpuid_t;
+
+MEM_STATIC ZSTD_cpuid_t ZSTD_cpuid(void) {
+ U32 f1c = 0;
+ U32 f1d = 0;
+ U32 f7b = 0;
+ U32 f7c = 0;
+#ifdef _MSC_VER
+ int reg[4];
+ __cpuid((int*)reg, 0);
+ {
+ int const n = reg[0];
+ if (n >= 1) {
+ __cpuid((int*)reg, 1);
+ f1c = (U32)reg[2];
+ f1d = (U32)reg[3];
+ }
+ if (n >= 7) {
+ __cpuidex((int*)reg, 7, 0);
+ f7b = (U32)reg[1];
+ f7c = (U32)reg[2];
+ }
+ }
+#elif defined(__i386__) && defined(__PIC__) && !defined(__clang__) && defined(__GNUC__)
+ /* The following block like the normal cpuid branch below, but gcc
+ * reserves ebx for use of its pic register so we must specially
+ * handle the save and restore to avoid clobbering the register
+ */
+ U32 n;
+ __asm__(
+ "pushl %%ebx\n\t"
+ "cpuid\n\t"
+ "popl %%ebx\n\t"
+ : "=a"(n)
+ : "a"(0)
+ : "ecx", "edx");
+ if (n >= 1) {
+ U32 f1a;
+ __asm__(
+ "pushl %%ebx\n\t"
+ "cpuid\n\t"
+ "popl %%ebx\n\t"
+ : "=a"(f1a), "=c"(f1c), "=d"(f1d)
+ : "a"(1)
+ :);
+ }
+ if (n >= 7) {
+ __asm__(
+ "pushl %%ebx\n\t"
+ "cpuid\n\t"
+ "movl %%ebx, %%eax\n\r"
+ "popl %%ebx"
+ : "=a"(f7b), "=c"(f7c)
+ : "a"(7), "c"(0)
+ : "edx");
+ }
+#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386__)
+ U32 n;
+ __asm__("cpuid" : "=a"(n) : "a"(0) : "ebx", "ecx", "edx");
+ if (n >= 1) {
+ U32 f1a;
+ __asm__("cpuid" : "=a"(f1a), "=c"(f1c), "=d"(f1d) : "a"(1) : "ebx");
+ }
+ if (n >= 7) {
+ U32 f7a;
+ __asm__("cpuid"
+ : "=a"(f7a), "=b"(f7b), "=c"(f7c)
+ : "a"(7), "c"(0)
+ : "edx");
+ }
+#endif
+ {
+ ZSTD_cpuid_t cpuid;
+ cpuid.f1c = f1c;
+ cpuid.f1d = f1d;
+ cpuid.f7b = f7b;
+ cpuid.f7c = f7c;
+ return cpuid;
+ }
+}
+
+#define X(name, r, bit) \
+ MEM_STATIC int ZSTD_cpuid_##name(ZSTD_cpuid_t const cpuid) { \
+ return ((cpuid.r) & (1U << bit)) != 0; \
+ }
+
+/* cpuid(1): Processor Info and Feature Bits. */
+#define C(name, bit) X(name, f1c, bit)
+ C(sse3, 0)
+ C(pclmuldq, 1)
+ C(dtes64, 2)
+ C(monitor, 3)
+ C(dscpl, 4)
+ C(vmx, 5)
+ C(smx, 6)
+ C(eist, 7)
+ C(tm2, 8)
+ C(ssse3, 9)
+ C(cnxtid, 10)
+ C(fma, 12)
+ C(cx16, 13)
+ C(xtpr, 14)
+ C(pdcm, 15)
+ C(pcid, 17)
+ C(dca, 18)
+ C(sse41, 19)
+ C(sse42, 20)
+ C(x2apic, 21)
+ C(movbe, 22)
+ C(popcnt, 23)
+ C(tscdeadline, 24)
+ C(aes, 25)
+ C(xsave, 26)
+ C(osxsave, 27)
+ C(avx, 28)
+ C(f16c, 29)
+ C(rdrand, 30)
+#undef C
+#define D(name, bit) X(name, f1d, bit)
+ D(fpu, 0)
+ D(vme, 1)
+ D(de, 2)
+ D(pse, 3)
+ D(tsc, 4)
+ D(msr, 5)
+ D(pae, 6)
+ D(mce, 7)
+ D(cx8, 8)
+ D(apic, 9)
+ D(sep, 11)
+ D(mtrr, 12)
+ D(pge, 13)
+ D(mca, 14)
+ D(cmov, 15)
+ D(pat, 16)
+ D(pse36, 17)
+ D(psn, 18)
+ D(clfsh, 19)
+ D(ds, 21)
+ D(acpi, 22)
+ D(mmx, 23)
+ D(fxsr, 24)
+ D(sse, 25)
+ D(sse2, 26)
+ D(ss, 27)
+ D(htt, 28)
+ D(tm, 29)
+ D(pbe, 31)
+#undef D
+
+/* cpuid(7): Extended Features. */
+#define B(name, bit) X(name, f7b, bit)
+ B(bmi1, 3)
+ B(hle, 4)
+ B(avx2, 5)
+ B(smep, 7)
+ B(bmi2, 8)
+ B(erms, 9)
+ B(invpcid, 10)
+ B(rtm, 11)
+ B(mpx, 14)
+ B(avx512f, 16)
+ B(avx512dq, 17)
+ B(rdseed, 18)
+ B(adx, 19)
+ B(smap, 20)
+ B(avx512ifma, 21)
+ B(pcommit, 22)
+ B(clflushopt, 23)
+ B(clwb, 24)
+ B(avx512pf, 26)
+ B(avx512er, 27)
+ B(avx512cd, 28)
+ B(sha, 29)
+ B(avx512bw, 30)
+ B(avx512vl, 31)
+#undef B
+#define C(name, bit) X(name, f7c, bit)
+ C(prefetchwt1, 0)
+ C(avx512vbmi, 1)
+#undef C
+
+#undef X
+
+#endif /* ZSTD_COMMON_CPU_H */
diff --git a/thirdparty/zstd/common/error_private.c b/thirdparty/zstd/common/error_private.c
index 11f7cdab1c..d004ee636c 100644
--- a/thirdparty/zstd/common/error_private.c
+++ b/thirdparty/zstd/common/error_private.c
@@ -29,6 +29,7 @@ const char* ERR_getErrorString(ERR_enum code)
case PREFIX(parameter_outOfBound): return "Parameter is out of bound";
case PREFIX(init_missing): return "Context should be init first";
case PREFIX(memory_allocation): return "Allocation error : not enough memory";
+ case PREFIX(workSpace_tooSmall): return "workSpace buffer is not large enough";
case PREFIX(stage_wrong): return "Operation not authorized at current processing stage";
case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported";
case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large";
diff --git a/thirdparty/zstd/common/fse.h b/thirdparty/zstd/common/fse.h
index afd7801963..6a1d272be5 100644
--- a/thirdparty/zstd/common/fse.h
+++ b/thirdparty/zstd/common/fse.h
@@ -345,7 +345,7 @@ size_t FSE_countFast(unsigned* count, unsigned* maxSymbolValuePtr, const void* s
*/
size_t FSE_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned* workSpace);
-/*! FSE_count_simple
+/*! FSE_count_simple() :
* Same as FSE_countFast(), but does not use any additional memory (not even on stack).
* This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr` (presuming it's also the size of `count`).
*/
diff --git a/thirdparty/zstd/common/fse_decompress.c b/thirdparty/zstd/common/fse_decompress.c
index 8e3f0035f6..4c66c3b774 100644
--- a/thirdparty/zstd/common/fse_decompress.c
+++ b/thirdparty/zstd/common/fse_decompress.c
@@ -139,8 +139,8 @@ size_t FSE_buildDTable(FSE_DTable* dt, const short* normalizedCounter, unsigned
{ U32 u;
for (u=0; u<tableSize; u++) {
FSE_FUNCTION_TYPE const symbol = (FSE_FUNCTION_TYPE)(tableDecode[u].symbol);
- U16 nextState = symbolNext[symbol]++;
- tableDecode[u].nbBits = (BYTE) (tableLog - BIT_highbit32 ((U32)nextState) );
+ U32 const nextState = symbolNext[symbol]++;
+ tableDecode[u].nbBits = (BYTE) (tableLog - BIT_highbit32(nextState) );
tableDecode[u].newState = (U16) ( (nextState << tableDecode[u].nbBits) - tableSize);
} }
diff --git a/thirdparty/zstd/common/huf.h b/thirdparty/zstd/common/huf.h
index 522bf9b6c0..b4645b4e51 100644
--- a/thirdparty/zstd/common/huf.h
+++ b/thirdparty/zstd/common/huf.h
@@ -58,32 +58,32 @@ extern "C" {
#endif
-/* *** simple functions *** */
-/**
-HUF_compress() :
- Compress content from buffer 'src', of size 'srcSize', into buffer 'dst'.
- 'dst' buffer must be already allocated.
- Compression runs faster if `dstCapacity` >= HUF_compressBound(srcSize).
- `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB.
- @return : size of compressed data (<= `dstCapacity`).
- Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!!
- if return == 1, srcData is a single repeated byte symbol (RLE compression).
- if HUF_isError(return), compression failed (more details using HUF_getErrorName())
-*/
+/* ========================== */
+/* *** simple functions *** */
+/* ========================== */
+
+/** HUF_compress() :
+ * Compress content from buffer 'src', of size 'srcSize', into buffer 'dst'.
+ * 'dst' buffer must be already allocated.
+ * Compression runs faster if `dstCapacity` >= HUF_compressBound(srcSize).
+ * `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB.
+ * @return : size of compressed data (<= `dstCapacity`).
+ * Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!!
+ * if HUF_isError(return), compression failed (more details using HUF_getErrorName())
+ */
HUF_PUBLIC_API size_t HUF_compress(void* dst, size_t dstCapacity,
const void* src, size_t srcSize);
-/**
-HUF_decompress() :
- Decompress HUF data from buffer 'cSrc', of size 'cSrcSize',
- into already allocated buffer 'dst', of minimum size 'dstSize'.
- `originalSize` : **must** be the ***exact*** size of original (uncompressed) data.
- Note : in contrast with FSE, HUF_decompress can regenerate
- RLE (cSrcSize==1) and uncompressed (cSrcSize==dstSize) data,
- because it knows size to regenerate.
- @return : size of regenerated data (== originalSize),
- or an error code, which can be tested using HUF_isError()
-*/
+/** HUF_decompress() :
+ * Decompress HUF data from buffer 'cSrc', of size 'cSrcSize',
+ * into already allocated buffer 'dst', of minimum size 'dstSize'.
+ * `originalSize` : **must** be the ***exact*** size of original (uncompressed) data.
+ * Note : in contrast with FSE, HUF_decompress can regenerate
+ * RLE (cSrcSize==1) and uncompressed (cSrcSize==dstSize) data,
+ * because it knows size to regenerate (originalSize).
+ * @return : size of regenerated data (== originalSize),
+ * or an error code, which can be tested using HUF_isError()
+ */
HUF_PUBLIC_API size_t HUF_decompress(void* dst, size_t originalSize,
const void* cSrc, size_t cSrcSize);
@@ -100,39 +100,32 @@ HUF_PUBLIC_API const char* HUF_getErrorName(size_t code); /**< provides error c
/* *** Advanced function *** */
/** HUF_compress2() :
- * Same as HUF_compress(), but offers direct control over `maxSymbolValue` and `tableLog`.
- * `tableLog` must be `<= HUF_TABLELOG_MAX` . */
-HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog);
+ * Same as HUF_compress(), but offers control over `maxSymbolValue` and `tableLog`.
+ * `maxSymbolValue` must be <= HUF_SYMBOLVALUE_MAX .
+ * `tableLog` must be `<= HUF_TABLELOG_MAX` . */
+HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned tableLog);
/** HUF_compress4X_wksp() :
* Same as HUF_compress2(), but uses externally allocated `workSpace`.
- * `workspace` must have minimum alignment of 4, and be at least as large as following macro */
+ * `workspace` must have minimum alignment of 4, and be at least as large as HUF_WORKSPACE_SIZE */
#define HUF_WORKSPACE_SIZE (6 << 10)
#define HUF_WORKSPACE_SIZE_U32 (HUF_WORKSPACE_SIZE / sizeof(U32))
-HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
-
-/**
- * The minimum workspace size for the `workSpace` used in
- * HUF_readDTableX2_wksp() and HUF_readDTableX4_wksp().
- *
- * The space used depends on HUF_TABLELOG_MAX, ranging from ~1500 bytes when
- * HUF_TABLE_LOG_MAX=12 to ~1850 bytes when HUF_TABLE_LOG_MAX=15.
- * Buffer overflow errors may potentially occur if code modifications result in
- * a required workspace size greater than that specified in the following
- * macro.
- */
-#define HUF_DECOMPRESS_WORKSPACE_SIZE (2 << 10)
-#define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32))
+HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned tableLog,
+ void* workSpace, size_t wkspSize);
#endif /* HUF_H_298734234 */
/* ******************************************************************
* WARNING !!
* The following section contains advanced and experimental definitions
- * which shall never be used in the context of dll
+ * which shall never be used in the context of a dynamic library,
* because they are not guaranteed to remain stable in the future.
* Only consider them in association with static linking.
- *******************************************************************/
+ * *****************************************************************/
#if defined(HUF_STATIC_LINKING_ONLY) && !defined(HUF_H_HUF_STATIC_LINKING_ONLY)
#define HUF_H_HUF_STATIC_LINKING_ONLY
@@ -141,11 +134,11 @@ HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const
/* *** Constants *** */
-#define HUF_TABLELOG_MAX 12 /* max configured tableLog (for static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */
-#define HUF_TABLELOG_DEFAULT 11 /* tableLog by default, when not specified */
+#define HUF_TABLELOG_MAX 12 /* max runtime value of tableLog (due to static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */
+#define HUF_TABLELOG_DEFAULT 11 /* default tableLog value when none specified */
#define HUF_SYMBOLVALUE_MAX 255
-#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */
+#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */
#if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX)
# error "HUF_TABLELOG_MAX is too large !"
#endif
@@ -192,24 +185,23 @@ size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
/* ****************************************
-* HUF detailed API
-******************************************/
-/*!
-HUF_compress() does the following:
-1. count symbol occurrence from source[] into table count[] using FSE_count()
-2. (optional) refine tableLog using HUF_optimalTableLog()
-3. build Huffman table from count using HUF_buildCTable()
-4. save Huffman table to memory buffer using HUF_writeCTable()
-5. encode the data stream using HUF_compress4X_usingCTable()
-
-The following API allows targeting specific sub-functions for advanced tasks.
-For example, it's possible to compress several blocks using the same 'CTable',
-or to save and regenerate 'CTable' using external methods.
-*/
-/* FSE_count() : find it within "fse.h" */
+ * HUF detailed API
+ * ****************************************/
+
+/*! HUF_compress() does the following:
+ * 1. count symbol occurrence from source[] into table count[] using FSE_count() (exposed within "fse.h")
+ * 2. (optional) refine tableLog using HUF_optimalTableLog()
+ * 3. build Huffman table from count using HUF_buildCTable()
+ * 4. save Huffman table to memory buffer using HUF_writeCTable()
+ * 5. encode the data stream using HUF_compress4X_usingCTable()
+ *
+ * The following API allows targeting specific sub-functions for advanced tasks.
+ * For example, it's possible to compress several blocks using the same 'CTable',
+ * or to save and regenerate 'CTable' using external methods.
+ */
unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue);
typedef struct HUF_CElt_s HUF_CElt; /* incomplete type */
-size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits);
+size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits); /* @return : maxNbBits; CTable and count can overlap. In which case, CTable will overwrite count content */
size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog);
size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable);
@@ -219,46 +211,65 @@ typedef enum {
HUF_repeat_valid /**< Can use the previous table and it is asumed to be valid */
} HUF_repeat;
/** HUF_compress4X_repeat() :
-* Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
-* If it uses hufTable it does not modify hufTable or repeat.
-* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
-* If preferRepeat then the old table will always be used if valid. */
-size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
+ * Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
+ * If it uses hufTable it does not modify hufTable or repeat.
+ * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
+ * If preferRepeat then the old table will always be used if valid. */
+size_t HUF_compress4X_repeat(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned tableLog,
+ void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */
+ HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2);
/** HUF_buildCTable_wksp() :
* Same as HUF_buildCTable(), but using externally allocated scratch buffer.
- * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of 1024 unsigned.
+ * `workSpace` must be aligned on 4-bytes boundaries, and its size must be >= HUF_CTABLE_WORKSPACE_SIZE.
*/
+#define HUF_CTABLE_WORKSPACE_SIZE_U32 (2*HUF_SYMBOLVALUE_MAX +1 +1)
+#define HUF_CTABLE_WORKSPACE_SIZE (HUF_CTABLE_WORKSPACE_SIZE_U32 * sizeof(unsigned))
size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize);
/*! HUF_readStats() :
- Read compact Huffman tree, saved by HUF_writeCTable().
- `huffWeight` is destination buffer.
- @return : size read from `src` , or an error Code .
- Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */
-size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats,
- U32* nbSymbolsPtr, U32* tableLogPtr,
+ * Read compact Huffman tree, saved by HUF_writeCTable().
+ * `huffWeight` is destination buffer.
+ * @return : size read from `src` , or an error Code .
+ * Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */
+size_t HUF_readStats(BYTE* huffWeight, size_t hwSize,
+ U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr,
const void* src, size_t srcSize);
/** HUF_readCTable() :
-* Loading a CTable saved with HUF_writeCTable() */
+ * Loading a CTable saved with HUF_writeCTable() */
size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize);
/*
-HUF_decompress() does the following:
-1. select the decompression algorithm (X2, X4) based on pre-computed heuristics
-2. build Huffman table from save, using HUF_readDTableXn()
-3. decode 1 or 4 segments in parallel using HUF_decompressSXn_usingDTable
-*/
+ * HUF_decompress() does the following:
+ * 1. select the decompression algorithm (X2, X4) based on pre-computed heuristics
+ * 2. build Huffman table from save, using HUF_readDTableX?()
+ * 3. decode 1 or 4 segments in parallel using HUF_decompress?X?_usingDTable()
+ */
/** HUF_selectDecoder() :
-* Tells which decoder is likely to decode faster,
-* based on a set of pre-determined metrics.
-* @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 .
-* Assumption : 0 < cSrcSize < dstSize <= 128 KB */
+ * Tells which decoder is likely to decode faster,
+ * based on a set of pre-computed metrics.
+ * @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 .
+ * Assumption : 0 < dstSize <= 128 KB */
U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize);
+/**
+ * The minimum workspace size for the `workSpace` used in
+ * HUF_readDTableX2_wksp() and HUF_readDTableX4_wksp().
+ *
+ * The space used depends on HUF_TABLELOG_MAX, ranging from ~1500 bytes when
+ * HUF_TABLE_LOG_MAX=12 to ~1850 bytes when HUF_TABLE_LOG_MAX=15.
+ * Buffer overflow errors may potentially occur if code modifications result in
+ * a required workspace size greater than that specified in the following
+ * macro.
+ */
+#define HUF_DECOMPRESS_WORKSPACE_SIZE (2 << 10)
+#define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32))
+
size_t HUF_readDTableX2 (HUF_DTable* DTable, const void* src, size_t srcSize);
size_t HUF_readDTableX2_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize);
size_t HUF_readDTableX4 (HUF_DTable* DTable, const void* src, size_t srcSize);
@@ -269,17 +280,23 @@ size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* c
size_t HUF_decompress4X4_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
+/* ====================== */
/* single stream variants */
+/* ====================== */
size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog);
size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable);
/** HUF_compress1X_repeat() :
-* Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
-* If it uses hufTable it does not modify hufTable or repeat.
-* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
-* If preferRepeat then the old table will always be used if valid. */
-size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
+ * Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
+ * If it uses hufTable it does not modify hufTable or repeat.
+ * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
+ * If preferRepeat then the old table will always be used if valid. */
+size_t HUF_compress1X_repeat(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned tableLog,
+ void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */
+ HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2);
size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */
size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */
@@ -295,6 +312,14 @@ size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cS
size_t HUF_decompress1X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
size_t HUF_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
+/* BMI2 variants.
+ * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0.
+ */
+size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2);
+size_t HUF_decompress1X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2);
+size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2);
+size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2);
+
#endif /* HUF_STATIC_LINKING_ONLY */
#if defined (__cplusplus)
diff --git a/thirdparty/zstd/common/pool.c b/thirdparty/zstd/common/pool.c
index 98b109e72a..773488b072 100644
--- a/thirdparty/zstd/common/pool.c
+++ b/thirdparty/zstd/common/pool.c
@@ -12,6 +12,7 @@
/* ====== Dependencies ======= */
#include <stddef.h> /* size_t */
#include "pool.h"
+#include "zstd_internal.h" /* ZSTD_malloc, ZSTD_free */
/* ====== Compiler specifics ====== */
#if defined(_MSC_VER)
@@ -193,32 +194,54 @@ static int isQueueFull(POOL_ctx const* ctx) {
}
}
-void POOL_add(void* ctxVoid, POOL_function function, void *opaque) {
- POOL_ctx* const ctx = (POOL_ctx*)ctxVoid;
- if (!ctx) { return; }
+static void POOL_add_internal(POOL_ctx* ctx, POOL_function function, void *opaque)
+{
+ POOL_job const job = {function, opaque};
+ assert(ctx != NULL);
+ if (ctx->shutdown) return;
+
+ ctx->queueEmpty = 0;
+ ctx->queue[ctx->queueTail] = job;
+ ctx->queueTail = (ctx->queueTail + 1) % ctx->queueSize;
+ ZSTD_pthread_cond_signal(&ctx->queuePopCond);
+}
+
+void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque)
+{
+ assert(ctx != NULL);
ZSTD_pthread_mutex_lock(&ctx->queueMutex);
- { POOL_job const job = {function, opaque};
+ /* Wait until there is space in the queue for the new job */
+ while (isQueueFull(ctx) && (!ctx->shutdown)) {
+ ZSTD_pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex);
+ }
+ POOL_add_internal(ctx, function, opaque);
+ ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+}
- /* Wait until there is space in the queue for the new job */
- while (isQueueFull(ctx) && !ctx->shutdown) {
- ZSTD_pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex);
- }
- /* The queue is still going => there is space */
- if (!ctx->shutdown) {
- ctx->queueEmpty = 0;
- ctx->queue[ctx->queueTail] = job;
- ctx->queueTail = (ctx->queueTail + 1) % ctx->queueSize;
- }
+
+int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque)
+{
+ assert(ctx != NULL);
+ ZSTD_pthread_mutex_lock(&ctx->queueMutex);
+ if (isQueueFull(ctx)) {
+ ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+ return 0;
}
+ POOL_add_internal(ctx, function, opaque);
ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
- ZSTD_pthread_cond_signal(&ctx->queuePopCond);
+ return 1;
}
+
#else /* ZSTD_MULTITHREAD not defined */
+
+/* ========================== */
/* No multi-threading support */
+/* ========================== */
-/* We don't need any data, but if it is empty malloc() might return NULL. */
+
+/* We don't need any data, but if it is empty, malloc() might return NULL. */
struct POOL_ctx_s {
int dummy;
};
@@ -240,9 +263,15 @@ void POOL_free(POOL_ctx* ctx) {
(void)ctx;
}
-void POOL_add(void* ctx, POOL_function function, void* opaque) {
+void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque) {
+ (void)ctx;
+ function(opaque);
+}
+
+int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque) {
(void)ctx;
function(opaque);
+ return 1;
}
size_t POOL_sizeof(POOL_ctx* ctx) {
diff --git a/thirdparty/zstd/common/pool.h b/thirdparty/zstd/common/pool.h
index 08c63715aa..a57e9b4fab 100644
--- a/thirdparty/zstd/common/pool.h
+++ b/thirdparty/zstd/common/pool.h
@@ -17,7 +17,8 @@ extern "C" {
#include <stddef.h> /* size_t */
-#include "zstd_internal.h" /* ZSTD_customMem */
+#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_customMem */
+#include "zstd.h"
typedef struct POOL_ctx_s POOL_ctx;
@@ -27,35 +28,43 @@ typedef struct POOL_ctx_s POOL_ctx;
* The maximum number of queued jobs before blocking is `queueSize`.
* @return : POOL_ctx pointer on success, else NULL.
*/
-POOL_ctx *POOL_create(size_t numThreads, size_t queueSize);
+POOL_ctx* POOL_create(size_t numThreads, size_t queueSize);
-POOL_ctx *POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem);
+POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem);
/*! POOL_free() :
Free a thread pool returned by POOL_create().
*/
-void POOL_free(POOL_ctx *ctx);
+void POOL_free(POOL_ctx* ctx);
/*! POOL_sizeof() :
return memory usage of pool returned by POOL_create().
*/
-size_t POOL_sizeof(POOL_ctx *ctx);
+size_t POOL_sizeof(POOL_ctx* ctx);
/*! POOL_function :
The function type that can be added to a thread pool.
*/
-typedef void (*POOL_function)(void *);
+typedef void (*POOL_function)(void*);
/*! POOL_add_function :
The function type for a generic thread pool add function.
*/
-typedef void (*POOL_add_function)(void *, POOL_function, void *);
+typedef void (*POOL_add_function)(void*, POOL_function, void*);
/*! POOL_add() :
- Add the job `function(opaque)` to the thread pool.
+ Add the job `function(opaque)` to the thread pool. `ctx` must be valid.
Possibly blocks until there is room in the queue.
Note : The function may be executed asynchronously, so `opaque` must live until the function has been completed.
*/
-void POOL_add(void *ctx, POOL_function function, void *opaque);
+void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque);
+
+
+/*! POOL_tryAdd() :
+ Add the job `function(opaque)` to the thread pool if a worker is available.
+ return immediately otherwise.
+ @return : 1 if successful, 0 if not.
+*/
+int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque);
#if defined (__cplusplus)
diff --git a/thirdparty/zstd/common/threading.h b/thirdparty/zstd/common/threading.h
index 197770db27..d806c89d01 100644
--- a/thirdparty/zstd/common/threading.h
+++ b/thirdparty/zstd/common/threading.h
@@ -45,15 +45,15 @@ extern "C" {
/* mutex */
#define ZSTD_pthread_mutex_t CRITICAL_SECTION
-#define ZSTD_pthread_mutex_init(a, b) (InitializeCriticalSection((a)), 0)
+#define ZSTD_pthread_mutex_init(a, b) ((void)(b), InitializeCriticalSection((a)), 0)
#define ZSTD_pthread_mutex_destroy(a) DeleteCriticalSection((a))
#define ZSTD_pthread_mutex_lock(a) EnterCriticalSection((a))
#define ZSTD_pthread_mutex_unlock(a) LeaveCriticalSection((a))
/* condition variable */
#define ZSTD_pthread_cond_t CONDITION_VARIABLE
-#define ZSTD_pthread_cond_init(a, b) (InitializeConditionVariable((a)), 0)
-#define ZSTD_pthread_cond_destroy(a) /* No delete */
+#define ZSTD_pthread_cond_init(a, b) ((void)(b), InitializeConditionVariable((a)), 0)
+#define ZSTD_pthread_cond_destroy(a) ((void)(a))
#define ZSTD_pthread_cond_wait(a, b) SleepConditionVariableCS((a), (b), INFINITE)
#define ZSTD_pthread_cond_signal(a) WakeConditionVariable((a))
#define ZSTD_pthread_cond_broadcast(a) WakeAllConditionVariable((a))
@@ -100,17 +100,17 @@ int ZSTD_pthread_join(ZSTD_pthread_t thread, void** value_ptr);
/* No multithreading support */
typedef int ZSTD_pthread_mutex_t;
-#define ZSTD_pthread_mutex_init(a, b) ((void)a, 0)
-#define ZSTD_pthread_mutex_destroy(a)
-#define ZSTD_pthread_mutex_lock(a)
-#define ZSTD_pthread_mutex_unlock(a)
+#define ZSTD_pthread_mutex_init(a, b) ((void)(a), (void)(b), 0)
+#define ZSTD_pthread_mutex_destroy(a) ((void)(a))
+#define ZSTD_pthread_mutex_lock(a) ((void)(a))
+#define ZSTD_pthread_mutex_unlock(a) ((void)(a))
typedef int ZSTD_pthread_cond_t;
-#define ZSTD_pthread_cond_init(a, b) ((void)a, 0)
-#define ZSTD_pthread_cond_destroy(a)
-#define ZSTD_pthread_cond_wait(a, b)
-#define ZSTD_pthread_cond_signal(a)
-#define ZSTD_pthread_cond_broadcast(a)
+#define ZSTD_pthread_cond_init(a, b) ((void)(a), (void)(b), 0)
+#define ZSTD_pthread_cond_destroy(a) ((void)(a))
+#define ZSTD_pthread_cond_wait(a, b) ((void)(a), (void)(b))
+#define ZSTD_pthread_cond_signal(a) ((void)(a))
+#define ZSTD_pthread_cond_broadcast(a) ((void)(a))
/* do not use ZSTD_pthread_t */
diff --git a/thirdparty/zstd/common/zstd_errors.h b/thirdparty/zstd/common/zstd_errors.h
index 4bcb7769fe..57533f2869 100644
--- a/thirdparty/zstd/common/zstd_errors.h
+++ b/thirdparty/zstd/common/zstd_errors.h
@@ -35,12 +35,20 @@ extern "C" {
# define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBILITY
#endif
-/*-****************************************
- * error codes list
- * note : this API is still considered unstable
- * and shall not be used with a dynamic library.
- * only static linking is allowed
- ******************************************/
+/*-*********************************************
+ * Error codes list
+ *-*********************************************
+ * Error codes _values_ are pinned down since v1.3.1 only.
+ * Therefore, don't rely on values if you may link to any version < v1.3.1.
+ *
+ * Only values < 100 are considered stable.
+ *
+ * note 1 : this API shall be used with static linking only.
+ * dynamic linking is not yet officially supported.
+ * note 2 : Prefer relying on the enum than on its value whenever possible
+ * This is the only supported way to use the error list < v1.3.1
+ * note 3 : ZSTD_isError() is always correct, whatever the library version.
+ **********************************************/
typedef enum {
ZSTD_error_no_error = 0,
ZSTD_error_GENERIC = 1,
@@ -61,9 +69,10 @@ typedef enum {
ZSTD_error_stage_wrong = 60,
ZSTD_error_init_missing = 62,
ZSTD_error_memory_allocation = 64,
+ ZSTD_error_workSpace_tooSmall= 66,
ZSTD_error_dstSize_tooSmall = 70,
ZSTD_error_srcSize_wrong = 72,
- /* following error codes are not stable and may be removed or changed in a future version */
+ /* following error codes are __NOT STABLE__, they can be removed or changed in future versions */
ZSTD_error_frameIndex_tooLarge = 100,
ZSTD_error_seekableIO = 102,
ZSTD_error_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */
diff --git a/thirdparty/zstd/common/zstd_internal.h b/thirdparty/zstd/common/zstd_internal.h
index 5d2900eb76..65c08a8257 100644
--- a/thirdparty/zstd/common/zstd_internal.h
+++ b/thirdparty/zstd/common/zstd_internal.h
@@ -132,14 +132,15 @@ typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingTy
#define Litbits 8
#define MaxLit ((1<<Litbits) - 1)
-#define MaxML 52
-#define MaxLL 35
+#define MaxML 52
+#define MaxLL 35
#define DefaultMaxOff 28
-#define MaxOff 31
+#define MaxOff 31
#define MaxSeq MAX(MaxLL, MaxML) /* Assumption : MaxOff < MaxLL,MaxML */
#define MLFSELog 9
#define LLFSELog 9
#define OffFSELog 8
+#define MaxFSELog MAX(MAX(MLFSELog, LLFSELog), OffFSELog)
static const U32 LL_bits[MaxLL+1] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -228,8 +229,6 @@ typedef struct {
BYTE* ofCode;
U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */
U32 longLengthPos;
- U32 rep[ZSTD_REP_NUM];
- U32 repToConfirm[ZSTD_REP_NUM];
} seqStore_t;
const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); /* compress & dictBuilder */
diff --git a/thirdparty/zstd/compress/fse_compress.c b/thirdparty/zstd/compress/fse_compress.c
index 549c115d42..cb8f1fa323 100644
--- a/thirdparty/zstd/compress/fse_compress.c
+++ b/thirdparty/zstd/compress/fse_compress.c
@@ -248,7 +248,7 @@ static size_t FSE_writeNCount_generic (void* header, size_t headerBufferSize,
bitCount -= (count<max);
previous0 = (count==1);
if (remaining<1) return ERROR(GENERIC);
- while (remaining<threshold) nbBits--, threshold>>=1;
+ while (remaining<threshold) { nbBits--; threshold>>=1; }
}
if (bitCount>16) {
if ((!writeIsSafe) && (out > oend - 2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */
@@ -292,7 +292,7 @@ size_t FSE_writeNCount (void* buffer, size_t bufferSize, const short* normalized
It doesn't use any additional memory.
But this function is unsafe : it doesn't check that all values within `src` can fit into `count`.
For this reason, prefer using a table `count` with 256 elements.
- @return : count of most numerous element
+ @return : count of most numerous element.
*/
size_t FSE_count_simple(unsigned* count, unsigned* maxSymbolValuePtr,
const void* src, size_t srcSize)
@@ -305,7 +305,10 @@ size_t FSE_count_simple(unsigned* count, unsigned* maxSymbolValuePtr,
memset(count, 0, (maxSymbolValue+1)*sizeof(*count));
if (srcSize==0) { *maxSymbolValuePtr = 0; return 0; }
- while (ip<end) count[*ip++]++;
+ while (ip<end) {
+ assert(*ip <= maxSymbolValue);
+ count[*ip++]++;
+ }
while (!count[maxSymbolValue]) maxSymbolValue--;
*maxSymbolValuePtr = maxSymbolValue;
@@ -318,7 +321,8 @@ size_t FSE_count_simple(unsigned* count, unsigned* maxSymbolValuePtr,
/* FSE_count_parallel_wksp() :
* Same as FSE_count_parallel(), but using an externally provided scratch buffer.
- * `workSpace` size must be a minimum of `1024 * sizeof(unsigned)`` */
+ * `workSpace` size must be a minimum of `1024 * sizeof(unsigned)`.
+ * @return : largest histogram frequency, or an error code (notably when histogram would be larger than *maxSymbolValuePtr). */
static size_t FSE_count_parallel_wksp(
unsigned* count, unsigned* maxSymbolValuePtr,
const void* source, size_t sourceSize,
@@ -333,7 +337,7 @@ static size_t FSE_count_parallel_wksp(
U32* const Counting3 = Counting2 + 256;
U32* const Counting4 = Counting3 + 256;
- memset(Counting1, 0, 4*256*sizeof(unsigned));
+ memset(workSpace, 0, 4*256*sizeof(unsigned));
/* safety checks */
if (!sourceSize) {
@@ -379,7 +383,9 @@ static size_t FSE_count_parallel_wksp(
if (Counting1[s]) return ERROR(maxSymbolValue_tooSmall);
} }
- { U32 s; for (s=0; s<=maxSymbolValue; s++) {
+ { U32 s;
+ if (maxSymbolValue > 255) maxSymbolValue = 255;
+ for (s=0; s<=maxSymbolValue; s++) {
count[s] = Counting1[s] + Counting2[s] + Counting3[s] + Counting4[s];
if (count[s] > max) max = count[s];
} }
@@ -393,9 +399,11 @@ static size_t FSE_count_parallel_wksp(
* Same as FSE_countFast(), but using an externally provided scratch buffer.
* `workSpace` size must be table of >= `1024` unsigned */
size_t FSE_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr,
- const void* source, size_t sourceSize, unsigned* workSpace)
+ const void* source, size_t sourceSize,
+ unsigned* workSpace)
{
- if (sourceSize < 1500) return FSE_count_simple(count, maxSymbolValuePtr, source, sourceSize);
+ if (sourceSize < 1500) /* heuristic threshold */
+ return FSE_count_simple(count, maxSymbolValuePtr, source, sourceSize);
return FSE_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 0, workSpace);
}
@@ -540,7 +548,7 @@ static size_t FSE_normalizeM2(short* norm, U32 tableLog, const unsigned* count,
find max, then give all remaining points to max */
U32 maxV = 0, maxC = 0;
for (s=0; s<=maxSymbolValue; s++)
- if (count[s] > maxC) maxV=s, maxC=count[s];
+ if (count[s] > maxC) { maxV=s; maxC=count[s]; }
norm[maxV] += (short)ToDistribute;
return 0;
}
@@ -548,7 +556,7 @@ static size_t FSE_normalizeM2(short* norm, U32 tableLog, const unsigned* count,
if (total == 0) {
/* all of the symbols were low enough for the lowOne or lowThreshold */
for (s=0; ToDistribute > 0; s = (s+1)%(maxSymbolValue+1))
- if (norm[s] > 0) ToDistribute--, norm[s]++;
+ if (norm[s] > 0) { ToDistribute--; norm[s]++; }
return 0;
}
@@ -604,7 +612,7 @@ size_t FSE_normalizeCount (short* normalizedCounter, unsigned tableLog,
U64 restToBeat = vStep * rtbTable[proba];
proba += (count[s]*step) - ((U64)proba<<scale) > restToBeat;
}
- if (proba > largestP) largestP=proba, largest=s;
+ if (proba > largestP) { largestP=proba; largest=s; }
normalizedCounter[s] = proba;
stillToDistribute -= proba;
} }
diff --git a/thirdparty/zstd/compress/huf_compress.c b/thirdparty/zstd/compress/huf_compress.c
index 5692d56e00..83230b415f 100644
--- a/thirdparty/zstd/compress/huf_compress.c
+++ b/thirdparty/zstd/compress/huf_compress.c
@@ -46,6 +46,7 @@
#include <string.h> /* memcpy, memset */
#include <stdio.h> /* printf (debug) */
#include "bitstream.h"
+#include "compiler.h"
#define FSE_STATIC_LINKING_ONLY /* FSE_optimalTableLog_internal */
#include "fse.h" /* header compression */
#define HUF_STATIC_LINKING_ONLY
@@ -322,7 +323,10 @@ static void HUF_sort(nodeElt* huffNode, const U32* count, U32 maxSymbolValue)
U32 const c = count[n];
U32 const r = BIT_highbit32(c+1) + 1;
U32 pos = rank[r].current++;
- while ((pos > rank[r].base) && (c > huffNode[pos-1].count)) huffNode[pos]=huffNode[pos-1], pos--;
+ while ((pos > rank[r].base) && (c > huffNode[pos-1].count)) {
+ huffNode[pos] = huffNode[pos-1];
+ pos--;
+ }
huffNode[pos].count = c;
huffNode[pos].byte = (BYTE)n;
}
@@ -331,10 +335,10 @@ static void HUF_sort(nodeElt* huffNode, const U32* count, U32 maxSymbolValue)
/** HUF_buildCTable_wksp() :
* Same as HUF_buildCTable(), but using externally allocated scratch buffer.
- * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of 1024 unsigned.
+ * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of HUF_CTABLE_WORKSPACE_SIZE_U32 unsigned.
*/
#define STARTNODE (HUF_SYMBOLVALUE_MAX+1)
-typedef nodeElt huffNodeTable[2*HUF_SYMBOLVALUE_MAX+1 +1];
+typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32];
size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize)
{
nodeElt* const huffNode0 = (nodeElt*)workSpace;
@@ -345,9 +349,10 @@ size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValu
U32 nodeRoot;
/* safety checks */
- if (wkspSize < sizeof(huffNodeTable)) return ERROR(GENERIC); /* workSpace is not large enough */
+ if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */
+ if (wkspSize < sizeof(huffNodeTable)) return ERROR(workSpace_tooSmall);
if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT;
- if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(GENERIC);
+ if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge);
memset(huffNode0, 0, sizeof(huffNodeTable));
/* sort, decreasing order */
@@ -405,6 +410,7 @@ size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValu
}
/** HUF_buildCTable() :
+ * @return : maxNbBits
* Note : count is used before tree is written, so they can safely overlap
*/
size_t HUF_buildCTable (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits)
@@ -432,13 +438,14 @@ static int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, uns
return !bad;
}
-static void HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable)
+size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); }
+
+FORCE_INLINE_TEMPLATE void
+HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable)
{
BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits);
}
-size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); }
-
#define HUF_FLUSHBITS(s) BIT_flushBits(s)
#define HUF_FLUSHBITS_1(stream) \
@@ -447,7 +454,10 @@ size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); }
#define HUF_FLUSHBITS_2(stream) \
if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*4+7) HUF_FLUSHBITS(stream)
-size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
+FORCE_INLINE_TEMPLATE size_t
+HUF_compress1X_usingCTable_internal_body(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable)
{
const BYTE* ip = (const BYTE*) src;
BYTE* const ostart = (BYTE*)dst;
@@ -491,8 +501,58 @@ size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, si
return BIT_closeCStream(&bitC);
}
+#if DYNAMIC_BMI2
-size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
+static TARGET_ATTRIBUTE("bmi2") size_t
+HUF_compress1X_usingCTable_internal_bmi2(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable)
+{
+ return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
+}
+
+static size_t
+HUF_compress1X_usingCTable_internal_default(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable)
+{
+ return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
+}
+
+static size_t
+HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable, const int bmi2)
+{
+ if (bmi2) {
+ return HUF_compress1X_usingCTable_internal_bmi2(dst, dstSize, src, srcSize, CTable);
+ }
+ return HUF_compress1X_usingCTable_internal_default(dst, dstSize, src, srcSize, CTable);
+}
+
+#else
+
+static size_t
+HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable, const int bmi2)
+{
+ (void)bmi2;
+ return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
+}
+
+#endif
+
+size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
+{
+ return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0);
+}
+
+
+static size_t
+HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable, int bmi2)
{
size_t const segmentSize = (srcSize+3)/4; /* first 3 segments */
const BYTE* ip = (const BYTE*) src;
@@ -505,28 +565,31 @@ size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, si
if (srcSize < 12) return 0; /* no saving possible : too small input */
op += 6; /* jumpTable */
- { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, segmentSize, CTable) );
+ { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
if (cSize==0) return 0;
+ assert(cSize <= 65535);
MEM_writeLE16(ostart, (U16)cSize);
op += cSize;
}
ip += segmentSize;
- { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, segmentSize, CTable) );
+ { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
if (cSize==0) return 0;
+ assert(cSize <= 65535);
MEM_writeLE16(ostart+2, (U16)cSize);
op += cSize;
}
ip += segmentSize;
- { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, segmentSize, CTable) );
+ { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
if (cSize==0) return 0;
+ assert(cSize <= 65535);
MEM_writeLE16(ostart+4, (U16)cSize);
op += cSize;
}
ip += segmentSize;
- { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, iend-ip, CTable) );
+ { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, iend-ip, CTable, bmi2) );
if (cSize==0) return 0;
op += cSize;
}
@@ -534,15 +597,20 @@ size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, si
return op-ostart;
}
+size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
+{
+ return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0);
+}
+
static size_t HUF_compressCTable_internal(
BYTE* const ostart, BYTE* op, BYTE* const oend,
const void* src, size_t srcSize,
- unsigned singleStream, const HUF_CElt* CTable)
+ unsigned singleStream, const HUF_CElt* CTable, const int bmi2)
{
size_t const cSize = singleStream ?
- HUF_compress1X_usingCTable(op, oend - op, src, srcSize, CTable) :
- HUF_compress4X_usingCTable(op, oend - op, src, srcSize, CTable);
+ HUF_compress1X_usingCTable_internal(op, oend - op, src, srcSize, CTable, bmi2) :
+ HUF_compress4X_usingCTable_internal(op, oend - op, src, srcSize, CTable, bmi2);
if (HUF_isError(cSize)) { return cSize; }
if (cSize==0) { return 0; } /* uncompressible */
op += cSize;
@@ -551,86 +619,98 @@ static size_t HUF_compressCTable_internal(
return op-ostart;
}
+typedef struct {
+ U32 count[HUF_SYMBOLVALUE_MAX + 1];
+ HUF_CElt CTable[HUF_SYMBOLVALUE_MAX + 1];
+ huffNodeTable nodeTable;
+} HUF_compress_tables_t;
-/* `workSpace` must a table of at least 1024 unsigned */
+/* HUF_compress_internal() :
+ * `workSpace` must a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
static size_t HUF_compress_internal (
void* dst, size_t dstSize,
const void* src, size_t srcSize,
unsigned maxSymbolValue, unsigned huffLog,
unsigned singleStream,
void* workSpace, size_t wkspSize,
- HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat)
+ HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat,
+ const int bmi2)
{
+ HUF_compress_tables_t* const table = (HUF_compress_tables_t*)workSpace;
BYTE* const ostart = (BYTE*)dst;
BYTE* const oend = ostart + dstSize;
BYTE* op = ostart;
- U32* count;
- size_t const countSize = sizeof(U32) * (HUF_SYMBOLVALUE_MAX + 1);
- HUF_CElt* CTable;
- size_t const CTableSize = sizeof(HUF_CElt) * (HUF_SYMBOLVALUE_MAX + 1);
-
/* checks & inits */
- if (wkspSize < sizeof(huffNodeTable) + countSize + CTableSize) return ERROR(GENERIC);
- if (!srcSize) return 0; /* Uncompressed (note : 1 means rle, so first byte must be correct) */
- if (!dstSize) return 0; /* cannot fit within dst budget */
+ if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */
+ if (wkspSize < sizeof(*table)) return ERROR(workSpace_tooSmall);
+ if (!srcSize) return 0; /* Uncompressed */
+ if (!dstSize) return 0; /* cannot fit anything within dst budget */
if (srcSize > HUF_BLOCKSIZE_MAX) return ERROR(srcSize_wrong); /* current block size limit */
if (huffLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge);
+ if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge);
if (!maxSymbolValue) maxSymbolValue = HUF_SYMBOLVALUE_MAX;
if (!huffLog) huffLog = HUF_TABLELOG_DEFAULT;
- count = (U32*)workSpace;
- workSpace = (BYTE*)workSpace + countSize;
- wkspSize -= countSize;
- CTable = (HUF_CElt*)workSpace;
- workSpace = (BYTE*)workSpace + CTableSize;
- wkspSize -= CTableSize;
-
- /* Heuristic : If we don't need to check the validity of the old table use the old table for small inputs */
+ /* Heuristic : If old table is valid, use it for small inputs */
if (preferRepeat && repeat && *repeat == HUF_repeat_valid) {
- return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable);
+ return HUF_compressCTable_internal(ostart, op, oend,
+ src, srcSize,
+ singleStream, oldHufTable, bmi2);
}
/* Scan input and build symbol stats */
- { CHECK_V_F(largest, FSE_count_wksp (count, &maxSymbolValue, (const BYTE*)src, srcSize, (U32*)workSpace) );
+ { CHECK_V_F(largest, FSE_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, table->count) );
if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */
- if (largest <= (srcSize >> 7)+1) return 0; /* Fast heuristic : not compressible enough */
+ if (largest <= (srcSize >> 7)+1) return 0; /* heuristic : probably not compressible enough */
}
/* Check validity of previous table */
- if (repeat && *repeat == HUF_repeat_check && !HUF_validateCTable(oldHufTable, count, maxSymbolValue)) {
+ if ( repeat
+ && *repeat == HUF_repeat_check
+ && !HUF_validateCTable(oldHufTable, table->count, maxSymbolValue)) {
*repeat = HUF_repeat_none;
}
/* Heuristic : use existing table for small inputs */
if (preferRepeat && repeat && *repeat != HUF_repeat_none) {
- return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable);
+ return HUF_compressCTable_internal(ostart, op, oend,
+ src, srcSize,
+ singleStream, oldHufTable, bmi2);
}
/* Build Huffman Tree */
huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue);
- { CHECK_V_F(maxBits, HUF_buildCTable_wksp (CTable, count, maxSymbolValue, huffLog, workSpace, wkspSize) );
+ { CHECK_V_F(maxBits, HUF_buildCTable_wksp(table->CTable, table->count,
+ maxSymbolValue, huffLog,
+ table->nodeTable, sizeof(table->nodeTable)) );
huffLog = (U32)maxBits;
- /* Zero the unused symbols so we can check it for validity */
- memset(CTable + maxSymbolValue + 1, 0, CTableSize - (maxSymbolValue + 1) * sizeof(HUF_CElt));
+ /* Zero unused symbols in CTable, so we can check it for validity */
+ memset(table->CTable + (maxSymbolValue + 1), 0,
+ sizeof(table->CTable) - ((maxSymbolValue + 1) * sizeof(HUF_CElt)));
}
/* Write table description header */
- { CHECK_V_F(hSize, HUF_writeCTable (op, dstSize, CTable, maxSymbolValue, huffLog) );
- /* Check if using the previous table will be beneficial */
+ { CHECK_V_F(hSize, HUF_writeCTable (op, dstSize, table->CTable, maxSymbolValue, huffLog) );
+ /* Check if using previous huffman table is beneficial */
if (repeat && *repeat != HUF_repeat_none) {
- size_t const oldSize = HUF_estimateCompressedSize(oldHufTable, count, maxSymbolValue);
- size_t const newSize = HUF_estimateCompressedSize(CTable, count, maxSymbolValue);
+ size_t const oldSize = HUF_estimateCompressedSize(oldHufTable, table->count, maxSymbolValue);
+ size_t const newSize = HUF_estimateCompressedSize(table->CTable, table->count, maxSymbolValue);
if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) {
- return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable);
- }
- }
- /* Use the new table */
+ return HUF_compressCTable_internal(ostart, op, oend,
+ src, srcSize,
+ singleStream, oldHufTable, bmi2);
+ } }
+
+ /* Use the new huffman table */
if (hSize + 12ul >= srcSize) { return 0; }
op += hSize;
if (repeat) { *repeat = HUF_repeat_none; }
- if (oldHufTable) { memcpy(oldHufTable, CTable, CTableSize); } /* Save the new table */
+ if (oldHufTable)
+ memcpy(oldHufTable, table->CTable, sizeof(table->CTable)); /* Save new table */
}
- return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, CTable);
+ return HUF_compressCTable_internal(ostart, op, oend,
+ src, srcSize,
+ singleStream, table->CTable, bmi2);
}
@@ -639,52 +719,70 @@ size_t HUF_compress1X_wksp (void* dst, size_t dstSize,
unsigned maxSymbolValue, unsigned huffLog,
void* workSpace, size_t wkspSize)
{
- return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, NULL, NULL, 0);
+ return HUF_compress_internal(dst, dstSize, src, srcSize,
+ maxSymbolValue, huffLog, 1 /*single stream*/,
+ workSpace, wkspSize,
+ NULL, NULL, 0, 0 /*bmi2*/);
}
size_t HUF_compress1X_repeat (void* dst, size_t dstSize,
const void* src, size_t srcSize,
unsigned maxSymbolValue, unsigned huffLog,
void* workSpace, size_t wkspSize,
- HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat)
+ HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2)
{
- return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, hufTable, repeat, preferRepeat);
+ return HUF_compress_internal(dst, dstSize, src, srcSize,
+ maxSymbolValue, huffLog, 1 /*single stream*/,
+ workSpace, wkspSize, hufTable,
+ repeat, preferRepeat, bmi2);
}
size_t HUF_compress1X (void* dst, size_t dstSize,
const void* src, size_t srcSize,
unsigned maxSymbolValue, unsigned huffLog)
{
- unsigned workSpace[1024];
+ unsigned workSpace[HUF_WORKSPACE_SIZE_U32];
return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace));
}
+/* HUF_compress4X_repeat():
+ * compress input using 4 streams.
+ * provide workspace to generate compression tables */
size_t HUF_compress4X_wksp (void* dst, size_t dstSize,
const void* src, size_t srcSize,
unsigned maxSymbolValue, unsigned huffLog,
void* workSpace, size_t wkspSize)
{
- return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, NULL, NULL, 0);
+ return HUF_compress_internal(dst, dstSize, src, srcSize,
+ maxSymbolValue, huffLog, 0 /*4 streams*/,
+ workSpace, wkspSize,
+ NULL, NULL, 0, 0 /*bmi2*/);
}
+/* HUF_compress4X_repeat():
+ * compress input using 4 streams.
+ * re-use an existing huffman compression table */
size_t HUF_compress4X_repeat (void* dst, size_t dstSize,
const void* src, size_t srcSize,
unsigned maxSymbolValue, unsigned huffLog,
void* workSpace, size_t wkspSize,
- HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat)
+ HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2)
{
- return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, hufTable, repeat, preferRepeat);
+ return HUF_compress_internal(dst, dstSize, src, srcSize,
+ maxSymbolValue, huffLog, 0 /* 4 streams */,
+ workSpace, wkspSize,
+ hufTable, repeat, preferRepeat, bmi2);
}
size_t HUF_compress2 (void* dst, size_t dstSize,
const void* src, size_t srcSize,
unsigned maxSymbolValue, unsigned huffLog)
{
- unsigned workSpace[1024];
+ unsigned workSpace[HUF_WORKSPACE_SIZE_U32];
return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace));
}
size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize)
{
- return HUF_compress2(dst, maxDstSize, src, (U32)srcSize, 255, HUF_TABLELOG_DEFAULT);
+ return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT);
}
diff --git a/thirdparty/zstd/compress/zstd_compress.c b/thirdparty/zstd/compress/zstd_compress.c
index 8d1629246d..2aa26da4cd 100644
--- a/thirdparty/zstd/compress/zstd_compress.c
+++ b/thirdparty/zstd/compress/zstd_compress.c
@@ -21,6 +21,7 @@
* Dependencies
***************************************/
#include <string.h> /* memset */
+#include "cpu.h"
#include "mem.h"
#define FSE_STATIC_LINKING_ONLY /* FSE_encodeSymbol */
#include "fse.h"
@@ -49,7 +50,13 @@ struct ZSTD_CDict_s {
void* dictBuffer;
const void* dictContent;
size_t dictContentSize;
- ZSTD_CCtx* refContext;
+ void* workspace;
+ size_t workspaceSize;
+ ZSTD_matchState_t matchState;
+ ZSTD_compressedBlockState_t cBlockState;
+ ZSTD_compressionParameters cParams;
+ ZSTD_customMem customMem;
+ U32 dictID;
}; /* typedef'd to ZSTD_CDict within "zstd.h" */
ZSTD_CCtx* ZSTD_createCCtx(void)
@@ -59,18 +66,17 @@ ZSTD_CCtx* ZSTD_createCCtx(void)
ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem)
{
- ZSTD_CCtx* cctx;
-
- if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
-
- cctx = (ZSTD_CCtx*) ZSTD_calloc(sizeof(ZSTD_CCtx), customMem);
- if (!cctx) return NULL;
- cctx->customMem = customMem;
- cctx->requestedParams.compressionLevel = ZSTD_CLEVEL_DEFAULT;
- cctx->requestedParams.fParams.contentSizeFlag = 1;
ZSTD_STATIC_ASSERT(zcss_init==0);
ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN==(0ULL - 1));
- return cctx;
+ if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
+ { ZSTD_CCtx* const cctx = (ZSTD_CCtx*)ZSTD_calloc(sizeof(ZSTD_CCtx), customMem);
+ if (!cctx) return NULL;
+ cctx->customMem = customMem;
+ cctx->requestedParams.compressionLevel = ZSTD_CLEVEL_DEFAULT;
+ cctx->requestedParams.fParams.contentSizeFlag = 1;
+ cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());
+ return cctx;
+ }
}
ZSTD_CCtx* ZSTD_initStaticCCtx(void *workspace, size_t workspaceSize)
@@ -83,11 +89,16 @@ ZSTD_CCtx* ZSTD_initStaticCCtx(void *workspace, size_t workspaceSize)
cctx->workSpace = (void*)(cctx+1);
cctx->workSpaceSize = workspaceSize - sizeof(ZSTD_CCtx);
- /* entropy space (never moves) */
- if (cctx->workSpaceSize < sizeof(ZSTD_entropyCTables_t)) return NULL;
+ /* statically sized space. entropyWorkspace never moves (but prev/next block swap places) */
+ if (cctx->workSpaceSize < HUF_WORKSPACE_SIZE + 2 * sizeof(ZSTD_compressedBlockState_t)) return NULL;
assert(((size_t)cctx->workSpace & (sizeof(void*)-1)) == 0); /* ensure correct alignment */
- cctx->entropy = (ZSTD_entropyCTables_t*)cctx->workSpace;
-
+ cctx->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)cctx->workSpace;
+ cctx->blockState.nextCBlock = cctx->blockState.prevCBlock + 1;
+ {
+ void* const ptr = cctx->blockState.nextCBlock + 1;
+ cctx->entropyWorkspace = (U32*)ptr;
+ }
+ cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());
return cctx;
}
@@ -95,13 +106,10 @@ size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx)
{
if (cctx==NULL) return 0; /* support free on NULL */
if (cctx->staticSize) return ERROR(memory_allocation); /* not compatible with static CCtx */
- ZSTD_free(cctx->workSpace, cctx->customMem);
- cctx->workSpace = NULL;
- ZSTD_freeCDict(cctx->cdictLocal);
- cctx->cdictLocal = NULL;
+ ZSTD_free(cctx->workSpace, cctx->customMem); cctx->workSpace = NULL;
+ ZSTD_freeCDict(cctx->cdictLocal); cctx->cdictLocal = NULL;
#ifdef ZSTD_MULTITHREAD
- ZSTDMT_freeCCtx(cctx->mtctx);
- cctx->mtctx = NULL;
+ ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = NULL;
#endif
ZSTD_free(cctx, cctx->customMem);
return 0; /* reserved as a potential error code in the future */
@@ -122,10 +130,6 @@ static size_t ZSTD_sizeof_mtctx(const ZSTD_CCtx* cctx)
size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx)
{
if (cctx==NULL) return 0; /* support sizeof on NULL */
- DEBUGLOG(3, "sizeof(*cctx) : %u", (U32)sizeof(*cctx));
- DEBUGLOG(3, "workSpaceSize (including streaming buffers): %u", (U32)cctx->workSpaceSize);
- DEBUGLOG(3, "inner cdict : %u", (U32)ZSTD_sizeof_CDict(cctx->cdictLocal));
- DEBUGLOG(3, "inner MTCTX : %u", (U32)ZSTD_sizeof_mtctx(cctx));
return sizeof(*cctx) + cctx->workSpaceSize
+ ZSTD_sizeof_CDict(cctx->cdictLocal)
+ ZSTD_sizeof_mtctx(cctx);
@@ -139,37 +143,19 @@ size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs)
/* private API call, for dictBuilder only */
const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStore); }
-#define ZSTD_CLEVEL_CUSTOM 999
-
-static ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(
- ZSTD_CCtx_params CCtxParams, U64 srcSizeHint, size_t dictSize)
-{
- DEBUGLOG(4, "ZSTD_getCParamsFromCCtxParams: srcSize = %u, dictSize = %u",
- (U32)srcSizeHint, (U32)dictSize);
- return (CCtxParams.compressionLevel == ZSTD_CLEVEL_CUSTOM) ?
- CCtxParams.cParams :
- ZSTD_getCParams(CCtxParams.compressionLevel, srcSizeHint, dictSize);
-}
-
-static void ZSTD_cLevelToCCtxParams_srcSize(ZSTD_CCtx_params* CCtxParams, U64 srcSize)
-{
- DEBUGLOG(4, "ZSTD_cLevelToCCtxParams_srcSize: srcSize = %u",
- (U32)srcSize);
- CCtxParams->cParams = ZSTD_getCParamsFromCCtxParams(*CCtxParams, srcSize, 0);
- CCtxParams->compressionLevel = ZSTD_CLEVEL_CUSTOM;
-}
-
-static void ZSTD_cLevelToCParams(ZSTD_CCtx* cctx)
-{
- DEBUGLOG(4, "ZSTD_cLevelToCParams: level=%i", cctx->requestedParams.compressionLevel);
- ZSTD_cLevelToCCtxParams_srcSize(
- &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1);
-}
-
-static void ZSTD_cLevelToCCtxParams(ZSTD_CCtx_params* CCtxParams)
-{
- DEBUGLOG(4, "ZSTD_cLevelToCCtxParams");
- ZSTD_cLevelToCCtxParams_srcSize(CCtxParams, ZSTD_CONTENTSIZE_UNKNOWN);
+ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(
+ const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize)
+{
+ ZSTD_compressionParameters cParams = ZSTD_getCParams(CCtxParams->compressionLevel, srcSizeHint, dictSize);
+ if (CCtxParams->ldmParams.enableLdm) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG;
+ if (CCtxParams->cParams.windowLog) cParams.windowLog = CCtxParams->cParams.windowLog;
+ if (CCtxParams->cParams.hashLog) cParams.hashLog = CCtxParams->cParams.hashLog;
+ if (CCtxParams->cParams.chainLog) cParams.chainLog = CCtxParams->cParams.chainLog;
+ if (CCtxParams->cParams.searchLog) cParams.searchLog = CCtxParams->cParams.searchLog;
+ if (CCtxParams->cParams.searchLength) cParams.searchLength = CCtxParams->cParams.searchLength;
+ if (CCtxParams->cParams.targetLength) cParams.targetLength = CCtxParams->cParams.targetLength;
+ if (CCtxParams->cParams.strategy) cParams.strategy = CCtxParams->cParams.strategy;
+ return cParams;
}
static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams(
@@ -178,7 +164,9 @@ static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams(
ZSTD_CCtx_params cctxParams;
memset(&cctxParams, 0, sizeof(cctxParams));
cctxParams.cParams = cParams;
- cctxParams.compressionLevel = ZSTD_CLEVEL_CUSTOM;
+ cctxParams.compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */
+ assert(!ZSTD_checkCParams(cParams));
+ cctxParams.fParams.contentSizeFlag = 1;
return cctxParams;
}
@@ -192,6 +180,7 @@ static ZSTD_CCtx_params* ZSTD_createCCtxParams_advanced(
if (!params) { return NULL; }
params->customMem = customMem;
params->compressionLevel = ZSTD_CLEVEL_DEFAULT;
+ params->fParams.contentSizeFlag = 1;
return params;
}
@@ -207,36 +196,41 @@ size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params)
return 0;
}
-size_t ZSTD_resetCCtxParams(ZSTD_CCtx_params* params)
+size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params)
{
- return ZSTD_initCCtxParams(params, ZSTD_CLEVEL_DEFAULT);
+ return ZSTD_CCtxParams_init(params, ZSTD_CLEVEL_DEFAULT);
}
-size_t ZSTD_initCCtxParams(ZSTD_CCtx_params* cctxParams, int compressionLevel) {
+size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) {
if (!cctxParams) { return ERROR(GENERIC); }
memset(cctxParams, 0, sizeof(*cctxParams));
cctxParams->compressionLevel = compressionLevel;
+ cctxParams->fParams.contentSizeFlag = 1;
return 0;
}
-size_t ZSTD_initCCtxParams_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params)
+size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params)
{
if (!cctxParams) { return ERROR(GENERIC); }
CHECK_F( ZSTD_checkCParams(params.cParams) );
memset(cctxParams, 0, sizeof(*cctxParams));
cctxParams->cParams = params.cParams;
cctxParams->fParams = params.fParams;
- cctxParams->compressionLevel = ZSTD_CLEVEL_CUSTOM;
+ cctxParams->compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */
+ assert(!ZSTD_checkCParams(params.cParams));
return 0;
}
+/* ZSTD_assignParamsToCCtxParams() :
+ * params is presumed valid at this stage */
static ZSTD_CCtx_params ZSTD_assignParamsToCCtxParams(
ZSTD_CCtx_params cctxParams, ZSTD_parameters params)
{
ZSTD_CCtx_params ret = cctxParams;
ret.cParams = params.cParams;
ret.fParams = params.fParams;
- ret.compressionLevel = ZSTD_CLEVEL_CUSTOM;
+ ret.compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */
+ assert(!ZSTD_checkCParams(params.cParams));
return ret;
}
@@ -245,10 +239,49 @@ static ZSTD_CCtx_params ZSTD_assignParamsToCCtxParams(
return ERROR(parameter_outOfBound); \
} }
+
+static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param)
+{
+ switch(param)
+ {
+ case ZSTD_p_compressionLevel:
+ case ZSTD_p_hashLog:
+ case ZSTD_p_chainLog:
+ case ZSTD_p_searchLog:
+ case ZSTD_p_minMatch:
+ case ZSTD_p_targetLength:
+ case ZSTD_p_compressionStrategy:
+ case ZSTD_p_compressLiterals:
+ return 1;
+
+ case ZSTD_p_format:
+ case ZSTD_p_windowLog:
+ case ZSTD_p_contentSizeFlag:
+ case ZSTD_p_checksumFlag:
+ case ZSTD_p_dictIDFlag:
+ case ZSTD_p_forceMaxWindow :
+ case ZSTD_p_nbWorkers:
+ case ZSTD_p_jobSize:
+ case ZSTD_p_overlapSizeLog:
+ case ZSTD_p_enableLongDistanceMatching:
+ case ZSTD_p_ldmHashLog:
+ case ZSTD_p_ldmMinMatch:
+ case ZSTD_p_ldmBucketSizeLog:
+ case ZSTD_p_ldmHashEveryLog:
+ default:
+ return 0;
+ }
+}
+
size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned value)
{
DEBUGLOG(4, "ZSTD_CCtx_setParameter (%u, %u)", (U32)param, value);
- if (cctx->streamStage != zcss_init) return ERROR(stage_wrong);
+ if (cctx->streamStage != zcss_init) {
+ if (ZSTD_isUpdateAuthorized(param)) {
+ cctx->cParamsChanged = 1;
+ } else {
+ return ERROR(stage_wrong);
+ } }
switch(param)
{
@@ -267,9 +300,9 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v
case ZSTD_p_targetLength:
case ZSTD_p_compressionStrategy:
if (cctx->cdict) return ERROR(stage_wrong);
- if (value>0) ZSTD_cLevelToCParams(cctx); /* Can optimize if srcSize is known */
return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
+ case ZSTD_p_compressLiterals:
case ZSTD_p_contentSizeFlag:
case ZSTD_p_checksumFlag:
case ZSTD_p_dictIDFlag:
@@ -280,23 +313,17 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v
* default : 0 when using a CDict, 1 when using a Prefix */
return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
- case ZSTD_p_nbThreads:
- if ((value > 1) && cctx->staticSize) {
+ case ZSTD_p_nbWorkers:
+ if ((value>0) && cctx->staticSize) {
return ERROR(parameter_unsupported); /* MT not compatible with static alloc */
}
return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
case ZSTD_p_jobSize:
- return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
-
case ZSTD_p_overlapSizeLog:
return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
case ZSTD_p_enableLongDistanceMatching:
- if (cctx->cdict) return ERROR(stage_wrong);
- if (value>0) ZSTD_cLevelToCParams(cctx);
- return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
-
case ZSTD_p_ldmHashLog:
case ZSTD_p_ldmMinMatch:
case ZSTD_p_ldmBucketSizeLog:
@@ -320,69 +347,62 @@ size_t ZSTD_CCtxParam_setParameter(
CCtxParams->format = (ZSTD_format_e)value;
return (size_t)CCtxParams->format;
- case ZSTD_p_compressionLevel :
- if ((int)value > ZSTD_maxCLevel()) value = ZSTD_maxCLevel();
- if (value) /* 0 : does not change current level */
- CCtxParams->compressionLevel = value;
- return CCtxParams->compressionLevel;
+ case ZSTD_p_compressionLevel : {
+ int cLevel = (int)value; /* cast expected to restore negative sign */
+ if (cLevel > ZSTD_maxCLevel()) cLevel = ZSTD_maxCLevel();
+ if (cLevel) { /* 0 : does not change current level */
+ CCtxParams->disableLiteralCompression = (cLevel<0); /* negative levels disable huffman */
+ CCtxParams->compressionLevel = cLevel;
+ }
+ if (CCtxParams->compressionLevel >= 0) return CCtxParams->compressionLevel;
+ return 0; /* return type (size_t) cannot represent negative values */
+ }
case ZSTD_p_windowLog :
- DEBUGLOG(4, "ZSTD_CCtxParam_setParameter: set windowLog=%u", value);
- if (value) { /* 0 : does not change current windowLog */
+ if (value>0) /* 0 => use default */
CLAMPCHECK(value, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX);
- ZSTD_cLevelToCCtxParams(CCtxParams);
- CCtxParams->cParams.windowLog = value;
- }
+ CCtxParams->cParams.windowLog = value;
return CCtxParams->cParams.windowLog;
case ZSTD_p_hashLog :
- if (value) { /* 0 : does not change current hashLog */
+ if (value>0) /* 0 => use default */
CLAMPCHECK(value, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX);
- ZSTD_cLevelToCCtxParams(CCtxParams);
- CCtxParams->cParams.hashLog = value;
- }
+ CCtxParams->cParams.hashLog = value;
return CCtxParams->cParams.hashLog;
case ZSTD_p_chainLog :
- if (value) { /* 0 : does not change current chainLog */
+ if (value>0) /* 0 => use default */
CLAMPCHECK(value, ZSTD_CHAINLOG_MIN, ZSTD_CHAINLOG_MAX);
- ZSTD_cLevelToCCtxParams(CCtxParams);
- CCtxParams->cParams.chainLog = value;
- }
+ CCtxParams->cParams.chainLog = value;
return CCtxParams->cParams.chainLog;
case ZSTD_p_searchLog :
- if (value) { /* 0 : does not change current searchLog */
+ if (value>0) /* 0 => use default */
CLAMPCHECK(value, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX);
- ZSTD_cLevelToCCtxParams(CCtxParams);
- CCtxParams->cParams.searchLog = value;
- }
+ CCtxParams->cParams.searchLog = value;
return value;
case ZSTD_p_minMatch :
- if (value) { /* 0 : does not change current minMatch length */
+ if (value>0) /* 0 => use default */
CLAMPCHECK(value, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX);
- ZSTD_cLevelToCCtxParams(CCtxParams);
- CCtxParams->cParams.searchLength = value;
- }
+ CCtxParams->cParams.searchLength = value;
return CCtxParams->cParams.searchLength;
case ZSTD_p_targetLength :
- if (value) { /* 0 : does not change current sufficient_len */
- CLAMPCHECK(value, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX);
- ZSTD_cLevelToCCtxParams(CCtxParams);
- CCtxParams->cParams.targetLength = value;
- }
+ /* all values are valid. 0 => use default */
+ CCtxParams->cParams.targetLength = value;
return CCtxParams->cParams.targetLength;
case ZSTD_p_compressionStrategy :
- if (value) { /* 0 : does not change currentstrategy */
+ if (value>0) /* 0 => use default */
CLAMPCHECK(value, (unsigned)ZSTD_fast, (unsigned)ZSTD_btultra);
- ZSTD_cLevelToCCtxParams(CCtxParams);
- CCtxParams->cParams.strategy = (ZSTD_strategy)value;
- }
+ CCtxParams->cParams.strategy = (ZSTD_strategy)value;
return (size_t)CCtxParams->cParams.strategy;
+ case ZSTD_p_compressLiterals:
+ CCtxParams->disableLiteralCompression = !value;
+ return !CCtxParams->disableLiteralCompression;
+
case ZSTD_p_contentSizeFlag :
/* Content size written in frame header _when known_ (default:1) */
DEBUGLOG(4, "set content size flag = %u", (value>0));
@@ -396,27 +416,25 @@ size_t ZSTD_CCtxParam_setParameter(
case ZSTD_p_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */
DEBUGLOG(4, "set dictIDFlag = %u", (value>0));
- CCtxParams->fParams.noDictIDFlag = (value == 0);
+ CCtxParams->fParams.noDictIDFlag = !value;
return !CCtxParams->fParams.noDictIDFlag;
case ZSTD_p_forceMaxWindow :
CCtxParams->forceWindow = (value > 0);
return CCtxParams->forceWindow;
- case ZSTD_p_nbThreads :
- if (value == 0) return CCtxParams->nbThreads;
+ case ZSTD_p_nbWorkers :
#ifndef ZSTD_MULTITHREAD
- if (value > 1) return ERROR(parameter_unsupported);
- return 1;
+ if (value>0) return ERROR(parameter_unsupported);
+ return 0;
#else
- return ZSTDMT_CCtxParam_setNbThreads(CCtxParams, value);
+ return ZSTDMT_CCtxParam_setNbWorkers(CCtxParams, value);
#endif
case ZSTD_p_jobSize :
#ifndef ZSTD_MULTITHREAD
return ERROR(parameter_unsupported);
#else
- if (CCtxParams->nbThreads <= 1) return ERROR(parameter_unsupported);
return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_jobSize, value);
#endif
@@ -424,44 +442,36 @@ size_t ZSTD_CCtxParam_setParameter(
#ifndef ZSTD_MULTITHREAD
return ERROR(parameter_unsupported);
#else
- if (CCtxParams->nbThreads <= 1) return ERROR(parameter_unsupported);
return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_overlapSectionLog, value);
#endif
case ZSTD_p_enableLongDistanceMatching :
- if (value) {
- ZSTD_cLevelToCCtxParams(CCtxParams);
- CCtxParams->cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG;
- }
- return ZSTD_ldm_initializeParameters(&CCtxParams->ldmParams, value);
+ CCtxParams->ldmParams.enableLdm = (value>0);
+ return CCtxParams->ldmParams.enableLdm;
case ZSTD_p_ldmHashLog :
- if (value) { /* 0 : does not change current ldmHashLog */
+ if (value>0) /* 0 ==> auto */
CLAMPCHECK(value, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX);
- CCtxParams->ldmParams.hashLog = value;
- }
+ CCtxParams->ldmParams.hashLog = value;
return CCtxParams->ldmParams.hashLog;
case ZSTD_p_ldmMinMatch :
- if (value) { /* 0 : does not change current ldmMinMatch */
+ if (value>0) /* 0 ==> default */
CLAMPCHECK(value, ZSTD_LDM_MINMATCH_MIN, ZSTD_LDM_MINMATCH_MAX);
- CCtxParams->ldmParams.minMatchLength = value;
- }
+ CCtxParams->ldmParams.minMatchLength = value;
return CCtxParams->ldmParams.minMatchLength;
case ZSTD_p_ldmBucketSizeLog :
- if (value > ZSTD_LDM_BUCKETSIZELOG_MAX) {
+ if (value > ZSTD_LDM_BUCKETSIZELOG_MAX)
return ERROR(parameter_outOfBound);
- }
CCtxParams->ldmParams.bucketSizeLog = value;
- return value;
+ return CCtxParams->ldmParams.bucketSizeLog;
case ZSTD_p_ldmHashEveryLog :
- if (value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN) {
+ if (value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN)
return ERROR(parameter_outOfBound);
- }
CCtxParams->ldmParams.hashEveryLog = value;
- return value;
+ return CCtxParams->ldmParams.hashEveryLog;
default: return ERROR(parameter_unsupported);
}
@@ -470,6 +480,9 @@ size_t ZSTD_CCtxParam_setParameter(
/** ZSTD_CCtx_setParametersUsingCCtxParams() :
* just applies `params` into `cctx`
* no action is performed, parameters are merely stored.
+ * If ZSTDMT is enabled, parameters are pushed to cctx->mtctx.
+ * This is possible even if a compression is ongoing.
+ * In which case, new parameters will be applied on the fly, starting with next compression job.
*/
size_t ZSTD_CCtx_setParametersUsingCCtxParams(
ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params)
@@ -478,7 +491,6 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams(
if (cctx->cdict) return ERROR(stage_wrong);
cctx->requestedParams = *params;
-
return 0;
}
@@ -492,7 +504,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long lo
size_t ZSTD_CCtx_loadDictionary_advanced(
ZSTD_CCtx* cctx, const void* dict, size_t dictSize,
- ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode)
+ ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType)
{
if (cctx->streamStage != zcss_init) return ERROR(stage_wrong);
if (cctx->staticSize) return ERROR(memory_allocation); /* no malloc for static CCtx */
@@ -503,10 +515,10 @@ size_t ZSTD_CCtx_loadDictionary_advanced(
cctx->cdict = NULL;
} else {
ZSTD_compressionParameters const cParams =
- ZSTD_getCParamsFromCCtxParams(cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, dictSize);
+ ZSTD_getCParamsFromCCtxParams(&cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, dictSize);
cctx->cdictLocal = ZSTD_createCDict_advanced(
dict, dictSize,
- dictLoadMethod, dictMode,
+ dictLoadMethod, dictContentType,
cParams, cctx->customMem);
cctx->cdict = cctx->cdictLocal;
if (cctx->cdictLocal == NULL)
@@ -519,13 +531,13 @@ ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(
ZSTD_CCtx* cctx, const void* dict, size_t dictSize)
{
return ZSTD_CCtx_loadDictionary_advanced(
- cctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dm_auto);
+ cctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto);
}
ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize)
{
return ZSTD_CCtx_loadDictionary_advanced(
- cctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dm_auto);
+ cctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto);
}
@@ -539,17 +551,17 @@ size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict)
size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize)
{
- return ZSTD_CCtx_refPrefix_advanced(cctx, prefix, prefixSize, ZSTD_dm_rawContent);
+ return ZSTD_CCtx_refPrefix_advanced(cctx, prefix, prefixSize, ZSTD_dct_rawContent);
}
size_t ZSTD_CCtx_refPrefix_advanced(
- ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictMode_e dictMode)
+ ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType)
{
if (cctx->streamStage != zcss_init) return ERROR(stage_wrong);
cctx->cdict = NULL; /* prefix discards any prior cdict */
cctx->prefixDict.dict = prefix;
cctx->prefixDict.dictSize = prefixSize;
- cctx->prefixDict.dictMode = dictMode;
+ cctx->prefixDict.dictContentType = dictContentType;
return 0;
}
@@ -577,7 +589,8 @@ size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams)
CLAMPCHECK(cParams.hashLog, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX);
CLAMPCHECK(cParams.searchLog, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX);
CLAMPCHECK(cParams.searchLength, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX);
- CLAMPCHECK(cParams.targetLength, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX);
+ if ((U32)(cParams.targetLength) < ZSTD_TARGETLENGTH_MIN)
+ return ERROR(parameter_unsupported);
if ((U32)(cParams.strategy) > (U32)ZSTD_btultra)
return ERROR(parameter_unsupported);
return 0;
@@ -597,7 +610,7 @@ static ZSTD_compressionParameters ZSTD_clampCParams(ZSTD_compressionParameters c
CLAMP(cParams.hashLog, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX);
CLAMP(cParams.searchLog, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX);
CLAMP(cParams.searchLength, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX);
- CLAMP(cParams.targetLength, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX);
+ if ((U32)(cParams.targetLength) < ZSTD_TARGETLENGTH_MIN) cParams.targetLength = ZSTD_TARGETLENGTH_MIN;
if ((U32)(cParams.strategy) > (U32)ZSTD_btultra) cParams.strategy = ZSTD_btultra;
return cParams;
}
@@ -653,36 +666,43 @@ ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, u
return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize);
}
+static size_t ZSTD_sizeof_matchState(ZSTD_compressionParameters const* cParams, const U32 forCCtx)
+{
+ size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);
+ size_t const hSize = ((size_t)1) << cParams->hashLog;
+ U32 const hashLog3 = (forCCtx && cParams->searchLength==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;
+ size_t const h3Size = ((size_t)1) << hashLog3;
+ size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
+ size_t const optPotentialSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<<Litbits)) * sizeof(U32)
+ + (ZSTD_OPT_NUM+1) * (sizeof(ZSTD_match_t)+sizeof(ZSTD_optimal_t));
+ size_t const optSpace = (forCCtx && ((cParams->strategy == ZSTD_btopt) ||
+ (cParams->strategy == ZSTD_btultra)))
+ ? optPotentialSpace
+ : 0;
+ DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u",
+ (U32)chainSize, (U32)hSize, (U32)h3Size);
+ return tableSpace + optSpace;
+}
+
size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params)
{
/* Estimate CCtx size is supported for single-threaded compression only. */
- if (params->nbThreads > 1) { return ERROR(GENERIC); }
+ if (params->nbWorkers > 0) { return ERROR(GENERIC); }
{ ZSTD_compressionParameters const cParams =
- ZSTD_getCParamsFromCCtxParams(*params, 0, 0);
+ ZSTD_getCParamsFromCCtxParams(params, 0, 0);
size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog);
U32 const divider = (cParams.searchLength==3) ? 3 : 4;
size_t const maxNbSeq = blockSize / divider;
size_t const tokenSpace = blockSize + 11*maxNbSeq;
- size_t const chainSize =
- (cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams.chainLog);
- size_t const hSize = ((size_t)1) << cParams.hashLog;
- U32 const hashLog3 = (cParams.searchLength>3) ?
- 0 : MIN(ZSTD_HASHLOG3_MAX, cParams.windowLog);
- size_t const h3Size = ((size_t)1) << hashLog3;
- size_t const entropySpace = sizeof(ZSTD_entropyCTables_t);
- size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
+ size_t const entropySpace = HUF_WORKSPACE_SIZE;
+ size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t);
+ size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 1);
- size_t const optBudget =
- ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<<Litbits))*sizeof(U32)
- + (ZSTD_OPT_NUM+1)*(sizeof(ZSTD_match_t) + sizeof(ZSTD_optimal_t));
- size_t const optSpace = ((cParams.strategy == ZSTD_btopt) || (cParams.strategy == ZSTD_btultra)) ? optBudget : 0;
+ size_t const ldmSpace = ZSTD_ldm_getTableSize(params->ldmParams);
+ size_t const ldmSeqSpace = ZSTD_ldm_getMaxNbSeq(params->ldmParams, blockSize) * sizeof(rawSeq);
- size_t const ldmSpace = params->ldmParams.enableLdm ?
- ZSTD_ldm_getTableSize(params->ldmParams.hashLog,
- params->ldmParams.bucketSizeLog) : 0;
-
- size_t const neededSpace = entropySpace + tableSpace + tokenSpace +
- optSpace + ldmSpace;
+ size_t const neededSpace = entropySpace + blockStateSpace + tokenSpace +
+ matchStateSize + ldmSpace + ldmSeqSpace;
DEBUGLOG(5, "sizeof(ZSTD_CCtx) : %u", (U32)sizeof(ZSTD_CCtx));
DEBUGLOG(5, "estimate workSpace : %u", (U32)neededSpace);
@@ -696,15 +716,26 @@ size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams)
return ZSTD_estimateCCtxSize_usingCCtxParams(&params);
}
-size_t ZSTD_estimateCCtxSize(int compressionLevel)
+static size_t ZSTD_estimateCCtxSize_internal(int compressionLevel)
{
ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0);
return ZSTD_estimateCCtxSize_usingCParams(cParams);
}
+size_t ZSTD_estimateCCtxSize(int compressionLevel)
+{
+ int level;
+ size_t memBudget = 0;
+ for (level=1; level<=compressionLevel; level++) {
+ size_t const newMB = ZSTD_estimateCCtxSize_internal(level);
+ if (newMB > memBudget) memBudget = newMB;
+ }
+ return memBudget;
+}
+
size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params)
{
- if (params->nbThreads > 1) { return ERROR(GENERIC); }
+ if (params->nbWorkers > 0) { return ERROR(GENERIC); }
{ size_t const CCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(params);
size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << params->cParams.windowLog);
size_t const inBuffSize = ((size_t)1 << params->cParams.windowLog) + blockSize;
@@ -721,11 +752,44 @@ size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams)
return ZSTD_estimateCStreamSize_usingCCtxParams(&params);
}
-size_t ZSTD_estimateCStreamSize(int compressionLevel) {
+static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel) {
ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0);
return ZSTD_estimateCStreamSize_usingCParams(cParams);
}
+size_t ZSTD_estimateCStreamSize(int compressionLevel) {
+ int level;
+ size_t memBudget = 0;
+ for (level=1; level<=compressionLevel; level++) {
+ size_t const newMB = ZSTD_estimateCStreamSize_internal(level);
+ if (newMB > memBudget) memBudget = newMB;
+ }
+ return memBudget;
+}
+
+/* ZSTD_getFrameProgression():
+ * tells how much data has been consumed (input) and produced (output) for current frame.
+ * able to count progression inside worker threads (non-blocking mode).
+ */
+ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx)
+{
+#ifdef ZSTD_MULTITHREAD
+ if (cctx->appliedParams.nbWorkers > 0) {
+ return ZSTDMT_getFrameProgression(cctx->mtctx);
+ }
+#endif
+ { ZSTD_frameProgression fp;
+ size_t const buffered = (cctx->inBuff == NULL) ? 0 :
+ cctx->inBuffPos - cctx->inToCompress;
+ if (buffered) assert(cctx->inBuffPos >= cctx->inToCompress);
+ assert(buffered <= ZSTD_BLOCKSIZE_MAX);
+ fp.ingested = cctx->consumedSrcSize + buffered;
+ fp.consumed = cctx->consumedSrcSize;
+ fp.produced = cctx->producedCSize;
+ return fp;
+} }
+
+
static U32 ZSTD_equivalentCParams(ZSTD_compressionParameters cParams1,
ZSTD_compressionParameters cParams2)
{
@@ -761,9 +825,9 @@ static U32 ZSTD_sufficientBuff(size_t bufferSize1, size_t blockSize1,
size_t const windowSize2 = MAX(1, (size_t)MIN(((U64)1 << cParams2.windowLog), pledgedSrcSize));
size_t const blockSize2 = MIN(ZSTD_BLOCKSIZE_MAX, windowSize2);
size_t const neededBufferSize2 = (buffPol2==ZSTDb_buffered) ? windowSize2 + blockSize2 : 0;
- DEBUGLOG(4, "ZSTD_sufficientBuff: windowSize2=%u from wlog=%u",
+ DEBUGLOG(4, "ZSTD_sufficientBuff: is windowSize2=%u <= wlog1=%u",
(U32)windowSize2, cParams2.windowLog);
- DEBUGLOG(4, "ZSTD_sufficientBuff: blockSize2 %u <=? blockSize1 %u",
+ DEBUGLOG(4, "ZSTD_sufficientBuff: is blockSize2=%u <= blockSize1=%u",
(U32)blockSize2, (U32)blockSize1);
return (blockSize2 <= blockSize1) /* seqStore space depends on blockSize */
& (neededBufferSize2 <= bufferSize1);
@@ -782,37 +846,101 @@ static U32 ZSTD_equivalentParams(ZSTD_CCtx_params params1,
ZSTD_sufficientBuff(buffSize1, blockSize1, buffPol2, params2.cParams, pledgedSrcSize);
}
+static void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs)
+{
+ int i;
+ for (i = 0; i < ZSTD_REP_NUM; ++i)
+ bs->rep[i] = repStartValue[i];
+ bs->entropy.hufCTable_repeatMode = HUF_repeat_none;
+ bs->entropy.offcode_repeatMode = FSE_repeat_none;
+ bs->entropy.matchlength_repeatMode = FSE_repeat_none;
+ bs->entropy.litlength_repeatMode = FSE_repeat_none;
+}
+
+/*! ZSTD_invalidateMatchState()
+ * Invalidate all the matches in the match finder tables.
+ * Requires nextSrc and base to be set (can be NULL).
+ */
+static void ZSTD_invalidateMatchState(ZSTD_matchState_t* ms)
+{
+ ZSTD_window_clear(&ms->window);
+
+ ms->nextToUpdate = ms->window.dictLimit + 1;
+ ms->loadedDictEnd = 0;
+ ms->opt.litLengthSum = 0; /* force reset of btopt stats */
+}
+
/*! ZSTD_continueCCtx() :
* reuse CCtx without reset (note : requires no dictionary) */
static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pledgedSrcSize)
{
- U32 const end = (U32)(cctx->nextSrc - cctx->base);
size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize));
size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize);
- DEBUGLOG(4, "ZSTD_continueCCtx");
+ DEBUGLOG(4, "ZSTD_continueCCtx: re-use context in place");
cctx->blockSize = blockSize; /* previous block size could be different even for same windowLog, due to pledgedSrcSize */
cctx->appliedParams = params;
cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1;
cctx->consumedSrcSize = 0;
+ cctx->producedCSize = 0;
if (pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN)
cctx->appliedParams.fParams.contentSizeFlag = 0;
DEBUGLOG(4, "pledged content size : %u ; flag : %u",
(U32)pledgedSrcSize, cctx->appliedParams.fParams.contentSizeFlag);
- cctx->lowLimit = end;
- cctx->dictLimit = end;
- cctx->nextToUpdate = end+1;
cctx->stage = ZSTDcs_init;
cctx->dictID = 0;
- cctx->loadedDictEnd = 0;
- { int i; for (i=0; i<ZSTD_REP_NUM; i++) cctx->seqStore.rep[i] = repStartValue[i]; }
- cctx->optState.litLengthSum = 0; /* force reset of btopt stats */
+ if (params.ldmParams.enableLdm)
+ ZSTD_window_clear(&cctx->ldmState.window);
+ ZSTD_referenceExternalSequences(cctx, NULL, 0);
+ ZSTD_invalidateMatchState(&cctx->blockState.matchState);
+ ZSTD_reset_compressedBlockState(cctx->blockState.prevCBlock);
XXH64_reset(&cctx->xxhState, 0);
return 0;
}
typedef enum { ZSTDcrp_continue, ZSTDcrp_noMemset } ZSTD_compResetPolicy_e;
+static void* ZSTD_reset_matchState(ZSTD_matchState_t* ms, void* ptr, ZSTD_compressionParameters const* cParams, ZSTD_compResetPolicy_e const crp, U32 const forCCtx)
+{
+ size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);
+ size_t const hSize = ((size_t)1) << cParams->hashLog;
+ U32 const hashLog3 = (forCCtx && cParams->searchLength==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;
+ size_t const h3Size = ((size_t)1) << hashLog3;
+ size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
+
+ assert(((size_t)ptr & 3) == 0);
+
+ ms->hashLog3 = hashLog3;
+ memset(&ms->window, 0, sizeof(ms->window));
+ ZSTD_invalidateMatchState(ms);
+
+ /* opt parser space */
+ if (forCCtx && ((cParams->strategy == ZSTD_btopt) | (cParams->strategy == ZSTD_btultra))) {
+ DEBUGLOG(4, "reserving optimal parser space");
+ ms->opt.litFreq = (U32*)ptr;
+ ms->opt.litLengthFreq = ms->opt.litFreq + (1<<Litbits);
+ ms->opt.matchLengthFreq = ms->opt.litLengthFreq + (MaxLL+1);
+ ms->opt.offCodeFreq = ms->opt.matchLengthFreq + (MaxML+1);
+ ptr = ms->opt.offCodeFreq + (MaxOff+1);
+ ms->opt.matchTable = (ZSTD_match_t*)ptr;
+ ptr = ms->opt.matchTable + ZSTD_OPT_NUM+1;
+ ms->opt.priceTable = (ZSTD_optimal_t*)ptr;
+ ptr = ms->opt.priceTable + ZSTD_OPT_NUM+1;
+ }
+
+ /* table Space */
+ DEBUGLOG(4, "reset table : %u", crp!=ZSTDcrp_noMemset);
+ assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */
+ if (crp!=ZSTDcrp_noMemset) memset(ptr, 0, tableSpace); /* reset tables only */
+ ms->hashTable = (U32*)(ptr);
+ ms->chainTable = ms->hashTable + hSize;
+ ms->hashTable3 = ms->chainTable + chainSize;
+ ptr = ms->hashTable3 + h3Size;
+
+ assert(((size_t)ptr & 3) == 0);
+ return ptr;
+}
+
/*! ZSTD_resetCCtx_internal() :
note : `params` are assumed fully validated at this stage */
static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
@@ -830,19 +958,14 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
zbuff, pledgedSrcSize)) {
DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog1=%u, blockSize1=%u)",
zc->appliedParams.cParams.windowLog, (U32)zc->blockSize);
- assert(!(params.ldmParams.enableLdm &&
- params.ldmParams.hashEveryLog == ZSTD_LDM_HASHEVERYLOG_NOTSET));
- zc->entropy->hufCTable_repeatMode = HUF_repeat_none;
- zc->entropy->offcode_repeatMode = FSE_repeat_none;
- zc->entropy->matchlength_repeatMode = FSE_repeat_none;
- zc->entropy->litlength_repeatMode = FSE_repeat_none;
return ZSTD_continueCCtx(zc, params, pledgedSrcSize);
} }
DEBUGLOG(4, "ZSTD_equivalentParams()==0 -> reset CCtx");
if (params.ldmParams.enableLdm) {
/* Adjust long distance matching parameters */
- ZSTD_ldm_adjustParameters(&params.ldmParams, params.cParams.windowLog);
+ params.ldmParams.windowLog = params.cParams.windowLog;
+ ZSTD_ldm_adjustParameters(&params.ldmParams, &params.cParams);
assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog);
assert(params.ldmParams.hashEveryLog < 32);
zc->ldmState.hashPower =
@@ -854,34 +977,25 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
U32 const divider = (params.cParams.searchLength==3) ? 3 : 4;
size_t const maxNbSeq = blockSize / divider;
size_t const tokenSpace = blockSize + 11*maxNbSeq;
- size_t const chainSize = (params.cParams.strategy == ZSTD_fast) ?
- 0 : ((size_t)1 << params.cParams.chainLog);
- size_t const hSize = ((size_t)1) << params.cParams.hashLog;
- U32 const hashLog3 = (params.cParams.searchLength>3) ?
- 0 : MIN(ZSTD_HASHLOG3_MAX, params.cParams.windowLog);
- size_t const h3Size = ((size_t)1) << hashLog3;
- size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
size_t const buffOutSize = (zbuff==ZSTDb_buffered) ? ZSTD_compressBound(blockSize)+1 : 0;
size_t const buffInSize = (zbuff==ZSTDb_buffered) ? windowSize + blockSize : 0;
+ size_t const matchStateSize = ZSTD_sizeof_matchState(&params.cParams, /* forCCtx */ 1);
+ size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(params.ldmParams, blockSize);
void* ptr;
/* Check if workSpace is large enough, alloc a new one if needed */
- { size_t const entropySpace = sizeof(ZSTD_entropyCTables_t);
- size_t const optPotentialSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<<Litbits)) * sizeof(U32)
- + (ZSTD_OPT_NUM+1) * (sizeof(ZSTD_match_t)+sizeof(ZSTD_optimal_t));
- size_t const optSpace = ( (params.cParams.strategy == ZSTD_btopt)
- || (params.cParams.strategy == ZSTD_btultra)) ?
- optPotentialSpace : 0;
+ { size_t const entropySpace = HUF_WORKSPACE_SIZE;
+ size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t);
size_t const bufferSpace = buffInSize + buffOutSize;
- size_t const ldmSpace = params.ldmParams.enableLdm
- ? ZSTD_ldm_getTableSize(params.ldmParams.hashLog, params.ldmParams.bucketSizeLog)
- : 0;
- size_t const neededSpace = entropySpace + optSpace + ldmSpace +
- tableSpace + tokenSpace + bufferSpace;
- DEBUGLOG(4, "Need %uKB workspace, including %uKB for tables, and %uKB for buffers",
- (U32)(neededSpace>>10), (U32)(tableSpace>>10), (U32)(bufferSpace>>10));
- DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u - windowSize: %u - blockSize: %u",
- (U32)chainSize, (U32)hSize, (U32)h3Size, (U32)windowSize, (U32)blockSize);
+ size_t const ldmSpace = ZSTD_ldm_getTableSize(params.ldmParams);
+ size_t const ldmSeqSpace = maxNbLdmSeq * sizeof(rawSeq);
+
+ size_t const neededSpace = entropySpace + blockStateSpace + ldmSpace +
+ ldmSeqSpace + matchStateSize + tokenSpace +
+ bufferSpace;
+ DEBUGLOG(4, "Need %uKB workspace, including %uKB for match state, and %uKB for buffers",
+ (U32)(neededSpace>>10), (U32)(matchStateSize>>10), (U32)(bufferSpace>>10));
+ DEBUGLOG(4, "windowSize: %u - blockSize: %u", (U32)windowSize, (U32)blockSize);
if (zc->workSpaceSize < neededSpace) { /* too small : resize */
DEBUGLOG(4, "Need to update workSpaceSize from %uK to %uK",
@@ -897,16 +1011,20 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
zc->workSpaceSize = neededSpace;
ptr = zc->workSpace;
- /* entropy space */
+ /* Statically sized space. entropyWorkspace never moves (but prev/next block swap places) */
assert(((size_t)zc->workSpace & 3) == 0); /* ensure correct alignment */
- assert(zc->workSpaceSize >= sizeof(ZSTD_entropyCTables_t));
- zc->entropy = (ZSTD_entropyCTables_t*)zc->workSpace;
+ assert(zc->workSpaceSize >= 2 * sizeof(ZSTD_compressedBlockState_t));
+ zc->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)zc->workSpace;
+ zc->blockState.nextCBlock = zc->blockState.prevCBlock + 1;
+ ptr = zc->blockState.nextCBlock + 1;
+ zc->entropyWorkspace = (U32*)ptr;
} }
/* init params */
zc->appliedParams = params;
zc->pledgedSrcSizePlusOne = pledgedSrcSize+1;
zc->consumedSrcSize = 0;
+ zc->producedCSize = 0;
if (pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN)
zc->appliedParams.fParams.contentSizeFlag = 0;
DEBUGLOG(4, "pledged content size : %u ; flag : %u",
@@ -916,37 +1034,10 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
XXH64_reset(&zc->xxhState, 0);
zc->stage = ZSTDcs_init;
zc->dictID = 0;
- zc->loadedDictEnd = 0;
- zc->entropy->hufCTable_repeatMode = HUF_repeat_none;
- zc->entropy->offcode_repeatMode = FSE_repeat_none;
- zc->entropy->matchlength_repeatMode = FSE_repeat_none;
- zc->entropy->litlength_repeatMode = FSE_repeat_none;
- zc->nextToUpdate = 1;
- zc->nextSrc = NULL;
- zc->base = NULL;
- zc->dictBase = NULL;
- zc->dictLimit = 0;
- zc->lowLimit = 0;
- { int i; for (i=0; i<ZSTD_REP_NUM; i++) zc->seqStore.rep[i] = repStartValue[i]; }
- zc->hashLog3 = hashLog3;
- zc->optState.litLengthSum = 0;
-
- ptr = zc->entropy + 1;
-
- /* opt parser space */
- if ((params.cParams.strategy == ZSTD_btopt) || (params.cParams.strategy == ZSTD_btultra)) {
- DEBUGLOG(4, "reserving optimal parser space");
- assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */
- zc->optState.litFreq = (U32*)ptr;
- zc->optState.litLengthFreq = zc->optState.litFreq + (1<<Litbits);
- zc->optState.matchLengthFreq = zc->optState.litLengthFreq + (MaxLL+1);
- zc->optState.offCodeFreq = zc->optState.matchLengthFreq + (MaxML+1);
- ptr = zc->optState.offCodeFreq + (MaxOff+1);
- zc->optState.matchTable = (ZSTD_match_t*)ptr;
- ptr = zc->optState.matchTable + ZSTD_OPT_NUM+1;
- zc->optState.priceTable = (ZSTD_optimal_t*)ptr;
- ptr = zc->optState.priceTable + ZSTD_OPT_NUM+1;
- }
+
+ ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock);
+
+ ptr = zc->entropyWorkspace + HUF_WORKSPACE_SIZE_U32;
/* ldm hash table */
/* initialize bucketOffsets table later for pointer alignment */
@@ -956,16 +1047,15 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */
zc->ldmState.hashTable = (ldmEntry_t*)ptr;
ptr = zc->ldmState.hashTable + ldmHSize;
+ zc->ldmSequences = (rawSeq*)ptr;
+ ptr = zc->ldmSequences + maxNbLdmSeq;
+ zc->maxNbLdmSequences = maxNbLdmSeq;
+
+ memset(&zc->ldmState.window, 0, sizeof(zc->ldmState.window));
}
+ assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */
- /* table Space */
- DEBUGLOG(4, "reset table : %u", crp!=ZSTDcrp_noMemset);
- if (crp!=ZSTDcrp_noMemset) memset(ptr, 0, tableSpace); /* reset tables only */
- assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */
- zc->hashTable = (U32*)(ptr);
- zc->chainTable = zc->hashTable + hSize;
- zc->hashTable3 = zc->chainTable + chainSize;
- ptr = zc->hashTable3 + h3Size;
+ ptr = ZSTD_reset_matchState(&zc->blockState.matchState, ptr, &params.cParams, crp, /* forCCtx */ 1);
/* sequences storage */
zc->seqStore.sequencesStart = (seqDef*)ptr;
@@ -984,7 +1074,9 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
memset(ptr, 0, ldmBucketSize);
zc->ldmState.bucketOffsets = (BYTE*)ptr;
ptr = zc->ldmState.bucketOffsets + ldmBucketSize;
+ ZSTD_window_clear(&zc->ldmState.window);
}
+ ZSTD_referenceExternalSequences(zc, NULL, 0);
/* buffers */
zc->inBuffSize = buffInSize;
@@ -1002,9 +1094,61 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
* do not use with extDict variant ! */
void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) {
int i;
- for (i=0; i<ZSTD_REP_NUM; i++) cctx->seqStore.rep[i] = 0;
+ for (i=0; i<ZSTD_REP_NUM; i++) cctx->blockState.prevCBlock->rep[i] = 0;
+ assert(!ZSTD_window_hasExtDict(cctx->blockState.matchState.window));
}
+static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx,
+ const ZSTD_CDict* cdict,
+ unsigned windowLog,
+ ZSTD_frameParameters fParams,
+ U64 pledgedSrcSize,
+ ZSTD_buffered_policy_e zbuff)
+{
+ { ZSTD_CCtx_params params = cctx->requestedParams;
+ /* Copy only compression parameters related to tables. */
+ params.cParams = cdict->cParams;
+ if (windowLog) params.cParams.windowLog = windowLog;
+ params.fParams = fParams;
+ ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,
+ ZSTDcrp_noMemset, zbuff);
+ assert(cctx->appliedParams.cParams.strategy == cdict->cParams.strategy);
+ assert(cctx->appliedParams.cParams.hashLog == cdict->cParams.hashLog);
+ assert(cctx->appliedParams.cParams.chainLog == cdict->cParams.chainLog);
+ }
+
+ /* copy tables */
+ { size_t const chainSize = (cdict->cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << cdict->cParams.chainLog);
+ size_t const hSize = (size_t)1 << cdict->cParams.hashLog;
+ size_t const tableSpace = (chainSize + hSize) * sizeof(U32);
+ assert((U32*)cctx->blockState.matchState.chainTable == (U32*)cctx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */
+ assert((U32*)cctx->blockState.matchState.hashTable3 == (U32*)cctx->blockState.matchState.chainTable + chainSize);
+ assert((U32*)cdict->matchState.chainTable == (U32*)cdict->matchState.hashTable + hSize); /* chainTable must follow hashTable */
+ assert((U32*)cdict->matchState.hashTable3 == (U32*)cdict->matchState.chainTable + chainSize);
+ memcpy(cctx->blockState.matchState.hashTable, cdict->matchState.hashTable, tableSpace); /* presumes all tables follow each other */
+ }
+ /* Zero the hashTable3, since the cdict never fills it */
+ { size_t const h3Size = (size_t)1 << cctx->blockState.matchState.hashLog3;
+ assert(cdict->matchState.hashLog3 == 0);
+ memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32));
+ }
+
+ /* copy dictionary offsets */
+ {
+ ZSTD_matchState_t const* srcMatchState = &cdict->matchState;
+ ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState;
+ dstMatchState->window = srcMatchState->window;
+ dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;
+ dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3;
+ dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;
+ }
+ cctx->dictID = cdict->dictID;
+
+ /* copy block state */
+ memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState));
+
+ return 0;
+}
/*! ZSTD_copyCCtx_internal() :
* Duplicate an existing context `srcCCtx` into another one `dstCCtx`.
@@ -1015,7 +1159,6 @@ void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) {
* @return : 0, or an error code */
static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx,
const ZSTD_CCtx* srcCCtx,
- unsigned windowLog,
ZSTD_frameParameters fParams,
U64 pledgedSrcSize,
ZSTD_buffered_policy_e zbuff)
@@ -1027,41 +1170,39 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx,
{ ZSTD_CCtx_params params = dstCCtx->requestedParams;
/* Copy only compression parameters related to tables. */
params.cParams = srcCCtx->appliedParams.cParams;
- if (windowLog) params.cParams.windowLog = windowLog;
params.fParams = fParams;
ZSTD_resetCCtx_internal(dstCCtx, params, pledgedSrcSize,
ZSTDcrp_noMemset, zbuff);
+ assert(dstCCtx->appliedParams.cParams.windowLog == srcCCtx->appliedParams.cParams.windowLog);
+ assert(dstCCtx->appliedParams.cParams.strategy == srcCCtx->appliedParams.cParams.strategy);
+ assert(dstCCtx->appliedParams.cParams.hashLog == srcCCtx->appliedParams.cParams.hashLog);
+ assert(dstCCtx->appliedParams.cParams.chainLog == srcCCtx->appliedParams.cParams.chainLog);
+ assert(dstCCtx->blockState.matchState.hashLog3 == srcCCtx->blockState.matchState.hashLog3);
}
/* copy tables */
{ size_t const chainSize = (srcCCtx->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << srcCCtx->appliedParams.cParams.chainLog);
size_t const hSize = (size_t)1 << srcCCtx->appliedParams.cParams.hashLog;
- size_t const h3Size = (size_t)1 << srcCCtx->hashLog3;
+ size_t const h3Size = (size_t)1 << srcCCtx->blockState.matchState.hashLog3;
size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
- assert((U32*)dstCCtx->chainTable == (U32*)dstCCtx->hashTable + hSize); /* chainTable must follow hashTable */
- assert((U32*)dstCCtx->hashTable3 == (U32*)dstCCtx->chainTable + chainSize);
- memcpy(dstCCtx->hashTable, srcCCtx->hashTable, tableSpace); /* presumes all tables follow each other */
+ assert((U32*)dstCCtx->blockState.matchState.chainTable == (U32*)dstCCtx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */
+ assert((U32*)dstCCtx->blockState.matchState.hashTable3 == (U32*)dstCCtx->blockState.matchState.chainTable + chainSize);
+ memcpy(dstCCtx->blockState.matchState.hashTable, srcCCtx->blockState.matchState.hashTable, tableSpace); /* presumes all tables follow each other */
}
/* copy dictionary offsets */
- dstCCtx->nextToUpdate = srcCCtx->nextToUpdate;
- dstCCtx->nextToUpdate3= srcCCtx->nextToUpdate3;
- dstCCtx->nextSrc = srcCCtx->nextSrc;
- dstCCtx->base = srcCCtx->base;
- dstCCtx->dictBase = srcCCtx->dictBase;
- dstCCtx->dictLimit = srcCCtx->dictLimit;
- dstCCtx->lowLimit = srcCCtx->lowLimit;
- dstCCtx->loadedDictEnd= srcCCtx->loadedDictEnd;
- dstCCtx->dictID = srcCCtx->dictID;
-
- /* copy entropy tables */
- memcpy(dstCCtx->entropy, srcCCtx->entropy, sizeof(ZSTD_entropyCTables_t));
- /* copy repcodes */
{
- int i;
- for (i = 0; i < ZSTD_REP_NUM; ++i)
- dstCCtx->seqStore.rep[i] = srcCCtx->seqStore.rep[i];
+ ZSTD_matchState_t const* srcMatchState = &srcCCtx->blockState.matchState;
+ ZSTD_matchState_t* dstMatchState = &dstCCtx->blockState.matchState;
+ dstMatchState->window = srcMatchState->window;
+ dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;
+ dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3;
+ dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;
}
+ dstCCtx->dictID = srcCCtx->dictID;
+
+ /* copy block state */
+ memcpy(dstCCtx->blockState.prevCBlock, srcCCtx->blockState.prevCBlock, sizeof(*srcCCtx->blockState.prevCBlock));
return 0;
}
@@ -1080,51 +1221,69 @@ size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long
fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN);
return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx,
- 0 /*windowLog from srcCCtx*/, fParams, pledgedSrcSize,
+ fParams, pledgedSrcSize,
zbuff);
}
+#define ZSTD_ROWSIZE 16
/*! ZSTD_reduceTable() :
- * reduce table indexes by `reducerValue` */
-static void ZSTD_reduceTable (U32* const table, U32 const size, U32 const reducerValue)
+ * reduce table indexes by `reducerValue`, or squash to zero.
+ * PreserveMark preserves "unsorted mark" for btlazy2 strategy.
+ * It must be set to a clear 0/1 value, to remove branch during inlining.
+ * Presume table size is a multiple of ZSTD_ROWSIZE
+ * to help auto-vectorization */
+FORCE_INLINE_TEMPLATE void
+ZSTD_reduceTable_internal (U32* const table, U32 const size, U32 const reducerValue, int const preserveMark)
+{
+ int const nbRows = (int)size / ZSTD_ROWSIZE;
+ int cellNb = 0;
+ int rowNb;
+ assert((size & (ZSTD_ROWSIZE-1)) == 0); /* multiple of ZSTD_ROWSIZE */
+ assert(size < (1U<<31)); /* can be casted to int */
+ for (rowNb=0 ; rowNb < nbRows ; rowNb++) {
+ int column;
+ for (column=0; column<ZSTD_ROWSIZE; column++) {
+ if (preserveMark) {
+ U32 const adder = (table[cellNb] == ZSTD_DUBT_UNSORTED_MARK) ? reducerValue : 0;
+ table[cellNb] += adder;
+ }
+ if (table[cellNb] < reducerValue) table[cellNb] = 0;
+ else table[cellNb] -= reducerValue;
+ cellNb++;
+ } }
+}
+
+static void ZSTD_reduceTable(U32* const table, U32 const size, U32 const reducerValue)
{
- U32 u;
- for (u=0 ; u < size ; u++) {
- if (table[u] < reducerValue) table[u] = 0;
- else table[u] -= reducerValue;
- }
+ ZSTD_reduceTable_internal(table, size, reducerValue, 0);
}
-/*! ZSTD_ldm_reduceTable() :
- * reduce table indexes by `reducerValue` */
-static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size,
- U32 const reducerValue)
+static void ZSTD_reduceTable_btlazy2(U32* const table, U32 const size, U32 const reducerValue)
{
- U32 u;
- for (u = 0; u < size; u++) {
- if (table[u].offset < reducerValue) table[u].offset = 0;
- else table[u].offset -= reducerValue;
- }
+ ZSTD_reduceTable_internal(table, size, reducerValue, 1);
}
/*! ZSTD_reduceIndex() :
* rescale all indexes to avoid future overflow (indexes are U32) */
static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue)
{
- { U32 const hSize = (U32)1 << zc->appliedParams.cParams.hashLog;
- ZSTD_reduceTable(zc->hashTable, hSize, reducerValue); }
-
- { U32 const chainSize = (zc->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((U32)1 << zc->appliedParams.cParams.chainLog);
- ZSTD_reduceTable(zc->chainTable, chainSize, reducerValue); }
+ ZSTD_matchState_t* const ms = &zc->blockState.matchState;
+ { U32 const hSize = (U32)1 << zc->appliedParams.cParams.hashLog;
+ ZSTD_reduceTable(ms->hashTable, hSize, reducerValue);
+ }
- { U32 const h3Size = (zc->hashLog3) ? (U32)1 << zc->hashLog3 : 0;
- ZSTD_reduceTable(zc->hashTable3, h3Size, reducerValue); }
+ if (zc->appliedParams.cParams.strategy != ZSTD_fast) {
+ U32 const chainSize = (U32)1 << zc->appliedParams.cParams.chainLog;
+ if (zc->appliedParams.cParams.strategy == ZSTD_btlazy2)
+ ZSTD_reduceTable_btlazy2(ms->chainTable, chainSize, reducerValue);
+ else
+ ZSTD_reduceTable(ms->chainTable, chainSize, reducerValue);
+ }
- { if (zc->appliedParams.ldmParams.enableLdm) {
- U32 const ldmHSize = (U32)1 << zc->appliedParams.ldmParams.hashLog;
- ZSTD_ldm_reduceTable(zc->ldmState.hashTable, ldmHSize, reducerValue);
- }
+ if (ms->hashLog3) {
+ U32 const h3Size = (U32)1 << ms->hashLog3;
+ ZSTD_reduceTable(ms->hashTable3, h3Size, reducerValue);
}
}
@@ -1199,10 +1358,12 @@ static size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, cons
static size_t ZSTD_minGain(size_t srcSize) { return (srcSize >> 6) + 2; }
-static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t * entropy,
- ZSTD_strategy strategy,
+static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy,
+ ZSTD_entropyCTables_t* nextEntropy,
+ ZSTD_strategy strategy, int disableLiteralCompression,
void* dst, size_t dstCapacity,
- const void* src, size_t srcSize)
+ const void* src, size_t srcSize,
+ U32* workspace, const int bmi2)
{
size_t const minGain = ZSTD_minGain(srcSize);
size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB);
@@ -1211,34 +1372,51 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t * entropy,
symbolEncodingType_e hType = set_compressed;
size_t cLitSize;
+ DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i)",
+ disableLiteralCompression);
+
+ /* Prepare nextEntropy assuming reusing the existing table */
+ nextEntropy->hufCTable_repeatMode = prevEntropy->hufCTable_repeatMode;
+ memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable,
+ sizeof(prevEntropy->hufCTable));
+
+ if (disableLiteralCompression)
+ return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
/* small ? don't even attempt compression (speed opt) */
-# define LITERAL_NOENTROPY 63
- { size_t const minLitSize = entropy->hufCTable_repeatMode == HUF_repeat_valid ? 6 : LITERAL_NOENTROPY;
+# define COMPRESS_LITERALS_SIZE_MIN 63
+ { size_t const minLitSize = (prevEntropy->hufCTable_repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN;
if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
}
if (dstCapacity < lhSize+1) return ERROR(dstSize_tooSmall); /* not enough space for compression */
- { HUF_repeat repeat = entropy->hufCTable_repeatMode;
+ { HUF_repeat repeat = prevEntropy->hufCTable_repeatMode;
int const preferRepeat = strategy < ZSTD_lazy ? srcSize <= 1024 : 0;
if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1;
cLitSize = singleStream ? HUF_compress1X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11,
- entropy->workspace, sizeof(entropy->workspace), (HUF_CElt*)entropy->hufCTable, &repeat, preferRepeat)
+ workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat, bmi2)
: HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11,
- entropy->workspace, sizeof(entropy->workspace), (HUF_CElt*)entropy->hufCTable, &repeat, preferRepeat);
- if (repeat != HUF_repeat_none) { hType = set_repeat; } /* reused the existing table */
- else { entropy->hufCTable_repeatMode = HUF_repeat_check; } /* now have a table to reuse */
+ workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat, bmi2);
+ if (repeat != HUF_repeat_none) {
+ /* reused the existing table */
+ hType = set_repeat;
+ }
}
if ((cLitSize==0) | (cLitSize >= srcSize - minGain) | ERR_isError(cLitSize)) {
- entropy->hufCTable_repeatMode = HUF_repeat_none;
+ memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, sizeof(prevEntropy->hufCTable));
return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
}
if (cLitSize==1) {
- entropy->hufCTable_repeatMode = HUF_repeat_none;
+ memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, sizeof(prevEntropy->hufCTable));
return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize);
}
+ if (hType == set_compressed) {
+ /* using a newly constructed table */
+ nextEntropy->hufCTable_repeatMode = HUF_repeat_check;
+ }
+
/* Build header */
switch(lhSize)
{
@@ -1332,10 +1510,11 @@ symbolEncodingType_e ZSTD_selectEncodingType(
MEM_STATIC
size_t ZSTD_buildCTable(void* dst, size_t dstCapacity,
- FSE_CTable* CTable, U32 FSELog, symbolEncodingType_e type,
+ FSE_CTable* nextCTable, U32 FSELog, symbolEncodingType_e type,
U32* count, U32 max,
BYTE const* codeTable, size_t nbSeq,
S16 const* defaultNorm, U32 defaultNormLog, U32 defaultMax,
+ FSE_CTable const* prevCTable, size_t prevCTableSize,
void* workspace, size_t workspaceSize)
{
BYTE* op = (BYTE*)dst;
@@ -1344,12 +1523,13 @@ size_t ZSTD_buildCTable(void* dst, size_t dstCapacity,
switch (type) {
case set_rle:
*op = codeTable[0];
- CHECK_F(FSE_buildCTable_rle(CTable, (BYTE)max));
+ CHECK_F(FSE_buildCTable_rle(nextCTable, (BYTE)max));
return 1;
case set_repeat:
+ memcpy(nextCTable, prevCTable, prevCTableSize);
return 0;
case set_basic:
- CHECK_F(FSE_buildCTable_wksp(CTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize)); /* note : could be pre-calculated */
+ CHECK_F(FSE_buildCTable_wksp(nextCTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize)); /* note : could be pre-calculated */
return 0;
case set_compressed: {
S16 norm[MaxSeq + 1];
@@ -1363,7 +1543,7 @@ size_t ZSTD_buildCTable(void* dst, size_t dstCapacity,
CHECK_F(FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max));
{ size_t const NCountSize = FSE_writeNCount(op, oend - op, norm, max, tableLog); /* overflow protected */
if (FSE_isError(NCountSize)) return NCountSize;
- CHECK_F(FSE_buildCTable_wksp(CTable, norm, max, tableLog, workspace, workspaceSize));
+ CHECK_F(FSE_buildCTable_wksp(nextCTable, norm, max, tableLog, workspace, workspaceSize));
return NCountSize;
}
}
@@ -1371,8 +1551,8 @@ size_t ZSTD_buildCTable(void* dst, size_t dstCapacity,
}
}
-MEM_STATIC
-size_t ZSTD_encodeSequences(
+FORCE_INLINE_TEMPLATE size_t
+ZSTD_encodeSequences_body(
void* dst, size_t dstCapacity,
FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,
FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,
@@ -1419,7 +1599,8 @@ size_t ZSTD_encodeSequences(
DEBUGLOG(6, "encoding: litlen:%2u - matchlen:%2u - offCode:%7u",
sequences[n].litLength,
sequences[n].matchLength + MINMATCH,
- sequences[n].offset); /* 32b*/ /* 64b*/
+ sequences[n].offset);
+ /* 32b*/ /* 64b*/
/* (7)*/ /* (7)*/
FSE_encodeSymbol(&blockStream, &stateOffsetBits, ofCode); /* 15 */ /* 15 */
FSE_encodeSymbol(&blockStream, &stateMatchLength, mlCode); /* 24 */ /* 24 */
@@ -1445,8 +1626,11 @@ size_t ZSTD_encodeSequences(
BIT_flushBits(&blockStream); /* (7)*/
} }
+ DEBUGLOG(6, "ZSTD_encodeSequences: flushing ML state with %u bits", stateMatchLength.stateLog);
FSE_flushCState(&blockStream, &stateMatchLength);
+ DEBUGLOG(6, "ZSTD_encodeSequences: flushing Off state with %u bits", stateOffsetBits.stateLog);
FSE_flushCState(&blockStream, &stateOffsetBits);
+ DEBUGLOG(6, "ZSTD_encodeSequences: flushing LL state with %u bits", stateLitLength.stateLog);
FSE_flushCState(&blockStream, &stateLitLength);
{ size_t const streamSize = BIT_closeCStream(&blockStream);
@@ -1455,16 +1639,77 @@ size_t ZSTD_encodeSequences(
}
}
+static size_t
+ZSTD_encodeSequences_default(
+ void* dst, size_t dstCapacity,
+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,
+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,
+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,
+ seqDef const* sequences, size_t nbSeq, int longOffsets)
+{
+ return ZSTD_encodeSequences_body(dst, dstCapacity,
+ CTable_MatchLength, mlCodeTable,
+ CTable_OffsetBits, ofCodeTable,
+ CTable_LitLength, llCodeTable,
+ sequences, nbSeq, longOffsets);
+}
+
+
+#if DYNAMIC_BMI2
+
+static TARGET_ATTRIBUTE("bmi2") size_t
+ZSTD_encodeSequences_bmi2(
+ void* dst, size_t dstCapacity,
+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,
+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,
+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,
+ seqDef const* sequences, size_t nbSeq, int longOffsets)
+{
+ return ZSTD_encodeSequences_body(dst, dstCapacity,
+ CTable_MatchLength, mlCodeTable,
+ CTable_OffsetBits, ofCodeTable,
+ CTable_LitLength, llCodeTable,
+ sequences, nbSeq, longOffsets);
+}
+
+#endif
+
+size_t ZSTD_encodeSequences(
+ void* dst, size_t dstCapacity,
+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,
+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,
+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,
+ seqDef const* sequences, size_t nbSeq, int longOffsets, int bmi2)
+{
+#if DYNAMIC_BMI2
+ if (bmi2) {
+ return ZSTD_encodeSequences_bmi2(dst, dstCapacity,
+ CTable_MatchLength, mlCodeTable,
+ CTable_OffsetBits, ofCodeTable,
+ CTable_LitLength, llCodeTable,
+ sequences, nbSeq, longOffsets);
+ }
+#endif
+ (void)bmi2;
+ return ZSTD_encodeSequences_default(dst, dstCapacity,
+ CTable_MatchLength, mlCodeTable,
+ CTable_OffsetBits, ofCodeTable,
+ CTable_LitLength, llCodeTable,
+ sequences, nbSeq, longOffsets);
+}
+
MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
- ZSTD_entropyCTables_t* entropy,
- ZSTD_compressionParameters const* cParams,
- void* dst, size_t dstCapacity)
+ ZSTD_entropyCTables_t const* prevEntropy,
+ ZSTD_entropyCTables_t* nextEntropy,
+ ZSTD_CCtx_params const* cctxParams,
+ void* dst, size_t dstCapacity, U32* workspace,
+ const int bmi2)
{
- const int longOffsets = cParams->windowLog > STREAM_ACCUMULATOR_MIN;
+ const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN;
U32 count[MaxSeq+1];
- FSE_CTable* CTable_LitLength = entropy->litlengthCTable;
- FSE_CTable* CTable_OffsetBits = entropy->offcodeCTable;
- FSE_CTable* CTable_MatchLength = entropy->matchlengthCTable;
+ FSE_CTable* CTable_LitLength = nextEntropy->litlengthCTable;
+ FSE_CTable* CTable_OffsetBits = nextEntropy->offcodeCTable;
+ FSE_CTable* CTable_MatchLength = nextEntropy->matchlengthCTable;
U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */
const seqDef* const sequences = seqStorePtr->sequencesStart;
const BYTE* const ofCodeTable = seqStorePtr->ofCode;
@@ -1476,13 +1721,17 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart;
BYTE* seqHead;
- ZSTD_STATIC_ASSERT(sizeof(entropy->workspace) >= (1<<MAX(MLFSELog,LLFSELog)));
+ ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog)));
/* Compress literals */
{ const BYTE* const literals = seqStorePtr->litStart;
size_t const litSize = seqStorePtr->lit - literals;
size_t const cSize = ZSTD_compressLiterals(
- entropy, cParams->strategy, op, dstCapacity, literals, litSize);
+ prevEntropy, nextEntropy,
+ cctxParams->cParams.strategy, cctxParams->disableLiteralCompression,
+ op, dstCapacity,
+ literals, litSize,
+ workspace, bmi2);
if (ZSTD_isError(cSize))
return cSize;
assert(cSize <= dstCapacity);
@@ -1497,7 +1746,15 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2;
else
op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3;
- if (nbSeq==0) return op - ostart;
+ if (nbSeq==0) {
+ memcpy(nextEntropy->litlengthCTable, prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable));
+ nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode;
+ memcpy(nextEntropy->offcodeCTable, prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable));
+ nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode;
+ memcpy(nextEntropy->matchlengthCTable, prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable));
+ nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode;
+ return op - ostart;
+ }
/* seqHead : flags for FSE encoding type */
seqHead = op++;
@@ -1506,36 +1763,42 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
ZSTD_seqToCodes(seqStorePtr);
/* build CTable for Literal Lengths */
{ U32 max = MaxLL;
- size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, entropy->workspace);
+ size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, workspace);
DEBUGLOG(5, "Building LL table");
- LLtype = ZSTD_selectEncodingType(&entropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog, ZSTD_defaultAllowed);
+ nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode;
+ LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog, ZSTD_defaultAllowed);
{ size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype,
count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL,
- entropy->workspace, sizeof(entropy->workspace));
+ prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable),
+ workspace, HUF_WORKSPACE_SIZE);
if (ZSTD_isError(countSize)) return countSize;
op += countSize;
} }
/* build CTable for Offsets */
{ U32 max = MaxOff;
- size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, entropy->workspace);
+ size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, workspace);
/* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */
ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed;
DEBUGLOG(5, "Building OF table");
- Offtype = ZSTD_selectEncodingType(&entropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog, defaultPolicy);
+ nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode;
+ Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog, defaultPolicy);
{ size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype,
count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,
- entropy->workspace, sizeof(entropy->workspace));
+ prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable),
+ workspace, HUF_WORKSPACE_SIZE);
if (ZSTD_isError(countSize)) return countSize;
op += countSize;
} }
/* build CTable for MatchLengths */
{ U32 max = MaxML;
- size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, entropy->workspace);
+ size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, workspace);
DEBUGLOG(5, "Building ML table");
- MLtype = ZSTD_selectEncodingType(&entropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog, ZSTD_defaultAllowed);
+ nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode;
+ MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog, ZSTD_defaultAllowed);
{ size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype,
count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML,
- entropy->workspace, sizeof(entropy->workspace));
+ prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable),
+ workspace, HUF_WORKSPACE_SIZE);
if (ZSTD_isError(countSize)) return countSize;
op += countSize;
} }
@@ -1548,7 +1811,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
CTable_OffsetBits, ofCodeTable,
CTable_LitLength, llCodeTable,
sequences, nbSeq,
- longOffsets);
+ longOffsets, bmi2);
if (ZSTD_isError(bitstreamSize)) return bitstreamSize;
op += bitstreamSize;
}
@@ -1557,48 +1820,40 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
}
MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr,
- ZSTD_entropyCTables_t* entropy,
- ZSTD_compressionParameters const* cParams,
+ ZSTD_entropyCTables_t const* prevEntropy,
+ ZSTD_entropyCTables_t* nextEntropy,
+ ZSTD_CCtx_params const* cctxParams,
void* dst, size_t dstCapacity,
- size_t srcSize)
+ size_t srcSize, U32* workspace, int bmi2)
{
- size_t const cSize = ZSTD_compressSequences_internal(seqStorePtr, entropy, cParams,
- dst, dstCapacity);
- /* If the srcSize <= dstCapacity, then there is enough space to write a
- * raw uncompressed block. Since we ran out of space, the block must not
- * be compressible, so fall back to a raw uncompressed block.
+ size_t const cSize = ZSTD_compressSequences_internal(
+ seqStorePtr, prevEntropy, nextEntropy, cctxParams, dst, dstCapacity,
+ workspace, bmi2);
+ /* When srcSize <= dstCapacity, there is enough space to write a raw uncompressed block.
+ * Since we ran out of space, block must be not compressible, so fall back to raw uncompressed block.
*/
- int const uncompressibleError = (cSize == ERROR(dstSize_tooSmall)) && (srcSize <= dstCapacity);
- if (ZSTD_isError(cSize) && !uncompressibleError)
- return cSize;
+ if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity))
+ return 0; /* block not compressed */
+ if (ZSTD_isError(cSize)) return cSize;
+
+ /* Check compressibility */
+ { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize); /* note : fixed formula, maybe should depend on compression level, or strategy */
+ if (cSize >= maxCSize) return 0; /* block not compressed */
+ }
+
/* We check that dictionaries have offset codes available for the first
* block. After the first block, the offcode table might not have large
* enough codes to represent the offsets in the data.
*/
- if (entropy->offcode_repeatMode == FSE_repeat_valid)
- entropy->offcode_repeatMode = FSE_repeat_check;
-
- /* Check compressibility */
- { size_t const minGain = ZSTD_minGain(srcSize); /* note : fixed formula, maybe should depend on compression level, or strategy */
- size_t const maxCSize = srcSize - minGain;
- if (cSize >= maxCSize || uncompressibleError) {
- entropy->hufCTable_repeatMode = HUF_repeat_none;
- entropy->offcode_repeatMode = FSE_repeat_none;
- entropy->matchlength_repeatMode = FSE_repeat_none;
- entropy->litlength_repeatMode = FSE_repeat_none;
- return 0; /* block not compressed */
- } }
- assert(!ZSTD_isError(cSize));
+ if (nextEntropy->offcode_repeatMode == FSE_repeat_valid)
+ nextEntropy->offcode_repeatMode = FSE_repeat_check;
- /* block is compressed => confirm repcodes in history */
- { int i; for (i=0; i<ZSTD_REP_NUM; i++) seqStorePtr->rep[i] = seqStorePtr->repToConfirm[i]; }
return cSize;
}
/* ZSTD_selectBlockCompressor() :
* Not static, but internal use only (used by long distance matcher)
* assumption : strat is a valid strategy */
-typedef size_t (*ZSTD_blockCompressor) (ZSTD_CCtx* ctx, const void* src, size_t srcSize);
ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict)
{
static const ZSTD_blockCompressor blockCompressor[2][(unsigned)ZSTD_btultra+1] = {
@@ -1632,32 +1887,83 @@ static void ZSTD_resetSeqStore(seqStore_t* ssPtr)
ssPtr->longLengthID = 0;
}
-static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize)
+static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize)
{
- DEBUGLOG(5, "ZSTD_compressBlock_internal : dstCapacity = %u", (U32)dstCapacity);
- if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1)
+ ZSTD_matchState_t* const ms = &zc->blockState.matchState;
+ DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",
+ (U32)dstCapacity, ms->window.dictLimit, ms->nextToUpdate);
+ if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) {
+ ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize, zc->appliedParams.cParams.searchLength);
return 0; /* don't even attempt compression below a certain srcSize */
+ }
ZSTD_resetSeqStore(&(zc->seqStore));
/* limited update after a very long match */
- { const BYTE* const base = zc->base;
+ { const BYTE* const base = ms->window.base;
const BYTE* const istart = (const BYTE*)src;
const U32 current = (U32)(istart-base);
- if (current > zc->nextToUpdate + 384)
- zc->nextToUpdate = current - MIN(192, (U32)(current - zc->nextToUpdate - 384));
+ if (current > ms->nextToUpdate + 384)
+ ms->nextToUpdate = current - MIN(192, (U32)(current - ms->nextToUpdate - 384));
}
- /* find and store sequences */
- { U32 const extDict = zc->lowLimit < zc->dictLimit;
- const ZSTD_blockCompressor blockCompressor =
- zc->appliedParams.ldmParams.enableLdm
- ? (extDict ? ZSTD_compressBlock_ldm_extDict : ZSTD_compressBlock_ldm)
- : ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict);
- size_t const lastLLSize = blockCompressor(zc, src, srcSize);
- const BYTE* const anchor = (const BYTE*)src + srcSize - lastLLSize;
- ZSTD_storeLastLiterals(&zc->seqStore, anchor, lastLLSize);
+
+ /* select and store sequences */
+ { U32 const extDict = ZSTD_window_hasExtDict(ms->window);
+ size_t lastLLSize;
+ { int i;
+ for (i = 0; i < ZSTD_REP_NUM; ++i)
+ zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i];
+ }
+ if (zc->externSeqStore.pos < zc->externSeqStore.size) {
+ assert(!zc->appliedParams.ldmParams.enableLdm);
+ /* Updates ldmSeqStore.pos */
+ lastLLSize =
+ ZSTD_ldm_blockCompress(&zc->externSeqStore,
+ ms, &zc->seqStore,
+ zc->blockState.nextCBlock->rep,
+ &zc->appliedParams.cParams,
+ src, srcSize, extDict);
+ assert(zc->externSeqStore.pos <= zc->externSeqStore.size);
+ } else if (zc->appliedParams.ldmParams.enableLdm) {
+ rawSeqStore_t ldmSeqStore = {NULL, 0, 0, 0};
+
+ ldmSeqStore.seq = zc->ldmSequences;
+ ldmSeqStore.capacity = zc->maxNbLdmSequences;
+ /* Updates ldmSeqStore.size */
+ CHECK_F(ZSTD_ldm_generateSequences(&zc->ldmState, &ldmSeqStore,
+ &zc->appliedParams.ldmParams,
+ src, srcSize));
+ /* Updates ldmSeqStore.pos */
+ lastLLSize =
+ ZSTD_ldm_blockCompress(&ldmSeqStore,
+ ms, &zc->seqStore,
+ zc->blockState.nextCBlock->rep,
+ &zc->appliedParams.cParams,
+ src, srcSize, extDict);
+ assert(ldmSeqStore.pos == ldmSeqStore.size);
+ } else { /* not long range mode */
+ ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict);
+ lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, src, srcSize);
+ }
+ { const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize;
+ ZSTD_storeLastLiterals(&zc->seqStore, lastLiterals, lastLLSize);
+ } }
+
+ /* encode sequences and literals */
+ { size_t const cSize = ZSTD_compressSequences(&zc->seqStore,
+ &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy,
+ &zc->appliedParams,
+ dst, dstCapacity,
+ srcSize, zc->entropyWorkspace, zc->bmi2);
+ if (ZSTD_isError(cSize) || cSize == 0) return cSize;
+ /* confirm repcodes and entropy tables */
+ { ZSTD_compressedBlockState_t* const tmp = zc->blockState.prevCBlock;
+ zc->blockState.prevCBlock = zc->blockState.nextCBlock;
+ zc->blockState.nextCBlock = tmp;
+ }
+ return cSize;
}
- /* encode */
- return ZSTD_compressSequences(&zc->seqStore, zc->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize);
}
@@ -1686,54 +1992,27 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx,
XXH64_update(&cctx->xxhState, src, srcSize);
while (remaining) {
+ ZSTD_matchState_t* const ms = &cctx->blockState.matchState;
U32 const lastBlock = lastFrameChunk & (blockSize >= remaining);
if (dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE)
return ERROR(dstSize_tooSmall); /* not enough space to store compressed block */
if (remaining < blockSize) blockSize = remaining;
- /* preemptive overflow correction:
- * 1. correction is large enough:
- * lowLimit > (3<<29) ==> current > 3<<29 + 1<<windowLog - blockSize
- * 1<<windowLog <= newCurrent < 1<<chainLog + 1<<windowLog
- *
- * current - newCurrent
- * > (3<<29 + 1<<windowLog - blockSize) - (1<<windowLog + 1<<chainLog)
- * > (3<<29 - blockSize) - (1<<chainLog)
- * > (3<<29 - blockSize) - (1<<30) (NOTE: chainLog <= 30)
- * > 1<<29 - 1<<17
- *
- * 2. (ip+blockSize - cctx->base) doesn't overflow:
- * In 32 bit mode we limit windowLog to 30 so we don't get
- * differences larger than 1<<31-1.
- * 3. cctx->lowLimit < 1<<32:
- * windowLog <= 31 ==> 3<<29 + 1<<windowLog < 7<<29 < 1<<32.
- */
- if (cctx->lowLimit > (3U<<29)) {
- U32 const cycleMask = ((U32)1 << ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy)) - 1;
- U32 const current = (U32)(ip - cctx->base);
- U32 const newCurrent = (current & cycleMask) + ((U32)1 << cctx->appliedParams.cParams.windowLog);
- U32 const correction = current - newCurrent;
+ if (ZSTD_window_needOverflowCorrection(ms->window, ip + blockSize)) {
+ U32 const cycleLog = ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy);
+ U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, maxDist, ip);
ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30);
ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30);
ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31);
- assert(current > newCurrent);
- assert(correction > 1<<28); /* Loose bound, should be about 1<<29 */
+
ZSTD_reduceIndex(cctx, correction);
- cctx->base += correction;
- cctx->dictBase += correction;
- cctx->lowLimit -= correction;
- cctx->dictLimit -= correction;
- if (cctx->nextToUpdate < correction) cctx->nextToUpdate = 0;
- else cctx->nextToUpdate -= correction;
- DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x\n", correction, cctx->lowLimit);
- }
- /* enforce maxDist */
- if ((U32)(ip+blockSize - cctx->base) > cctx->loadedDictEnd + maxDist) {
- U32 const newLowLimit = (U32)(ip+blockSize - cctx->base) - maxDist;
- if (cctx->lowLimit < newLowLimit) cctx->lowLimit = newLowLimit;
- if (cctx->dictLimit < cctx->lowLimit) cctx->dictLimit = cctx->lowLimit;
+ if (ms->nextToUpdate < correction) ms->nextToUpdate = 0;
+ else ms->nextToUpdate -= correction;
+ ms->loadedDictEnd = 0;
}
+ ZSTD_window_enforceMaxDist(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd);
+ if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit;
{ size_t cSize = ZSTD_compressBlock_internal(cctx,
op+ZSTD_blockHeaderSize, dstCapacity-ZSTD_blockHeaderSize,
@@ -1810,16 +2089,44 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity,
return pos;
}
+/* ZSTD_writeLastEmptyBlock() :
+ * output an empty Block with end-of-frame mark to complete a frame
+ * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h))
+ * or an error code if `dstCapcity` is too small (<ZSTD_blockHeaderSize)
+ */
+size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity)
+{
+ if (dstCapacity < ZSTD_blockHeaderSize) return ERROR(dstSize_tooSmall);
+ { U32 const cBlockHeader24 = 1 /*lastBlock*/ + (((U32)bt_raw)<<1); /* 0 size */
+ MEM_writeLE24(dst, cBlockHeader24);
+ return ZSTD_blockHeaderSize;
+ }
+}
+
+size_t ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq)
+{
+ if (cctx->stage != ZSTDcs_init)
+ return ERROR(stage_wrong);
+ if (cctx->appliedParams.ldmParams.enableLdm)
+ return ERROR(parameter_unsupported);
+ cctx->externSeqStore.seq = seq;
+ cctx->externSeqStore.size = nbSeq;
+ cctx->externSeqStore.capacity = nbSeq;
+ cctx->externSeqStore.pos = 0;
+ return 0;
+}
+
static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
U32 frame, U32 lastFrameChunk)
{
- const BYTE* const ip = (const BYTE*) src;
+ ZSTD_matchState_t* ms = &cctx->blockState.matchState;
size_t fhSize = 0;
- DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u", cctx->stage);
+ DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u, srcSize: %u",
+ cctx->stage, (U32)srcSize);
if (cctx->stage==ZSTDcs_created) return ERROR(stage_wrong); /* missing init (ZSTD_compressBegin) */
if (frame && (cctx->stage==ZSTDcs_init)) {
@@ -1833,26 +2140,11 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,
if (!srcSize) return fhSize; /* do not generate an empty block if no input */
- /* Check if blocks follow each other */
- if (src != cctx->nextSrc) {
- /* not contiguous */
- size_t const distanceFromBase = (size_t)(cctx->nextSrc - cctx->base);
- cctx->lowLimit = cctx->dictLimit;
- assert(distanceFromBase == (size_t)(U32)distanceFromBase); /* should never overflow */
- cctx->dictLimit = (U32)distanceFromBase;
- cctx->dictBase = cctx->base;
- cctx->base = ip - distanceFromBase;
- cctx->nextToUpdate = cctx->dictLimit;
- if (cctx->dictLimit - cctx->lowLimit < HASH_READ_SIZE) cctx->lowLimit = cctx->dictLimit; /* too small extDict */
- }
- cctx->nextSrc = ip + srcSize;
-
- /* if input and dictionary overlap : reduce dictionary (area presumed modified by input) */
- if ((ip+srcSize > cctx->dictBase + cctx->lowLimit) & (ip < cctx->dictBase + cctx->dictLimit)) {
- ptrdiff_t const highInputIdx = (ip + srcSize) - cctx->dictBase;
- U32 const lowLimitMax = (highInputIdx > (ptrdiff_t)cctx->dictLimit) ? cctx->dictLimit : (U32)highInputIdx;
- cctx->lowLimit = lowLimitMax;
+ if (!ZSTD_window_update(&ms->window, src, srcSize)) {
+ ms->nextToUpdate = ms->window.dictLimit;
}
+ if (cctx->appliedParams.ldmParams.enableLdm)
+ ZSTD_window_update(&cctx->ldmState.window, src, srcSize);
DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (U32)cctx->blockSize);
{ size_t const cSize = frame ?
@@ -1860,6 +2152,14 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,
ZSTD_compressBlock_internal (cctx, dst, dstCapacity, src, srcSize);
if (ZSTD_isError(cSize)) return cSize;
cctx->consumedSrcSize += srcSize;
+ cctx->producedCSize += (cSize + fhSize);
+ if (cctx->appliedParams.fParams.contentSizeFlag) { /* control src size */
+ if (cctx->consumedSrcSize+1 > cctx->pledgedSrcSizePlusOne) {
+ DEBUGLOG(4, "error : pledgedSrcSize = %u, while realSrcSize >= %u",
+ (U32)cctx->pledgedSrcSizePlusOne-1, (U32)cctx->consumedSrcSize);
+ return ERROR(srcSize_wrong);
+ }
+ }
return cSize + fhSize;
}
}
@@ -1868,14 +2168,15 @@ size_t ZSTD_compressContinue (ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize)
{
+ DEBUGLOG(5, "ZSTD_compressContinue (srcSize=%u)", (U32)srcSize);
return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1 /* frame mode */, 0 /* last chunk */);
}
size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx)
{
- ZSTD_compressionParameters const cParams =
- ZSTD_getCParamsFromCCtxParams(cctx->appliedParams, 0, 0);
+ ZSTD_compressionParameters const cParams = cctx->appliedParams.cParams;
+ assert(!ZSTD_checkCParams(cParams));
return MIN (ZSTD_BLOCKSIZE_MAX, (U32)1 << cParams.windowLog);
}
@@ -1889,50 +2190,45 @@ size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const
/*! ZSTD_loadDictionaryContent() :
* @return : 0, or an error code
*/
-static size_t ZSTD_loadDictionaryContent(ZSTD_CCtx* zc, const void* src, size_t srcSize)
+static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, ZSTD_CCtx_params const* params, const void* src, size_t srcSize)
{
const BYTE* const ip = (const BYTE*) src;
const BYTE* const iend = ip + srcSize;
+ ZSTD_compressionParameters const* cParams = &params->cParams;
- /* input becomes current prefix */
- zc->lowLimit = zc->dictLimit;
- zc->dictLimit = (U32)(zc->nextSrc - zc->base);
- zc->dictBase = zc->base;
- zc->base = ip - zc->dictLimit;
- zc->nextToUpdate = zc->dictLimit;
- zc->loadedDictEnd = zc->appliedParams.forceWindow ? 0 : (U32)(iend - zc->base);
+ ZSTD_window_update(&ms->window, src, srcSize);
+ ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base);
- zc->nextSrc = iend;
if (srcSize <= HASH_READ_SIZE) return 0;
- switch(zc->appliedParams.cParams.strategy)
+ switch(params->cParams.strategy)
{
case ZSTD_fast:
- ZSTD_fillHashTable (zc, iend, zc->appliedParams.cParams.searchLength);
+ ZSTD_fillHashTable(ms, cParams, iend);
break;
case ZSTD_dfast:
- ZSTD_fillDoubleHashTable (zc, iend, zc->appliedParams.cParams.searchLength);
+ ZSTD_fillDoubleHashTable(ms, cParams, iend);
break;
case ZSTD_greedy:
case ZSTD_lazy:
case ZSTD_lazy2:
if (srcSize >= HASH_READ_SIZE)
- ZSTD_insertAndFindFirstIndex(zc, iend-HASH_READ_SIZE, zc->appliedParams.cParams.searchLength);
+ ZSTD_insertAndFindFirstIndex(ms, cParams, iend-HASH_READ_SIZE);
break;
- case ZSTD_btlazy2:
+ case ZSTD_btlazy2: /* we want the dictionary table fully sorted */
case ZSTD_btopt:
case ZSTD_btultra:
if (srcSize >= HASH_READ_SIZE)
- ZSTD_updateTree(zc, iend-HASH_READ_SIZE, iend, (U32)1 << zc->appliedParams.cParams.searchLog, zc->appliedParams.cParams.searchLength);
+ ZSTD_updateTree(ms, cParams, iend-HASH_READ_SIZE, iend);
break;
default:
assert(0); /* not possible : not a valid strategy id */
}
- zc->nextToUpdate = (U32)(iend - zc->base);
+ ms->nextToUpdate = (U32)(iend - ms->window.base);
return 0;
}
@@ -1956,25 +2252,26 @@ static size_t ZSTD_checkDictNCount(short* normalizedCounter, unsigned dictMaxSym
* https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format
*/
/*! ZSTD_loadZstdDictionary() :
- * @return : 0, or an error code
+ * @return : dictID, or an error code
* assumptions : magic number supposed already checked
* dictSize supposed > 8
*/
-static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize)
+static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_matchState_t* ms, ZSTD_CCtx_params const* params, const void* dict, size_t dictSize, void* workspace)
{
const BYTE* dictPtr = (const BYTE*)dict;
const BYTE* const dictEnd = dictPtr + dictSize;
short offcodeNCount[MaxOff+1];
unsigned offcodeMaxValue = MaxOff;
+ size_t dictID;
- ZSTD_STATIC_ASSERT(sizeof(cctx->entropy->workspace) >= (1<<MAX(MLFSELog,LLFSELog)));
+ ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog)));
dictPtr += 4; /* skip magic number */
- cctx->dictID = cctx->appliedParams.fParams.noDictIDFlag ? 0 : MEM_readLE32(dictPtr);
+ dictID = params->fParams.noDictIDFlag ? 0 : MEM_readLE32(dictPtr);
dictPtr += 4;
{ unsigned maxSymbolValue = 255;
- size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)cctx->entropy->hufCTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr);
+ size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.hufCTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr);
if (HUF_isError(hufHeaderSize)) return ERROR(dictionary_corrupted);
if (maxSymbolValue < 255) return ERROR(dictionary_corrupted);
dictPtr += hufHeaderSize;
@@ -1985,7 +2282,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t
if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted);
if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted);
/* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */
- CHECK_E( FSE_buildCTable_wksp(cctx->entropy->offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)),
+ CHECK_E( FSE_buildCTable_wksp(bs->entropy.offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog, workspace, HUF_WORKSPACE_SIZE),
dictionary_corrupted);
dictPtr += offcodeHeaderSize;
}
@@ -1997,7 +2294,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t
if (matchlengthLog > MLFSELog) return ERROR(dictionary_corrupted);
/* Every match length code must have non-zero probability */
CHECK_F( ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML));
- CHECK_E( FSE_buildCTable_wksp(cctx->entropy->matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)),
+ CHECK_E( FSE_buildCTable_wksp(bs->entropy.matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, workspace, HUF_WORKSPACE_SIZE),
dictionary_corrupted);
dictPtr += matchlengthHeaderSize;
}
@@ -2009,15 +2306,15 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t
if (litlengthLog > LLFSELog) return ERROR(dictionary_corrupted);
/* Every literal length code must have non-zero probability */
CHECK_F( ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL));
- CHECK_E( FSE_buildCTable_wksp(cctx->entropy->litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)),
+ CHECK_E( FSE_buildCTable_wksp(bs->entropy.litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, workspace, HUF_WORKSPACE_SIZE),
dictionary_corrupted);
dictPtr += litlengthHeaderSize;
}
if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted);
- cctx->seqStore.rep[0] = MEM_readLE32(dictPtr+0);
- cctx->seqStore.rep[1] = MEM_readLE32(dictPtr+4);
- cctx->seqStore.rep[2] = MEM_readLE32(dictPtr+8);
+ bs->rep[0] = MEM_readLE32(dictPtr+0);
+ bs->rep[1] = MEM_readLE32(dictPtr+4);
+ bs->rep[2] = MEM_readLE32(dictPtr+8);
dictPtr += 12;
{ size_t const dictContentSize = (size_t)(dictEnd - dictPtr);
@@ -2031,50 +2328,55 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t
/* All repCodes must be <= dictContentSize and != 0*/
{ U32 u;
for (u=0; u<3; u++) {
- if (cctx->seqStore.rep[u] == 0) return ERROR(dictionary_corrupted);
- if (cctx->seqStore.rep[u] > dictContentSize) return ERROR(dictionary_corrupted);
+ if (bs->rep[u] == 0) return ERROR(dictionary_corrupted);
+ if (bs->rep[u] > dictContentSize) return ERROR(dictionary_corrupted);
} }
- cctx->entropy->hufCTable_repeatMode = HUF_repeat_valid;
- cctx->entropy->offcode_repeatMode = FSE_repeat_valid;
- cctx->entropy->matchlength_repeatMode = FSE_repeat_valid;
- cctx->entropy->litlength_repeatMode = FSE_repeat_valid;
- return ZSTD_loadDictionaryContent(cctx, dictPtr, dictContentSize);
+ bs->entropy.hufCTable_repeatMode = HUF_repeat_valid;
+ bs->entropy.offcode_repeatMode = FSE_repeat_valid;
+ bs->entropy.matchlength_repeatMode = FSE_repeat_valid;
+ bs->entropy.litlength_repeatMode = FSE_repeat_valid;
+ CHECK_F(ZSTD_loadDictionaryContent(ms, params, dictPtr, dictContentSize));
+ return dictID;
}
}
/** ZSTD_compress_insertDictionary() :
-* @return : 0, or an error code */
-static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* cctx,
+* @return : dictID, or an error code */
+static size_t ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_matchState_t* ms,
+ ZSTD_CCtx_params const* params,
const void* dict, size_t dictSize,
- ZSTD_dictMode_e dictMode)
+ ZSTD_dictContentType_e dictContentType,
+ void* workspace)
{
DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize);
if ((dict==NULL) || (dictSize<=8)) return 0;
+ ZSTD_reset_compressedBlockState(bs);
+
/* dict restricted modes */
- if (dictMode==ZSTD_dm_rawContent)
- return ZSTD_loadDictionaryContent(cctx, dict, dictSize);
+ if (dictContentType == ZSTD_dct_rawContent)
+ return ZSTD_loadDictionaryContent(ms, params, dict, dictSize);
if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) {
- if (dictMode == ZSTD_dm_auto) {
+ if (dictContentType == ZSTD_dct_auto) {
DEBUGLOG(4, "raw content dictionary detected");
- return ZSTD_loadDictionaryContent(cctx, dict, dictSize);
+ return ZSTD_loadDictionaryContent(ms, params, dict, dictSize);
}
- if (dictMode == ZSTD_dm_fullDict)
+ if (dictContentType == ZSTD_dct_fullDict)
return ERROR(dictionary_wrong);
assert(0); /* impossible */
}
/* dict as full zstd dictionary */
- return ZSTD_loadZstdDictionary(cctx, dict, dictSize);
+ return ZSTD_loadZstdDictionary(bs, ms, params, dict, dictSize, workspace);
}
/*! ZSTD_compressBegin_internal() :
* @return : 0, or an error code */
size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx,
const void* dict, size_t dictSize,
- ZSTD_dictMode_e dictMode,
+ ZSTD_dictContentType_e dictContentType,
const ZSTD_CDict* cdict,
ZSTD_CCtx_params params, U64 pledgedSrcSize,
ZSTD_buffered_policy_e zbuff)
@@ -2086,19 +2388,26 @@ size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx,
if (cdict && cdict->dictContentSize>0) {
cctx->requestedParams = params;
- return ZSTD_copyCCtx_internal(cctx, cdict->refContext,
- params.cParams.windowLog, params.fParams, pledgedSrcSize,
- zbuff);
+ return ZSTD_resetCCtx_usingCDict(cctx, cdict, params.cParams.windowLog,
+ params.fParams, pledgedSrcSize, zbuff);
}
CHECK_F( ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,
ZSTDcrp_continue, zbuff) );
- return ZSTD_compress_insertDictionary(cctx, dict, dictSize, dictMode);
+ {
+ size_t const dictID = ZSTD_compress_insertDictionary(
+ cctx->blockState.prevCBlock, &cctx->blockState.matchState,
+ &params, dict, dictSize, dictContentType, cctx->entropyWorkspace);
+ if (ZSTD_isError(dictID)) return dictID;
+ assert(dictID <= (size_t)(U32)-1);
+ cctx->dictID = (U32)dictID;
+ }
+ return 0;
}
size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx,
const void* dict, size_t dictSize,
- ZSTD_dictMode_e dictMode,
+ ZSTD_dictContentType_e dictContentType,
const ZSTD_CDict* cdict,
ZSTD_CCtx_params params,
unsigned long long pledgedSrcSize)
@@ -2107,7 +2416,7 @@ size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx,
/* compression parameters verification and optimization */
CHECK_F( ZSTD_checkCParams(params.cParams) );
return ZSTD_compressBegin_internal(cctx,
- dict, dictSize, dictMode,
+ dict, dictSize, dictContentType,
cdict,
params, pledgedSrcSize,
ZSTDb_not_buffered);
@@ -2122,18 +2431,18 @@ size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx,
ZSTD_CCtx_params const cctxParams =
ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);
return ZSTD_compressBegin_advanced_internal(cctx,
- dict, dictSize, ZSTD_dm_auto,
+ dict, dictSize, ZSTD_dct_auto,
NULL /*cdict*/,
cctxParams, pledgedSrcSize);
}
size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel)
{
- ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, dictSize);
+ ZSTD_parameters const params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);
ZSTD_CCtx_params const cctxParams =
ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);
- DEBUGLOG(4, "ZSTD_compressBegin_usingDict");
- return ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dm_auto, NULL,
+ DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (U32)dictSize);
+ return ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, NULL,
cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered);
}
@@ -2152,7 +2461,7 @@ static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity)
BYTE* op = ostart;
size_t fhSize = 0;
- DEBUGLOG(5, "ZSTD_writeEpilogue");
+ DEBUGLOG(4, "ZSTD_writeEpilogue");
if (cctx->stage == ZSTDcs_created) return ERROR(stage_wrong); /* init missing */
/* special case : empty frame */
@@ -2176,6 +2485,7 @@ static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity)
if (cctx->appliedParams.fParams.checksumFlag) {
U32 const checksum = (U32) XXH64_digest(&cctx->xxhState);
if (dstCapacity<4) return ERROR(dstSize_tooSmall);
+ DEBUGLOG(4, "ZSTD_writeEpilogue: write checksum : %08X", checksum);
MEM_writeLE32(op, checksum);
op += 4;
}
@@ -2184,7 +2494,6 @@ static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity)
return op-ostart;
}
-
size_t ZSTD_compressEnd (ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize)
@@ -2242,25 +2551,27 @@ size_t ZSTD_compress_advanced_internal(
const void* dict,size_t dictSize,
ZSTD_CCtx_params params)
{
- DEBUGLOG(4, "ZSTD_compress_advanced_internal");
- CHECK_F( ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dm_auto, NULL,
+ DEBUGLOG(4, "ZSTD_compress_advanced_internal (srcSize:%u)",
+ (U32)srcSize);
+ CHECK_F( ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, NULL,
params, srcSize, ZSTDb_not_buffered) );
return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize);
}
-size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize,
+size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize,
const void* dict, size_t dictSize, int compressionLevel)
{
- ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize ? srcSize : 1, dict ? dictSize : 0);
- params.fParams.contentSizeFlag = 1;
- DEBUGLOG(4, "ZSTD_compress_usingDict (level=%i, srcSize=%u, dictSize=%u)",
- compressionLevel, (U32)srcSize, (U32)dictSize);
- return ZSTD_compress_internal(ctx, dst, dstCapacity, src, srcSize, dict, dictSize, params);
+ ZSTD_parameters const params = ZSTD_getParams(compressionLevel, srcSize ? srcSize : 1, dict ? dictSize : 0);
+ ZSTD_CCtx_params cctxParams = ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);
+ assert(params.fParams.contentSizeFlag == 1);
+ ZSTD_CCtxParam_setParameter(&cctxParams, ZSTD_p_compressLiterals, compressionLevel>=0);
+ return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, dict, dictSize, cctxParams);
}
-size_t ZSTD_compressCCtx (ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel)
+size_t ZSTD_compressCCtx (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel)
{
- return ZSTD_compress_usingDict(ctx, dst, dstCapacity, src, srcSize, NULL, 0, compressionLevel);
+ DEBUGLOG(4, "ZSTD_compressCCtx (srcSize=%u)", (U32)srcSize);
+ return ZSTD_compress_usingDict(cctx, dst, dstCapacity, src, srcSize, NULL, 0, compressionLevel);
}
size_t ZSTD_compress(void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel)
@@ -2284,9 +2595,7 @@ size_t ZSTD_estimateCDictSize_advanced(
ZSTD_dictLoadMethod_e dictLoadMethod)
{
DEBUGLOG(5, "sizeof(ZSTD_CDict) : %u", (U32)sizeof(ZSTD_CDict));
- DEBUGLOG(5, "CCtx estimate : %u",
- (U32)ZSTD_estimateCCtxSize_usingCParams(cParams));
- return sizeof(ZSTD_CDict) + ZSTD_estimateCCtxSize_usingCParams(cParams)
+ return sizeof(ZSTD_CDict) + HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0)
+ (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize);
}
@@ -2300,23 +2609,24 @@ size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict)
{
if (cdict==NULL) return 0; /* support sizeof on NULL */
DEBUGLOG(5, "sizeof(*cdict) : %u", (U32)sizeof(*cdict));
- DEBUGLOG(5, "ZSTD_sizeof_CCtx : %u", (U32)ZSTD_sizeof_CCtx(cdict->refContext));
- return ZSTD_sizeof_CCtx(cdict->refContext) + (cdict->dictBuffer ? cdict->dictContentSize : 0) + sizeof(*cdict);
+ return cdict->workspaceSize + (cdict->dictBuffer ? cdict->dictContentSize : 0) + sizeof(*cdict);
}
static size_t ZSTD_initCDict_internal(
ZSTD_CDict* cdict,
const void* dictBuffer, size_t dictSize,
ZSTD_dictLoadMethod_e dictLoadMethod,
- ZSTD_dictMode_e dictMode,
+ ZSTD_dictContentType_e dictContentType,
ZSTD_compressionParameters cParams)
{
- DEBUGLOG(3, "ZSTD_initCDict_internal, mode %u", (U32)dictMode);
+ DEBUGLOG(3, "ZSTD_initCDict_internal, dictContentType %u", (U32)dictContentType);
+ assert(!ZSTD_checkCParams(cParams));
+ cdict->cParams = cParams;
if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) {
cdict->dictBuffer = NULL;
cdict->dictContent = dictBuffer;
} else {
- void* const internalBuffer = ZSTD_malloc(dictSize, cdict->refContext->customMem);
+ void* const internalBuffer = ZSTD_malloc(dictSize, cdict->customMem);
cdict->dictBuffer = internalBuffer;
cdict->dictContent = internalBuffer;
if (!internalBuffer) return ERROR(memory_allocation);
@@ -2324,13 +2634,31 @@ static size_t ZSTD_initCDict_internal(
}
cdict->dictContentSize = dictSize;
- { ZSTD_CCtx_params cctxParams = cdict->refContext->requestedParams;
- cctxParams.cParams = cParams;
- CHECK_F( ZSTD_compressBegin_internal(cdict->refContext,
- cdict->dictContent, dictSize, dictMode,
- NULL,
- cctxParams, ZSTD_CONTENTSIZE_UNKNOWN,
- ZSTDb_not_buffered) );
+ /* Reset the state to no dictionary */
+ ZSTD_reset_compressedBlockState(&cdict->cBlockState);
+ { void* const end = ZSTD_reset_matchState(
+ &cdict->matchState,
+ (U32*)cdict->workspace + HUF_WORKSPACE_SIZE_U32,
+ &cParams, ZSTDcrp_continue, /* forCCtx */ 0);
+ assert(end == (char*)cdict->workspace + cdict->workspaceSize);
+ (void)end;
+ }
+ /* (Maybe) load the dictionary
+ * Skips loading the dictionary if it is <= 8 bytes.
+ */
+ { ZSTD_CCtx_params params;
+ memset(&params, 0, sizeof(params));
+ params.compressionLevel = ZSTD_CLEVEL_DEFAULT;
+ params.fParams.contentSizeFlag = 1;
+ params.cParams = cParams;
+ { size_t const dictID = ZSTD_compress_insertDictionary(
+ &cdict->cBlockState, &cdict->matchState, &params,
+ cdict->dictContent, cdict->dictContentSize,
+ dictContentType, cdict->workspace);
+ if (ZSTD_isError(dictID)) return dictID;
+ assert(dictID <= (size_t)(U32)-1);
+ cdict->dictID = (U32)dictID;
+ }
}
return 0;
@@ -2338,24 +2666,27 @@ static size_t ZSTD_initCDict_internal(
ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize,
ZSTD_dictLoadMethod_e dictLoadMethod,
- ZSTD_dictMode_e dictMode,
+ ZSTD_dictContentType_e dictContentType,
ZSTD_compressionParameters cParams, ZSTD_customMem customMem)
{
- DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (U32)dictMode);
+ DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (U32)dictContentType);
if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
{ ZSTD_CDict* const cdict = (ZSTD_CDict*)ZSTD_malloc(sizeof(ZSTD_CDict), customMem);
- ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(customMem);
+ size_t const workspaceSize = HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0);
+ void* const workspace = ZSTD_malloc(workspaceSize, customMem);
- if (!cdict || !cctx) {
+ if (!cdict || !workspace) {
ZSTD_free(cdict, customMem);
- ZSTD_freeCCtx(cctx);
+ ZSTD_free(workspace, customMem);
return NULL;
}
- cdict->refContext = cctx;
+ cdict->customMem = customMem;
+ cdict->workspace = workspace;
+ cdict->workspaceSize = workspaceSize;
if (ZSTD_isError( ZSTD_initCDict_internal(cdict,
dictBuffer, dictSize,
- dictLoadMethod, dictMode,
+ dictLoadMethod, dictContentType,
cParams) )) {
ZSTD_freeCDict(cdict);
return NULL;
@@ -2369,7 +2700,7 @@ ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionL
{
ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, 0, dictSize);
return ZSTD_createCDict_advanced(dict, dictSize,
- ZSTD_dlm_byCopy, ZSTD_dm_auto,
+ ZSTD_dlm_byCopy, ZSTD_dct_auto,
cParams, ZSTD_defaultCMem);
}
@@ -2377,15 +2708,15 @@ ZSTD_CDict* ZSTD_createCDict_byReference(const void* dict, size_t dictSize, int
{
ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, 0, dictSize);
return ZSTD_createCDict_advanced(dict, dictSize,
- ZSTD_dlm_byRef, ZSTD_dm_auto,
+ ZSTD_dlm_byRef, ZSTD_dct_auto,
cParams, ZSTD_defaultCMem);
}
size_t ZSTD_freeCDict(ZSTD_CDict* cdict)
{
if (cdict==NULL) return 0; /* support free on NULL */
- { ZSTD_customMem const cMem = cdict->refContext->customMem;
- ZSTD_freeCCtx(cdict->refContext);
+ { ZSTD_customMem const cMem = cdict->customMem;
+ ZSTD_free(cdict->workspace, cMem);
ZSTD_free(cdict->dictBuffer, cMem);
ZSTD_free(cdict, cMem);
return 0;
@@ -2405,18 +2736,18 @@ size_t ZSTD_freeCDict(ZSTD_CDict* cdict)
* Note : there is no corresponding "free" function.
* Since workspace was allocated externally, it must be freed externally.
*/
-ZSTD_CDict* ZSTD_initStaticCDict(void* workspace, size_t workspaceSize,
+const ZSTD_CDict* ZSTD_initStaticCDict(
+ void* workspace, size_t workspaceSize,
const void* dict, size_t dictSize,
ZSTD_dictLoadMethod_e dictLoadMethod,
- ZSTD_dictMode_e dictMode,
+ ZSTD_dictContentType_e dictContentType,
ZSTD_compressionParameters cParams)
{
- size_t const cctxSize = ZSTD_estimateCCtxSize_usingCParams(cParams);
+ size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0);
size_t const neededSize = sizeof(ZSTD_CDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize)
- + cctxSize;
+ + HUF_WORKSPACE_SIZE + matchStateSize;
ZSTD_CDict* const cdict = (ZSTD_CDict*) workspace;
void* ptr;
- DEBUGLOG(4, "(size_t)workspace & 7 : %u", (U32)(size_t)workspace & 7);
if ((size_t)workspace & 7) return NULL; /* 8-aligned */
DEBUGLOG(4, "(workspaceSize < neededSize) : (%u < %u) => %u",
(U32)workspaceSize, (U32)neededSize, (U32)(workspaceSize < neededSize));
@@ -2429,19 +2760,22 @@ ZSTD_CDict* ZSTD_initStaticCDict(void* workspace, size_t workspaceSize,
} else {
ptr = cdict+1;
}
- cdict->refContext = ZSTD_initStaticCCtx(ptr, cctxSize);
+ cdict->workspace = ptr;
+ cdict->workspaceSize = HUF_WORKSPACE_SIZE + matchStateSize;
if (ZSTD_isError( ZSTD_initCDict_internal(cdict,
dict, dictSize,
- ZSTD_dlm_byRef, dictMode,
+ ZSTD_dlm_byRef, dictContentType,
cParams) ))
return NULL;
return cdict;
}
-ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict) {
- return cdict->refContext->appliedParams.cParams;
+ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict)
+{
+ assert(cdict != NULL);
+ return cdict->cParams;
}
/* ZSTD_compressBegin_usingCDict_advanced() :
@@ -2454,9 +2788,18 @@ size_t ZSTD_compressBegin_usingCDict_advanced(
if (cdict==NULL) return ERROR(dictionary_wrong);
{ ZSTD_CCtx_params params = cctx->requestedParams;
params.cParams = ZSTD_getCParamsFromCDict(cdict);
+ /* Increase window log to fit the entire dictionary and source if the
+ * source size is known. Limit the increase to 19, which is the
+ * window log for compression level 1 with the largest source size.
+ */
+ if (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN) {
+ U32 const limitedSrcSize = (U32)MIN(pledgedSrcSize, 1U << 19);
+ U32 const limitedSrcLog = limitedSrcSize > 1 ? ZSTD_highbit32(limitedSrcSize - 1) + 1 : 1;
+ params.cParams.windowLog = MAX(params.cParams.windowLog, limitedSrcLog);
+ }
params.fParams = fParams;
return ZSTD_compressBegin_internal(cctx,
- NULL, 0, ZSTD_dm_auto,
+ NULL, 0, ZSTD_dct_auto,
cdict,
params, pledgedSrcSize,
ZSTDb_not_buffered);
@@ -2534,29 +2877,30 @@ size_t ZSTD_CStreamOutSize(void)
return ZSTD_compressBound(ZSTD_BLOCKSIZE_MAX) + ZSTD_blockHeaderSize + 4 /* 32-bits hash */ ;
}
-static size_t ZSTD_resetCStream_internal(ZSTD_CStream* zcs,
- const void* const dict, size_t const dictSize, ZSTD_dictMode_e const dictMode,
+static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx,
+ const void* const dict, size_t const dictSize, ZSTD_dictContentType_e const dictContentType,
const ZSTD_CDict* const cdict,
ZSTD_CCtx_params const params, unsigned long long const pledgedSrcSize)
{
- DEBUGLOG(4, "ZSTD_resetCStream_internal");
+ DEBUGLOG(4, "ZSTD_resetCStream_internal (disableLiteralCompression=%i)",
+ params.disableLiteralCompression);
/* params are supposed to be fully validated at this point */
assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
assert(!((dict) && (cdict))); /* either dict or cdict, not both */
- CHECK_F( ZSTD_compressBegin_internal(zcs,
- dict, dictSize, dictMode,
+ CHECK_F( ZSTD_compressBegin_internal(cctx,
+ dict, dictSize, dictContentType,
cdict,
params, pledgedSrcSize,
ZSTDb_buffered) );
- zcs->inToCompress = 0;
- zcs->inBuffPos = 0;
- zcs->inBuffTarget = zcs->blockSize
- + (zcs->blockSize == pledgedSrcSize); /* for small input: avoid automatic flush on reaching end of block, since it would require to add a 3-bytes null block to end frame */
- zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0;
- zcs->streamStage = zcss_load;
- zcs->frameEnded = 0;
+ cctx->inToCompress = 0;
+ cctx->inBuffPos = 0;
+ cctx->inBuffTarget = cctx->blockSize
+ + (cctx->blockSize == pledgedSrcSize); /* for small input: avoid automatic flush on reaching end of block, since it would require to add a 3-bytes null block to end frame */
+ cctx->outBuffContentSize = cctx->outBuffFlushedSize = 0;
+ cctx->streamStage = zcss_load;
+ cctx->frameEnded = 0;
return 0; /* ready to go */
}
@@ -2568,8 +2912,8 @@ size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize)
DEBUGLOG(4, "ZSTD_resetCStream: pledgedSrcSize = %u", (U32)pledgedSrcSize);
if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
params.fParams.contentSizeFlag = 1;
- params.cParams = ZSTD_getCParamsFromCCtxParams(params, pledgedSrcSize, 0);
- return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, zcs->cdict, params, pledgedSrcSize);
+ params.cParams = ZSTD_getCParamsFromCCtxParams(&params, pledgedSrcSize, 0);
+ return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dct_auto, zcs->cdict, params, pledgedSrcSize);
}
/*! ZSTD_initCStream_internal() :
@@ -2592,7 +2936,7 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs,
}
ZSTD_freeCDict(zcs->cdictLocal);
zcs->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize,
- ZSTD_dlm_byCopy, ZSTD_dm_auto,
+ ZSTD_dlm_byCopy, ZSTD_dct_auto,
params.cParams, zcs->customMem);
zcs->cdict = zcs->cdictLocal;
if (zcs->cdictLocal == NULL) return ERROR(memory_allocation);
@@ -2605,10 +2949,7 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs,
zcs->cdict = cdict;
}
- params.compressionLevel = ZSTD_CLEVEL_CUSTOM; /* enforce usage of cParams, instead of a dynamic derivation from cLevel (but does that happen ?) */
- zcs->requestedParams = params;
-
- return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, zcs->cdict, params, pledgedSrcSize);
+ return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dct_auto, zcs->cdict, params, pledgedSrcSize);
}
/* ZSTD_initCStream_usingCDict_advanced() :
@@ -2637,20 +2978,22 @@ size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict)
return ZSTD_initCStream_usingCDict_advanced(zcs, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN); /* note : will check that cdict != NULL */
}
+
/* ZSTD_initCStream_advanced() :
- * pledgedSrcSize must be correct.
+ * pledgedSrcSize must be exact.
* if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN.
* dict is loaded with default parameters ZSTD_dm_auto and ZSTD_dlm_byCopy. */
size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
const void* dict, size_t dictSize,
ZSTD_parameters params, unsigned long long pledgedSrcSize)
{
- ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params);
DEBUGLOG(4, "ZSTD_initCStream_advanced: pledgedSrcSize=%u, flag=%u",
(U32)pledgedSrcSize, params.fParams.contentSizeFlag);
CHECK_F( ZSTD_checkCParams(params.cParams) );
if ((pledgedSrcSize==0) && (params.fParams.contentSizeFlag==0)) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* for compatibility with older programs relying on this behavior. Users should now specify ZSTD_CONTENTSIZE_UNKNOWN. This line will be removed in the future. */
- return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL /*cdict*/, cctxParams, pledgedSrcSize);
+ { ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params);
+ return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL /*cdict*/, cctxParams, pledgedSrcSize);
+ }
}
size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel)
@@ -2687,7 +3030,7 @@ MEM_STATIC size_t ZSTD_limitCopy(void* dst, size_t dstCapacity,
/** ZSTD_compressStream_generic():
* internal function for all *compressStream*() variants and *compress_generic()
- * non-static, because can be called from zstdmt.c
+ * non-static, because can be called from zstdmt_compress.c
* @return : hint size for next input */
size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
ZSTD_outBuffer* output,
@@ -2862,46 +3205,56 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx,
DEBUGLOG(4, "ZSTD_compress_generic : transparent init stage");
if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = input->size + 1; /* auto-fix pledgedSrcSize */
params.cParams = ZSTD_getCParamsFromCCtxParams(
- cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/);
+ &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/);
#ifdef ZSTD_MULTITHREAD
- if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN)
- params.nbThreads = 1; /* do not invoke multi-threading when src size is too small */
- if (params.nbThreads > 1) {
- if (cctx->mtctx == NULL || (params.nbThreads != ZSTDMT_getNbThreads(cctx->mtctx))) {
- DEBUGLOG(4, "ZSTD_compress_generic: creating new mtctx for nbThreads=%u (previous: %u)",
- params.nbThreads, ZSTDMT_getNbThreads(cctx->mtctx));
+ if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) {
+ params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */
+ }
+ if (params.nbWorkers > 0) {
+ /* mt context creation */
+ if (cctx->mtctx == NULL || (params.nbWorkers != ZSTDMT_getNbWorkers(cctx->mtctx))) {
+ DEBUGLOG(4, "ZSTD_compress_generic: creating new mtctx for nbWorkers=%u",
+ params.nbWorkers);
+ if (cctx->mtctx != NULL)
+ DEBUGLOG(4, "ZSTD_compress_generic: previous nbWorkers was %u",
+ ZSTDMT_getNbWorkers(cctx->mtctx));
ZSTDMT_freeCCtx(cctx->mtctx);
- cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbThreads, cctx->customMem);
+ cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbWorkers, cctx->customMem);
if (cctx->mtctx == NULL) return ERROR(memory_allocation);
}
- DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbThreads=%u", params.nbThreads);
+ /* mt compression */
+ DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbWorkers=%u", params.nbWorkers);
CHECK_F( ZSTDMT_initCStream_internal(
cctx->mtctx,
- prefixDict.dict, prefixDict.dictSize, ZSTD_dm_rawContent,
+ prefixDict.dict, prefixDict.dictSize, ZSTD_dct_rawContent,
cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) );
cctx->streamStage = zcss_load;
- cctx->appliedParams.nbThreads = params.nbThreads;
+ cctx->appliedParams.nbWorkers = params.nbWorkers;
} else
#endif
- { CHECK_F( ZSTD_resetCStream_internal(
- cctx, prefixDict.dict, prefixDict.dictSize,
- prefixDict.dictMode, cctx->cdict, params,
- cctx->pledgedSrcSizePlusOne-1) );
+ { CHECK_F( ZSTD_resetCStream_internal(cctx,
+ prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType,
+ cctx->cdict,
+ params, cctx->pledgedSrcSizePlusOne-1) );
assert(cctx->streamStage == zcss_load);
- assert(cctx->appliedParams.nbThreads <= 1);
+ assert(cctx->appliedParams.nbWorkers == 0);
} }
/* compression stage */
#ifdef ZSTD_MULTITHREAD
- if (cctx->appliedParams.nbThreads > 1) {
- size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp);
- if ( ZSTD_isError(flushMin)
- || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */
- ZSTD_startNewCompression(cctx);
+ if (cctx->appliedParams.nbWorkers > 0) {
+ if (cctx->cParamsChanged) {
+ ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams);
+ cctx->cParamsChanged = 0;
}
- return flushMin;
- }
+ { size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp);
+ if ( ZSTD_isError(flushMin)
+ || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */
+ ZSTD_startNewCompression(cctx);
+ }
+ return flushMin;
+ } }
#endif
CHECK_F( ZSTD_compressStream_generic(cctx, output, input, endOp) );
DEBUGLOG(5, "completed ZSTD_compress_generic");
@@ -2927,7 +3280,7 @@ size_t ZSTD_compress_generic_simpleArgs (
/*====== Finalize ======*/
/*! ZSTD_flushStream() :
-* @return : amount of data remaining to flush */
+ * @return : amount of data remaining to flush */
size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
{
ZSTD_inBuffer input = { NULL, 0, 0 };
@@ -2959,11 +3312,11 @@ int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; }
static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = {
{ /* "default" - guarantees a monotonically increasing memory budget */
/* W, C, H, S, L, TL, strat */
- { 18, 12, 12, 1, 7, 16, ZSTD_fast }, /* level 0 - never used */
- { 19, 13, 14, 1, 7, 16, ZSTD_fast }, /* level 1 */
- { 19, 15, 16, 1, 6, 16, ZSTD_fast }, /* level 2 */
- { 20, 16, 17, 1, 5, 16, ZSTD_dfast }, /* level 3 */
- { 20, 17, 18, 1, 5, 16, ZSTD_dfast }, /* level 4 */
+ { 19, 12, 13, 1, 6, 1, ZSTD_fast }, /* base for negative levels */
+ { 19, 13, 14, 1, 7, 1, ZSTD_fast }, /* level 1 */
+ { 19, 15, 16, 1, 6, 1, ZSTD_fast }, /* level 2 */
+ { 20, 16, 17, 1, 5, 8, ZSTD_dfast }, /* level 3 */
+ { 20, 17, 18, 1, 5, 8, ZSTD_dfast }, /* level 4 */
{ 20, 17, 18, 2, 5, 16, ZSTD_greedy }, /* level 5 */
{ 21, 17, 19, 2, 5, 16, ZSTD_lazy }, /* level 6 */
{ 21, 18, 19, 3, 5, 16, ZSTD_lazy }, /* level 7 */
@@ -2972,9 +3325,9 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV
{ 21, 19, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */
{ 22, 20, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */
{ 22, 20, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */
- { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 13 */
- { 22, 21, 22, 6, 5, 16, ZSTD_lazy2 }, /* level 14 */
- { 22, 21, 22, 4, 5, 16, ZSTD_btlazy2 }, /* level 15 */
+ { 22, 21, 22, 4, 5, 32, ZSTD_btlazy2 }, /* level 13 */
+ { 22, 21, 22, 5, 5, 32, ZSTD_btlazy2 }, /* level 14 */
+ { 22, 22, 22, 6, 5, 32, ZSTD_btlazy2 }, /* level 15 */
{ 22, 21, 22, 4, 5, 48, ZSTD_btopt }, /* level 16 */
{ 23, 22, 22, 4, 4, 48, ZSTD_btopt }, /* level 17 */
{ 23, 22, 22, 5, 3, 64, ZSTD_btopt }, /* level 18 */
@@ -2985,8 +3338,8 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV
},
{ /* for srcSize <= 256 KB */
/* W, C, H, S, L, T, strat */
- { 0, 0, 0, 0, 0, 0, ZSTD_fast }, /* level 0 - not used */
- { 18, 13, 14, 1, 6, 8, ZSTD_fast }, /* level 1 */
+ { 18, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */
+ { 18, 13, 14, 1, 6, 1, ZSTD_fast }, /* level 1 */
{ 18, 14, 13, 1, 5, 8, ZSTD_dfast }, /* level 2 */
{ 18, 16, 15, 1, 5, 8, ZSTD_dfast }, /* level 3 */
{ 18, 15, 17, 1, 5, 8, ZSTD_greedy }, /* level 4.*/
@@ -2997,8 +3350,8 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV
{ 18, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */
{ 18, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */
{ 18, 18, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 11.*/
- { 18, 18, 17, 7, 4, 8, ZSTD_lazy2 }, /* level 12.*/
- { 18, 19, 17, 6, 4, 8, ZSTD_btlazy2 }, /* level 13 */
+ { 18, 18, 17, 5, 4, 8, ZSTD_btlazy2 }, /* level 12.*/
+ { 18, 19, 17, 7, 4, 8, ZSTD_btlazy2 }, /* level 13 */
{ 18, 18, 18, 4, 4, 16, ZSTD_btopt }, /* level 14.*/
{ 18, 18, 18, 4, 3, 16, ZSTD_btopt }, /* level 15.*/
{ 18, 19, 18, 6, 3, 32, ZSTD_btopt }, /* level 16.*/
@@ -3011,9 +3364,9 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV
},
{ /* for srcSize <= 128 KB */
/* W, C, H, S, L, T, strat */
- { 17, 12, 12, 1, 7, 8, ZSTD_fast }, /* level 0 - not used */
- { 17, 12, 13, 1, 6, 8, ZSTD_fast }, /* level 1 */
- { 17, 13, 16, 1, 5, 8, ZSTD_fast }, /* level 2 */
+ { 17, 12, 12, 1, 5, 1, ZSTD_fast }, /* level 0 - not used */
+ { 17, 12, 13, 1, 6, 1, ZSTD_fast }, /* level 1 */
+ { 17, 13, 16, 1, 5, 1, ZSTD_fast }, /* level 2 */
{ 17, 16, 16, 2, 5, 8, ZSTD_dfast }, /* level 3 */
{ 17, 13, 15, 3, 4, 8, ZSTD_greedy }, /* level 4 */
{ 17, 15, 17, 4, 4, 8, ZSTD_greedy }, /* level 5 */
@@ -3037,9 +3390,9 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV
},
{ /* for srcSize <= 16 KB */
/* W, C, H, S, L, T, strat */
- { 14, 12, 12, 1, 7, 6, ZSTD_fast }, /* level 0 - not used */
- { 14, 14, 14, 1, 6, 6, ZSTD_fast }, /* level 1 */
- { 14, 14, 14, 1, 4, 6, ZSTD_fast }, /* level 2 */
+ { 14, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */
+ { 14, 14, 14, 1, 6, 1, ZSTD_fast }, /* level 1 */
+ { 14, 14, 14, 1, 4, 1, ZSTD_fast }, /* level 2 */
{ 14, 14, 14, 1, 4, 6, ZSTD_dfast }, /* level 3.*/
{ 14, 14, 14, 4, 4, 6, ZSTD_greedy }, /* level 4.*/
{ 14, 14, 14, 3, 4, 6, ZSTD_lazy }, /* level 5.*/
@@ -3063,47 +3416,22 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV
},
};
-#if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=1)
-/* This function just controls
- * the monotonic memory budget increase of ZSTD_defaultCParameters[0].
- * Run once, on first ZSTD_getCParams() usage, if ZSTD_DEBUG is enabled
- */
-MEM_STATIC void ZSTD_check_compressionLevel_monotonicIncrease_memoryBudget(void)
-{
- int level;
- for (level=1; level<ZSTD_maxCLevel(); level++) {
- ZSTD_compressionParameters const c1 = ZSTD_defaultCParameters[0][level];
- ZSTD_compressionParameters const c2 = ZSTD_defaultCParameters[0][level+1];
- assert(c1.windowLog <= c2.windowLog);
-# define ZSTD_TABLECOST(h,c) ((1<<(h)) + (1<<(c)))
- assert(ZSTD_TABLECOST(c1.hashLog, c1.chainLog) <= ZSTD_TABLECOST(c2.hashLog, c2.chainLog));
- }
-}
-#endif
-
/*! ZSTD_getCParams() :
-* @return ZSTD_compressionParameters structure for a selected compression level, `srcSize` and `dictSize`.
+* @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize.
* Size values are optional, provide 0 if not known or unused */
ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize)
{
size_t const addedSize = srcSizeHint ? 0 : 500;
U64 const rSize = srcSizeHint+dictSize ? srcSizeHint+dictSize+addedSize : (U64)-1;
U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB); /* intentional underflow for srcSizeHint == 0 */
-
-#if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=1)
- static int g_monotonicTest = 1;
- if (g_monotonicTest) {
- ZSTD_check_compressionLevel_monotonicIncrease_memoryBudget();
- g_monotonicTest=0;
- }
-#endif
-
- DEBUGLOG(4, "ZSTD_getCParams: cLevel=%i, srcSize=%u, dictSize=%u => table %u",
- compressionLevel, (U32)srcSizeHint, (U32)dictSize, tableID);
- if (compressionLevel <= 0) compressionLevel = ZSTD_CLEVEL_DEFAULT; /* 0 == default; no negative compressionLevel yet */
- if (compressionLevel > ZSTD_MAX_CLEVEL) compressionLevel = ZSTD_MAX_CLEVEL;
- { ZSTD_compressionParameters const cp = ZSTD_defaultCParameters[tableID][compressionLevel];
- return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize); }
+ int row = compressionLevel;
+ DEBUGLOG(5, "ZSTD_getCParams (cLevel=%i)", compressionLevel);
+ if (compressionLevel == 0) row = ZSTD_CLEVEL_DEFAULT; /* 0 == default */
+ if (compressionLevel < 0) row = 0; /* entry 0 is baseline for fast mode */
+ if (compressionLevel > ZSTD_MAX_CLEVEL) row = ZSTD_MAX_CLEVEL;
+ { ZSTD_compressionParameters cp = ZSTD_defaultCParameters[tableID][row];
+ if (compressionLevel < 0) cp.targetLength = (unsigned)(-compressionLevel); /* acceleration factor */
+ return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize); }
}
@@ -3113,6 +3441,7 @@ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long l
ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) {
ZSTD_parameters params;
ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, srcSizeHint, dictSize);
+ DEBUGLOG(5, "ZSTD_getParams (cLevel=%i)", compressionLevel);
memset(&params, 0, sizeof(params));
params.cParams = cParams;
params.fParams.contentSizeFlag = 1;
diff --git a/thirdparty/zstd/compress/zstd_compress_internal.h b/thirdparty/zstd/compress/zstd_compress_internal.h
index f104fe981e..81f12ca6df 100644
--- a/thirdparty/zstd/compress/zstd_compress_internal.h
+++ b/thirdparty/zstd/compress/zstd_compress_internal.h
@@ -30,8 +30,14 @@ extern "C" {
/*-*************************************
* Constants
***************************************/
-static const U32 g_searchStrength = 8;
-#define HASH_READ_SIZE 8
+#define kSearchStrength 8
+#define HASH_READ_SIZE 8
+#define ZSTD_DUBT_UNSORTED_MARK 1 /* For btlazy2 strategy, index 1 now means "unsorted".
+ It could be confused for a real successor at index "1", if sorted as larger than its predecessor.
+ It's not a big deal though : candidate will just be sorted again.
+ Additionnally, candidate position 1 will be lost.
+ But candidate 1 cannot hide a large tree of candidates, so it's a minimal loss.
+ The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled after table re-use with a different strategy */
/*-*************************************
@@ -43,7 +49,7 @@ typedef enum { zcss_init=0, zcss_load, zcss_flush } ZSTD_cStreamStage;
typedef struct ZSTD_prefixDict_s {
const void* dict;
size_t dictSize;
- ZSTD_dictMode_e dictMode;
+ ZSTD_dictContentType_e dictContentType;
} ZSTD_prefixDict;
typedef struct {
@@ -51,7 +57,6 @@ typedef struct {
FSE_CTable offcodeCTable[FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)];
FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)];
FSE_CTable litlengthCTable[FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)];
- U32 workspace[HUF_WORKSPACE_SIZE_U32];
HUF_repeat hufCTable_repeatMode;
FSE_repeat offcode_repeatMode;
FSE_repeat matchlength_repeatMode;
@@ -94,11 +99,43 @@ typedef struct {
} optState_t;
typedef struct {
+ ZSTD_entropyCTables_t entropy;
+ U32 rep[ZSTD_REP_NUM];
+} ZSTD_compressedBlockState_t;
+
+typedef struct {
+ BYTE const* nextSrc; /* next block here to continue on current prefix */
+ BYTE const* base; /* All regular indexes relative to this position */
+ BYTE const* dictBase; /* extDict indexes relative to this position */
+ U32 dictLimit; /* below that point, need extDict */
+ U32 lowLimit; /* below that point, no more data */
+} ZSTD_window_t;
+
+typedef struct {
+ ZSTD_window_t window; /* State for window round buffer management */
+ U32 loadedDictEnd; /* index of end of dictionary */
+ U32 nextToUpdate; /* index from which to continue table update */
+ U32 nextToUpdate3; /* index from which to continue table update */
+ U32 hashLog3; /* dispatch table : larger == faster, more memory */
+ U32* hashTable;
+ U32* hashTable3;
+ U32* chainTable;
+ optState_t opt; /* optimal parser state */
+} ZSTD_matchState_t;
+
+typedef struct {
+ ZSTD_compressedBlockState_t* prevCBlock;
+ ZSTD_compressedBlockState_t* nextCBlock;
+ ZSTD_matchState_t matchState;
+} ZSTD_blockState_t;
+
+typedef struct {
U32 offset;
U32 checksum;
} ldmEntry_t;
typedef struct {
+ ZSTD_window_t window; /* State for the window round buffer management */
ldmEntry_t* hashTable;
BYTE* bucketOffsets; /* Next position in bucket to insert entry */
U64 hashPower; /* Used to compute the rolling hash.
@@ -111,60 +148,68 @@ typedef struct {
U32 bucketSizeLog; /* Log bucket size for collision resolution, at most 8 */
U32 minMatchLength; /* Minimum match length */
U32 hashEveryLog; /* Log number of entries to skip */
+ U32 windowLog; /* Window log for the LDM */
} ldmParams_t;
+typedef struct {
+ U32 offset;
+ U32 litLength;
+ U32 matchLength;
+} rawSeq;
+
+typedef struct {
+ rawSeq* seq; /* The start of the sequences */
+ size_t pos; /* The position where reading stopped. <= size. */
+ size_t size; /* The number of sequences. <= capacity. */
+ size_t capacity; /* The capacity of the `seq` pointer */
+} rawSeqStore_t;
+
struct ZSTD_CCtx_params_s {
ZSTD_format_e format;
ZSTD_compressionParameters cParams;
ZSTD_frameParameters fParams;
int compressionLevel;
- U32 forceWindow; /* force back-references to respect limit of
+ int disableLiteralCompression;
+ int forceWindow; /* force back-references to respect limit of
* 1<<wLog, even for dictionary */
/* Multithreading: used to pass parameters to mtctx */
- U32 nbThreads;
+ unsigned nbWorkers;
unsigned jobSize;
unsigned overlapSizeLog;
/* Long distance matching parameters */
ldmParams_t ldmParams;
- /* For use with createCCtxParams() and freeCCtxParams() only */
+ /* Internal use, for createCCtxParams() and freeCCtxParams() only */
ZSTD_customMem customMem;
-
}; /* typedef'd to ZSTD_CCtx_params within "zstd.h" */
struct ZSTD_CCtx_s {
- const BYTE* nextSrc; /* next block here to continue on current prefix */
- const BYTE* base; /* All regular indexes relative to this position */
- const BYTE* dictBase; /* extDict indexes relative to this position */
- U32 dictLimit; /* below that point, need extDict */
- U32 lowLimit; /* below that point, no more data */
- U32 nextToUpdate; /* index from which to continue dictionary update */
- U32 nextToUpdate3; /* index from which to continue dictionary update */
- U32 hashLog3; /* dispatch table : larger == faster, more memory */
- U32 loadedDictEnd; /* index of end of dictionary */
ZSTD_compressionStage_e stage;
- U32 dictID;
+ int cParamsChanged; /* == 1 if cParams(except wlog) or compression level are changed in requestedParams. Triggers transmission of new params to ZSTDMT (if available) then reset to 0. */
+ int bmi2; /* == 1 if the CPU supports BMI2 and 0 otherwise. CPU support is determined dynamically once per context lifetime. */
ZSTD_CCtx_params requestedParams;
ZSTD_CCtx_params appliedParams;
+ U32 dictID;
void* workSpace;
size_t workSpaceSize;
size_t blockSize;
- U64 pledgedSrcSizePlusOne; /* this way, 0 (default) == unknown */
- U64 consumedSrcSize;
+ unsigned long long pledgedSrcSizePlusOne; /* this way, 0 (default) == unknown */
+ unsigned long long consumedSrcSize;
+ unsigned long long producedCSize;
XXH64_state_t xxhState;
ZSTD_customMem customMem;
size_t staticSize;
- seqStore_t seqStore; /* sequences storage ptrs */
- optState_t optState;
- ldmState_t ldmState; /* long distance matching state */
- U32* hashTable;
- U32* hashTable3;
- U32* chainTable;
- ZSTD_entropyCTables_t* entropy;
+ seqStore_t seqStore; /* sequences storage ptrs */
+ ldmState_t ldmState; /* long distance matching state */
+ rawSeq* ldmSequences; /* Storage for the ldm output sequences */
+ size_t maxNbLdmSequences;
+ rawSeqStore_t externSeqStore; /* Mutable reference to external sequences */
+ ZSTD_blockState_t blockState;
+ U32* entropyWorkspace; /* entropy workspace of HUF_WORKSPACE_SIZE bytes */
/* streaming */
char* inBuff;
@@ -191,6 +236,12 @@ struct ZSTD_CCtx_s {
};
+typedef size_t (*ZSTD_blockCompressor) (
+ ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict);
+
+
MEM_STATIC U32 ZSTD_LLcode(U32 litLength)
{
static const BYTE LL_Code[64] = { 0, 1, 2, 3, 4, 5, 6, 7,
@@ -359,10 +410,12 @@ MEM_STATIC size_t ZSTD_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* co
}
/** ZSTD_count_2segments() :
-* can count match length with `ip` & `match` in 2 different segments.
-* convention : on reaching mEnd, match count continue starting from iStart
-*/
-MEM_STATIC size_t ZSTD_count_2segments(const BYTE* ip, const BYTE* match, const BYTE* iEnd, const BYTE* mEnd, const BYTE* iStart)
+ * can count match length with `ip` & `match` in 2 different segments.
+ * convention : on reaching mEnd, match count continue starting from iStart
+ */
+MEM_STATIC size_t
+ZSTD_count_2segments(const BYTE* ip, const BYTE* match,
+ const BYTE* iEnd, const BYTE* mEnd, const BYTE* iStart)
{
const BYTE* const vEnd = MIN( ip + (mEnd - match), iEnd);
size_t const matchLength = ZSTD_count(ip, match, vEnd);
@@ -372,8 +425,8 @@ MEM_STATIC size_t ZSTD_count_2segments(const BYTE* ip, const BYTE* match, const
/*-*************************************
-* Hashes
-***************************************/
+ * Hashes
+ ***************************************/
static const U32 prime3bytes = 506832829U;
static U32 ZSTD_hash3(U32 u, U32 h) { return ((u << (32-24)) * prime3bytes) >> (32-h) ; }
MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h); } /* only in zstd_opt.h */
@@ -411,6 +464,171 @@ MEM_STATIC size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls)
}
}
+/*-*************************************
+* Round buffer management
+***************************************/
+/* Max current allowed */
+#define ZSTD_CURRENT_MAX ((3U << 29) + (1U << ZSTD_WINDOWLOG_MAX))
+/* Maximum chunk size before overflow correction needs to be called again */
+#define ZSTD_CHUNKSIZE_MAX \
+ ( ((U32)-1) /* Maximum ending current index */ \
+ - ZSTD_CURRENT_MAX) /* Maximum beginning lowLimit */
+
+/**
+ * ZSTD_window_clear():
+ * Clears the window containing the history by simply setting it to empty.
+ */
+MEM_STATIC void ZSTD_window_clear(ZSTD_window_t* window)
+{
+ size_t const endT = (size_t)(window->nextSrc - window->base);
+ U32 const end = (U32)endT;
+
+ window->lowLimit = end;
+ window->dictLimit = end;
+}
+
+/**
+ * ZSTD_window_hasExtDict():
+ * Returns non-zero if the window has a non-empty extDict.
+ */
+MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window)
+{
+ return window.lowLimit < window.dictLimit;
+}
+
+/**
+ * ZSTD_window_needOverflowCorrection():
+ * Returns non-zero if the indices are getting too large and need overflow
+ * protection.
+ */
+MEM_STATIC U32 ZSTD_window_needOverflowCorrection(ZSTD_window_t const window,
+ void const* srcEnd)
+{
+ U32 const current = (U32)((BYTE const*)srcEnd - window.base);
+ return current > ZSTD_CURRENT_MAX;
+}
+
+/**
+ * ZSTD_window_correctOverflow():
+ * Reduces the indices to protect from index overflow.
+ * Returns the correction made to the indices, which must be applied to every
+ * stored index.
+ *
+ * The least significant cycleLog bits of the indices must remain the same,
+ * which may be 0. Every index up to maxDist in the past must be valid.
+ * NOTE: (maxDist & cycleMask) must be zero.
+ */
+MEM_STATIC U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog,
+ U32 maxDist, void const* src)
+{
+ /* preemptive overflow correction:
+ * 1. correction is large enough:
+ * lowLimit > (3<<29) ==> current > 3<<29 + 1<<windowLog
+ * 1<<windowLog <= newCurrent < 1<<chainLog + 1<<windowLog
+ *
+ * current - newCurrent
+ * > (3<<29 + 1<<windowLog) - (1<<windowLog + 1<<chainLog)
+ * > (3<<29) - (1<<chainLog)
+ * > (3<<29) - (1<<30) (NOTE: chainLog <= 30)
+ * > 1<<29
+ *
+ * 2. (ip+ZSTD_CHUNKSIZE_MAX - cctx->base) doesn't overflow:
+ * After correction, current is less than (1<<chainLog + 1<<windowLog).
+ * In 64-bit mode we are safe, because we have 64-bit ptrdiff_t.
+ * In 32-bit mode we are safe, because (chainLog <= 29), so
+ * ip+ZSTD_CHUNKSIZE_MAX - cctx->base < 1<<32.
+ * 3. (cctx->lowLimit + 1<<windowLog) < 1<<32:
+ * windowLog <= 31 ==> 3<<29 + 1<<windowLog < 7<<29 < 1<<32.
+ */
+ U32 const cycleMask = (1U << cycleLog) - 1;
+ U32 const current = (U32)((BYTE const*)src - window->base);
+ U32 const newCurrent = (current & cycleMask) + maxDist;
+ U32 const correction = current - newCurrent;
+ assert((maxDist & cycleMask) == 0);
+ assert(current > newCurrent);
+ /* Loose bound, should be around 1<<29 (see above) */
+ assert(correction > 1<<28);
+
+ window->base += correction;
+ window->dictBase += correction;
+ window->lowLimit -= correction;
+ window->dictLimit -= correction;
+
+ DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x", correction,
+ window->lowLimit);
+ return correction;
+}
+
+/**
+ * ZSTD_window_enforceMaxDist():
+ * Updates lowLimit so that:
+ * (srcEnd - base) - lowLimit == maxDist + loadedDictEnd
+ * This allows a simple check that index >= lowLimit to see if index is valid.
+ * This must be called before a block compression call, with srcEnd as the block
+ * source end.
+ * If loadedDictEndPtr is not NULL, we set it to zero once we update lowLimit.
+ * This is because dictionaries are allowed to be referenced as long as the last
+ * byte of the dictionary is in the window, but once they are out of range,
+ * they cannot be referenced. If loadedDictEndPtr is NULL, we use
+ * loadedDictEnd == 0.
+ */
+MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window,
+ void const* srcEnd, U32 maxDist,
+ U32* loadedDictEndPtr)
+{
+ U32 const current = (U32)((BYTE const*)srcEnd - window->base);
+ U32 loadedDictEnd = loadedDictEndPtr != NULL ? *loadedDictEndPtr : 0;
+ if (current > maxDist + loadedDictEnd) {
+ U32 const newLowLimit = current - maxDist;
+ if (window->lowLimit < newLowLimit) window->lowLimit = newLowLimit;
+ if (window->dictLimit < window->lowLimit) {
+ DEBUGLOG(5, "Update dictLimit from %u to %u", window->dictLimit,
+ window->lowLimit);
+ window->dictLimit = window->lowLimit;
+ }
+ if (loadedDictEndPtr)
+ *loadedDictEndPtr = 0;
+ }
+}
+
+/**
+ * ZSTD_window_update():
+ * Updates the window by appending [src, src + srcSize) to the window.
+ * If it is not contiguous, the current prefix becomes the extDict, and we
+ * forget about the extDict. Handles overlap of the prefix and extDict.
+ * Returns non-zero if the segment is contiguous.
+ */
+MEM_STATIC U32 ZSTD_window_update(ZSTD_window_t* window,
+ void const* src, size_t srcSize)
+{
+ BYTE const* const ip = (BYTE const*)src;
+ U32 contiguous = 1;
+ /* Check if blocks follow each other */
+ if (src != window->nextSrc) {
+ /* not contiguous */
+ size_t const distanceFromBase = (size_t)(window->nextSrc - window->base);
+ DEBUGLOG(5, "Non contiguous blocks, new segment starts at %u",
+ window->dictLimit);
+ window->lowLimit = window->dictLimit;
+ assert(distanceFromBase == (size_t)(U32)distanceFromBase); /* should never overflow */
+ window->dictLimit = (U32)distanceFromBase;
+ window->dictBase = window->base;
+ window->base = ip - distanceFromBase;
+ // ms->nextToUpdate = window->dictLimit;
+ if (window->dictLimit - window->lowLimit < HASH_READ_SIZE) window->lowLimit = window->dictLimit; /* too small extDict */
+ contiguous = 0;
+ }
+ window->nextSrc = ip + srcSize;
+ /* if input and dictionary overlap : reduce dictionary (area presumed modified by input) */
+ if ( (ip+srcSize > window->dictBase + window->lowLimit)
+ & (ip < window->dictBase + window->dictLimit)) {
+ ptrdiff_t const highInputIdx = (ip + srcSize) - window->dictBase;
+ U32 const lowLimitMax = (highInputIdx > (ptrdiff_t)window->dictLimit) ? window->dictLimit : (U32)highInputIdx;
+ window->lowLimit = lowLimitMax;
+ }
+ return contiguous;
+}
+
#if defined (__cplusplus)
}
#endif
@@ -421,6 +639,13 @@ MEM_STATIC size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls)
* These prototypes shall only be called from within lib/compress
* ============================================================== */
+/* ZSTD_getCParamsFromCCtxParams() :
+ * cParams are built depending on compressionLevel, src size hints,
+ * LDM and manually set compression parameters.
+ */
+ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(
+ const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize);
+
/*! ZSTD_initCStream_internal() :
* Private use only. Init streaming operation.
* expects params to be valid.
@@ -446,7 +671,7 @@ ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict);
* Private use only. To be called from zstdmt_compress.c. */
size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx,
const void* dict, size_t dictSize,
- ZSTD_dictMode_e dictMode,
+ ZSTD_dictContentType_e dictContentType,
const ZSTD_CDict* cdict,
ZSTD_CCtx_params params,
unsigned long long pledgedSrcSize);
@@ -459,4 +684,26 @@ size_t ZSTD_compress_advanced_internal(ZSTD_CCtx* cctx,
const void* dict,size_t dictSize,
ZSTD_CCtx_params params);
+
+/* ZSTD_writeLastEmptyBlock() :
+ * output an empty Block with end-of-frame mark to complete a frame
+ * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h))
+ * or an error code if `dstCapcity` is too small (<ZSTD_blockHeaderSize)
+ */
+size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity);
+
+
+/* ZSTD_referenceExternalSequences() :
+ * Must be called before starting a compression operation.
+ * seqs must parse a prefix of the source.
+ * This cannot be used when long range matching is enabled.
+ * Zstd will use these sequences, and pass the literals to a secondary block
+ * compressor.
+ * @return : An error code on failure.
+ * NOTE: seqs are not verified! Invalid sequences can cause out-of-bounds memory
+ * access and data corruption.
+ */
+size_t ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq);
+
+
#endif /* ZSTD_COMPRESS_H */
diff --git a/thirdparty/zstd/compress/zstd_double_fast.c b/thirdparty/zstd/compress/zstd_double_fast.c
index fee5127c35..86e6b39621 100644
--- a/thirdparty/zstd/compress/zstd_double_fast.c
+++ b/thirdparty/zstd/compress/zstd_double_fast.c
@@ -12,44 +12,58 @@
#include "zstd_double_fast.h"
-void ZSTD_fillDoubleHashTable(ZSTD_CCtx* cctx, const void* end, const U32 mls)
+void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
+ ZSTD_compressionParameters const* cParams,
+ void const* end)
{
- U32* const hashLarge = cctx->hashTable;
- U32 const hBitsL = cctx->appliedParams.cParams.hashLog;
- U32* const hashSmall = cctx->chainTable;
- U32 const hBitsS = cctx->appliedParams.cParams.chainLog;
- const BYTE* const base = cctx->base;
- const BYTE* ip = base + cctx->nextToUpdate;
+ U32* const hashLarge = ms->hashTable;
+ U32 const hBitsL = cParams->hashLog;
+ U32 const mls = cParams->searchLength;
+ U32* const hashSmall = ms->chainTable;
+ U32 const hBitsS = cParams->chainLog;
+ const BYTE* const base = ms->window.base;
+ const BYTE* ip = base + ms->nextToUpdate;
const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE;
- const size_t fastHashFillStep = 3;
-
- while(ip <= iend) {
- hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip - base);
- hashLarge[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip - base);
- ip += fastHashFillStep;
+ const U32 fastHashFillStep = 3;
+
+ /* Always insert every fastHashFillStep position into the hash tables.
+ * Insert the other positions into the large hash table if their entry
+ * is empty.
+ */
+ for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) {
+ U32 const current = (U32)(ip - base);
+ U32 i;
+ for (i = 0; i < fastHashFillStep; ++i) {
+ size_t const smHash = ZSTD_hashPtr(ip + i, hBitsS, mls);
+ size_t const lgHash = ZSTD_hashPtr(ip + i, hBitsL, 8);
+ if (i == 0)
+ hashSmall[smHash] = current + i;
+ if (i == 0 || hashLarge[lgHash] == 0)
+ hashLarge[lgHash] = current + i;
+ }
}
}
FORCE_INLINE_TEMPLATE
-size_t ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx,
- const void* src, size_t srcSize,
- const U32 mls)
+size_t ZSTD_compressBlock_doubleFast_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize,
+ U32 const mls /* template */)
{
- U32* const hashLong = cctx->hashTable;
- const U32 hBitsL = cctx->appliedParams.cParams.hashLog;
- U32* const hashSmall = cctx->chainTable;
- const U32 hBitsS = cctx->appliedParams.cParams.chainLog;
- seqStore_t* seqStorePtr = &(cctx->seqStore);
- const BYTE* const base = cctx->base;
+ U32* const hashLong = ms->hashTable;
+ const U32 hBitsL = cParams->hashLog;
+ U32* const hashSmall = ms->chainTable;
+ const U32 hBitsS = cParams->chainLog;
+ const BYTE* const base = ms->window.base;
const BYTE* const istart = (const BYTE*)src;
const BYTE* ip = istart;
const BYTE* anchor = istart;
- const U32 lowestIndex = cctx->dictLimit;
+ const U32 lowestIndex = ms->window.dictLimit;
const BYTE* const lowest = base + lowestIndex;
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - HASH_READ_SIZE;
- U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1];
+ U32 offset_1=rep[0], offset_2=rep[1];
U32 offsetSaved = 0;
/* init */
@@ -76,7 +90,7 @@ size_t ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx,
/* favor repcode */
mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4;
ip++;
- ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
} else {
U32 offset;
if ( (matchIndexL > lowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) {
@@ -99,14 +113,14 @@ size_t ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx,
while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
}
} else {
- ip += ((ip-anchor) >> g_searchStrength) + 1;
+ ip += ((ip-anchor) >> kSearchStrength) + 1;
continue;
}
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
}
/* match found */
@@ -129,61 +143,63 @@ size_t ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx,
{ U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */
hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base);
hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base);
- ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, rLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH);
ip += rLength;
anchor = ip;
continue; /* faster when present ... (?) */
} } }
/* save reps for next block */
- seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved;
- seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved;
+ rep[0] = offset_1 ? offset_1 : offsetSaved;
+ rep[1] = offset_2 ? offset_2 : offsetSaved;
/* Return the last literals size */
return iend - anchor;
}
-size_t ZSTD_compressBlock_doubleFast(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_doubleFast(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- const U32 mls = ctx->appliedParams.cParams.searchLength;
+ const U32 mls = cParams->searchLength;
switch(mls)
{
default: /* includes case 3 */
case 4 :
- return ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 4);
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 4);
case 5 :
- return ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 5);
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 5);
case 6 :
- return ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 6);
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 6);
case 7 :
- return ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 7);
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 7);
}
}
-static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize,
- const U32 mls)
+static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize,
+ U32 const mls /* template */)
{
- U32* const hashLong = ctx->hashTable;
- U32 const hBitsL = ctx->appliedParams.cParams.hashLog;
- U32* const hashSmall = ctx->chainTable;
- U32 const hBitsS = ctx->appliedParams.cParams.chainLog;
- seqStore_t* seqStorePtr = &(ctx->seqStore);
- const BYTE* const base = ctx->base;
- const BYTE* const dictBase = ctx->dictBase;
+ U32* const hashLong = ms->hashTable;
+ U32 const hBitsL = cParams->hashLog;
+ U32* const hashSmall = ms->chainTable;
+ U32 const hBitsS = cParams->chainLog;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const dictBase = ms->window.dictBase;
const BYTE* const istart = (const BYTE*)src;
const BYTE* ip = istart;
const BYTE* anchor = istart;
- const U32 lowestIndex = ctx->lowLimit;
+ const U32 lowestIndex = ms->window.lowLimit;
const BYTE* const dictStart = dictBase + lowestIndex;
- const U32 dictLimit = ctx->dictLimit;
+ const U32 dictLimit = ms->window.dictLimit;
const BYTE* const lowPrefixPtr = base + dictLimit;
const BYTE* const dictEnd = dictBase + dictLimit;
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - 8;
- U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1];
+ U32 offset_1=rep[0], offset_2=rep[1];
/* Search Loop */
while (ip < ilimit) { /* < instead of <=, because (ip+1) */
@@ -209,7 +225,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx,
const BYTE* repMatchEnd = repIndex < dictLimit ? dictEnd : iend;
mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, lowPrefixPtr) + 4;
ip++;
- ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
} else {
if ((matchLongIndex > lowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) {
const BYTE* matchEnd = matchLongIndex < dictLimit ? dictEnd : iend;
@@ -220,7 +236,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx,
while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
} else if ((matchIndex > lowestIndex) && (MEM_read32(match) == MEM_read32(ip))) {
size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8);
@@ -245,10 +261,10 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx,
}
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
} else {
- ip += ((ip-anchor) >> g_searchStrength) + 1;
+ ip += ((ip-anchor) >> kSearchStrength) + 1;
continue;
} }
@@ -272,7 +288,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx,
const BYTE* const repEnd2 = repIndex2 < dictLimit ? dictEnd : iend;
size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, lowPrefixPtr) + 4;
U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */
- ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, repLength2-MINMATCH);
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH);
hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2;
hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2;
ip += repLength2;
@@ -283,27 +299,29 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx,
} } }
/* save reps for next block */
- seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2;
+ rep[0] = offset_1;
+ rep[1] = offset_2;
/* Return the last literals size */
return iend - anchor;
}
-size_t ZSTD_compressBlock_doubleFast_extDict(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_doubleFast_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- U32 const mls = ctx->appliedParams.cParams.searchLength;
+ U32 const mls = cParams->searchLength;
switch(mls)
{
default: /* includes case 3 */
case 4 :
- return ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 4);
+ return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 4);
case 5 :
- return ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 5);
+ return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 5);
case 6 :
- return ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 6);
+ return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 6);
case 7 :
- return ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 7);
+ return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 7);
}
}
diff --git a/thirdparty/zstd/compress/zstd_double_fast.h b/thirdparty/zstd/compress/zstd_double_fast.h
index 75e0415809..6d80b2774c 100644
--- a/thirdparty/zstd/compress/zstd_double_fast.h
+++ b/thirdparty/zstd/compress/zstd_double_fast.h
@@ -16,11 +16,18 @@ extern "C" {
#endif
#include "mem.h" /* U32 */
-#include "zstd.h" /* ZSTD_CCtx, size_t */
+#include "zstd_compress_internal.h" /* ZSTD_CCtx, size_t */
+
+void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
+ ZSTD_compressionParameters const* cParams,
+ void const* end);
+size_t ZSTD_compressBlock_doubleFast(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_doubleFast_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
-void ZSTD_fillDoubleHashTable(ZSTD_CCtx* cctx, const void* end, const U32 mls);
-size_t ZSTD_compressBlock_doubleFast(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
-size_t ZSTD_compressBlock_doubleFast_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
#if defined (__cplusplus)
}
diff --git a/thirdparty/zstd/compress/zstd_fast.c b/thirdparty/zstd/compress/zstd_fast.c
index 7b56c3d6ad..df4d28b340 100644
--- a/thirdparty/zstd/compress/zstd_fast.c
+++ b/thirdparty/zstd/compress/zstd_fast.c
@@ -12,39 +12,48 @@
#include "zstd_fast.h"
-void ZSTD_fillHashTable (ZSTD_CCtx* zc, const void* end, const U32 mls)
+void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
+ ZSTD_compressionParameters const* cParams,
+ void const* end)
{
- U32* const hashTable = zc->hashTable;
- U32 const hBits = zc->appliedParams.cParams.hashLog;
- const BYTE* const base = zc->base;
- const BYTE* ip = base + zc->nextToUpdate;
+ U32* const hashTable = ms->hashTable;
+ U32 const hBits = cParams->hashLog;
+ U32 const mls = cParams->searchLength;
+ const BYTE* const base = ms->window.base;
+ const BYTE* ip = base + ms->nextToUpdate;
const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE;
- const size_t fastHashFillStep = 3;
+ const U32 fastHashFillStep = 3;
- while(ip <= iend) {
- hashTable[ZSTD_hashPtr(ip, hBits, mls)] = (U32)(ip - base);
- ip += fastHashFillStep;
+ /* Always insert every fastHashFillStep position into the hash table.
+ * Insert the other positions if their hash entry is empty.
+ */
+ for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) {
+ U32 const current = (U32)(ip - base);
+ U32 i;
+ for (i = 0; i < fastHashFillStep; ++i) {
+ size_t const hash = ZSTD_hashPtr(ip + i, hBits, mls);
+ if (i == 0 || hashTable[hash] == 0)
+ hashTable[hash] = current + i;
+ }
}
}
-
FORCE_INLINE_TEMPLATE
-size_t ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx,
- const void* src, size_t srcSize,
- const U32 mls)
+size_t ZSTD_compressBlock_fast_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize,
+ U32 const hlog, U32 const stepSize, U32 const mls)
{
- U32* const hashTable = cctx->hashTable;
- U32 const hBits = cctx->appliedParams.cParams.hashLog;
- seqStore_t* seqStorePtr = &(cctx->seqStore);
- const BYTE* const base = cctx->base;
+ U32* const hashTable = ms->hashTable;
+ const BYTE* const base = ms->window.base;
const BYTE* const istart = (const BYTE*)src;
const BYTE* ip = istart;
const BYTE* anchor = istart;
- const U32 lowestIndex = cctx->dictLimit;
+ const U32 lowestIndex = ms->window.dictLimit;
const BYTE* const lowest = base + lowestIndex;
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - HASH_READ_SIZE;
- U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1];
+ U32 offset_1=rep[0], offset_2=rep[1];
U32 offsetSaved = 0;
/* init */
@@ -57,7 +66,7 @@ size_t ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx,
/* Main Search Loop */
while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */
size_t mLength;
- size_t const h = ZSTD_hashPtr(ip, hBits, mls);
+ size_t const h = ZSTD_hashPtr(ip, hlog, mls);
U32 const current = (U32)(ip-base);
U32 const matchIndex = hashTable[h];
const BYTE* match = base + matchIndex;
@@ -66,21 +75,21 @@ size_t ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx,
if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) {
mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4;
ip++;
- ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
} else {
- U32 offset;
- if ( (matchIndex <= lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) {
- ip += ((ip-anchor) >> g_searchStrength) + 1;
+ if ( (matchIndex <= lowestIndex)
+ || (MEM_read32(match) != MEM_read32(ip)) ) {
+ assert(stepSize >= 1);
+ ip += ((ip-anchor) >> kSearchStrength) + stepSize;
continue;
}
mLength = ZSTD_count(ip+4, match+4, iend) + 4;
- offset = (U32)(ip-match);
- while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
- offset_2 = offset_1;
- offset_1 = offset;
-
- ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
- }
+ { U32 const offset = (U32)(ip-match);
+ while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
+ offset_2 = offset_1;
+ offset_1 = offset;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ } }
/* match found */
ip += mLength;
@@ -88,8 +97,8 @@ size_t ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx,
if (ip <= ilimit) {
/* Fill Table */
- hashTable[ZSTD_hashPtr(base+current+2, hBits, mls)] = current+2; /* here because current+2 could be > iend-8 */
- hashTable[ZSTD_hashPtr(ip-2, hBits, mls)] = (U32)(ip-2-base);
+ hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2; /* here because current+2 could be > iend-8 */
+ hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base);
/* check immediate repcode */
while ( (ip <= ilimit)
&& ( (offset_2>0)
@@ -97,65 +106,67 @@ size_t ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx,
/* store sequence */
size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4;
{ U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */
- hashTable[ZSTD_hashPtr(ip, hBits, mls)] = (U32)(ip-base);
- ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, rLength-MINMATCH);
+ hashTable[ZSTD_hashPtr(ip, hlog, mls)] = (U32)(ip-base);
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH);
ip += rLength;
anchor = ip;
continue; /* faster when present ... (?) */
} } }
/* save reps for next block */
- seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved;
- seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved;
+ rep[0] = offset_1 ? offset_1 : offsetSaved;
+ rep[1] = offset_2 ? offset_2 : offsetSaved;
/* Return the last literals size */
return iend - anchor;
}
-size_t ZSTD_compressBlock_fast(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_fast(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- const U32 mls = ctx->appliedParams.cParams.searchLength;
+ U32 const hlog = cParams->hashLog;
+ U32 const mls = cParams->searchLength;
+ U32 const stepSize = cParams->targetLength;
switch(mls)
{
default: /* includes case 3 */
case 4 :
- return ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 4);
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4);
case 5 :
- return ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 5);
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5);
case 6 :
- return ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 6);
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6);
case 7 :
- return ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 7);
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7);
}
}
-static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize,
- const U32 mls)
+static size_t ZSTD_compressBlock_fast_extDict_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize,
+ U32 const hlog, U32 const stepSize, U32 const mls)
{
- U32* hashTable = ctx->hashTable;
- const U32 hBits = ctx->appliedParams.cParams.hashLog;
- seqStore_t* seqStorePtr = &(ctx->seqStore);
- const BYTE* const base = ctx->base;
- const BYTE* const dictBase = ctx->dictBase;
+ U32* hashTable = ms->hashTable;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const dictBase = ms->window.dictBase;
const BYTE* const istart = (const BYTE*)src;
const BYTE* ip = istart;
const BYTE* anchor = istart;
- const U32 lowestIndex = ctx->lowLimit;
+ const U32 lowestIndex = ms->window.lowLimit;
const BYTE* const dictStart = dictBase + lowestIndex;
- const U32 dictLimit = ctx->dictLimit;
+ const U32 dictLimit = ms->window.dictLimit;
const BYTE* const lowPrefixPtr = base + dictLimit;
const BYTE* const dictEnd = dictBase + dictLimit;
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - 8;
- U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1];
+ U32 offset_1=rep[0], offset_2=rep[1];
/* Search Loop */
while (ip < ilimit) { /* < instead of <=, because (ip+1) */
- const size_t h = ZSTD_hashPtr(ip, hBits, mls);
+ const size_t h = ZSTD_hashPtr(ip, hlog, mls);
const U32 matchIndex = hashTable[h];
const BYTE* matchBase = matchIndex < dictLimit ? dictBase : base;
const BYTE* match = matchBase + matchIndex;
@@ -171,11 +182,12 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx,
const BYTE* repMatchEnd = repIndex < dictLimit ? dictEnd : iend;
mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, lowPrefixPtr) + 4;
ip++;
- ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
} else {
if ( (matchIndex < lowestIndex) ||
(MEM_read32(match) != MEM_read32(ip)) ) {
- ip += ((ip-anchor) >> g_searchStrength) + 1;
+ assert(stepSize >= 1);
+ ip += ((ip-anchor) >> kSearchStrength) + stepSize;
continue;
}
{ const BYTE* matchEnd = matchIndex < dictLimit ? dictEnd : iend;
@@ -186,7 +198,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx,
offset = current - matchIndex;
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
} }
/* found a match : store it */
@@ -195,8 +207,8 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx,
if (ip <= ilimit) {
/* Fill Table */
- hashTable[ZSTD_hashPtr(base+current+2, hBits, mls)] = current+2;
- hashTable[ZSTD_hashPtr(ip-2, hBits, mls)] = (U32)(ip-2-base);
+ hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2;
+ hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base);
/* check immediate repcode */
while (ip <= ilimit) {
U32 const current2 = (U32)(ip-base);
@@ -207,8 +219,8 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx,
const BYTE* const repEnd2 = repIndex2 < dictLimit ? dictEnd : iend;
size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, lowPrefixPtr) + 4;
U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */
- ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, repLength2-MINMATCH);
- hashTable[ZSTD_hashPtr(ip, hBits, mls)] = current2;
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH);
+ hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2;
ip += repLength2;
anchor = ip;
continue;
@@ -217,27 +229,31 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx,
} } }
/* save reps for next block */
- seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2;
+ rep[0] = offset_1;
+ rep[1] = offset_2;
/* Return the last literals size */
return iend - anchor;
}
-size_t ZSTD_compressBlock_fast_extDict(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_fast_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- U32 const mls = ctx->appliedParams.cParams.searchLength;
+ U32 const hlog = cParams->hashLog;
+ U32 const mls = cParams->searchLength;
+ U32 const stepSize = cParams->targetLength;
switch(mls)
{
default: /* includes case 3 */
case 4 :
- return ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 4);
+ return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4);
case 5 :
- return ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 5);
+ return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5);
case 6 :
- return ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 6);
+ return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6);
case 7 :
- return ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 7);
+ return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7);
}
}
diff --git a/thirdparty/zstd/compress/zstd_fast.h b/thirdparty/zstd/compress/zstd_fast.h
index d8b7771954..f0438ad5b4 100644
--- a/thirdparty/zstd/compress/zstd_fast.h
+++ b/thirdparty/zstd/compress/zstd_fast.h
@@ -16,13 +16,17 @@ extern "C" {
#endif
#include "mem.h" /* U32 */
-#include "zstd.h" /* ZSTD_CCtx, size_t */
+#include "zstd_compress_internal.h"
-void ZSTD_fillHashTable(ZSTD_CCtx* zc, const void* end, const U32 mls);
-size_t ZSTD_compressBlock_fast(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize);
-size_t ZSTD_compressBlock_fast_extDict(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize);
+void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
+ ZSTD_compressionParameters const* cParams,
+ void const* end);
+size_t ZSTD_compressBlock_fast(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_fast_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
#if defined (__cplusplus)
}
diff --git a/thirdparty/zstd/compress/zstd_lazy.c b/thirdparty/zstd/compress/zstd_lazy.c
index 6d4804961d..9f158123f0 100644
--- a/thirdparty/zstd/compress/zstd_lazy.c
+++ b/thirdparty/zstd/compress/zstd_lazy.c
@@ -15,76 +15,90 @@
/*-*************************************
* Binary Tree search
***************************************/
-/** ZSTD_insertBt1() : add one or multiple positions to tree.
- * ip : assumed <= iend-8 .
- * @return : nb of positions added */
-static U32 ZSTD_insertBt1(ZSTD_CCtx* zc,
- const BYTE* const ip, const BYTE* const iend,
- U32 nbCompares, U32 const mls, U32 const extDict)
+
+void ZSTD_updateDUBT(
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ const BYTE* ip, const BYTE* iend,
+ U32 mls)
{
- U32* const hashTable = zc->hashTable;
- U32 const hashLog = zc->appliedParams.cParams.hashLog;
- size_t const h = ZSTD_hashPtr(ip, hashLog, mls);
- U32* const bt = zc->chainTable;
- U32 const btLog = zc->appliedParams.cParams.chainLog - 1;
+ U32* const hashTable = ms->hashTable;
+ U32 const hashLog = cParams->hashLog;
+
+ U32* const bt = ms->chainTable;
+ U32 const btLog = cParams->chainLog - 1;
+ U32 const btMask = (1 << btLog) - 1;
+
+ const BYTE* const base = ms->window.base;
+ U32 const target = (U32)(ip - base);
+ U32 idx = ms->nextToUpdate;
+
+ if (idx != target)
+ DEBUGLOG(7, "ZSTD_updateDUBT, from %u to %u (dictLimit:%u)",
+ idx, target, ms->window.dictLimit);
+ assert(ip + 8 <= iend); /* condition for ZSTD_hashPtr */
+ (void)iend;
+
+ assert(idx >= ms->window.dictLimit); /* condition for valid base+idx */
+ for ( ; idx < target ; idx++) {
+ size_t const h = ZSTD_hashPtr(base + idx, hashLog, mls); /* assumption : ip + 8 <= iend */
+ U32 const matchIndex = hashTable[h];
+
+ U32* const nextCandidatePtr = bt + 2*(idx&btMask);
+ U32* const sortMarkPtr = nextCandidatePtr + 1;
+
+ DEBUGLOG(8, "ZSTD_updateDUBT: insert %u", idx);
+ hashTable[h] = idx; /* Update Hash Table */
+ *nextCandidatePtr = matchIndex; /* update BT like a chain */
+ *sortMarkPtr = ZSTD_DUBT_UNSORTED_MARK;
+ }
+ ms->nextToUpdate = target;
+}
+
+
+/** ZSTD_insertDUBT1() :
+ * sort one already inserted but unsorted position
+ * assumption : current >= btlow == (current - btmask)
+ * doesn't fail */
+static void ZSTD_insertDUBT1(
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ U32 current, const BYTE* inputEnd,
+ U32 nbCompares, U32 btLow, int extDict)
+{
+ U32* const bt = ms->chainTable;
+ U32 const btLog = cParams->chainLog - 1;
U32 const btMask = (1 << btLog) - 1;
- U32 matchIndex = hashTable[h];
size_t commonLengthSmaller=0, commonLengthLarger=0;
- const BYTE* const base = zc->base;
- const BYTE* const dictBase = zc->dictBase;
- const U32 dictLimit = zc->dictLimit;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const dictBase = ms->window.dictBase;
+ const U32 dictLimit = ms->window.dictLimit;
+ const BYTE* const ip = (current>=dictLimit) ? base + current : dictBase + current;
+ const BYTE* const iend = (current>=dictLimit) ? inputEnd : dictBase + dictLimit;
const BYTE* const dictEnd = dictBase + dictLimit;
const BYTE* const prefixStart = base + dictLimit;
const BYTE* match;
- const U32 current = (U32)(ip-base);
- const U32 btLow = btMask >= current ? 0 : current - btMask;
U32* smallerPtr = bt + 2*(current&btMask);
U32* largerPtr = smallerPtr + 1;
+ U32 matchIndex = *smallerPtr;
U32 dummy32; /* to be nullified at the end */
- U32 const windowLow = zc->lowLimit;
- U32 matchEndIdx = current+8+1;
- size_t bestLength = 8;
-#ifdef ZSTD_C_PREDICT
- U32 predictedSmall = *(bt + 2*((current-1)&btMask) + 0);
- U32 predictedLarge = *(bt + 2*((current-1)&btMask) + 1);
- predictedSmall += (predictedSmall>0);
- predictedLarge += (predictedLarge>0);
-#endif /* ZSTD_C_PREDICT */
-
- DEBUGLOG(8, "ZSTD_insertBt1 (%u)", current);
+ U32 const windowLow = ms->window.lowLimit;
- assert(ip <= iend-8); /* required for h calculation */
- hashTable[h] = current; /* Update Hash Table */
+ DEBUGLOG(8, "ZSTD_insertDUBT1(%u) (dictLimit=%u, lowLimit=%u)",
+ current, dictLimit, windowLow);
+ assert(current >= btLow);
+ assert(ip < iend); /* condition for ZSTD_count */
while (nbCompares-- && (matchIndex > windowLow)) {
U32* const nextPtr = bt + 2*(matchIndex & btMask);
size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
assert(matchIndex < current);
-#ifdef ZSTD_C_PREDICT /* note : can create issues when hlog small <= 11 */
- const U32* predictPtr = bt + 2*((matchIndex-1) & btMask); /* written this way, as bt is a roll buffer */
- if (matchIndex == predictedSmall) {
- /* no need to check length, result known */
- *smallerPtr = matchIndex;
- if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */
- smallerPtr = nextPtr+1; /* new "smaller" => larger of match */
- matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */
- predictedSmall = predictPtr[1] + (predictPtr[1]>0);
- continue;
- }
- if (matchIndex == predictedLarge) {
- *largerPtr = matchIndex;
- if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */
- largerPtr = nextPtr;
- matchIndex = nextPtr[0];
- predictedLarge = predictPtr[0] + (predictPtr[0]>0);
- continue;
- }
-#endif
-
- if ((!extDict) || (matchIndex+matchLength >= dictLimit)) {
- assert(matchIndex+matchLength >= dictLimit); /* might be wrong if extDict is incorrectly set to 0 */
- match = base + matchIndex;
+ if ( (!extDict)
+ || (matchIndex+matchLength >= dictLimit) /* both in current segment*/
+ || (current < dictLimit) /* both in extDict */) {
+ const BYTE* const mBase = !extDict || ((matchIndex+matchLength) >= dictLimit) ? base : dictBase;
+ assert( (matchIndex+matchLength >= dictLimit) /* might be wrong if extDict is incorrectly set to 0 */
+ || (current < dictLimit) );
+ match = mBase + matchIndex;
matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend);
} else {
match = dictBase + matchIndex;
@@ -93,11 +107,8 @@ static U32 ZSTD_insertBt1(ZSTD_CCtx* zc,
match = base + matchIndex; /* to prepare for next usage of match[matchLength] */
}
- if (matchLength > bestLength) {
- bestLength = matchLength;
- if (matchLength > matchEndIdx - matchIndex)
- matchEndIdx = matchIndex + (U32)matchLength;
- }
+ DEBUGLOG(8, "ZSTD_insertDUBT1: comparing %u with %u : found %u common bytes ",
+ current, matchIndex, (U32)matchLength);
if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */
break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt tree */
@@ -108,6 +119,8 @@ static U32 ZSTD_insertBt1(ZSTD_CCtx* zc,
*smallerPtr = matchIndex; /* update smaller idx */
commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */
if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop searching */
+ DEBUGLOG(8, "ZSTD_insertDUBT1: %u (>btLow=%u) is smaller : next => %u",
+ matchIndex, btLow, nextPtr[1]);
smallerPtr = nextPtr+1; /* new "candidate" => larger than match, which was smaller than target */
matchIndex = nextPtr[1]; /* new matchIndex, larger than previous and closer to current */
} else {
@@ -115,184 +128,205 @@ static U32 ZSTD_insertBt1(ZSTD_CCtx* zc,
*largerPtr = matchIndex;
commonLengthLarger = matchLength;
if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop searching */
+ DEBUGLOG(8, "ZSTD_insertDUBT1: %u (>btLow=%u) is larger => %u",
+ matchIndex, btLow, nextPtr[0]);
largerPtr = nextPtr;
matchIndex = nextPtr[0];
} }
*smallerPtr = *largerPtr = 0;
- if (bestLength > 384) return MIN(192, (U32)(bestLength - 384)); /* speed optimization */
- assert(matchEndIdx > current + 8);
- return matchEndIdx - (current + 8);
}
-FORCE_INLINE_TEMPLATE
-void ZSTD_updateTree_internal(ZSTD_CCtx* zc,
- const BYTE* const ip, const BYTE* const iend,
- const U32 nbCompares, const U32 mls, const U32 extDict)
-{
- const BYTE* const base = zc->base;
- U32 const target = (U32)(ip - base);
- U32 idx = zc->nextToUpdate;
- DEBUGLOG(7, "ZSTD_updateTree_internal, from %u to %u (extDict:%u)",
- idx, target, extDict);
-
- while(idx < target)
- idx += ZSTD_insertBt1(zc, base+idx, iend, nbCompares, mls, extDict);
- zc->nextToUpdate = target;
-}
-void ZSTD_updateTree(ZSTD_CCtx* zc,
- const BYTE* const ip, const BYTE* const iend,
- const U32 nbCompares, const U32 mls)
+static size_t ZSTD_DUBT_findBestMatch (
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ const BYTE* const ip, const BYTE* const iend,
+ size_t* offsetPtr,
+ U32 const mls,
+ U32 const extDict)
{
- ZSTD_updateTree_internal(zc, ip, iend, nbCompares, mls, 0 /*extDict*/);
-}
-
-void ZSTD_updateTree_extDict(ZSTD_CCtx* zc,
- const BYTE* const ip, const BYTE* const iend,
- const U32 nbCompares, const U32 mls)
-{
- ZSTD_updateTree_internal(zc, ip, iend, nbCompares, mls, 1 /*extDict*/);
-}
+ U32* const hashTable = ms->hashTable;
+ U32 const hashLog = cParams->hashLog;
+ size_t const h = ZSTD_hashPtr(ip, hashLog, mls);
+ U32 matchIndex = hashTable[h];
+ const BYTE* const base = ms->window.base;
+ U32 const current = (U32)(ip-base);
+ U32 const windowLow = ms->window.lowLimit;
-static size_t ZSTD_insertBtAndFindBestMatch (
- ZSTD_CCtx* zc,
- const BYTE* const ip, const BYTE* const iend,
- size_t* offsetPtr,
- U32 nbCompares, const U32 mls,
- U32 extDict)
-{
- U32* const hashTable = zc->hashTable;
- U32 const hashLog = zc->appliedParams.cParams.hashLog;
- size_t const h = ZSTD_hashPtr(ip, hashLog, mls);
- U32* const bt = zc->chainTable;
- U32 const btLog = zc->appliedParams.cParams.chainLog - 1;
+ U32* const bt = ms->chainTable;
+ U32 const btLog = cParams->chainLog - 1;
U32 const btMask = (1 << btLog) - 1;
- U32 matchIndex = hashTable[h];
- size_t commonLengthSmaller=0, commonLengthLarger=0;
- const BYTE* const base = zc->base;
- const BYTE* const dictBase = zc->dictBase;
- const U32 dictLimit = zc->dictLimit;
- const BYTE* const dictEnd = dictBase + dictLimit;
- const BYTE* const prefixStart = base + dictLimit;
- const U32 current = (U32)(ip-base);
- const U32 btLow = btMask >= current ? 0 : current - btMask;
- const U32 windowLow = zc->lowLimit;
- U32* smallerPtr = bt + 2*(current&btMask);
- U32* largerPtr = bt + 2*(current&btMask) + 1;
- U32 matchEndIdx = current+8+1;
- U32 dummy32; /* to be nullified at the end */
- size_t bestLength = 0;
+ U32 const btLow = (btMask >= current) ? 0 : current - btMask;
+ U32 const unsortLimit = MAX(btLow, windowLow);
+
+ U32* nextCandidate = bt + 2*(matchIndex&btMask);
+ U32* unsortedMark = bt + 2*(matchIndex&btMask) + 1;
+ U32 nbCompares = 1U << cParams->searchLog;
+ U32 nbCandidates = nbCompares;
+ U32 previousCandidate = 0;
+ DEBUGLOG(7, "ZSTD_DUBT_findBestMatch (%u) ", current);
assert(ip <= iend-8); /* required for h calculation */
- hashTable[h] = current; /* Update Hash Table */
- while (nbCompares-- && (matchIndex > windowLow)) {
- U32* const nextPtr = bt + 2*(matchIndex & btMask);
- size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
- const BYTE* match;
+ /* reach end of unsorted candidates list */
+ while ( (matchIndex > unsortLimit)
+ && (*unsortedMark == ZSTD_DUBT_UNSORTED_MARK)
+ && (nbCandidates > 1) ) {
+ DEBUGLOG(8, "ZSTD_DUBT_findBestMatch: candidate %u is unsorted",
+ matchIndex);
+ *unsortedMark = previousCandidate;
+ previousCandidate = matchIndex;
+ matchIndex = *nextCandidate;
+ nextCandidate = bt + 2*(matchIndex&btMask);
+ unsortedMark = bt + 2*(matchIndex&btMask) + 1;
+ nbCandidates --;
+ }
- if ((!extDict) || (matchIndex+matchLength >= dictLimit)) {
- match = base + matchIndex;
- matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend);
- } else {
- match = dictBase + matchIndex;
- matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart);
- if (matchIndex+matchLength >= dictLimit)
- match = base + matchIndex; /* to prepare for next usage of match[matchLength] */
- }
+ if ( (matchIndex > unsortLimit)
+ && (*unsortedMark==ZSTD_DUBT_UNSORTED_MARK) ) {
+ DEBUGLOG(7, "ZSTD_DUBT_findBestMatch: nullify last unsorted candidate %u",
+ matchIndex);
+ *nextCandidate = *unsortedMark = 0; /* nullify next candidate if it's still unsorted (note : simplification, detrimental to compression ratio, beneficial for speed) */
+ }
+
+ /* batch sort stacked candidates */
+ matchIndex = previousCandidate;
+ while (matchIndex) { /* will end on matchIndex == 0 */
+ U32* const nextCandidateIdxPtr = bt + 2*(matchIndex&btMask) + 1;
+ U32 const nextCandidateIdx = *nextCandidateIdxPtr;
+ ZSTD_insertDUBT1(ms, cParams, matchIndex, iend,
+ nbCandidates, unsortLimit, extDict);
+ matchIndex = nextCandidateIdx;
+ nbCandidates++;
+ }
- if (matchLength > bestLength) {
- if (matchLength > matchEndIdx - matchIndex)
- matchEndIdx = matchIndex + (U32)matchLength;
- if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(current-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) )
- bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + current - matchIndex;
- if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */
- break; /* drop, to guarantee consistency (miss a little bit of compression) */
+ /* find longest match */
+ { size_t commonLengthSmaller=0, commonLengthLarger=0;
+ const BYTE* const dictBase = ms->window.dictBase;
+ const U32 dictLimit = ms->window.dictLimit;
+ const BYTE* const dictEnd = dictBase + dictLimit;
+ const BYTE* const prefixStart = base + dictLimit;
+ U32* smallerPtr = bt + 2*(current&btMask);
+ U32* largerPtr = bt + 2*(current&btMask) + 1;
+ U32 matchEndIdx = current+8+1;
+ U32 dummy32; /* to be nullified at the end */
+ size_t bestLength = 0;
+
+ matchIndex = hashTable[h];
+ hashTable[h] = current; /* Update Hash Table */
+
+ while (nbCompares-- && (matchIndex > windowLow)) {
+ U32* const nextPtr = bt + 2*(matchIndex & btMask);
+ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
+ const BYTE* match;
+
+ if ((!extDict) || (matchIndex+matchLength >= dictLimit)) {
+ match = base + matchIndex;
+ matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend);
+ } else {
+ match = dictBase + matchIndex;
+ matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart);
+ if (matchIndex+matchLength >= dictLimit)
+ match = base + matchIndex; /* to prepare for next usage of match[matchLength] */
}
- }
- if (match[matchLength] < ip[matchLength]) {
- /* match is smaller than current */
- *smallerPtr = matchIndex; /* update smaller idx */
- commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */
- if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */
- smallerPtr = nextPtr+1; /* new "smaller" => larger of match */
- matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */
- } else {
- /* match is larger than current */
- *largerPtr = matchIndex;
- commonLengthLarger = matchLength;
- if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */
- largerPtr = nextPtr;
- matchIndex = nextPtr[0];
- } }
+ if (matchLength > bestLength) {
+ if (matchLength > matchEndIdx - matchIndex)
+ matchEndIdx = matchIndex + (U32)matchLength;
+ if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(current-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) )
+ bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + current - matchIndex;
+ if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */
+ break; /* drop, to guarantee consistency (miss a little bit of compression) */
+ }
+ }
- *smallerPtr = *largerPtr = 0;
+ if (match[matchLength] < ip[matchLength]) {
+ /* match is smaller than current */
+ *smallerPtr = matchIndex; /* update smaller idx */
+ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */
+ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */
+ smallerPtr = nextPtr+1; /* new "smaller" => larger of match */
+ matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */
+ } else {
+ /* match is larger than current */
+ *largerPtr = matchIndex;
+ commonLengthLarger = matchLength;
+ if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */
+ largerPtr = nextPtr;
+ matchIndex = nextPtr[0];
+ } }
+
+ *smallerPtr = *largerPtr = 0;
- assert(matchEndIdx > current+8);
- zc->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */
- return bestLength;
+ assert(matchEndIdx > current+8); /* ensure nextToUpdate is increased */
+ ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */
+ if (bestLength >= MINMATCH) {
+ U32 const mIndex = current - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex;
+ DEBUGLOG(8, "ZSTD_DUBT_findBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)",
+ current, (U32)bestLength, (U32)*offsetPtr, mIndex);
+ }
+ return bestLength;
+ }
}
/** ZSTD_BtFindBestMatch() : Tree updater, providing best match */
static size_t ZSTD_BtFindBestMatch (
- ZSTD_CCtx* zc,
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
const BYTE* const ip, const BYTE* const iLimit,
size_t* offsetPtr,
- const U32 maxNbAttempts, const U32 mls)
+ const U32 mls /* template */)
{
- if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */
- ZSTD_updateTree(zc, ip, iLimit, maxNbAttempts, mls);
- return ZSTD_insertBtAndFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 0);
+ DEBUGLOG(7, "ZSTD_BtFindBestMatch");
+ if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */
+ ZSTD_updateDUBT(ms, cParams, ip, iLimit, mls);
+ return ZSTD_DUBT_findBestMatch(ms, cParams, ip, iLimit, offsetPtr, mls, 0);
}
static size_t ZSTD_BtFindBestMatch_selectMLS (
- ZSTD_CCtx* zc, /* Index table will be updated */
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
const BYTE* ip, const BYTE* const iLimit,
- size_t* offsetPtr,
- const U32 maxNbAttempts, const U32 matchLengthSearch)
+ size_t* offsetPtr)
{
- switch(matchLengthSearch)
+ switch(cParams->searchLength)
{
default : /* includes case 3 */
- case 4 : return ZSTD_BtFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4);
- case 5 : return ZSTD_BtFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5);
+ case 4 : return ZSTD_BtFindBestMatch(ms, cParams, ip, iLimit, offsetPtr, 4);
+ case 5 : return ZSTD_BtFindBestMatch(ms, cParams, ip, iLimit, offsetPtr, 5);
case 7 :
- case 6 : return ZSTD_BtFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6);
+ case 6 : return ZSTD_BtFindBestMatch(ms, cParams, ip, iLimit, offsetPtr, 6);
}
}
/** Tree updater, providing best match */
static size_t ZSTD_BtFindBestMatch_extDict (
- ZSTD_CCtx* zc,
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
const BYTE* const ip, const BYTE* const iLimit,
size_t* offsetPtr,
- const U32 maxNbAttempts, const U32 mls)
+ const U32 mls)
{
- if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */
- ZSTD_updateTree_extDict(zc, ip, iLimit, maxNbAttempts, mls);
- return ZSTD_insertBtAndFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 1);
+ DEBUGLOG(7, "ZSTD_BtFindBestMatch_extDict");
+ if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */
+ ZSTD_updateDUBT(ms, cParams, ip, iLimit, mls);
+ return ZSTD_DUBT_findBestMatch(ms, cParams, ip, iLimit, offsetPtr, mls, 1);
}
static size_t ZSTD_BtFindBestMatch_selectMLS_extDict (
- ZSTD_CCtx* zc, /* Index table will be updated */
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
const BYTE* ip, const BYTE* const iLimit,
- size_t* offsetPtr,
- const U32 maxNbAttempts, const U32 matchLengthSearch)
+ size_t* offsetPtr)
{
- switch(matchLengthSearch)
+ switch(cParams->searchLength)
{
default : /* includes case 3 */
- case 4 : return ZSTD_BtFindBestMatch_extDict(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4);
- case 5 : return ZSTD_BtFindBestMatch_extDict(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5);
+ case 4 : return ZSTD_BtFindBestMatch_extDict(ms, cParams, ip, iLimit, offsetPtr, 4);
+ case 5 : return ZSTD_BtFindBestMatch_extDict(ms, cParams, ip, iLimit, offsetPtr, 5);
case 7 :
- case 6 : return ZSTD_BtFindBestMatch_extDict(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6);
+ case 6 : return ZSTD_BtFindBestMatch_extDict(ms, cParams, ip, iLimit, offsetPtr, 6);
}
}
@@ -305,15 +339,17 @@ static size_t ZSTD_BtFindBestMatch_selectMLS_extDict (
/* Update chains up to ip (excluded)
Assumption : always within prefix (i.e. not within extDict) */
-U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls)
+static U32 ZSTD_insertAndFindFirstIndex_internal(
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ const BYTE* ip, U32 const mls)
{
- U32* const hashTable = zc->hashTable;
- const U32 hashLog = zc->appliedParams.cParams.hashLog;
- U32* const chainTable = zc->chainTable;
- const U32 chainMask = (1 << zc->appliedParams.cParams.chainLog) - 1;
- const BYTE* const base = zc->base;
+ U32* const hashTable = ms->hashTable;
+ const U32 hashLog = cParams->hashLog;
+ U32* const chainTable = ms->chainTable;
+ const U32 chainMask = (1 << cParams->chainLog) - 1;
+ const BYTE* const base = ms->window.base;
const U32 target = (U32)(ip - base);
- U32 idx = zc->nextToUpdate;
+ U32 idx = ms->nextToUpdate;
while(idx < target) { /* catch up */
size_t const h = ZSTD_hashPtr(base+idx, hashLog, mls);
@@ -322,35 +358,42 @@ U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls)
idx++;
}
- zc->nextToUpdate = target;
+ ms->nextToUpdate = target;
return hashTable[ZSTD_hashPtr(ip, hashLog, mls)];
}
+U32 ZSTD_insertAndFindFirstIndex(
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ const BYTE* ip)
+{
+ return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, cParams->searchLength);
+}
+
/* inlining is important to hardwire a hot branch (template emulation) */
FORCE_INLINE_TEMPLATE
size_t ZSTD_HcFindBestMatch_generic (
- ZSTD_CCtx* zc, /* Index table will be updated */
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
const BYTE* const ip, const BYTE* const iLimit,
size_t* offsetPtr,
- const U32 maxNbAttempts, const U32 mls, const U32 extDict)
+ const U32 mls, const U32 extDict)
{
- U32* const chainTable = zc->chainTable;
- const U32 chainSize = (1 << zc->appliedParams.cParams.chainLog);
+ U32* const chainTable = ms->chainTable;
+ const U32 chainSize = (1 << cParams->chainLog);
const U32 chainMask = chainSize-1;
- const BYTE* const base = zc->base;
- const BYTE* const dictBase = zc->dictBase;
- const U32 dictLimit = zc->dictLimit;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const dictBase = ms->window.dictBase;
+ const U32 dictLimit = ms->window.dictLimit;
const BYTE* const prefixStart = base + dictLimit;
const BYTE* const dictEnd = dictBase + dictLimit;
- const U32 lowLimit = zc->lowLimit;
+ const U32 lowLimit = ms->window.lowLimit;
const U32 current = (U32)(ip-base);
const U32 minChain = current > chainSize ? current - chainSize : 0;
- int nbAttempts=maxNbAttempts;
+ U32 nbAttempts = 1U << cParams->searchLog;
size_t ml=4-1;
/* HC4 match finder */
- U32 matchIndex = ZSTD_insertAndFindFirstIndex (zc, ip, mls);
+ U32 matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls);
for ( ; (matchIndex>lowLimit) & (nbAttempts>0) ; nbAttempts--) {
size_t currentMl=0;
@@ -381,35 +424,33 @@ size_t ZSTD_HcFindBestMatch_generic (
FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_selectMLS (
- ZSTD_CCtx* zc,
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
const BYTE* ip, const BYTE* const iLimit,
- size_t* offsetPtr,
- const U32 maxNbAttempts, const U32 matchLengthSearch)
+ size_t* offsetPtr)
{
- switch(matchLengthSearch)
+ switch(cParams->searchLength)
{
default : /* includes case 3 */
- case 4 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4, 0);
- case 5 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5, 0);
+ case 4 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 4, 0);
+ case 5 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 5, 0);
case 7 :
- case 6 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6, 0);
+ case 6 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 6, 0);
}
}
FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_extDict_selectMLS (
- ZSTD_CCtx* const zc,
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
const BYTE* ip, const BYTE* const iLimit,
- size_t* const offsetPtr,
- U32 const maxNbAttempts, U32 const matchLengthSearch)
+ size_t* const offsetPtr)
{
- switch(matchLengthSearch)
+ switch(cParams->searchLength)
{
default : /* includes case 3 */
- case 4 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4, 1);
- case 5 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5, 1);
+ case 4 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 4, 1);
+ case 5 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 5, 1);
case 7 :
- case 6 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6, 1);
+ case 6 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 6, 1);
}
}
@@ -418,30 +459,29 @@ FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_extDict_selectMLS (
* Common parser - lazy strategy
*********************************/
FORCE_INLINE_TEMPLATE
-size_t ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize,
- const U32 searchMethod, const U32 depth)
+size_t ZSTD_compressBlock_lazy_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore,
+ U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams,
+ const void* src, size_t srcSize,
+ const U32 searchMethod, const U32 depth)
{
- seqStore_t* seqStorePtr = &(ctx->seqStore);
const BYTE* const istart = (const BYTE*)src;
const BYTE* ip = istart;
const BYTE* anchor = istart;
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - 8;
- const BYTE* const base = ctx->base + ctx->dictLimit;
+ const BYTE* const base = ms->window.base + ms->window.dictLimit;
- U32 const maxSearches = 1 << ctx->appliedParams.cParams.searchLog;
- U32 const mls = ctx->appliedParams.cParams.searchLength;
-
- typedef size_t (*searchMax_f)(ZSTD_CCtx* zc, const BYTE* ip, const BYTE* iLimit,
- size_t* offsetPtr,
- U32 maxNbAttempts, U32 matchLengthSearch);
+ typedef size_t (*searchMax_f)(
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr);
searchMax_f const searchMax = searchMethod ? ZSTD_BtFindBestMatch_selectMLS : ZSTD_HcFindBestMatch_selectMLS;
- U32 offset_1 = seqStorePtr->rep[0], offset_2 = seqStorePtr->rep[1], savedOffset=0;
+ U32 offset_1 = rep[0], offset_2 = rep[1], savedOffset=0;
/* init */
ip += (ip==base);
- ctx->nextToUpdate3 = ctx->nextToUpdate;
+ ms->nextToUpdate3 = ms->nextToUpdate;
{ U32 const maxRep = (U32)(ip-base);
if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0;
if (offset_1 > maxRep) savedOffset = offset_1, offset_1 = 0;
@@ -462,13 +502,13 @@ size_t ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx,
/* first search (depth 0) */
{ size_t offsetFound = 99999999;
- size_t const ml2 = searchMax(ctx, ip, iend, &offsetFound, maxSearches, mls);
+ size_t const ml2 = searchMax(ms, cParams, ip, iend, &offsetFound);
if (ml2 > matchLength)
matchLength = ml2, start = ip, offset=offsetFound;
}
if (matchLength < 4) {
- ip += ((ip-anchor) >> g_searchStrength) + 1; /* jump faster over incompressible sections */
+ ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */
continue;
}
@@ -484,7 +524,7 @@ size_t ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx,
matchLength = mlRep, offset = 0, start = ip;
}
{ size_t offset2=99999999;
- size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls);
+ size_t const ml2 = searchMax(ms, cParams, ip, iend, &offset2);
int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */
int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4);
if ((ml2 >= 4) && (gain2 > gain1)) {
@@ -503,7 +543,7 @@ size_t ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx,
matchLength = ml2, offset = 0, start = ip;
}
{ size_t offset2=99999999;
- size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls);
+ size_t const ml2 = searchMax(ms, cParams, ip, iend, &offset2);
int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */
int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7);
if ((ml2 >= 4) && (gain2 > gain1)) {
@@ -528,7 +568,7 @@ size_t ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx,
/* store sequence */
_storeSequence:
{ size_t const litLength = start - anchor;
- ZSTD_storeSeq(seqStorePtr, litLength, anchor, (U32)offset, matchLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, litLength, anchor, (U32)offset, matchLength-MINMATCH);
anchor = ip = start + matchLength;
}
@@ -538,73 +578,80 @@ _storeSequence:
/* store sequence */
matchLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4;
offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap repcodes */
- ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, matchLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, matchLength-MINMATCH);
ip += matchLength;
anchor = ip;
continue; /* faster when present ... (?) */
} }
/* Save reps for next block */
- seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : savedOffset;
- seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : savedOffset;
+ rep[0] = offset_1 ? offset_1 : savedOffset;
+ rep[1] = offset_2 ? offset_2 : savedOffset;
/* Return the last literals size */
return iend - anchor;
}
-size_t ZSTD_compressBlock_btlazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_btlazy2(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- return ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 1, 2);
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, cParams, src, srcSize, 1, 2);
}
-size_t ZSTD_compressBlock_lazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_lazy2(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- return ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 0, 2);
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 2);
}
-size_t ZSTD_compressBlock_lazy(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_lazy(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- return ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 0, 1);
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 1);
}
-size_t ZSTD_compressBlock_greedy(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_greedy(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- return ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 0, 0);
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 0);
}
FORCE_INLINE_TEMPLATE
-size_t ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize,
- const U32 searchMethod, const U32 depth)
+size_t ZSTD_compressBlock_lazy_extDict_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore,
+ U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams,
+ const void* src, size_t srcSize,
+ const U32 searchMethod, const U32 depth)
{
- seqStore_t* seqStorePtr = &(ctx->seqStore);
const BYTE* const istart = (const BYTE*)src;
const BYTE* ip = istart;
const BYTE* anchor = istart;
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - 8;
- const BYTE* const base = ctx->base;
- const U32 dictLimit = ctx->dictLimit;
- const U32 lowestIndex = ctx->lowLimit;
+ const BYTE* const base = ms->window.base;
+ const U32 dictLimit = ms->window.dictLimit;
+ const U32 lowestIndex = ms->window.lowLimit;
const BYTE* const prefixStart = base + dictLimit;
- const BYTE* const dictBase = ctx->dictBase;
+ const BYTE* const dictBase = ms->window.dictBase;
const BYTE* const dictEnd = dictBase + dictLimit;
- const BYTE* const dictStart = dictBase + ctx->lowLimit;
+ const BYTE* const dictStart = dictBase + lowestIndex;
- const U32 maxSearches = 1 << ctx->appliedParams.cParams.searchLog;
- const U32 mls = ctx->appliedParams.cParams.searchLength;
-
- typedef size_t (*searchMax_f)(ZSTD_CCtx* zc, const BYTE* ip, const BYTE* iLimit,
- size_t* offsetPtr,
- U32 maxNbAttempts, U32 matchLengthSearch);
+ typedef size_t (*searchMax_f)(
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr);
searchMax_f searchMax = searchMethod ? ZSTD_BtFindBestMatch_selectMLS_extDict : ZSTD_HcFindBestMatch_extDict_selectMLS;
- U32 offset_1 = seqStorePtr->rep[0], offset_2 = seqStorePtr->rep[1];
+ U32 offset_1 = rep[0], offset_2 = rep[1];
/* init */
- ctx->nextToUpdate3 = ctx->nextToUpdate;
+ ms->nextToUpdate3 = ms->nextToUpdate;
ip += (ip == prefixStart);
/* Match Loop */
@@ -628,13 +675,13 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx,
/* first search (depth 0) */
{ size_t offsetFound = 99999999;
- size_t const ml2 = searchMax(ctx, ip, iend, &offsetFound, maxSearches, mls);
+ size_t const ml2 = searchMax(ms, cParams, ip, iend, &offsetFound);
if (ml2 > matchLength)
matchLength = ml2, start = ip, offset=offsetFound;
}
if (matchLength < 4) {
- ip += ((ip-anchor) >> g_searchStrength) + 1; /* jump faster over incompressible sections */
+ ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */
continue;
}
@@ -661,7 +708,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx,
/* search match, depth 1 */
{ size_t offset2=99999999;
- size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls);
+ size_t const ml2 = searchMax(ms, cParams, ip, iend, &offset2);
int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */
int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4);
if ((ml2 >= 4) && (gain2 > gain1)) {
@@ -691,7 +738,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx,
/* search match, depth 2 */
{ size_t offset2=99999999;
- size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls);
+ size_t const ml2 = searchMax(ms, cParams, ip, iend, &offset2);
int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */
int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7);
if ((ml2 >= 4) && (gain2 > gain1)) {
@@ -713,7 +760,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx,
/* store sequence */
_storeSequence:
{ size_t const litLength = start - anchor;
- ZSTD_storeSeq(seqStorePtr, litLength, anchor, (U32)offset, matchLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, litLength, anchor, (U32)offset, matchLength-MINMATCH);
anchor = ip = start + matchLength;
}
@@ -728,7 +775,7 @@ _storeSequence:
const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4;
offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap offset history */
- ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, matchLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, matchLength-MINMATCH);
ip += matchLength;
anchor = ip;
continue; /* faster when present ... (?) */
@@ -737,29 +784,41 @@ _storeSequence:
} }
/* Save reps for next block */
- seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2;
+ rep[0] = offset_1;
+ rep[1] = offset_2;
/* Return the last literals size */
return iend - anchor;
}
-size_t ZSTD_compressBlock_greedy_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_greedy_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- return ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 0, 0);
+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 0);
}
-size_t ZSTD_compressBlock_lazy_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_lazy_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
+
{
- return ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 0, 1);
+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 1);
}
-size_t ZSTD_compressBlock_lazy2_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_lazy2_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
+
{
- return ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 0, 2);
+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 2);
}
-size_t ZSTD_compressBlock_btlazy2_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_btlazy2_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
+
{
- return ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 1, 2);
+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 1, 2);
}
diff --git a/thirdparty/zstd/compress/zstd_lazy.h b/thirdparty/zstd/compress/zstd_lazy.h
index 74e1fd6970..bda064f199 100644
--- a/thirdparty/zstd/compress/zstd_lazy.h
+++ b/thirdparty/zstd/compress/zstd_lazy.h
@@ -15,22 +15,39 @@
extern "C" {
#endif
-#include "mem.h" /* U32 */
-#include "zstd.h" /* ZSTD_CCtx, size_t */
-
-U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls);
-void ZSTD_updateTree(ZSTD_CCtx* zc, const BYTE* const ip, const BYTE* const iend, const U32 nbCompares, const U32 mls);
-void ZSTD_updateTree_extDict(ZSTD_CCtx* zc, const BYTE* const ip, const BYTE* const iend, const U32 nbCompares, const U32 mls);
-
-size_t ZSTD_compressBlock_btlazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
-size_t ZSTD_compressBlock_lazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
-size_t ZSTD_compressBlock_lazy(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
-size_t ZSTD_compressBlock_greedy(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
-
-size_t ZSTD_compressBlock_greedy_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
-size_t ZSTD_compressBlock_lazy_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
-size_t ZSTD_compressBlock_lazy2_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
-size_t ZSTD_compressBlock_btlazy2_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
+#include "zstd_compress_internal.h"
+
+U32 ZSTD_insertAndFindFirstIndex(
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ const BYTE* ip);
+
+void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const reducerValue); /*! used in ZSTD_reduceIndex(). pre-emptively increase value of ZSTD_DUBT_UNSORTED_MARK */
+
+size_t ZSTD_compressBlock_btlazy2(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy2(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_greedy(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+
+size_t ZSTD_compressBlock_greedy_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy2_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_btlazy2_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
#if defined (__cplusplus)
}
diff --git a/thirdparty/zstd/compress/zstd_ldm.c b/thirdparty/zstd/compress/zstd_ldm.c
index be50872cf7..bffd8a3dfa 100644
--- a/thirdparty/zstd/compress/zstd_ldm.c
+++ b/thirdparty/zstd/compress/zstd_ldm.c
@@ -17,36 +17,45 @@
#define LDM_HASH_RLOG 7
#define LDM_HASH_CHAR_OFFSET 10
-size_t ZSTD_ldm_initializeParameters(ldmParams_t* params, U32 enableLdm)
+void ZSTD_ldm_adjustParameters(ldmParams_t* params,
+ ZSTD_compressionParameters const* cParams)
{
+ U32 const windowLog = cParams->windowLog;
ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);
- params->enableLdm = enableLdm>0;
- params->hashLog = 0;
- params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
- params->minMatchLength = LDM_MIN_MATCH_LENGTH;
- params->hashEveryLog = ZSTD_LDM_HASHEVERYLOG_NOTSET;
- return 0;
-}
-
-void ZSTD_ldm_adjustParameters(ldmParams_t* params, U32 windowLog)
-{
+ DEBUGLOG(4, "ZSTD_ldm_adjustParameters");
+ if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
+ if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH;
+ if (cParams->strategy >= ZSTD_btopt) {
+ /* Get out of the way of the optimal parser */
+ U32 const minMatch = MAX(cParams->targetLength, params->minMatchLength);
+ assert(minMatch >= ZSTD_LDM_MINMATCH_MIN);
+ assert(minMatch <= ZSTD_LDM_MINMATCH_MAX);
+ params->minMatchLength = minMatch;
+ }
if (params->hashLog == 0) {
params->hashLog = MAX(ZSTD_HASHLOG_MIN, windowLog - LDM_HASH_RLOG);
assert(params->hashLog <= ZSTD_HASHLOG_MAX);
}
- if (params->hashEveryLog == ZSTD_LDM_HASHEVERYLOG_NOTSET) {
+ if (params->hashEveryLog == 0) {
params->hashEveryLog =
windowLog < params->hashLog ? 0 : windowLog - params->hashLog;
}
params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog);
}
-size_t ZSTD_ldm_getTableSize(U32 hashLog, U32 bucketSizeLog) {
- size_t const ldmHSize = ((size_t)1) << hashLog;
- size_t const ldmBucketSizeLog = MIN(bucketSizeLog, hashLog);
+size_t ZSTD_ldm_getTableSize(ldmParams_t params)
+{
+ size_t const ldmHSize = ((size_t)1) << params.hashLog;
+ size_t const ldmBucketSizeLog = MIN(params.bucketSizeLog, params.hashLog);
size_t const ldmBucketSize =
- ((size_t)1) << (hashLog - ldmBucketSizeLog);
- return ldmBucketSize + (ldmHSize * (sizeof(ldmEntry_t)));
+ ((size_t)1) << (params.hashLog - ldmBucketSizeLog);
+ size_t const totalSize = ldmBucketSize + ldmHSize * sizeof(ldmEntry_t);
+ return params.enableLdm ? totalSize : 0;
+}
+
+size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize)
+{
+ return params.enableLdm ? (maxChunkSize / params.minMatchLength) : 0;
}
/** ZSTD_ldm_getSmallHash() :
@@ -167,6 +176,7 @@ static U64 ZSTD_ldm_ipow(U64 base, U64 exp)
}
U64 ZSTD_ldm_getHashPower(U32 minMatchLength) {
+ DEBUGLOG(4, "ZSTD_ldm_getHashPower: mml=%u", minMatchLength);
assert(minMatchLength >= ZSTD_LDM_MINMATCH_MIN);
return ZSTD_ldm_ipow(prime8bytes, minMatchLength - 1);
}
@@ -205,21 +215,22 @@ static size_t ZSTD_ldm_countBackwardsMatch(
*
* The tables for the other strategies are filled within their
* block compressors. */
-static size_t ZSTD_ldm_fillFastTables(ZSTD_CCtx* zc, const void* end)
+static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms,
+ ZSTD_compressionParameters const* cParams,
+ void const* end)
{
const BYTE* const iend = (const BYTE*)end;
- const U32 mls = zc->appliedParams.cParams.searchLength;
- switch(zc->appliedParams.cParams.strategy)
+ switch(cParams->strategy)
{
case ZSTD_fast:
- ZSTD_fillHashTable(zc, iend, mls);
- zc->nextToUpdate = (U32)(iend - zc->base);
+ ZSTD_fillHashTable(ms, cParams, iend);
+ ms->nextToUpdate = (U32)(iend - ms->window.base);
break;
case ZSTD_dfast:
- ZSTD_fillDoubleHashTable(zc, iend, mls);
- zc->nextToUpdate = (U32)(iend - zc->base);
+ ZSTD_fillDoubleHashTable(ms, cParams, iend);
+ ms->nextToUpdate = (U32)(iend - ms->window.base);
break;
case ZSTD_greedy:
@@ -268,69 +279,62 @@ static U64 ZSTD_ldm_fillLdmHashTable(ldmState_t* state,
* Sets cctx->nextToUpdate to a position corresponding closer to anchor
* if it is far way
* (after a long match, only update tables a limited amount). */
-static void ZSTD_ldm_limitTableUpdate(ZSTD_CCtx* cctx, const BYTE* anchor)
+static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor)
{
- U32 const current = (U32)(anchor - cctx->base);
- if (current > cctx->nextToUpdate + 1024) {
- cctx->nextToUpdate =
- current - MIN(512, current - cctx->nextToUpdate - 1024);
+ U32 const current = (U32)(anchor - ms->window.base);
+ if (current > ms->nextToUpdate + 1024) {
+ ms->nextToUpdate =
+ current - MIN(512, current - ms->nextToUpdate - 1024);
}
}
-typedef size_t (*ZSTD_blockCompressor) (ZSTD_CCtx* ctx, const void* src, size_t srcSize);
-/* defined in zstd_compress.c */
-ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict);
-
-FORCE_INLINE_TEMPLATE
-size_t ZSTD_compressBlock_ldm_generic(ZSTD_CCtx* cctx,
- const void* src, size_t srcSize)
+static size_t ZSTD_ldm_generateSequences_internal(
+ ldmState_t* ldmState, rawSeqStore_t* rawSeqStore,
+ ldmParams_t const* params, void const* src, size_t srcSize)
{
- ldmState_t* const ldmState = &(cctx->ldmState);
- const ldmParams_t ldmParams = cctx->appliedParams.ldmParams;
- const U64 hashPower = ldmState->hashPower;
- const U32 hBits = ldmParams.hashLog - ldmParams.bucketSizeLog;
- const U32 ldmBucketSize = ((U32)1 << ldmParams.bucketSizeLog);
- const U32 ldmTagMask = ((U32)1 << ldmParams.hashEveryLog) - 1;
- seqStore_t* const seqStorePtr = &(cctx->seqStore);
- const BYTE* const base = cctx->base;
- const BYTE* const istart = (const BYTE*)src;
- const BYTE* ip = istart;
- const BYTE* anchor = istart;
- const U32 lowestIndex = cctx->dictLimit;
- const BYTE* const lowest = base + lowestIndex;
- const BYTE* const iend = istart + srcSize;
- const BYTE* const ilimit = iend - MAX(ldmParams.minMatchLength, HASH_READ_SIZE);
-
- const ZSTD_blockCompressor blockCompressor =
- ZSTD_selectBlockCompressor(cctx->appliedParams.cParams.strategy, 0);
- U32* const repToConfirm = seqStorePtr->repToConfirm;
- U32 savedRep[ZSTD_REP_NUM];
+ /* LDM parameters */
+ int const extDict = ZSTD_window_hasExtDict(ldmState->window);
+ U32 const minMatchLength = params->minMatchLength;
+ U64 const hashPower = ldmState->hashPower;
+ U32 const hBits = params->hashLog - params->bucketSizeLog;
+ U32 const ldmBucketSize = 1U << params->bucketSizeLog;
+ U32 const hashEveryLog = params->hashEveryLog;
+ U32 const ldmTagMask = (1U << params->hashEveryLog) - 1;
+ /* Prefix and extDict parameters */
+ U32 const dictLimit = ldmState->window.dictLimit;
+ U32 const lowestIndex = extDict ? ldmState->window.lowLimit : dictLimit;
+ BYTE const* const base = ldmState->window.base;
+ BYTE const* const dictBase = extDict ? ldmState->window.dictBase : NULL;
+ BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL;
+ BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL;
+ BYTE const* const lowPrefixPtr = base + dictLimit;
+ /* Input bounds */
+ BYTE const* const istart = (BYTE const*)src;
+ BYTE const* const iend = istart + srcSize;
+ BYTE const* const ilimit = iend - MAX(minMatchLength, HASH_READ_SIZE);
+ /* Input positions */
+ BYTE const* anchor = istart;
+ BYTE const* ip = istart;
+ /* Rolling hash */
+ BYTE const* lastHashed = NULL;
U64 rollingHash = 0;
- const BYTE* lastHashed = NULL;
- size_t i, lastLiterals;
- /* Save seqStorePtr->rep and copy repToConfirm */
- for (i = 0; i < ZSTD_REP_NUM; i++)
- savedRep[i] = repToConfirm[i] = seqStorePtr->rep[i];
-
- /* Main Search Loop */
- while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */
+ while (ip <= ilimit) {
size_t mLength;
U32 const current = (U32)(ip - base);
size_t forwardMatchLength = 0, backwardMatchLength = 0;
ldmEntry_t* bestEntry = NULL;
if (ip != istart) {
rollingHash = ZSTD_ldm_updateHash(rollingHash, lastHashed[0],
- lastHashed[ldmParams.minMatchLength],
+ lastHashed[minMatchLength],
hashPower);
} else {
- rollingHash = ZSTD_ldm_getRollingHash(ip, ldmParams.minMatchLength);
+ rollingHash = ZSTD_ldm_getRollingHash(ip, minMatchLength);
}
lastHashed = ip;
/* Do not insert and do not look for a match */
- if (ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashEveryLog) !=
- ldmTagMask) {
+ if (ZSTD_ldm_getTag(rollingHash, hBits, hashEveryLog) != ldmTagMask) {
ip++;
continue;
}
@@ -340,27 +344,49 @@ size_t ZSTD_compressBlock_ldm_generic(ZSTD_CCtx* cctx,
ldmEntry_t* const bucket =
ZSTD_ldm_getBucket(ldmState,
ZSTD_ldm_getSmallHash(rollingHash, hBits),
- ldmParams);
+ *params);
ldmEntry_t* cur;
size_t bestMatchLength = 0;
U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
for (cur = bucket; cur < bucket + ldmBucketSize; ++cur) {
- const BYTE* const pMatch = cur->offset + base;
size_t curForwardMatchLength, curBackwardMatchLength,
curTotalMatchLength;
if (cur->checksum != checksum || cur->offset <= lowestIndex) {
continue;
}
-
- curForwardMatchLength = ZSTD_count(ip, pMatch, iend);
- if (curForwardMatchLength < ldmParams.minMatchLength) {
- continue;
+ if (extDict) {
+ BYTE const* const curMatchBase =
+ cur->offset < dictLimit ? dictBase : base;
+ BYTE const* const pMatch = curMatchBase + cur->offset;
+ BYTE const* const matchEnd =
+ cur->offset < dictLimit ? dictEnd : iend;
+ BYTE const* const lowMatchPtr =
+ cur->offset < dictLimit ? dictStart : lowPrefixPtr;
+
+ curForwardMatchLength = ZSTD_count_2segments(
+ ip, pMatch, iend,
+ matchEnd, lowPrefixPtr);
+ if (curForwardMatchLength < minMatchLength) {
+ continue;
+ }
+ curBackwardMatchLength =
+ ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch,
+ lowMatchPtr);
+ curTotalMatchLength = curForwardMatchLength +
+ curBackwardMatchLength;
+ } else { /* !extDict */
+ BYTE const* const pMatch = base + cur->offset;
+ curForwardMatchLength = ZSTD_count(ip, pMatch, iend);
+ if (curForwardMatchLength < minMatchLength) {
+ continue;
+ }
+ curBackwardMatchLength =
+ ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch,
+ lowPrefixPtr);
+ curTotalMatchLength = curForwardMatchLength +
+ curBackwardMatchLength;
}
- curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch(
- ip, anchor, pMatch, lowest);
- curTotalMatchLength = curForwardMatchLength +
- curBackwardMatchLength;
if (curTotalMatchLength > bestMatchLength) {
bestMatchLength = curTotalMatchLength;
@@ -375,7 +401,7 @@ size_t ZSTD_compressBlock_ldm_generic(ZSTD_CCtx* cctx,
if (bestEntry == NULL) {
ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash,
hBits, current,
- ldmParams);
+ *params);
ip++;
continue;
}
@@ -384,324 +410,244 @@ size_t ZSTD_compressBlock_ldm_generic(ZSTD_CCtx* cctx,
mLength = forwardMatchLength + backwardMatchLength;
ip -= backwardMatchLength;
- /* Call the block compressor on the remaining literals */
{
+ /* Store the sequence:
+ * ip = current - backwardMatchLength
+ * The match is at (bestEntry->offset - backwardMatchLength)
+ */
U32 const matchIndex = bestEntry->offset;
- const BYTE* const match = base + matchIndex - backwardMatchLength;
- U32 const offset = (U32)(ip - match);
-
- /* Overwrite rep codes */
- for (i = 0; i < ZSTD_REP_NUM; i++)
- seqStorePtr->rep[i] = repToConfirm[i];
-
- /* Fill tables for block compressor */
- ZSTD_ldm_limitTableUpdate(cctx, anchor);
- ZSTD_ldm_fillFastTables(cctx, anchor);
-
- /* Call block compressor and get remaining literals */
- lastLiterals = blockCompressor(cctx, anchor, ip - anchor);
- cctx->nextToUpdate = (U32)(ip - base);
-
- /* Update repToConfirm with the new offset */
- for (i = ZSTD_REP_NUM - 1; i > 0; i--)
- repToConfirm[i] = repToConfirm[i-1];
- repToConfirm[0] = offset;
-
- /* Store the sequence with the leftover literals */
- ZSTD_storeSeq(seqStorePtr, lastLiterals, ip - lastLiterals,
- offset + ZSTD_REP_MOVE, mLength - MINMATCH);
+ U32 const offset = current - matchIndex;
+ rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size;
+
+ /* Out of sequence storage */
+ if (rawSeqStore->size == rawSeqStore->capacity)
+ return ERROR(dstSize_tooSmall);
+ seq->litLength = (U32)(ip - anchor);
+ seq->matchLength = (U32)mLength;
+ seq->offset = offset;
+ rawSeqStore->size++;
}
/* Insert the current entry into the hash table */
ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits,
(U32)(lastHashed - base),
- ldmParams);
+ *params);
assert(ip + backwardMatchLength == lastHashed);
/* Fill the hash table from lastHashed+1 to ip+mLength*/
/* Heuristic: don't need to fill the entire table at end of block */
- if (ip + mLength < ilimit) {
+ if (ip + mLength <= ilimit) {
rollingHash = ZSTD_ldm_fillLdmHashTable(
ldmState, rollingHash, lastHashed,
- ip + mLength, base, hBits, ldmParams);
+ ip + mLength, base, hBits, *params);
lastHashed = ip + mLength - 1;
}
ip += mLength;
anchor = ip;
- /* Check immediate repcode */
- while ( (ip < ilimit)
- && ( (repToConfirm[1] > 0) && (repToConfirm[1] <= (U32)(ip-lowest))
- && (MEM_read32(ip) == MEM_read32(ip - repToConfirm[1])) )) {
-
- size_t const rLength = ZSTD_count(ip+4, ip+4-repToConfirm[1],
- iend) + 4;
- /* Swap repToConfirm[1] <=> repToConfirm[0] */
- {
- U32 const tmpOff = repToConfirm[1];
- repToConfirm[1] = repToConfirm[0];
- repToConfirm[0] = tmpOff;
- }
-
- ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, rLength-MINMATCH);
-
- /* Fill the hash table from lastHashed+1 to ip+rLength*/
- if (ip + rLength < ilimit) {
- rollingHash = ZSTD_ldm_fillLdmHashTable(
- ldmState, rollingHash, lastHashed,
- ip + rLength, base, hBits, ldmParams);
- lastHashed = ip + rLength - 1;
- }
- ip += rLength;
- anchor = ip;
- }
}
-
- /* Overwrite rep */
- for (i = 0; i < ZSTD_REP_NUM; i++)
- seqStorePtr->rep[i] = repToConfirm[i];
-
- ZSTD_ldm_limitTableUpdate(cctx, anchor);
- ZSTD_ldm_fillFastTables(cctx, anchor);
-
- lastLiterals = blockCompressor(cctx, anchor, iend - anchor);
- cctx->nextToUpdate = (U32)(iend - base);
-
- /* Restore seqStorePtr->rep */
- for (i = 0; i < ZSTD_REP_NUM; i++)
- seqStorePtr->rep[i] = savedRep[i];
-
- /* Return the last literals size */
- return lastLiterals;
+ return iend - anchor;
}
-size_t ZSTD_compressBlock_ldm(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize)
+/*! ZSTD_ldm_reduceTable() :
+ * reduce table indexes by `reducerValue` */
+static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size,
+ U32 const reducerValue)
{
- return ZSTD_compressBlock_ldm_generic(ctx, src, srcSize);
+ U32 u;
+ for (u = 0; u < size; u++) {
+ if (table[u].offset < reducerValue) table[u].offset = 0;
+ else table[u].offset -= reducerValue;
+ }
}
-static size_t ZSTD_compressBlock_ldm_extDict_generic(
- ZSTD_CCtx* ctx,
- const void* src, size_t srcSize)
+size_t ZSTD_ldm_generateSequences(
+ ldmState_t* ldmState, rawSeqStore_t* sequences,
+ ldmParams_t const* params, void const* src, size_t srcSize)
{
- ldmState_t* const ldmState = &(ctx->ldmState);
- const ldmParams_t ldmParams = ctx->appliedParams.ldmParams;
- const U64 hashPower = ldmState->hashPower;
- const U32 hBits = ldmParams.hashLog - ldmParams.bucketSizeLog;
- const U32 ldmBucketSize = ((U32)1 << ldmParams.bucketSizeLog);
- const U32 ldmTagMask = ((U32)1 << ldmParams.hashEveryLog) - 1;
- seqStore_t* const seqStorePtr = &(ctx->seqStore);
- const BYTE* const base = ctx->base;
- const BYTE* const dictBase = ctx->dictBase;
- const BYTE* const istart = (const BYTE*)src;
- const BYTE* ip = istart;
- const BYTE* anchor = istart;
- const U32 lowestIndex = ctx->lowLimit;
- const BYTE* const dictStart = dictBase + lowestIndex;
- const U32 dictLimit = ctx->dictLimit;
- const BYTE* const lowPrefixPtr = base + dictLimit;
- const BYTE* const dictEnd = dictBase + dictLimit;
- const BYTE* const iend = istart + srcSize;
- const BYTE* const ilimit = iend - MAX(ldmParams.minMatchLength, HASH_READ_SIZE);
-
- const ZSTD_blockCompressor blockCompressor =
- ZSTD_selectBlockCompressor(ctx->appliedParams.cParams.strategy, 1);
- U32* const repToConfirm = seqStorePtr->repToConfirm;
- U32 savedRep[ZSTD_REP_NUM];
- U64 rollingHash = 0;
- const BYTE* lastHashed = NULL;
- size_t i, lastLiterals;
-
- /* Save seqStorePtr->rep and copy repToConfirm */
- for (i = 0; i < ZSTD_REP_NUM; i++) {
- savedRep[i] = repToConfirm[i] = seqStorePtr->rep[i];
- }
-
- /* Search Loop */
- while (ip < ilimit) { /* < instead of <=, because (ip+1) */
- size_t mLength;
- const U32 current = (U32)(ip-base);
- size_t forwardMatchLength = 0, backwardMatchLength = 0;
- ldmEntry_t* bestEntry = NULL;
- if (ip != istart) {
- rollingHash = ZSTD_ldm_updateHash(rollingHash, lastHashed[0],
- lastHashed[ldmParams.minMatchLength],
- hashPower);
+ U32 const maxDist = 1U << params->windowLog;
+ BYTE const* const istart = (BYTE const*)src;
+ BYTE const* const iend = istart + srcSize;
+ size_t const kMaxChunkSize = 1 << 20;
+ size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0);
+ size_t chunk;
+ size_t leftoverSize = 0;
+
+ assert(ZSTD_CHUNKSIZE_MAX >= kMaxChunkSize);
+ /* Check that ZSTD_window_update() has been called for this chunk prior
+ * to passing it to this function.
+ */
+ assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize);
+ /* The input could be very large (in zstdmt), so it must be broken up into
+ * chunks to enforce the maximmum distance and handle overflow correction.
+ */
+ assert(sequences->pos <= sequences->size);
+ assert(sequences->size <= sequences->capacity);
+ for (chunk = 0; chunk < nbChunks && sequences->size < sequences->capacity; ++chunk) {
+ BYTE const* const chunkStart = istart + chunk * kMaxChunkSize;
+ size_t const remaining = (size_t)(iend - chunkStart);
+ BYTE const *const chunkEnd =
+ (remaining < kMaxChunkSize) ? iend : chunkStart + kMaxChunkSize;
+ size_t const chunkSize = chunkEnd - chunkStart;
+ size_t newLeftoverSize;
+ size_t const prevSize = sequences->size;
+
+ assert(chunkStart < iend);
+ /* 1. Perform overflow correction if necessary. */
+ if (ZSTD_window_needOverflowCorrection(ldmState->window, chunkEnd)) {
+ U32 const ldmHSize = 1U << params->hashLog;
+ U32 const correction = ZSTD_window_correctOverflow(
+ &ldmState->window, /* cycleLog */ 0, maxDist, src);
+ ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction);
+ }
+ /* 2. We enforce the maximum offset allowed.
+ *
+ * kMaxChunkSize should be small enough that we don't lose too much of
+ * the window through early invalidation.
+ * TODO: * Test the chunk size.
+ * * Try invalidation after the sequence generation and test the
+ * the offset against maxDist directly.
+ */
+ ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, NULL);
+ /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */
+ newLeftoverSize = ZSTD_ldm_generateSequences_internal(
+ ldmState, sequences, params, chunkStart, chunkSize);
+ if (ZSTD_isError(newLeftoverSize))
+ return newLeftoverSize;
+ /* 4. We add the leftover literals from previous iterations to the first
+ * newly generated sequence, or add the `newLeftoverSize` if none are
+ * generated.
+ */
+ /* Prepend the leftover literals from the last call */
+ if (prevSize < sequences->size) {
+ sequences->seq[prevSize].litLength += (U32)leftoverSize;
+ leftoverSize = newLeftoverSize;
} else {
- rollingHash = ZSTD_ldm_getRollingHash(ip, ldmParams.minMatchLength);
+ assert(newLeftoverSize == chunkSize);
+ leftoverSize += chunkSize;
}
- lastHashed = ip;
+ }
+ return 0;
+}
- if (ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashEveryLog) !=
- ldmTagMask) {
- /* Don't insert and don't look for a match */
- ip++;
- continue;
+void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch) {
+ while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) {
+ rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos;
+ if (srcSize <= seq->litLength) {
+ /* Skip past srcSize literals */
+ seq->litLength -= (U32)srcSize;
+ return;
}
-
- /* Get the best entry and compute the match lengths */
- {
- ldmEntry_t* const bucket =
- ZSTD_ldm_getBucket(ldmState,
- ZSTD_ldm_getSmallHash(rollingHash, hBits),
- ldmParams);
- ldmEntry_t* cur;
- size_t bestMatchLength = 0;
- U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
-
- for (cur = bucket; cur < bucket + ldmBucketSize; ++cur) {
- const BYTE* const curMatchBase =
- cur->offset < dictLimit ? dictBase : base;
- const BYTE* const pMatch = curMatchBase + cur->offset;
- const BYTE* const matchEnd =
- cur->offset < dictLimit ? dictEnd : iend;
- const BYTE* const lowMatchPtr =
- cur->offset < dictLimit ? dictStart : lowPrefixPtr;
- size_t curForwardMatchLength, curBackwardMatchLength,
- curTotalMatchLength;
-
- if (cur->checksum != checksum || cur->offset <= lowestIndex) {
- continue;
- }
-
- curForwardMatchLength = ZSTD_count_2segments(
- ip, pMatch, iend,
- matchEnd, lowPrefixPtr);
- if (curForwardMatchLength < ldmParams.minMatchLength) {
- continue;
- }
- curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch(
- ip, anchor, pMatch, lowMatchPtr);
- curTotalMatchLength = curForwardMatchLength +
- curBackwardMatchLength;
-
- if (curTotalMatchLength > bestMatchLength) {
- bestMatchLength = curTotalMatchLength;
- forwardMatchLength = curForwardMatchLength;
- backwardMatchLength = curBackwardMatchLength;
- bestEntry = cur;
+ srcSize -= seq->litLength;
+ seq->litLength = 0;
+ if (srcSize < seq->matchLength) {
+ /* Skip past the first srcSize of the match */
+ seq->matchLength -= (U32)srcSize;
+ if (seq->matchLength < minMatch) {
+ /* The match is too short, omit it */
+ if (rawSeqStore->pos + 1 < rawSeqStore->size) {
+ seq[1].litLength += seq[0].matchLength;
}
+ rawSeqStore->pos++;
}
+ return;
}
+ srcSize -= seq->matchLength;
+ seq->matchLength = 0;
+ rawSeqStore->pos++;
+ }
+}
- /* No match found -- continue searching */
- if (bestEntry == NULL) {
- ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits,
- (U32)(lastHashed - base),
- ldmParams);
- ip++;
- continue;
+/**
+ * If the sequence length is longer than remaining then the sequence is split
+ * between this block and the next.
+ *
+ * Returns the current sequence to handle, or if the rest of the block should
+ * be literals, it returns a sequence with offset == 0.
+ */
+static rawSeq maybeSplitSequence(rawSeqStore_t* rawSeqStore,
+ U32 const remaining, U32 const minMatch)
+{
+ rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos];
+ assert(sequence.offset > 0);
+ /* Likely: No partial sequence */
+ if (remaining >= sequence.litLength + sequence.matchLength) {
+ rawSeqStore->pos++;
+ return sequence;
+ }
+ /* Cut the sequence short (offset == 0 ==> rest is literals). */
+ if (remaining <= sequence.litLength) {
+ sequence.offset = 0;
+ } else if (remaining < sequence.litLength + sequence.matchLength) {
+ sequence.matchLength = remaining - sequence.litLength;
+ if (sequence.matchLength < minMatch) {
+ sequence.offset = 0;
}
+ }
+ /* Skip past `remaining` bytes for the future sequences. */
+ ZSTD_ldm_skipSequences(rawSeqStore, remaining, minMatch);
+ return sequence;
+}
- /* Match found */
- mLength = forwardMatchLength + backwardMatchLength;
- ip -= backwardMatchLength;
-
- /* Call the block compressor on the remaining literals */
- {
- /* ip = current - backwardMatchLength
- * The match is at (bestEntry->offset - backwardMatchLength) */
- U32 const matchIndex = bestEntry->offset;
- U32 const offset = current - matchIndex;
-
- /* Overwrite rep codes */
- for (i = 0; i < ZSTD_REP_NUM; i++)
- seqStorePtr->rep[i] = repToConfirm[i];
-
- /* Fill the hash table for the block compressor */
- ZSTD_ldm_limitTableUpdate(ctx, anchor);
- ZSTD_ldm_fillFastTables(ctx, anchor);
+size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize,
+ int const extDict)
+{
+ unsigned const minMatch = cParams->searchLength;
+ ZSTD_blockCompressor const blockCompressor =
+ ZSTD_selectBlockCompressor(cParams->strategy, extDict);
+ BYTE const* const base = ms->window.base;
+ /* Input bounds */
+ BYTE const* const istart = (BYTE const*)src;
+ BYTE const* const iend = istart + srcSize;
+ /* Input positions */
+ BYTE const* ip = istart;
+
+ assert(rawSeqStore->pos <= rawSeqStore->size);
+ assert(rawSeqStore->size <= rawSeqStore->capacity);
+ /* Loop through each sequence and apply the block compressor to the lits */
+ while (rawSeqStore->pos < rawSeqStore->size && ip < iend) {
+ /* maybeSplitSequence updates rawSeqStore->pos */
+ rawSeq const sequence = maybeSplitSequence(rawSeqStore,
+ (U32)(iend - ip), minMatch);
+ int i;
+ /* End signal */
+ if (sequence.offset == 0)
+ break;
- /* Call block compressor and get remaining literals */
- lastLiterals = blockCompressor(ctx, anchor, ip - anchor);
- ctx->nextToUpdate = (U32)(ip - base);
+ assert(sequence.offset <= (1U << cParams->windowLog));
+ assert(ip + sequence.litLength + sequence.matchLength <= iend);
- /* Update repToConfirm with the new offset */
+ /* Fill tables for block compressor */
+ ZSTD_ldm_limitTableUpdate(ms, ip);
+ ZSTD_ldm_fillFastTables(ms, cParams, ip);
+ /* Run the block compressor */
+ {
+ size_t const newLitLength =
+ blockCompressor(ms, seqStore, rep, cParams, ip,
+ sequence.litLength);
+ ip += sequence.litLength;
+ ms->nextToUpdate = (U32)(ip - base);
+ /* Update the repcodes */
for (i = ZSTD_REP_NUM - 1; i > 0; i--)
- repToConfirm[i] = repToConfirm[i-1];
- repToConfirm[0] = offset;
-
- /* Store the sequence with the leftover literals */
- ZSTD_storeSeq(seqStorePtr, lastLiterals, ip - lastLiterals,
- offset + ZSTD_REP_MOVE, mLength - MINMATCH);
- }
-
- /* Insert the current entry into the hash table */
- ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits,
- (U32)(lastHashed - base),
- ldmParams);
-
- /* Fill the hash table from lastHashed+1 to ip+mLength */
- assert(ip + backwardMatchLength == lastHashed);
- if (ip + mLength < ilimit) {
- rollingHash = ZSTD_ldm_fillLdmHashTable(
- ldmState, rollingHash, lastHashed,
- ip + mLength, base, hBits,
- ldmParams);
- lastHashed = ip + mLength - 1;
- }
- ip += mLength;
- anchor = ip;
-
- /* check immediate repcode */
- while (ip < ilimit) {
- U32 const current2 = (U32)(ip-base);
- U32 const repIndex2 = current2 - repToConfirm[1];
- const BYTE* repMatch2 = repIndex2 < dictLimit ?
- dictBase + repIndex2 : base + repIndex2;
- if ( (((U32)((dictLimit-1) - repIndex2) >= 3) &
- (repIndex2 > lowestIndex)) /* intentional overflow */
- && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
- const BYTE* const repEnd2 = repIndex2 < dictLimit ?
- dictEnd : iend;
- size_t const repLength2 =
- ZSTD_count_2segments(ip+4, repMatch2+4, iend,
- repEnd2, lowPrefixPtr) + 4;
-
- U32 tmpOffset = repToConfirm[1];
- repToConfirm[1] = repToConfirm[0];
- repToConfirm[0] = tmpOffset;
-
- ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, repLength2-MINMATCH);
-
- /* Fill the hash table from lastHashed+1 to ip+repLength2*/
- if (ip + repLength2 < ilimit) {
- rollingHash = ZSTD_ldm_fillLdmHashTable(
- ldmState, rollingHash, lastHashed,
- ip + repLength2, base, hBits,
- ldmParams);
- lastHashed = ip + repLength2 - 1;
- }
- ip += repLength2;
- anchor = ip;
- continue;
- }
- break;
+ rep[i] = rep[i-1];
+ rep[0] = sequence.offset;
+ /* Store the sequence */
+ ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength,
+ sequence.offset + ZSTD_REP_MOVE,
+ sequence.matchLength - MINMATCH);
+ ip += sequence.matchLength;
}
}
-
- /* Overwrite rep */
- for (i = 0; i < ZSTD_REP_NUM; i++)
- seqStorePtr->rep[i] = repToConfirm[i];
-
- ZSTD_ldm_limitTableUpdate(ctx, anchor);
- ZSTD_ldm_fillFastTables(ctx, anchor);
-
- /* Call the block compressor one last time on the last literals */
- lastLiterals = blockCompressor(ctx, anchor, iend - anchor);
- ctx->nextToUpdate = (U32)(iend - base);
-
- /* Restore seqStorePtr->rep */
- for (i = 0; i < ZSTD_REP_NUM; i++)
- seqStorePtr->rep[i] = savedRep[i];
-
- /* Return the last literals size */
- return lastLiterals;
-}
-
-size_t ZSTD_compressBlock_ldm_extDict(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize)
-{
- return ZSTD_compressBlock_ldm_extDict_generic(ctx, src, srcSize);
+ /* Fill the tables for the block compressor */
+ ZSTD_ldm_limitTableUpdate(ms, ip);
+ ZSTD_ldm_fillFastTables(ms, cParams, ip);
+ /* Compress the last literals */
+ {
+ size_t const lastLiterals = blockCompressor(ms, seqStore, rep, cParams,
+ ip, iend - ip);
+ ms->nextToUpdate = (U32)(iend - base);
+ return lastLiterals;
+ }
}
diff --git a/thirdparty/zstd/compress/zstd_ldm.h b/thirdparty/zstd/compress/zstd_ldm.h
index 8f12c677aa..0c3789ff13 100644
--- a/thirdparty/zstd/compress/zstd_ldm.h
+++ b/thirdparty/zstd/compress/zstd_ldm.h
@@ -22,32 +22,71 @@ extern "C" {
***************************************/
#define ZSTD_LDM_DEFAULT_WINDOW_LOG ZSTD_WINDOWLOG_DEFAULTMAX
-#define ZSTD_LDM_HASHEVERYLOG_NOTSET 9999
-/** ZSTD_compressBlock_ldm_generic() :
+/**
+ * ZSTD_ldm_generateSequences():
*
- * This is a block compressor intended for long distance matching.
+ * Generates the sequences using the long distance match finder.
+ * Generates long range matching sequences in `sequences`, which parse a prefix
+ * of the source. `sequences` must be large enough to store every sequence,
+ * which can be checked with `ZSTD_ldm_getMaxNbSeq()`.
+ * @returns 0 or an error code.
*
- * The function searches for matches of length at least
- * ldmParams.minMatchLength using a hash table in cctx->ldmState.
- * Matches can be at a distance of up to cParams.windowLog.
+ * NOTE: The user must have called ZSTD_window_update() for all of the input
+ * they have, even if they pass it to ZSTD_ldm_generateSequences() in chunks.
+ * NOTE: This function returns an error if it runs out of space to store
+ * sequences.
+ */
+size_t ZSTD_ldm_generateSequences(
+ ldmState_t* ldms, rawSeqStore_t* sequences,
+ ldmParams_t const* params, void const* src, size_t srcSize);
+
+/**
+ * ZSTD_ldm_blockCompress():
+ *
+ * Compresses a block using the predefined sequences, along with a secondary
+ * block compressor. The literals section of every sequence is passed to the
+ * secondary block compressor, and those sequences are interspersed with the
+ * predefined sequences. Returns the length of the last literals.
+ * Updates `rawSeqStore.pos` to indicate how many sequences have been consumed.
+ * `rawSeqStore.seq` may also be updated to split the last sequence between two
+ * blocks.
+ * @return The length of the last literals.
+ *
+ * NOTE: The source must be at most the maximum block size, but the predefined
+ * sequences can be any size, and may be longer than the block. In the case that
+ * they are longer than the block, the last sequences may need to be split into
+ * two. We handle that case correctly, and update `rawSeqStore` appropriately.
+ * NOTE: This function does not return any errors.
+ */
+size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams,
+ void const* src, size_t srcSize,
+ int const extDict);
+
+/**
+ * ZSTD_ldm_skipSequences():
*
- * Upon finding a match, the unmatched literals are compressed using a
- * ZSTD_blockCompressor (depending on the strategy in the compression
- * parameters), which stores the matched sequences. The "long distance"
- * match is then stored with the remaining literals from the
- * ZSTD_blockCompressor. */
-size_t ZSTD_compressBlock_ldm(ZSTD_CCtx* cctx, const void* src, size_t srcSize);
-size_t ZSTD_compressBlock_ldm_extDict(ZSTD_CCtx* ctx,
- const void* src, size_t srcSize);
+ * Skip past `srcSize` bytes worth of sequences in `rawSeqStore`.
+ * Avoids emitting matches less than `minMatch` bytes.
+ * Must be called for data with is not passed to ZSTD_ldm_blockCompress().
+ */
+void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize,
+ U32 const minMatch);
-/** ZSTD_ldm_initializeParameters() :
- * Initialize the long distance matching parameters to their default values. */
-size_t ZSTD_ldm_initializeParameters(ldmParams_t* params, U32 enableLdm);
/** ZSTD_ldm_getTableSize() :
- * Estimate the space needed for long distance matching tables. */
-size_t ZSTD_ldm_getTableSize(U32 hashLog, U32 bucketSizeLog);
+ * Estimate the space needed for long distance matching tables or 0 if LDM is
+ * disabled.
+ */
+size_t ZSTD_ldm_getTableSize(ldmParams_t params);
+
+/** ZSTD_ldm_getSeqSpace() :
+ * Return an upper bound on the number of sequences that can be produced by
+ * the long distance matcher, or 0 if LDM is disabled.
+ */
+size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize);
/** ZSTD_ldm_getTableSize() :
* Return prime8bytes^(minMatchLength-1) */
@@ -58,8 +97,12 @@ U64 ZSTD_ldm_getHashPower(U32 minMatchLength);
* windowLog and params->hashLog.
*
* Ensures that params->bucketSizeLog is <= params->hashLog (setting it to
- * params->hashLog if it is not). */
-void ZSTD_ldm_adjustParameters(ldmParams_t* params, U32 windowLog);
+ * params->hashLog if it is not).
+ *
+ * Ensures that the minMatchLength >= targetLength during optimal parsing.
+ */
+void ZSTD_ldm_adjustParameters(ldmParams_t* params,
+ ZSTD_compressionParameters const* cParams);
#if defined (__cplusplus)
}
diff --git a/thirdparty/zstd/compress/zstd_opt.c b/thirdparty/zstd/compress/zstd_opt.c
index 7171ff5373..f63f0c5852 100644
--- a/thirdparty/zstd/compress/zstd_opt.c
+++ b/thirdparty/zstd/compress/zstd_opt.c
@@ -10,7 +10,6 @@
#include "zstd_compress_internal.h"
#include "zstd_opt.h"
-#include "zstd_lazy.h" /* ZSTD_updateTree, ZSTD_updateTree_extDict */
#define ZSTD_LITFREQ_ADD 2 /* scaling factor for litFreq, so that frequencies adapt faster to new stats. Also used for matchSum (?) */
@@ -244,14 +243,15 @@ MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length)
/* Update hashTable3 up to ip (excluded)
Assumption : always within prefix (i.e. not within extDict) */
-static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_CCtx* const cctx, const BYTE* const ip)
+static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE* const ip)
{
- U32* const hashTable3 = cctx->hashTable3;
- U32 const hashLog3 = cctx->hashLog3;
- const BYTE* const base = cctx->base;
- U32 idx = cctx->nextToUpdate3;
- U32 const target = cctx->nextToUpdate3 = (U32)(ip - base);
+ U32* const hashTable3 = ms->hashTable3;
+ U32 const hashLog3 = ms->hashLog3;
+ const BYTE* const base = ms->window.base;
+ U32 idx = ms->nextToUpdate3;
+ U32 const target = ms->nextToUpdate3 = (U32)(ip - base);
size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3);
+ assert(hashLog3 > 0);
while(idx < target) {
hashTable3[ZSTD_hash3Ptr(base+idx, hashLog3)] = idx;
@@ -265,36 +265,173 @@ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_CCtx* const cctx, const BYTE*
/*-*************************************
* Binary Tree search
***************************************/
+/** ZSTD_insertBt1() : add one or multiple positions to tree.
+ * ip : assumed <= iend-8 .
+ * @return : nb of positions added */
+static U32 ZSTD_insertBt1(
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ const BYTE* const ip, const BYTE* const iend,
+ U32 const mls, U32 const extDict)
+{
+ U32* const hashTable = ms->hashTable;
+ U32 const hashLog = cParams->hashLog;
+ size_t const h = ZSTD_hashPtr(ip, hashLog, mls);
+ U32* const bt = ms->chainTable;
+ U32 const btLog = cParams->chainLog - 1;
+ U32 const btMask = (1 << btLog) - 1;
+ U32 matchIndex = hashTable[h];
+ size_t commonLengthSmaller=0, commonLengthLarger=0;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const dictBase = ms->window.dictBase;
+ const U32 dictLimit = ms->window.dictLimit;
+ const BYTE* const dictEnd = dictBase + dictLimit;
+ const BYTE* const prefixStart = base + dictLimit;
+ const BYTE* match;
+ const U32 current = (U32)(ip-base);
+ const U32 btLow = btMask >= current ? 0 : current - btMask;
+ U32* smallerPtr = bt + 2*(current&btMask);
+ U32* largerPtr = smallerPtr + 1;
+ U32 dummy32; /* to be nullified at the end */
+ U32 const windowLow = ms->window.lowLimit;
+ U32 matchEndIdx = current+8+1;
+ size_t bestLength = 8;
+ U32 nbCompares = 1U << cParams->searchLog;
+#ifdef ZSTD_C_PREDICT
+ U32 predictedSmall = *(bt + 2*((current-1)&btMask) + 0);
+ U32 predictedLarge = *(bt + 2*((current-1)&btMask) + 1);
+ predictedSmall += (predictedSmall>0);
+ predictedLarge += (predictedLarge>0);
+#endif /* ZSTD_C_PREDICT */
+
+ DEBUGLOG(8, "ZSTD_insertBt1 (%u)", current);
+
+ assert(ip <= iend-8); /* required for h calculation */
+ hashTable[h] = current; /* Update Hash Table */
+
+ while (nbCompares-- && (matchIndex > windowLow)) {
+ U32* const nextPtr = bt + 2*(matchIndex & btMask);
+ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
+ assert(matchIndex < current);
+
+#ifdef ZSTD_C_PREDICT /* note : can create issues when hlog small <= 11 */
+ const U32* predictPtr = bt + 2*((matchIndex-1) & btMask); /* written this way, as bt is a roll buffer */
+ if (matchIndex == predictedSmall) {
+ /* no need to check length, result known */
+ *smallerPtr = matchIndex;
+ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */
+ smallerPtr = nextPtr+1; /* new "smaller" => larger of match */
+ matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */
+ predictedSmall = predictPtr[1] + (predictPtr[1]>0);
+ continue;
+ }
+ if (matchIndex == predictedLarge) {
+ *largerPtr = matchIndex;
+ if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */
+ largerPtr = nextPtr;
+ matchIndex = nextPtr[0];
+ predictedLarge = predictPtr[0] + (predictPtr[0]>0);
+ continue;
+ }
+#endif
+
+ if ((!extDict) || (matchIndex+matchLength >= dictLimit)) {
+ assert(matchIndex+matchLength >= dictLimit); /* might be wrong if extDict is incorrectly set to 0 */
+ match = base + matchIndex;
+ matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend);
+ } else {
+ match = dictBase + matchIndex;
+ matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart);
+ if (matchIndex+matchLength >= dictLimit)
+ match = base + matchIndex; /* to prepare for next usage of match[matchLength] */
+ }
+
+ if (matchLength > bestLength) {
+ bestLength = matchLength;
+ if (matchLength > matchEndIdx - matchIndex)
+ matchEndIdx = matchIndex + (U32)matchLength;
+ }
+
+ if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */
+ break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt tree */
+ }
+
+ if (match[matchLength] < ip[matchLength]) { /* necessarily within buffer */
+ /* match is smaller than current */
+ *smallerPtr = matchIndex; /* update smaller idx */
+ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */
+ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop searching */
+ smallerPtr = nextPtr+1; /* new "candidate" => larger than match, which was smaller than target */
+ matchIndex = nextPtr[1]; /* new matchIndex, larger than previous and closer to current */
+ } else {
+ /* match is larger than current */
+ *largerPtr = matchIndex;
+ commonLengthLarger = matchLength;
+ if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop searching */
+ largerPtr = nextPtr;
+ matchIndex = nextPtr[0];
+ } }
+
+ *smallerPtr = *largerPtr = 0;
+ if (bestLength > 384) return MIN(192, (U32)(bestLength - 384)); /* speed optimization */
+ assert(matchEndIdx > current + 8);
+ return matchEndIdx - (current + 8);
+}
+
+FORCE_INLINE_TEMPLATE
+void ZSTD_updateTree_internal(
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ const BYTE* const ip, const BYTE* const iend,
+ const U32 mls, const U32 extDict)
+{
+ const BYTE* const base = ms->window.base;
+ U32 const target = (U32)(ip - base);
+ U32 idx = ms->nextToUpdate;
+ DEBUGLOG(7, "ZSTD_updateTree_internal, from %u to %u (extDict:%u)",
+ idx, target, extDict);
+
+ while(idx < target)
+ idx += ZSTD_insertBt1(ms, cParams, base+idx, iend, mls, extDict);
+ ms->nextToUpdate = target;
+}
+
+void ZSTD_updateTree(
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ const BYTE* ip, const BYTE* iend)
+{
+ ZSTD_updateTree_internal(ms, cParams, ip, iend, cParams->searchLength, 0 /*extDict*/);
+}
+
FORCE_INLINE_TEMPLATE
U32 ZSTD_insertBtAndGetAllMatches (
- ZSTD_CCtx* zc,
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
const BYTE* const ip, const BYTE* const iLimit, int const extDict,
- U32 nbCompares, U32 const mls, U32 const sufficient_len,
U32 rep[ZSTD_REP_NUM], U32 const ll0,
- ZSTD_match_t* matches, const U32 lengthToBeat)
+ ZSTD_match_t* matches, const U32 lengthToBeat, U32 const mls /* template */)
{
- const BYTE* const base = zc->base;
+ U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);
+ const BYTE* const base = ms->window.base;
U32 const current = (U32)(ip-base);
- U32 const hashLog = zc->appliedParams.cParams.hashLog;
+ U32 const hashLog = cParams->hashLog;
U32 const minMatch = (mls==3) ? 3 : 4;
- U32* const hashTable = zc->hashTable;
+ U32* const hashTable = ms->hashTable;
size_t const h = ZSTD_hashPtr(ip, hashLog, mls);
U32 matchIndex = hashTable[h];
- U32* const bt = zc->chainTable;
- U32 const btLog = zc->appliedParams.cParams.chainLog - 1;
+ U32* const bt = ms->chainTable;
+ U32 const btLog = cParams->chainLog - 1;
U32 const btMask= (1U << btLog) - 1;
size_t commonLengthSmaller=0, commonLengthLarger=0;
- const BYTE* const dictBase = zc->dictBase;
- U32 const dictLimit = zc->dictLimit;
+ const BYTE* const dictBase = ms->window.dictBase;
+ U32 const dictLimit = ms->window.dictLimit;
const BYTE* const dictEnd = dictBase + dictLimit;
const BYTE* const prefixStart = base + dictLimit;
U32 const btLow = btMask >= current ? 0 : current - btMask;
- U32 const windowLow = zc->lowLimit;
+ U32 const windowLow = ms->window.lowLimit;
U32* smallerPtr = bt + 2*(current&btMask);
U32* largerPtr = bt + 2*(current&btMask) + 1;
U32 matchEndIdx = current+8+1; /* farthest referenced position of any match => detects repetitive patterns */
U32 dummy32; /* to be nullified at the end */
U32 mnum = 0;
+ U32 nbCompares = 1U << cParams->searchLog;
size_t bestLength = lengthToBeat-1;
DEBUGLOG(7, "ZSTD_insertBtAndGetAllMatches");
@@ -335,7 +472,7 @@ U32 ZSTD_insertBtAndGetAllMatches (
/* HC3 match finder */
if ((mls == 3) /*static*/ && (bestLength < mls)) {
- U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3 (zc, ip);
+ U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, ip);
if ((matchIndex3 > windowLow)
& (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) {
size_t mlen;
@@ -359,7 +496,7 @@ U32 ZSTD_insertBtAndGetAllMatches (
mnum = 1;
if ( (mlen > sufficient_len) |
(ip+mlen == iLimit) ) { /* best possible length */
- zc->nextToUpdate = current+1; /* skip insertion */
+ ms->nextToUpdate = current+1; /* skip insertion */
return 1;
} } } }
@@ -416,30 +553,29 @@ U32 ZSTD_insertBtAndGetAllMatches (
*smallerPtr = *largerPtr = 0;
assert(matchEndIdx > current+8);
- zc->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */
+ ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */
return mnum;
}
FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches (
- ZSTD_CCtx* zc, /* Index table will be updated */
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
const BYTE* ip, const BYTE* const iHighLimit, int const extDict,
- U32 const maxNbAttempts, U32 const matchLengthSearch, U32 const sufficient_len,
U32 rep[ZSTD_REP_NUM], U32 const ll0,
ZSTD_match_t* matches, U32 const lengthToBeat)
{
+ U32 const matchLengthSearch = cParams->searchLength;
DEBUGLOG(7, "ZSTD_BtGetAllMatches");
- if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */
- if (extDict) ZSTD_updateTree_extDict(zc, ip, iHighLimit, maxNbAttempts, matchLengthSearch);
- else ZSTD_updateTree(zc, ip, iHighLimit, maxNbAttempts, matchLengthSearch);
+ if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */
+ ZSTD_updateTree_internal(ms, cParams, ip, iHighLimit, matchLengthSearch, extDict);
switch(matchLengthSearch)
{
- case 3 : return ZSTD_insertBtAndGetAllMatches(zc, ip, iHighLimit, extDict, maxNbAttempts, 3, sufficient_len, rep, ll0, matches, lengthToBeat);
+ case 3 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 3);
default :
- case 4 : return ZSTD_insertBtAndGetAllMatches(zc, ip, iHighLimit, extDict, maxNbAttempts, 4, sufficient_len, rep, ll0, matches, lengthToBeat);
- case 5 : return ZSTD_insertBtAndGetAllMatches(zc, ip, iHighLimit, extDict, maxNbAttempts, 5, sufficient_len, rep, ll0, matches, lengthToBeat);
+ case 4 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 4);
+ case 5 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 5);
case 7 :
- case 6 : return ZSTD_insertBtAndGetAllMatches(zc, ip, iHighLimit, extDict, maxNbAttempts, 6, sufficient_len, rep, ll0, matches, lengthToBeat);
+ case 6 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 6);
}
}
@@ -527,36 +663,33 @@ static int ZSTD_literalsContribution_cached(
}
FORCE_INLINE_TEMPLATE
-size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx,
+size_t ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,seqStore_t* seqStore,
+ U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams,
const void* src, size_t srcSize,
const int optLevel, const int extDict)
{
- seqStore_t* const seqStorePtr = &(ctx->seqStore);
- optState_t* const optStatePtr = &(ctx->optState);
+ optState_t* const optStatePtr = &ms->opt;
const BYTE* const istart = (const BYTE*)src;
const BYTE* ip = istart;
const BYTE* anchor = istart;
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - 8;
- const BYTE* const base = ctx->base;
- const BYTE* const prefixStart = base + ctx->dictLimit;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const prefixStart = base + ms->window.dictLimit;
- U32 const maxSearches = 1U << ctx->appliedParams.cParams.searchLog;
- U32 const sufficient_len = MIN(ctx->appliedParams.cParams.targetLength, ZSTD_OPT_NUM -1);
- U32 const mls = ctx->appliedParams.cParams.searchLength;
- U32 const minMatch = (ctx->appliedParams.cParams.searchLength == 3) ? 3 : 4;
+ U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);
+ U32 const minMatch = (cParams->searchLength == 3) ? 3 : 4;
ZSTD_optimal_t* const opt = optStatePtr->priceTable;
ZSTD_match_t* const matches = optStatePtr->matchTable;
cachedLiteralPrice_t cachedLitPrice;
- U32 rep[ZSTD_REP_NUM];
/* init */
DEBUGLOG(5, "ZSTD_compressBlock_opt_generic");
- ctx->nextToUpdate3 = ctx->nextToUpdate;
+ ms->nextToUpdate3 = ms->nextToUpdate;
ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize);
ip += (ip==prefixStart);
- { int i; for (i=0; i<ZSTD_REP_NUM; i++) rep[i]=seqStorePtr->rep[i]; }
memset(&cachedLitPrice, 0, sizeof(cachedLitPrice));
/* Match Loop */
@@ -567,7 +700,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx,
/* find first match */
{ U32 const litlen = (U32)(ip - anchor);
U32 const ll0 = !litlen;
- U32 const nbMatches = ZSTD_BtGetAllMatches(ctx, ip, iend, extDict, maxSearches, mls, sufficient_len, rep, ll0, matches, minMatch);
+ U32 const nbMatches = ZSTD_BtGetAllMatches(ms, cParams, ip, iend, extDict, rep, ll0, matches, minMatch);
if (!nbMatches) { ip++; continue; }
/* initialize opt[0] */
@@ -653,7 +786,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx,
U32 const litlen = (opt[cur].mlen == 1) ? opt[cur].litlen : 0;
U32 const previousPrice = (cur > litlen) ? opt[cur-litlen].price : 0;
U32 const basePrice = previousPrice + ZSTD_fullLiteralsCost(inr-litlen, litlen, optStatePtr);
- U32 const nbMatches = ZSTD_BtGetAllMatches(ctx, inr, iend, extDict, maxSearches, mls, sufficient_len, opt[cur].rep, ll0, matches, minMatch);
+ U32 const nbMatches = ZSTD_BtGetAllMatches(ms, cParams, inr, iend, extDict, opt[cur].rep, ll0, matches, minMatch);
U32 matchNb;
if (!nbMatches) continue;
@@ -749,37 +882,42 @@ _shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */
}
ZSTD_updateStats(optStatePtr, llen, anchor, offset, mlen);
- ZSTD_storeSeq(seqStorePtr, llen, anchor, offset, mlen-MINMATCH);
+ ZSTD_storeSeq(seqStore, llen, anchor, offset, mlen-MINMATCH);
anchor = ip;
} }
ZSTD_setLog2Prices(optStatePtr);
} /* while (ip < ilimit) */
- /* Save reps for next block */
- { int i; for (i=0; i<ZSTD_REP_NUM; i++) seqStorePtr->repToConfirm[i] = rep[i]; }
-
/* Return the last literals size */
return iend - anchor;
}
-size_t ZSTD_compressBlock_btopt(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_btopt(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
DEBUGLOG(5, "ZSTD_compressBlock_btopt");
- return ZSTD_compressBlock_opt_generic(ctx, src, srcSize, 0 /*optLevel*/, 0 /*extDict*/);
+ return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, 0 /*extDict*/);
}
-size_t ZSTD_compressBlock_btultra(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_btultra(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- return ZSTD_compressBlock_opt_generic(ctx, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/);
+ return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/);
}
-size_t ZSTD_compressBlock_btopt_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_btopt_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- return ZSTD_compressBlock_opt_generic(ctx, src, srcSize, 0 /*optLevel*/, 1 /*extDict*/);
+ return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, 1 /*extDict*/);
}
-size_t ZSTD_compressBlock_btultra_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize)
+size_t ZSTD_compressBlock_btultra_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize)
{
- return ZSTD_compressBlock_opt_generic(ctx, src, srcSize, 2 /*optLevel*/, 1 /*extDict*/);
+ return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, 1 /*extDict*/);
}
diff --git a/thirdparty/zstd/compress/zstd_opt.h b/thirdparty/zstd/compress/zstd_opt.h
index 82e810c293..b8dc389f31 100644
--- a/thirdparty/zstd/compress/zstd_opt.h
+++ b/thirdparty/zstd/compress/zstd_opt.h
@@ -15,13 +15,25 @@
extern "C" {
#endif
-#include "zstd.h" /* ZSTD_CCtx, size_t */
+#include "zstd_compress_internal.h"
-size_t ZSTD_compressBlock_btopt(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
-size_t ZSTD_compressBlock_btultra(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
+void ZSTD_updateTree(
+ ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams,
+ const BYTE* ip, const BYTE* iend); /* used in ZSTD_loadDictionaryContent() */
-size_t ZSTD_compressBlock_btopt_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
-size_t ZSTD_compressBlock_btultra_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize);
+size_t ZSTD_compressBlock_btopt(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_btultra(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+
+size_t ZSTD_compressBlock_btopt_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_btultra_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize);
#if defined (__cplusplus)
}
diff --git a/thirdparty/zstd/compress/zstdmt_compress.c b/thirdparty/zstd/compress/zstdmt_compress.c
index e51edf124f..c7a205d8c7 100644
--- a/thirdparty/zstd/compress/zstdmt_compress.c
+++ b/thirdparty/zstd/compress/zstdmt_compress.c
@@ -10,7 +10,8 @@
/* ====== Tuning parameters ====== */
-#define ZSTDMT_NBTHREADS_MAX 200
+#define ZSTDMT_NBWORKERS_MAX 200
+#define ZSTDMT_JOBSIZE_MAX (MEM_32bits() ? (512 MB) : (2 GB)) /* note : limited by `jobSize` type, which is `unsigned` */
#define ZSTDMT_OVERLAPLOG_DEFAULT 6
@@ -22,11 +23,18 @@
/* ====== Dependencies ====== */
#include <string.h> /* memcpy, memset */
+#include <limits.h> /* INT_MAX */
#include "pool.h" /* threadpool */
#include "threading.h" /* mutex */
#include "zstd_compress_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */
+#include "zstd_ldm.h"
#include "zstdmt_compress.h"
+/* Guards code to support resizing the SeqPool.
+ * We will want to resize the SeqPool to save memory in the future.
+ * Until then, comment the code out since it is unused.
+ */
+#define ZSTD_RESIZE_SEQPOOL 0
/* ====== Debug ====== */
#if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=2)
@@ -81,7 +89,7 @@ static unsigned long long GetCurrentClockTimeMicroseconds(void)
typedef struct buffer_s {
void* start;
- size_t size;
+ size_t capacity;
} buffer_t;
static const buffer_t g_nullBuffer = { NULL, 0 };
@@ -95,9 +103,9 @@ typedef struct ZSTDMT_bufferPool_s {
buffer_t bTable[1]; /* variable size */
} ZSTDMT_bufferPool;
-static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbThreads, ZSTD_customMem cMem)
+static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbWorkers, ZSTD_customMem cMem)
{
- unsigned const maxNbBuffers = 2*nbThreads + 3;
+ unsigned const maxNbBuffers = 2*nbWorkers + 3;
ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)ZSTD_calloc(
sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t), cMem);
if (bufPool==NULL) return NULL;
@@ -129,17 +137,21 @@ static void ZSTDMT_freeBufferPool(ZSTDMT_bufferPool* bufPool)
static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool)
{
size_t const poolSize = sizeof(*bufPool)
- + (bufPool->totalBuffers - 1) * sizeof(buffer_t);
+ + (bufPool->totalBuffers - 1) * sizeof(buffer_t);
unsigned u;
size_t totalBufferSize = 0;
ZSTD_pthread_mutex_lock(&bufPool->poolMutex);
for (u=0; u<bufPool->totalBuffers; u++)
- totalBufferSize += bufPool->bTable[u].size;
+ totalBufferSize += bufPool->bTable[u].capacity;
ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
return poolSize + totalBufferSize;
}
+/* ZSTDMT_setBufferSize() :
+ * all future buffers provided by this buffer pool will have _at least_ this size
+ * note : it's better for all buffers to have same size,
+ * as they become freely interchangeable, reducing malloc/free usages and memory fragmentation */
static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* const bufPool, size_t const bSize)
{
ZSTD_pthread_mutex_lock(&bufPool->poolMutex);
@@ -149,7 +161,9 @@ static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* const bufPool, size_t const
}
/** ZSTDMT_getBuffer() :
- * assumption : bufPool must be valid */
+ * assumption : bufPool must be valid
+ * @return : a buffer, with start pointer and size
+ * note: allocation may fail, in this case, start==NULL and size==0 */
static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool)
{
size_t const bSize = bufPool->bufferSize;
@@ -157,12 +171,12 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool)
ZSTD_pthread_mutex_lock(&bufPool->poolMutex);
if (bufPool->nbBuffers) { /* try to use an existing buffer */
buffer_t const buf = bufPool->bTable[--(bufPool->nbBuffers)];
- size_t const availBufferSize = buf.size;
+ size_t const availBufferSize = buf.capacity;
bufPool->bTable[bufPool->nbBuffers] = g_nullBuffer;
if ((availBufferSize >= bSize) & ((availBufferSize>>3) <= bSize)) {
/* large enough, but not too much */
DEBUGLOG(5, "ZSTDMT_getBuffer: provide buffer %u of size %u",
- bufPool->nbBuffers, (U32)buf.size);
+ bufPool->nbBuffers, (U32)buf.capacity);
ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
return buf;
}
@@ -176,12 +190,42 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool)
{ buffer_t buffer;
void* const start = ZSTD_malloc(bSize, bufPool->cMem);
buffer.start = start; /* note : start can be NULL if malloc fails ! */
- buffer.size = (start==NULL) ? 0 : bSize;
- DEBUGLOG(5, "ZSTDMT_getBuffer: created buffer of size %u", (U32)bSize);
+ buffer.capacity = (start==NULL) ? 0 : bSize;
+ if (start==NULL) {
+ DEBUGLOG(5, "ZSTDMT_getBuffer: buffer allocation failure !!");
+ } else {
+ DEBUGLOG(5, "ZSTDMT_getBuffer: created buffer of size %u", (U32)bSize);
+ }
return buffer;
}
}
+#if ZSTD_RESIZE_SEQPOOL
+/** ZSTDMT_resizeBuffer() :
+ * assumption : bufPool must be valid
+ * @return : a buffer that is at least the buffer pool buffer size.
+ * If a reallocation happens, the data in the input buffer is copied.
+ */
+static buffer_t ZSTDMT_resizeBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buffer)
+{
+ size_t const bSize = bufPool->bufferSize;
+ if (buffer.capacity < bSize) {
+ void* const start = ZSTD_malloc(bSize, bufPool->cMem);
+ buffer_t newBuffer;
+ newBuffer.start = start;
+ newBuffer.capacity = start == NULL ? 0 : bSize;
+ if (start != NULL) {
+ assert(newBuffer.capacity >= buffer.capacity);
+ memcpy(newBuffer.start, buffer.start, buffer.capacity);
+ DEBUGLOG(5, "ZSTDMT_resizeBuffer: created buffer of size %u", (U32)bSize);
+ return newBuffer;
+ }
+ DEBUGLOG(5, "ZSTDMT_resizeBuffer: buffer allocation failure !!");
+ }
+ return buffer;
+}
+#endif
+
/* store buffer for later re-use, up to pool capacity */
static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf)
{
@@ -191,7 +235,7 @@ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf)
if (bufPool->nbBuffers < bufPool->totalBuffers) {
bufPool->bTable[bufPool->nbBuffers++] = buf; /* stored for later use */
DEBUGLOG(5, "ZSTDMT_releaseBuffer: stored buffer of size %u in slot %u",
- (U32)buf.size, (U32)(bufPool->nbBuffers-1));
+ (U32)buf.capacity, (U32)(bufPool->nbBuffers-1));
ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
return;
}
@@ -201,21 +245,73 @@ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf)
ZSTD_free(buf.start, bufPool->cMem);
}
-/* Sets parameters relevant to the compression job, initializing others to
- * default values. Notably, nbThreads should probably be zero. */
-static ZSTD_CCtx_params ZSTDMT_makeJobCCtxParams(ZSTD_CCtx_params const params)
+
+/* ===== Seq Pool Wrapper ====== */
+
+static rawSeqStore_t kNullRawSeqStore = {NULL, 0, 0, 0};
+
+typedef ZSTDMT_bufferPool ZSTDMT_seqPool;
+
+static size_t ZSTDMT_sizeof_seqPool(ZSTDMT_seqPool* seqPool)
+{
+ return ZSTDMT_sizeof_bufferPool(seqPool);
+}
+
+static rawSeqStore_t bufferToSeq(buffer_t buffer)
{
- ZSTD_CCtx_params jobParams;
- memset(&jobParams, 0, sizeof(jobParams));
+ rawSeqStore_t seq = {NULL, 0, 0, 0};
+ seq.seq = (rawSeq*)buffer.start;
+ seq.capacity = buffer.capacity / sizeof(rawSeq);
+ return seq;
+}
- jobParams.cParams = params.cParams;
- jobParams.fParams = params.fParams;
- jobParams.compressionLevel = params.compressionLevel;
+static buffer_t seqToBuffer(rawSeqStore_t seq)
+{
+ buffer_t buffer;
+ buffer.start = seq.seq;
+ buffer.capacity = seq.capacity * sizeof(rawSeq);
+ return buffer;
+}
- jobParams.ldmParams = params.ldmParams;
- return jobParams;
+static rawSeqStore_t ZSTDMT_getSeq(ZSTDMT_seqPool* seqPool)
+{
+ if (seqPool->bufferSize == 0) {
+ return kNullRawSeqStore;
+ }
+ return bufferToSeq(ZSTDMT_getBuffer(seqPool));
}
+#if ZSTD_RESIZE_SEQPOOL
+static rawSeqStore_t ZSTDMT_resizeSeq(ZSTDMT_seqPool* seqPool, rawSeqStore_t seq)
+{
+ return bufferToSeq(ZSTDMT_resizeBuffer(seqPool, seqToBuffer(seq)));
+}
+#endif
+
+static void ZSTDMT_releaseSeq(ZSTDMT_seqPool* seqPool, rawSeqStore_t seq)
+{
+ ZSTDMT_releaseBuffer(seqPool, seqToBuffer(seq));
+}
+
+static void ZSTDMT_setNbSeq(ZSTDMT_seqPool* const seqPool, size_t const nbSeq)
+{
+ ZSTDMT_setBufferSize(seqPool, nbSeq * sizeof(rawSeq));
+}
+
+static ZSTDMT_seqPool* ZSTDMT_createSeqPool(unsigned nbWorkers, ZSTD_customMem cMem)
+{
+ ZSTDMT_seqPool* seqPool = ZSTDMT_createBufferPool(nbWorkers, cMem);
+ ZSTDMT_setNbSeq(seqPool, 0);
+ return seqPool;
+}
+
+static void ZSTDMT_freeSeqPool(ZSTDMT_seqPool* seqPool)
+{
+ ZSTDMT_freeBufferPool(seqPool);
+}
+
+
+
/* ===== CCtx Pool ===== */
/* a single CCtx Pool can be invoked from multiple threads in parallel */
@@ -238,23 +334,24 @@ static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool)
}
/* ZSTDMT_createCCtxPool() :
- * implies nbThreads >= 1 , checked by caller ZSTDMT_createCCtx() */
-static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads,
+ * implies nbWorkers >= 1 , checked by caller ZSTDMT_createCCtx() */
+static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbWorkers,
ZSTD_customMem cMem)
{
ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) ZSTD_calloc(
- sizeof(ZSTDMT_CCtxPool) + (nbThreads-1)*sizeof(ZSTD_CCtx*), cMem);
+ sizeof(ZSTDMT_CCtxPool) + (nbWorkers-1)*sizeof(ZSTD_CCtx*), cMem);
+ assert(nbWorkers > 0);
if (!cctxPool) return NULL;
if (ZSTD_pthread_mutex_init(&cctxPool->poolMutex, NULL)) {
ZSTD_free(cctxPool, cMem);
return NULL;
}
cctxPool->cMem = cMem;
- cctxPool->totalCCtx = nbThreads;
+ cctxPool->totalCCtx = nbWorkers;
cctxPool->availCCtx = 1; /* at least one cctx for single-thread mode */
cctxPool->cctx[0] = ZSTD_createCCtx_advanced(cMem);
if (!cctxPool->cctx[0]) { ZSTDMT_freeCCtxPool(cctxPool); return NULL; }
- DEBUGLOG(3, "cctxPool created, with %u threads", nbThreads);
+ DEBUGLOG(3, "cctxPool created, with %u workers", nbWorkers);
return cctxPool;
}
@@ -262,15 +359,16 @@ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads,
static size_t ZSTDMT_sizeof_CCtxPool(ZSTDMT_CCtxPool* cctxPool)
{
ZSTD_pthread_mutex_lock(&cctxPool->poolMutex);
- { unsigned const nbThreads = cctxPool->totalCCtx;
+ { unsigned const nbWorkers = cctxPool->totalCCtx;
size_t const poolSize = sizeof(*cctxPool)
- + (nbThreads-1)*sizeof(ZSTD_CCtx*);
+ + (nbWorkers-1) * sizeof(ZSTD_CCtx*);
unsigned u;
size_t totalCCtxSize = 0;
- for (u=0; u<nbThreads; u++) {
+ for (u=0; u<nbWorkers; u++) {
totalCCtxSize += ZSTD_sizeof_CCtx(cctxPool->cctx[u]);
}
ZSTD_pthread_mutex_unlock(&cctxPool->poolMutex);
+ assert(nbWorkers > 0);
return poolSize + totalCCtxSize;
}
}
@@ -297,111 +395,318 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx)
if (pool->availCCtx < pool->totalCCtx)
pool->cctx[pool->availCCtx++] = cctx;
else {
- /* pool overflow : should not happen, since totalCCtx==nbThreads */
- DEBUGLOG(5, "CCtx pool overflow : free cctx");
+ /* pool overflow : should not happen, since totalCCtx==nbWorkers */
+ DEBUGLOG(4, "CCtx pool overflow : free cctx");
ZSTD_freeCCtx(cctx);
}
ZSTD_pthread_mutex_unlock(&pool->poolMutex);
}
+/* ==== Serial State ==== */
-/* ===== Thread worker ===== */
+typedef struct {
+ void const* start;
+ size_t size;
+} range_t;
typedef struct {
- buffer_t src;
- const void* srcStart;
- size_t prefixSize;
- size_t srcSize;
- buffer_t dstBuff;
- size_t cSize;
- size_t dstFlushed;
- unsigned firstChunk;
- unsigned lastChunk;
- unsigned jobCompleted;
- unsigned jobScanned;
- ZSTD_pthread_mutex_t* jobCompleted_mutex;
- ZSTD_pthread_cond_t* jobCompleted_cond;
+ /* All variables in the struct are protected by mutex. */
+ ZSTD_pthread_mutex_t mutex;
+ ZSTD_pthread_cond_t cond;
ZSTD_CCtx_params params;
- const ZSTD_CDict* cdict;
- ZSTDMT_CCtxPool* cctxPool;
- ZSTDMT_bufferPool* bufPool;
- unsigned long long fullFrameSize;
+ ldmState_t ldmState;
+ XXH64_state_t xxhState;
+ unsigned nextJobID;
+ /* Protects ldmWindow.
+ * Must be acquired after the main mutex when acquiring both.
+ */
+ ZSTD_pthread_mutex_t ldmWindowMutex;
+ ZSTD_pthread_cond_t ldmWindowCond; /* Signaled when ldmWindow is udpated */
+ ZSTD_window_t ldmWindow; /* A thread-safe copy of ldmState.window */
+} serialState_t;
+
+static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* seqPool, ZSTD_CCtx_params params)
+{
+ /* Adjust parameters */
+ if (params.ldmParams.enableLdm) {
+ DEBUGLOG(4, "LDM window size = %u KB", (1U << params.cParams.windowLog) >> 10);
+ params.ldmParams.windowLog = params.cParams.windowLog;
+ ZSTD_ldm_adjustParameters(&params.ldmParams, &params.cParams);
+ assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog);
+ assert(params.ldmParams.hashEveryLog < 32);
+ serialState->ldmState.hashPower =
+ ZSTD_ldm_getHashPower(params.ldmParams.minMatchLength);
+ } else {
+ memset(&params.ldmParams, 0, sizeof(params.ldmParams));
+ }
+ serialState->nextJobID = 0;
+ if (params.fParams.checksumFlag)
+ XXH64_reset(&serialState->xxhState, 0);
+ if (params.ldmParams.enableLdm) {
+ ZSTD_customMem cMem = params.customMem;
+ unsigned const hashLog = params.ldmParams.hashLog;
+ size_t const hashSize = ((size_t)1 << hashLog) * sizeof(ldmEntry_t);
+ unsigned const bucketLog =
+ params.ldmParams.hashLog - params.ldmParams.bucketSizeLog;
+ size_t const bucketSize = (size_t)1 << bucketLog;
+ unsigned const prevBucketLog =
+ serialState->params.ldmParams.hashLog -
+ serialState->params.ldmParams.bucketSizeLog;
+ /* Size the seq pool tables */
+ ZSTDMT_setNbSeq(seqPool, ZSTD_ldm_getMaxNbSeq(params.ldmParams, params.jobSize));
+ /* Reset the window */
+ ZSTD_window_clear(&serialState->ldmState.window);
+ serialState->ldmWindow = serialState->ldmState.window;
+ /* Resize tables and output space if necessary. */
+ if (serialState->ldmState.hashTable == NULL || serialState->params.ldmParams.hashLog < hashLog) {
+ ZSTD_free(serialState->ldmState.hashTable, cMem);
+ serialState->ldmState.hashTable = (ldmEntry_t*)ZSTD_malloc(hashSize, cMem);
+ }
+ if (serialState->ldmState.bucketOffsets == NULL || prevBucketLog < bucketLog) {
+ ZSTD_free(serialState->ldmState.bucketOffsets, cMem);
+ serialState->ldmState.bucketOffsets = (BYTE*)ZSTD_malloc(bucketSize, cMem);
+ }
+ if (!serialState->ldmState.hashTable || !serialState->ldmState.bucketOffsets)
+ return 1;
+ /* Zero the tables */
+ memset(serialState->ldmState.hashTable, 0, hashSize);
+ memset(serialState->ldmState.bucketOffsets, 0, bucketSize);
+ }
+ serialState->params = params;
+ return 0;
+}
+
+static int ZSTDMT_serialState_init(serialState_t* serialState)
+{
+ int initError = 0;
+ memset(serialState, 0, sizeof(*serialState));
+ initError |= ZSTD_pthread_mutex_init(&serialState->mutex, NULL);
+ initError |= ZSTD_pthread_cond_init(&serialState->cond, NULL);
+ initError |= ZSTD_pthread_mutex_init(&serialState->ldmWindowMutex, NULL);
+ initError |= ZSTD_pthread_cond_init(&serialState->ldmWindowCond, NULL);
+ return initError;
+}
+
+static void ZSTDMT_serialState_free(serialState_t* serialState)
+{
+ ZSTD_customMem cMem = serialState->params.customMem;
+ ZSTD_pthread_mutex_destroy(&serialState->mutex);
+ ZSTD_pthread_cond_destroy(&serialState->cond);
+ ZSTD_pthread_mutex_destroy(&serialState->ldmWindowMutex);
+ ZSTD_pthread_cond_destroy(&serialState->ldmWindowCond);
+ ZSTD_free(serialState->ldmState.hashTable, cMem);
+ ZSTD_free(serialState->ldmState.bucketOffsets, cMem);
+}
+
+static void ZSTDMT_serialState_update(serialState_t* serialState,
+ ZSTD_CCtx* jobCCtx, rawSeqStore_t seqStore,
+ range_t src, unsigned jobID)
+{
+ /* Wait for our turn */
+ ZSTD_PTHREAD_MUTEX_LOCK(&serialState->mutex);
+ while (serialState->nextJobID < jobID) {
+ ZSTD_pthread_cond_wait(&serialState->cond, &serialState->mutex);
+ }
+ /* A future job may error and skip our job */
+ if (serialState->nextJobID == jobID) {
+ /* It is now our turn, do any processing necessary */
+ if (serialState->params.ldmParams.enableLdm) {
+ size_t error;
+ assert(seqStore.seq != NULL && seqStore.pos == 0 &&
+ seqStore.size == 0 && seqStore.capacity > 0);
+ ZSTD_window_update(&serialState->ldmState.window, src.start, src.size);
+ error = ZSTD_ldm_generateSequences(
+ &serialState->ldmState, &seqStore,
+ &serialState->params.ldmParams, src.start, src.size);
+ /* We provide a large enough buffer to never fail. */
+ assert(!ZSTD_isError(error)); (void)error;
+ /* Update ldmWindow to match the ldmState.window and signal the main
+ * thread if it is waiting for a buffer.
+ */
+ ZSTD_PTHREAD_MUTEX_LOCK(&serialState->ldmWindowMutex);
+ serialState->ldmWindow = serialState->ldmState.window;
+ ZSTD_pthread_cond_signal(&serialState->ldmWindowCond);
+ ZSTD_pthread_mutex_unlock(&serialState->ldmWindowMutex);
+ }
+ if (serialState->params.fParams.checksumFlag && src.size > 0)
+ XXH64_update(&serialState->xxhState, src.start, src.size);
+ }
+ /* Now it is the next jobs turn */
+ serialState->nextJobID++;
+ ZSTD_pthread_cond_broadcast(&serialState->cond);
+ ZSTD_pthread_mutex_unlock(&serialState->mutex);
+
+ if (seqStore.size > 0) {
+ size_t const err = ZSTD_referenceExternalSequences(
+ jobCCtx, seqStore.seq, seqStore.size);
+ assert(serialState->params.ldmParams.enableLdm);
+ assert(!ZSTD_isError(err));
+ (void)err;
+ }
+}
+
+static void ZSTDMT_serialState_ensureFinished(serialState_t* serialState,
+ unsigned jobID, size_t cSize)
+{
+ ZSTD_PTHREAD_MUTEX_LOCK(&serialState->mutex);
+ if (serialState->nextJobID <= jobID) {
+ assert(ZSTD_isError(cSize)); (void)cSize;
+ DEBUGLOG(5, "Skipping past job %u because of error", jobID);
+ serialState->nextJobID = jobID + 1;
+ ZSTD_pthread_cond_broadcast(&serialState->cond);
+
+ ZSTD_PTHREAD_MUTEX_LOCK(&serialState->ldmWindowMutex);
+ ZSTD_window_clear(&serialState->ldmWindow);
+ ZSTD_pthread_cond_signal(&serialState->ldmWindowCond);
+ ZSTD_pthread_mutex_unlock(&serialState->ldmWindowMutex);
+ }
+ ZSTD_pthread_mutex_unlock(&serialState->mutex);
+
+}
+
+
+/* ------------------------------------------ */
+/* ===== Worker thread ===== */
+/* ------------------------------------------ */
+
+static const range_t kNullRange = { NULL, 0 };
+
+typedef struct {
+ size_t consumed; /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx */
+ size_t cSize; /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx, then set0 by mtctx */
+ ZSTD_pthread_mutex_t job_mutex; /* Thread-safe - used by mtctx and worker */
+ ZSTD_pthread_cond_t job_cond; /* Thread-safe - used by mtctx and worker */
+ ZSTDMT_CCtxPool* cctxPool; /* Thread-safe - used by mtctx and (all) workers */
+ ZSTDMT_bufferPool* bufPool; /* Thread-safe - used by mtctx and (all) workers */
+ ZSTDMT_seqPool* seqPool; /* Thread-safe - used by mtctx and (all) workers */
+ serialState_t* serial; /* Thread-safe - used by mtctx and (all) workers */
+ buffer_t dstBuff; /* set by worker (or mtctx), then read by worker & mtctx, then modified by mtctx => no barrier */
+ range_t prefix; /* set by mtctx, then read by worker & mtctx => no barrier */
+ range_t src; /* set by mtctx, then read by worker & mtctx => no barrier */
+ unsigned jobID; /* set by mtctx, then read by worker => no barrier */
+ unsigned firstJob; /* set by mtctx, then read by worker => no barrier */
+ unsigned lastJob; /* set by mtctx, then read by worker => no barrier */
+ ZSTD_CCtx_params params; /* set by mtctx, then read by worker => no barrier */
+ const ZSTD_CDict* cdict; /* set by mtctx, then read by worker => no barrier */
+ unsigned long long fullFrameSize; /* set by mtctx, then read by worker => no barrier */
+ size_t dstFlushed; /* used only by mtctx */
+ unsigned frameChecksumNeeded; /* used only by mtctx */
} ZSTDMT_jobDescription;
-/* ZSTDMT_compressChunk() : POOL_function type */
-void ZSTDMT_compressChunk(void* jobDescription)
+/* ZSTDMT_compressionJob() is a POOL_function type */
+void ZSTDMT_compressionJob(void* jobDescription)
{
ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription;
+ ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */
ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool);
- const void* const src = (const char*)job->srcStart + job->prefixSize;
+ rawSeqStore_t rawSeqStore = ZSTDMT_getSeq(job->seqPool);
buffer_t dstBuff = job->dstBuff;
- DEBUGLOG(5, "ZSTDMT_compressChunk: job (first:%u) (last:%u) : prefixSize %u, srcSize %u ",
- job->firstChunk, job->lastChunk, (U32)job->prefixSize, (U32)job->srcSize);
+ /* Don't compute the checksum for chunks, since we compute it externally,
+ * but write it in the header.
+ */
+ if (job->jobID != 0) jobParams.fParams.checksumFlag = 0;
+ /* Don't run LDM for the chunks, since we handle it externally */
+ jobParams.ldmParams.enableLdm = 0;
+
+ /* ressources */
if (cctx==NULL) {
job->cSize = ERROR(memory_allocation);
goto _endJob;
}
-
- if (dstBuff.start == NULL) {
+ if (dstBuff.start == NULL) { /* streaming job : doesn't provide a dstBuffer */
dstBuff = ZSTDMT_getBuffer(job->bufPool);
if (dstBuff.start==NULL) {
job->cSize = ERROR(memory_allocation);
goto _endJob;
}
- job->dstBuff = dstBuff;
- DEBUGLOG(5, "ZSTDMT_compressChunk: received dstBuff of size %u", (U32)dstBuff.size);
+ job->dstBuff = dstBuff; /* this value can be read in ZSTDMT_flush, when it copies the whole job */
}
+ /* init */
if (job->cdict) {
- size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dm_auto, job->cdict, job->params, job->fullFrameSize);
- DEBUGLOG(4, "ZSTDMT_compressChunk: init using CDict (windowLog=%u)", job->params.cParams.windowLog);
- assert(job->firstChunk); /* only allowed for first job */
+ size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dct_auto, job->cdict, jobParams, job->fullFrameSize);
+ assert(job->firstJob); /* only allowed for first job */
if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; }
} else { /* srcStart points at reloaded section */
- U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : ZSTD_CONTENTSIZE_UNKNOWN;
- ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */
- size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk);
- if (ZSTD_isError(forceWindowError)) {
- DEBUGLOG(5, "ZSTD_CCtxParam_setParameter error : %s ", ZSTD_getErrorName(forceWindowError));
- job->cSize = forceWindowError;
- goto _endJob;
- }
- DEBUGLOG(5, "ZSTDMT_compressChunk: invoking ZSTD_compressBegin_advanced_internal with windowLog = %u ", jobParams.cParams.windowLog);
+ U64 const pledgedSrcSize = job->firstJob ? job->fullFrameSize : job->src.size;
+ { size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstJob);
+ if (ZSTD_isError(forceWindowError)) {
+ job->cSize = forceWindowError;
+ goto _endJob;
+ } }
{ size_t const initError = ZSTD_compressBegin_advanced_internal(cctx,
- job->srcStart, job->prefixSize, ZSTD_dm_rawContent, /* load dictionary in "content-only" mode (no header analysis) */
- NULL,
+ job->prefix.start, job->prefix.size, ZSTD_dct_rawContent, /* load dictionary in "content-only" mode (no header analysis) */
+ NULL, /*cdict*/
jobParams, pledgedSrcSize);
if (ZSTD_isError(initError)) {
- DEBUGLOG(5, "ZSTD_compressBegin_advanced_internal error : %s ", ZSTD_getErrorName(initError));
job->cSize = initError;
goto _endJob;
- } }
- }
- if (!job->firstChunk) { /* flush and overwrite frame header when it's not first job */
- size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, 0);
+ } } }
+
+ /* Perform serial step as early as possible, but after CCtx initialization */
+ ZSTDMT_serialState_update(job->serial, cctx, rawSeqStore, job->src, job->jobID);
+
+ if (!job->firstJob) { /* flush and overwrite frame header when it's not first job */
+ size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0);
if (ZSTD_isError(hSize)) { job->cSize = hSize; /* save error code */ goto _endJob; }
+ DEBUGLOG(5, "ZSTDMT_compressionJob: flush and overwrite %u bytes of frame header (not first job)", (U32)hSize);
ZSTD_invalidateRepCodes(cctx);
}
- DEBUGLOG(5, "Compressing into dstBuff of size %u", (U32)dstBuff.size);
- DEBUG_PRINTHEX(6, job->srcStart, 12);
- job->cSize = (job->lastChunk) ?
- ZSTD_compressEnd (cctx, dstBuff.start, dstBuff.size, src, job->srcSize) :
- ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, job->srcSize);
- DEBUGLOG(5, "compressed %u bytes into %u bytes (first:%u) (last:%u) ",
- (unsigned)job->srcSize, (unsigned)job->cSize, job->firstChunk, job->lastChunk);
- DEBUGLOG(5, "dstBuff.size : %u ; => %s ", (U32)dstBuff.size, ZSTD_getErrorName(job->cSize));
+ /* compress */
+ { size_t const chunkSize = 4*ZSTD_BLOCKSIZE_MAX;
+ int const nbChunks = (int)((job->src.size + (chunkSize-1)) / chunkSize);
+ const BYTE* ip = (const BYTE*) job->src.start;
+ BYTE* const ostart = (BYTE*)dstBuff.start;
+ BYTE* op = ostart;
+ BYTE* oend = op + dstBuff.capacity;
+ int chunkNb;
+ if (sizeof(size_t) > sizeof(int)) assert(job->src.size < ((size_t)INT_MAX) * chunkSize); /* check overflow */
+ DEBUGLOG(5, "ZSTDMT_compressionJob: compress %u bytes in %i blocks", (U32)job->src.size, nbChunks);
+ assert(job->cSize == 0);
+ for (chunkNb = 1; chunkNb < nbChunks; chunkNb++) {
+ size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, chunkSize);
+ if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; }
+ ip += chunkSize;
+ op += cSize; assert(op < oend);
+ /* stats */
+ ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex);
+ job->cSize += cSize;
+ job->consumed = chunkSize * chunkNb;
+ DEBUGLOG(5, "ZSTDMT_compressionJob: compress new block : cSize==%u bytes (total: %u)",
+ (U32)cSize, (U32)job->cSize);
+ ZSTD_pthread_cond_signal(&job->job_cond); /* warns some more data is ready to be flushed */
+ ZSTD_pthread_mutex_unlock(&job->job_mutex);
+ }
+ /* last block */
+ assert(chunkSize > 0); assert((chunkSize & (chunkSize - 1)) == 0); /* chunkSize must be power of 2 for mask==(chunkSize-1) to work */
+ if ((nbChunks > 0) | job->lastJob /*must output a "last block" flag*/ ) {
+ size_t const lastBlockSize1 = job->src.size & (chunkSize-1);
+ size_t const lastBlockSize = ((lastBlockSize1==0) & (job->src.size>=chunkSize)) ? chunkSize : lastBlockSize1;
+ size_t const cSize = (job->lastJob) ?
+ ZSTD_compressEnd (cctx, op, oend-op, ip, lastBlockSize) :
+ ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize);
+ if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; }
+ /* stats */
+ ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex);
+ job->cSize += cSize;
+ ZSTD_pthread_mutex_unlock(&job->job_mutex);
+ } }
_endJob:
+ ZSTDMT_serialState_ensureFinished(job->serial, job->jobID, job->cSize);
+ if (job->prefix.size > 0)
+ DEBUGLOG(5, "Finished with prefix: %zx", (size_t)job->prefix.start);
+ DEBUGLOG(5, "Finished with source: %zx", (size_t)job->src.start);
+ /* release resources */
+ ZSTDMT_releaseSeq(job->seqPool, rawSeqStore);
ZSTDMT_releaseCCtx(job->cctxPool, cctx);
- ZSTDMT_releaseBuffer(job->bufPool, job->src);
- job->src = g_nullBuffer; job->srcStart = NULL;
- ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex);
- job->jobCompleted = 1;
- job->jobScanned = 0;
- ZSTD_pthread_cond_signal(job->jobCompleted_cond);
- ZSTD_pthread_mutex_unlock(job->jobCompleted_mutex);
+ /* report */
+ ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex);
+ job->consumed = job->src.size;
+ ZSTD_pthread_cond_signal(&job->job_cond);
+ ZSTD_pthread_mutex_unlock(&job->job_mutex);
}
@@ -410,109 +715,141 @@ _endJob:
/* ------------------------------------------ */
typedef struct {
+ range_t prefix; /* read-only non-owned prefix buffer */
buffer_t buffer;
size_t filled;
} inBuff_t;
+typedef struct {
+ BYTE* buffer; /* The round input buffer. All jobs get references
+ * to pieces of the buffer. ZSTDMT_tryGetInputRange()
+ * handles handing out job input buffers, and makes
+ * sure it doesn't overlap with any pieces still in use.
+ */
+ size_t capacity; /* The capacity of buffer. */
+ size_t pos; /* The position of the current inBuff in the round
+ * buffer. Updated past the end if the inBuff once
+ * the inBuff is sent to the worker thread.
+ * pos <= capacity.
+ */
+} roundBuff_t;
+
+static const roundBuff_t kNullRoundBuff = {NULL, 0, 0};
+
struct ZSTDMT_CCtx_s {
POOL_ctx* factory;
ZSTDMT_jobDescription* jobs;
ZSTDMT_bufferPool* bufPool;
ZSTDMT_CCtxPool* cctxPool;
- ZSTD_pthread_mutex_t jobCompleted_mutex;
- ZSTD_pthread_cond_t jobCompleted_cond;
+ ZSTDMT_seqPool* seqPool;
ZSTD_CCtx_params params;
size_t targetSectionSize;
- size_t inBuffSize;
- size_t dictSize;
- size_t targetDictSize;
+ size_t targetPrefixSize;
+ roundBuff_t roundBuff;
inBuff_t inBuff;
- XXH64_state_t xxhState;
- unsigned singleThreaded;
+ int jobReady; /* 1 => one job is already prepared, but pool has shortage of workers. Don't create another one. */
+ serialState_t serial;
+ unsigned singleBlockingThread;
unsigned jobIDMask;
unsigned doneJobID;
unsigned nextJobID;
unsigned frameEnded;
unsigned allJobsCompleted;
unsigned long long frameContentSize;
+ unsigned long long consumed;
+ unsigned long long produced;
ZSTD_customMem cMem;
ZSTD_CDict* cdictLocal;
const ZSTD_CDict* cdict;
};
-static ZSTDMT_jobDescription* ZSTDMT_allocJobsTable(U32* nbJobsPtr, ZSTD_customMem cMem)
+static void ZSTDMT_freeJobsTable(ZSTDMT_jobDescription* jobTable, U32 nbJobs, ZSTD_customMem cMem)
+{
+ U32 jobNb;
+ if (jobTable == NULL) return;
+ for (jobNb=0; jobNb<nbJobs; jobNb++) {
+ ZSTD_pthread_mutex_destroy(&jobTable[jobNb].job_mutex);
+ ZSTD_pthread_cond_destroy(&jobTable[jobNb].job_cond);
+ }
+ ZSTD_free(jobTable, cMem);
+}
+
+/* ZSTDMT_allocJobsTable()
+ * allocate and init a job table.
+ * update *nbJobsPtr to next power of 2 value, as size of table */
+static ZSTDMT_jobDescription* ZSTDMT_createJobsTable(U32* nbJobsPtr, ZSTD_customMem cMem)
{
U32 const nbJobsLog2 = ZSTD_highbit32(*nbJobsPtr) + 1;
U32 const nbJobs = 1 << nbJobsLog2;
+ U32 jobNb;
+ ZSTDMT_jobDescription* const jobTable = (ZSTDMT_jobDescription*)
+ ZSTD_calloc(nbJobs * sizeof(ZSTDMT_jobDescription), cMem);
+ int initError = 0;
+ if (jobTable==NULL) return NULL;
*nbJobsPtr = nbJobs;
- return (ZSTDMT_jobDescription*) ZSTD_calloc(
- nbJobs * sizeof(ZSTDMT_jobDescription), cMem);
+ for (jobNb=0; jobNb<nbJobs; jobNb++) {
+ initError |= ZSTD_pthread_mutex_init(&jobTable[jobNb].job_mutex, NULL);
+ initError |= ZSTD_pthread_cond_init(&jobTable[jobNb].job_cond, NULL);
+ }
+ if (initError != 0) {
+ ZSTDMT_freeJobsTable(jobTable, nbJobs, cMem);
+ return NULL;
+ }
+ return jobTable;
}
-/* ZSTDMT_CCtxParam_setNbThreads():
+/* ZSTDMT_CCtxParam_setNbWorkers():
* Internal use only */
-size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThreads)
+size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers)
{
- if (nbThreads > ZSTDMT_NBTHREADS_MAX) nbThreads = ZSTDMT_NBTHREADS_MAX;
- if (nbThreads < 1) nbThreads = 1;
- params->nbThreads = nbThreads;
+ if (nbWorkers > ZSTDMT_NBWORKERS_MAX) nbWorkers = ZSTDMT_NBWORKERS_MAX;
+ params->nbWorkers = nbWorkers;
params->overlapSizeLog = ZSTDMT_OVERLAPLOG_DEFAULT;
params->jobSize = 0;
- return nbThreads;
-}
-
-/* ZSTDMT_getNbThreads():
- * @return nb threads currently active in mtctx.
- * mtctx must be valid */
-size_t ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx)
-{
- assert(mtctx != NULL);
- return mtctx->params.nbThreads;
+ return nbWorkers;
}
-ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem)
+ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem)
{
ZSTDMT_CCtx* mtctx;
- U32 nbJobs = nbThreads + 2;
- DEBUGLOG(3, "ZSTDMT_createCCtx_advanced (nbThreads = %u)", nbThreads);
+ U32 nbJobs = nbWorkers + 2;
+ int initError;
+ DEBUGLOG(3, "ZSTDMT_createCCtx_advanced (nbWorkers = %u)", nbWorkers);
- if (nbThreads < 1) return NULL;
- nbThreads = MIN(nbThreads , ZSTDMT_NBTHREADS_MAX);
+ if (nbWorkers < 1) return NULL;
+ nbWorkers = MIN(nbWorkers , ZSTDMT_NBWORKERS_MAX);
if ((cMem.customAlloc!=NULL) ^ (cMem.customFree!=NULL))
/* invalid custom allocator */
return NULL;
mtctx = (ZSTDMT_CCtx*) ZSTD_calloc(sizeof(ZSTDMT_CCtx), cMem);
if (!mtctx) return NULL;
- ZSTDMT_CCtxParam_setNbThreads(&mtctx->params, nbThreads);
+ ZSTDMT_CCtxParam_setNbWorkers(&mtctx->params, nbWorkers);
mtctx->cMem = cMem;
mtctx->allJobsCompleted = 1;
- mtctx->factory = POOL_create_advanced(nbThreads, 0, cMem);
- mtctx->jobs = ZSTDMT_allocJobsTable(&nbJobs, cMem);
+ mtctx->factory = POOL_create_advanced(nbWorkers, 0, cMem);
+ mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, cMem);
+ assert(nbJobs > 0); assert((nbJobs & (nbJobs - 1)) == 0); /* ensure nbJobs is a power of 2 */
mtctx->jobIDMask = nbJobs - 1;
- mtctx->bufPool = ZSTDMT_createBufferPool(nbThreads, cMem);
- mtctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads, cMem);
- if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool) {
+ mtctx->bufPool = ZSTDMT_createBufferPool(nbWorkers, cMem);
+ mtctx->cctxPool = ZSTDMT_createCCtxPool(nbWorkers, cMem);
+ mtctx->seqPool = ZSTDMT_createSeqPool(nbWorkers, cMem);
+ initError = ZSTDMT_serialState_init(&mtctx->serial);
+ mtctx->roundBuff = kNullRoundBuff;
+ if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool | !mtctx->seqPool | initError) {
ZSTDMT_freeCCtx(mtctx);
return NULL;
}
- if (ZSTD_pthread_mutex_init(&mtctx->jobCompleted_mutex, NULL)) {
- ZSTDMT_freeCCtx(mtctx);
- return NULL;
- }
- if (ZSTD_pthread_cond_init(&mtctx->jobCompleted_cond, NULL)) {
- ZSTDMT_freeCCtx(mtctx);
- return NULL;
- }
- DEBUGLOG(3, "mt_cctx created, for %u threads", nbThreads);
+ DEBUGLOG(3, "mt_cctx created, for %u threads", nbWorkers);
return mtctx;
}
-ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbThreads)
+ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers)
{
- return ZSTDMT_createCCtx_advanced(nbThreads, ZSTD_defaultCMem);
+ return ZSTDMT_createCCtx_advanced(nbWorkers, ZSTD_defaultCMem);
}
+
/* ZSTDMT_releaseAllJobResources() :
* note : ensure all workers are killed first ! */
static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx)
@@ -523,29 +860,26 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx)
DEBUGLOG(4, "job%02u: release dst address %08X", jobID, (U32)(size_t)mtctx->jobs[jobID].dstBuff.start);
ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);
mtctx->jobs[jobID].dstBuff = g_nullBuffer;
- DEBUGLOG(4, "job%02u: release src address %08X", jobID, (U32)(size_t)mtctx->jobs[jobID].src.start);
- ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].src);
- mtctx->jobs[jobID].src = g_nullBuffer;
+ mtctx->jobs[jobID].cSize = 0;
}
memset(mtctx->jobs, 0, (mtctx->jobIDMask+1)*sizeof(ZSTDMT_jobDescription));
- DEBUGLOG(4, "input: release address %08X", (U32)(size_t)mtctx->inBuff.buffer.start);
- ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->inBuff.buffer);
mtctx->inBuff.buffer = g_nullBuffer;
+ mtctx->inBuff.filled = 0;
mtctx->allJobsCompleted = 1;
}
-static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* zcs)
+static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* mtctx)
{
DEBUGLOG(4, "ZSTDMT_waitForAllJobsCompleted");
- while (zcs->doneJobID < zcs->nextJobID) {
- unsigned const jobID = zcs->doneJobID & zcs->jobIDMask;
- ZSTD_PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex);
- while (zcs->jobs[jobID].jobCompleted==0) {
- DEBUGLOG(5, "waiting for jobCompleted signal from chunk %u", zcs->doneJobID); /* we want to block when waiting for data to flush */
- ZSTD_pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex);
+ while (mtctx->doneJobID < mtctx->nextJobID) {
+ unsigned const jobID = mtctx->doneJobID & mtctx->jobIDMask;
+ ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[jobID].job_mutex);
+ while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].src.size) {
+ DEBUGLOG(5, "waiting for jobCompleted signal from job %u", mtctx->doneJobID); /* we want to block when waiting for data to flush */
+ ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex);
}
- ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex);
- zcs->doneJobID++;
+ ZSTD_pthread_mutex_unlock(&mtctx->jobs[jobID].job_mutex);
+ mtctx->doneJobID++;
}
}
@@ -554,12 +888,14 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx)
if (mtctx==NULL) return 0; /* compatible with free on NULL */
POOL_free(mtctx->factory); /* stop and free worker threads */
ZSTDMT_releaseAllJobResources(mtctx); /* release job resources into pools first */
- ZSTD_free(mtctx->jobs, mtctx->cMem);
+ ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem);
ZSTDMT_freeBufferPool(mtctx->bufPool);
ZSTDMT_freeCCtxPool(mtctx->cctxPool);
+ ZSTDMT_freeSeqPool(mtctx->seqPool);
+ ZSTDMT_serialState_free(&mtctx->serial);
ZSTD_freeCDict(mtctx->cdictLocal);
- ZSTD_pthread_mutex_destroy(&mtctx->jobCompleted_mutex);
- ZSTD_pthread_cond_destroy(&mtctx->jobCompleted_cond);
+ if (mtctx->roundBuff.buffer)
+ ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem);
ZSTD_free(mtctx, mtctx->cMem);
return 0;
}
@@ -572,7 +908,9 @@ size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx)
+ ZSTDMT_sizeof_bufferPool(mtctx->bufPool)
+ (mtctx->jobIDMask+1) * sizeof(ZSTDMT_jobDescription)
+ ZSTDMT_sizeof_CCtxPool(mtctx->cctxPool)
- + ZSTD_sizeof_CDict(mtctx->cdictLocal);
+ + ZSTDMT_sizeof_seqPool(mtctx->seqPool)
+ + ZSTD_sizeof_CDict(mtctx->cdictLocal)
+ + mtctx->roundBuff.capacity;
}
/* Internal only */
@@ -612,133 +950,224 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter,
}
}
+/* Sets parameters relevant to the compression job,
+ * initializing others to default values. */
+static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params)
+{
+ ZSTD_CCtx_params jobParams;
+ memset(&jobParams, 0, sizeof(jobParams));
+
+ jobParams.cParams = params.cParams;
+ jobParams.fParams = params.fParams;
+ jobParams.compressionLevel = params.compressionLevel;
+ jobParams.disableLiteralCompression = params.disableLiteralCompression;
+
+ return jobParams;
+}
+
+/*! ZSTDMT_updateCParams_whileCompressing() :
+ * Updates only a selected set of compression parameters, to remain compatible with current frame.
+ * New parameters will be applied to next compression job. */
+void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* cctxParams)
+{
+ U32 const saved_wlog = mtctx->params.cParams.windowLog; /* Do not modify windowLog while compressing */
+ int const compressionLevel = cctxParams->compressionLevel;
+ DEBUGLOG(5, "ZSTDMT_updateCParams_whileCompressing (level:%i)",
+ compressionLevel);
+ mtctx->params.compressionLevel = compressionLevel;
+ { ZSTD_compressionParameters cParams = ZSTD_getCParamsFromCCtxParams(cctxParams, 0, 0);
+ cParams.windowLog = saved_wlog;
+ mtctx->params.cParams = cParams;
+ }
+}
+
+/* ZSTDMT_getNbWorkers():
+ * @return nb threads currently active in mtctx.
+ * mtctx must be valid */
+unsigned ZSTDMT_getNbWorkers(const ZSTDMT_CCtx* mtctx)
+{
+ assert(mtctx != NULL);
+ return mtctx->params.nbWorkers;
+}
+
+/* ZSTDMT_getFrameProgression():
+ * tells how much data has been consumed (input) and produced (output) for current frame.
+ * able to count progression inside worker threads.
+ * Note : mutex will be acquired during statistics collection. */
+ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
+{
+ ZSTD_frameProgression fps;
+ DEBUGLOG(6, "ZSTDMT_getFrameProgression");
+ fps.consumed = mtctx->consumed;
+ fps.produced = mtctx->produced;
+ fps.ingested = mtctx->consumed + mtctx->inBuff.filled;
+ { unsigned jobNb;
+ unsigned lastJobNb = mtctx->nextJobID + mtctx->jobReady; assert(mtctx->jobReady <= 1);
+ DEBUGLOG(6, "ZSTDMT_getFrameProgression: jobs: from %u to <%u (jobReady:%u)",
+ mtctx->doneJobID, lastJobNb, mtctx->jobReady)
+ for (jobNb = mtctx->doneJobID ; jobNb < lastJobNb ; jobNb++) {
+ unsigned const wJobID = jobNb & mtctx->jobIDMask;
+ ZSTD_pthread_mutex_lock(&mtctx->jobs[wJobID].job_mutex);
+ { size_t const cResult = mtctx->jobs[wJobID].cSize;
+ size_t const produced = ZSTD_isError(cResult) ? 0 : cResult;
+ fps.consumed += mtctx->jobs[wJobID].consumed;
+ fps.ingested += mtctx->jobs[wJobID].src.size;
+ fps.produced += produced;
+ }
+ ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
+ }
+ }
+ return fps;
+}
+
+
/* ------------------------------------------ */
/* ===== Multi-threaded compression ===== */
/* ------------------------------------------ */
-static unsigned computeNbChunks(size_t srcSize, unsigned windowLog, unsigned nbThreads) {
- size_t const chunkSizeTarget = (size_t)1 << (windowLog + 2);
- size_t const chunkMaxSize = chunkSizeTarget << 2;
- size_t const passSizeMax = chunkMaxSize * nbThreads;
- unsigned const multiplier = (unsigned)(srcSize / passSizeMax) + 1;
- unsigned const nbChunksLarge = multiplier * nbThreads;
- unsigned const nbChunksMax = (unsigned)(srcSize / chunkSizeTarget) + 1;
- unsigned const nbChunksSmall = MIN(nbChunksMax, nbThreads);
- return (multiplier>1) ? nbChunksLarge : nbChunksSmall;
+static size_t ZSTDMT_computeTargetJobLog(ZSTD_CCtx_params const params)
+{
+ if (params.ldmParams.enableLdm)
+ return MAX(21, params.cParams.chainLog + 4);
+ return MAX(20, params.cParams.windowLog + 2);
+}
+
+static size_t ZSTDMT_computeOverlapLog(ZSTD_CCtx_params const params)
+{
+ unsigned const overlapRLog = (params.overlapSizeLog>9) ? 0 : 9-params.overlapSizeLog;
+ if (params.ldmParams.enableLdm)
+ return (MIN(params.cParams.windowLog, ZSTDMT_computeTargetJobLog(params) - 2) - overlapRLog);
+ return overlapRLog >= 9 ? 0 : (params.cParams.windowLog - overlapRLog);
}
+static unsigned ZSTDMT_computeNbJobs(ZSTD_CCtx_params params, size_t srcSize, unsigned nbWorkers) {
+ assert(nbWorkers>0);
+ { size_t const jobSizeTarget = (size_t)1 << ZSTDMT_computeTargetJobLog(params);
+ size_t const jobMaxSize = jobSizeTarget << 2;
+ size_t const passSizeMax = jobMaxSize * nbWorkers;
+ unsigned const multiplier = (unsigned)(srcSize / passSizeMax) + 1;
+ unsigned const nbJobsLarge = multiplier * nbWorkers;
+ unsigned const nbJobsMax = (unsigned)(srcSize / jobSizeTarget) + 1;
+ unsigned const nbJobsSmall = MIN(nbJobsMax, nbWorkers);
+ return (multiplier>1) ? nbJobsLarge : nbJobsSmall;
+} }
+
+/* ZSTDMT_compress_advanced_internal() :
+ * This is a blocking function : it will only give back control to caller after finishing its compression job.
+ */
static size_t ZSTDMT_compress_advanced_internal(
ZSTDMT_CCtx* mtctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
const ZSTD_CDict* cdict,
- ZSTD_CCtx_params const params)
+ ZSTD_CCtx_params params)
{
- ZSTD_CCtx_params const jobParams = ZSTDMT_makeJobCCtxParams(params);
- unsigned const overlapRLog = (params.overlapSizeLog>9) ? 0 : 9-params.overlapSizeLog;
- size_t const overlapSize = (overlapRLog>=9) ? 0 : (size_t)1 << (params.cParams.windowLog - overlapRLog);
- unsigned nbChunks = computeNbChunks(srcSize, params.cParams.windowLog, params.nbThreads);
- size_t const proposedChunkSize = (srcSize + (nbChunks-1)) / nbChunks;
- size_t const avgChunkSize = (((proposedChunkSize-1) & 0x1FFFF) < 0x7FFF) ? proposedChunkSize + 0xFFFF : proposedChunkSize; /* avoid too small last block */
+ ZSTD_CCtx_params const jobParams = ZSTDMT_initJobCCtxParams(params);
+ size_t const overlapSize = (size_t)1 << ZSTDMT_computeOverlapLog(params);
+ unsigned const nbJobs = ZSTDMT_computeNbJobs(params, srcSize, params.nbWorkers);
+ size_t const proposedJobSize = (srcSize + (nbJobs-1)) / nbJobs;
+ size_t const avgJobSize = (((proposedJobSize-1) & 0x1FFFF) < 0x7FFF) ? proposedJobSize + 0xFFFF : proposedJobSize; /* avoid too small last block */
const char* const srcStart = (const char*)src;
size_t remainingSrcSize = srcSize;
- unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbChunks : (unsigned)(dstCapacity / ZSTD_compressBound(avgChunkSize)); /* presumes avgChunkSize >= 256 KB, which should be the case */
+ unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbJobs : (unsigned)(dstCapacity / ZSTD_compressBound(avgJobSize)); /* presumes avgJobSize >= 256 KB, which should be the case */
size_t frameStartPos = 0, dstBufferPos = 0;
- XXH64_state_t xxh64;
- assert(jobParams.nbThreads == 0);
- assert(mtctx->cctxPool->totalCCtx == params.nbThreads);
+ assert(jobParams.nbWorkers == 0);
+ assert(mtctx->cctxPool->totalCCtx == params.nbWorkers);
- DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbChunks=%2u (rawSize=%u bytes; fixedSize=%u) ",
- nbChunks, (U32)proposedChunkSize, (U32)avgChunkSize);
- if (nbChunks==1) { /* fallback to single-thread mode */
+ params.jobSize = (U32)avgJobSize;
+ DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbJobs=%2u (rawSize=%u bytes; fixedSize=%u) ",
+ nbJobs, (U32)proposedJobSize, (U32)avgJobSize);
+
+ if ((nbJobs==1) | (params.nbWorkers<=1)) { /* fallback to single-thread mode : this is a blocking invocation anyway */
ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0];
+ DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: fallback to single-thread mode");
if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams);
return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, NULL, 0, jobParams);
}
- assert(avgChunkSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), which is required for compressWithinDst */
- ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgChunkSize) );
- XXH64_reset(&xxh64, 0);
- if (nbChunks > mtctx->jobIDMask+1) { /* enlarge job table */
- U32 nbJobs = nbChunks;
- ZSTD_free(mtctx->jobs, mtctx->cMem);
+ assert(avgJobSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), required to compress directly into Dst (no additional buffer) */
+ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgJobSize) );
+ if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params))
+ return ERROR(memory_allocation);
+
+ if (nbJobs > mtctx->jobIDMask+1) { /* enlarge job table */
+ U32 jobsTableSize = nbJobs;
+ ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem);
mtctx->jobIDMask = 0;
- mtctx->jobs = ZSTDMT_allocJobsTable(&nbJobs, mtctx->cMem);
+ mtctx->jobs = ZSTDMT_createJobsTable(&jobsTableSize, mtctx->cMem);
if (mtctx->jobs==NULL) return ERROR(memory_allocation);
- mtctx->jobIDMask = nbJobs - 1;
+ assert((jobsTableSize != 0) && ((jobsTableSize & (jobsTableSize - 1)) == 0)); /* ensure jobsTableSize is a power of 2 */
+ mtctx->jobIDMask = jobsTableSize - 1;
}
{ unsigned u;
- for (u=0; u<nbChunks; u++) {
- size_t const chunkSize = MIN(remainingSrcSize, avgChunkSize);
- size_t const dstBufferCapacity = ZSTD_compressBound(chunkSize);
+ for (u=0; u<nbJobs; u++) {
+ size_t const jobSize = MIN(remainingSrcSize, avgJobSize);
+ size_t const dstBufferCapacity = ZSTD_compressBound(jobSize);
buffer_t const dstAsBuffer = { (char*)dst + dstBufferPos, dstBufferCapacity };
buffer_t const dstBuffer = u < compressWithinDst ? dstAsBuffer : g_nullBuffer;
size_t dictSize = u ? overlapSize : 0;
- mtctx->jobs[u].src = g_nullBuffer;
- mtctx->jobs[u].srcStart = srcStart + frameStartPos - dictSize;
- mtctx->jobs[u].prefixSize = dictSize;
- mtctx->jobs[u].srcSize = chunkSize;
+ mtctx->jobs[u].prefix.start = srcStart + frameStartPos - dictSize;
+ mtctx->jobs[u].prefix.size = dictSize;
+ mtctx->jobs[u].src.start = srcStart + frameStartPos;
+ mtctx->jobs[u].src.size = jobSize; assert(jobSize > 0); /* avoid job.src.size == 0 */
+ mtctx->jobs[u].consumed = 0;
+ mtctx->jobs[u].cSize = 0;
mtctx->jobs[u].cdict = (u==0) ? cdict : NULL;
mtctx->jobs[u].fullFrameSize = srcSize;
mtctx->jobs[u].params = jobParams;
/* do not calculate checksum within sections, but write it in header for first section */
- if (u!=0) mtctx->jobs[u].params.fParams.checksumFlag = 0;
mtctx->jobs[u].dstBuff = dstBuffer;
mtctx->jobs[u].cctxPool = mtctx->cctxPool;
mtctx->jobs[u].bufPool = mtctx->bufPool;
- mtctx->jobs[u].firstChunk = (u==0);
- mtctx->jobs[u].lastChunk = (u==nbChunks-1);
- mtctx->jobs[u].jobCompleted = 0;
- mtctx->jobs[u].jobCompleted_mutex = &mtctx->jobCompleted_mutex;
- mtctx->jobs[u].jobCompleted_cond = &mtctx->jobCompleted_cond;
-
- if (params.fParams.checksumFlag) {
- XXH64_update(&xxh64, srcStart + frameStartPos, chunkSize);
- }
+ mtctx->jobs[u].seqPool = mtctx->seqPool;
+ mtctx->jobs[u].serial = &mtctx->serial;
+ mtctx->jobs[u].jobID = u;
+ mtctx->jobs[u].firstJob = (u==0);
+ mtctx->jobs[u].lastJob = (u==nbJobs-1);
- DEBUGLOG(5, "ZSTDMT_compress_advanced_internal: posting job %u (%u bytes)", u, (U32)chunkSize);
- DEBUG_PRINTHEX(6, mtctx->jobs[u].srcStart, 12);
- POOL_add(mtctx->factory, ZSTDMT_compressChunk, &mtctx->jobs[u]);
+ DEBUGLOG(5, "ZSTDMT_compress_advanced_internal: posting job %u (%u bytes)", u, (U32)jobSize);
+ DEBUG_PRINTHEX(6, mtctx->jobs[u].prefix.start, 12);
+ POOL_add(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[u]);
- frameStartPos += chunkSize;
+ frameStartPos += jobSize;
dstBufferPos += dstBufferCapacity;
- remainingSrcSize -= chunkSize;
+ remainingSrcSize -= jobSize;
} }
/* collect result */
{ size_t error = 0, dstPos = 0;
- unsigned chunkID;
- for (chunkID=0; chunkID<nbChunks; chunkID++) {
- DEBUGLOG(5, "waiting for chunk %u ", chunkID);
- ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobCompleted_mutex);
- while (mtctx->jobs[chunkID].jobCompleted==0) {
- DEBUGLOG(5, "waiting for jobCompleted signal from chunk %u", chunkID);
- ZSTD_pthread_cond_wait(&mtctx->jobCompleted_cond, &mtctx->jobCompleted_mutex);
+ unsigned jobID;
+ for (jobID=0; jobID<nbJobs; jobID++) {
+ DEBUGLOG(5, "waiting for job %u ", jobID);
+ ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[jobID].job_mutex);
+ while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].src.size) {
+ DEBUGLOG(5, "waiting for jobCompleted signal from job %u", jobID);
+ ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex);
}
- ZSTD_pthread_mutex_unlock(&mtctx->jobCompleted_mutex);
- DEBUGLOG(5, "ready to write chunk %u ", chunkID);
+ ZSTD_pthread_mutex_unlock(&mtctx->jobs[jobID].job_mutex);
+ DEBUGLOG(5, "ready to write job %u ", jobID);
- mtctx->jobs[chunkID].srcStart = NULL;
- { size_t const cSize = mtctx->jobs[chunkID].cSize;
+ { size_t const cSize = mtctx->jobs[jobID].cSize;
if (ZSTD_isError(cSize)) error = cSize;
if ((!error) && (dstPos + cSize > dstCapacity)) error = ERROR(dstSize_tooSmall);
- if (chunkID) { /* note : chunk 0 is written directly at dst, which is correct position */
+ if (jobID) { /* note : job 0 is written directly at dst, which is correct position */
if (!error)
- memmove((char*)dst + dstPos, mtctx->jobs[chunkID].dstBuff.start, cSize); /* may overlap when chunk compressed within dst */
- if (chunkID >= compressWithinDst) { /* chunk compressed into its own buffer, which must be released */
- DEBUGLOG(5, "releasing buffer %u>=%u", chunkID, compressWithinDst);
- ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[chunkID].dstBuff);
+ memmove((char*)dst + dstPos, mtctx->jobs[jobID].dstBuff.start, cSize); /* may overlap when job compressed within dst */
+ if (jobID >= compressWithinDst) { /* job compressed into its own buffer, which must be released */
+ DEBUGLOG(5, "releasing buffer %u>=%u", jobID, compressWithinDst);
+ ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);
} }
- mtctx->jobs[chunkID].dstBuff = g_nullBuffer;
+ mtctx->jobs[jobID].dstBuff = g_nullBuffer;
+ mtctx->jobs[jobID].cSize = 0;
dstPos += cSize ;
}
- } /* for (chunkID=0; chunkID<nbChunks; chunkID++) */
+ } /* for (jobID=0; jobID<nbJobs; jobID++) */
DEBUGLOG(4, "checksumFlag : %u ", params.fParams.checksumFlag);
if (params.fParams.checksumFlag) {
- U32 const checksum = (U32)XXH64_digest(&xxh64);
+ U32 const checksum = (U32)XXH64_digest(&mtctx->serial.xxhState);
if (dstPos + 4 > dstCapacity) {
error = ERROR(dstSize_tooSmall);
} else {
@@ -756,7 +1185,7 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
const ZSTD_CDict* cdict,
- ZSTD_parameters const params,
+ ZSTD_parameters params,
unsigned overlapLog)
{
ZSTD_CCtx_params cctxParams = mtctx->params;
@@ -787,66 +1216,104 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx,
/* ====================================== */
size_t ZSTDMT_initCStream_internal(
- ZSTDMT_CCtx* zcs,
- const void* dict, size_t dictSize, ZSTD_dictMode_e dictMode,
+ ZSTDMT_CCtx* mtctx,
+ const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType,
const ZSTD_CDict* cdict, ZSTD_CCtx_params params,
unsigned long long pledgedSrcSize)
{
- DEBUGLOG(4, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u)", (U32)pledgedSrcSize);
+ DEBUGLOG(4, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u, nbWorkers=%u, cctxPool=%u, disableLiteralCompression=%i)",
+ (U32)pledgedSrcSize, params.nbWorkers, mtctx->cctxPool->totalCCtx, params.disableLiteralCompression);
/* params are supposed to be fully validated at this point */
assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
assert(!((dict) && (cdict))); /* either dict or cdict, not both */
- assert(zcs->cctxPool->totalCCtx == params.nbThreads);
- zcs->singleThreaded = (params.nbThreads==1) | (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */
-
- if (zcs->singleThreaded) {
- ZSTD_CCtx_params const singleThreadParams = ZSTDMT_makeJobCCtxParams(params);
- DEBUGLOG(4, "single thread mode");
- assert(singleThreadParams.nbThreads == 0);
- return ZSTD_initCStream_internal(zcs->cctxPool->cctx[0],
+ assert(mtctx->cctxPool->totalCCtx == params.nbWorkers);
+
+ /* init */
+ if (params.jobSize == 0) {
+ params.jobSize = 1U << ZSTDMT_computeTargetJobLog(params);
+ }
+ if (params.jobSize > ZSTDMT_JOBSIZE_MAX) params.jobSize = ZSTDMT_JOBSIZE_MAX;
+
+ mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */
+ if (mtctx->singleBlockingThread) {
+ ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(params);
+ DEBUGLOG(5, "ZSTDMT_initCStream_internal: switch to single blocking thread mode");
+ assert(singleThreadParams.nbWorkers == 0);
+ return ZSTD_initCStream_internal(mtctx->cctxPool->cctx[0],
dict, dictSize, cdict,
singleThreadParams, pledgedSrcSize);
}
- DEBUGLOG(4, "multi-threading mode (%u threads)", params.nbThreads);
- if (zcs->allJobsCompleted == 0) { /* previous compression not correctly finished */
- ZSTDMT_waitForAllJobsCompleted(zcs);
- ZSTDMT_releaseAllJobResources(zcs);
- zcs->allJobsCompleted = 1;
+ DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u workers", params.nbWorkers);
+
+ if (mtctx->allJobsCompleted == 0) { /* previous compression not correctly finished */
+ ZSTDMT_waitForAllJobsCompleted(mtctx);
+ ZSTDMT_releaseAllJobResources(mtctx);
+ mtctx->allJobsCompleted = 1;
}
- zcs->params = params;
- zcs->frameContentSize = pledgedSrcSize;
+ mtctx->params = params;
+ mtctx->frameContentSize = pledgedSrcSize;
if (dict) {
- ZSTD_freeCDict(zcs->cdictLocal);
- zcs->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize,
- ZSTD_dlm_byCopy, dictMode, /* note : a loadPrefix becomes an internal CDict */
- params.cParams, zcs->cMem);
- zcs->cdict = zcs->cdictLocal;
- if (zcs->cdictLocal == NULL) return ERROR(memory_allocation);
+ ZSTD_freeCDict(mtctx->cdictLocal);
+ mtctx->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize,
+ ZSTD_dlm_byCopy, dictContentType, /* note : a loadPrefix becomes an internal CDict */
+ params.cParams, mtctx->cMem);
+ mtctx->cdict = mtctx->cdictLocal;
+ if (mtctx->cdictLocal == NULL) return ERROR(memory_allocation);
} else {
- ZSTD_freeCDict(zcs->cdictLocal);
- zcs->cdictLocal = NULL;
- zcs->cdict = cdict;
+ ZSTD_freeCDict(mtctx->cdictLocal);
+ mtctx->cdictLocal = NULL;
+ mtctx->cdict = cdict;
}
- assert(params.overlapSizeLog <= 9);
- zcs->targetDictSize = (params.overlapSizeLog==0) ? 0 : (size_t)1 << (params.cParams.windowLog - (9 - params.overlapSizeLog));
- DEBUGLOG(4, "overlapLog=%u => %u KB", params.overlapSizeLog, (U32)(zcs->targetDictSize>>10));
- zcs->targetSectionSize = params.jobSize ? params.jobSize : (size_t)1 << (params.cParams.windowLog + 2);
- if (zcs->targetSectionSize < ZSTDMT_JOBSIZE_MIN) zcs->targetSectionSize = ZSTDMT_JOBSIZE_MIN;
- if (zcs->targetSectionSize < zcs->targetDictSize) zcs->targetSectionSize = zcs->targetDictSize; /* job size must be >= overlap size */
- DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(zcs->targetSectionSize>>10), params.jobSize);
- zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize;
- DEBUGLOG(4, "inBuff Size : %u KB", (U32)(zcs->inBuffSize>>10));
- ZSTDMT_setBufferSize(zcs->bufPool, MAX(zcs->inBuffSize, ZSTD_compressBound(zcs->targetSectionSize)) );
- zcs->inBuff.buffer = g_nullBuffer;
- zcs->dictSize = 0;
- zcs->doneJobID = 0;
- zcs->nextJobID = 0;
- zcs->frameEnded = 0;
- zcs->allJobsCompleted = 0;
- if (params.fParams.checksumFlag) XXH64_reset(&zcs->xxhState, 0);
+ mtctx->targetPrefixSize = (size_t)1 << ZSTDMT_computeOverlapLog(params);
+ DEBUGLOG(4, "overlapLog=%u => %u KB", params.overlapSizeLog, (U32)(mtctx->targetPrefixSize>>10));
+ mtctx->targetSectionSize = params.jobSize;
+ if (mtctx->targetSectionSize < ZSTDMT_JOBSIZE_MIN) mtctx->targetSectionSize = ZSTDMT_JOBSIZE_MIN;
+ if (mtctx->targetSectionSize < mtctx->targetPrefixSize) mtctx->targetSectionSize = mtctx->targetPrefixSize; /* job size must be >= overlap size */
+ DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(mtctx->targetSectionSize>>10), params.jobSize);
+ DEBUGLOG(4, "inBuff Size : %u KB", (U32)(mtctx->targetSectionSize>>10));
+ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(mtctx->targetSectionSize));
+ {
+ /* If ldm is enabled we need windowSize space. */
+ size_t const windowSize = mtctx->params.ldmParams.enableLdm ? (1U << mtctx->params.cParams.windowLog) : 0;
+ /* Two buffers of slack, plus extra space for the overlap
+ * This is the minimum slack that LDM works with. One extra because
+ * flush might waste up to targetSectionSize-1 bytes. Another extra
+ * for the overlap (if > 0), then one to fill which doesn't overlap
+ * with the LDM window.
+ */
+ size_t const nbSlackBuffers = 2 + (mtctx->targetPrefixSize > 0);
+ size_t const slackSize = mtctx->targetSectionSize * nbSlackBuffers;
+ /* Compute the total size, and always have enough slack */
+ size_t const nbWorkers = MAX(mtctx->params.nbWorkers, 1);
+ size_t const sectionsSize = mtctx->targetSectionSize * nbWorkers;
+ size_t const capacity = MAX(windowSize, sectionsSize) + slackSize;
+ if (mtctx->roundBuff.capacity < capacity) {
+ if (mtctx->roundBuff.buffer)
+ ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem);
+ mtctx->roundBuff.buffer = (BYTE*)ZSTD_malloc(capacity, mtctx->cMem);
+ if (mtctx->roundBuff.buffer == NULL) {
+ mtctx->roundBuff.capacity = 0;
+ return ERROR(memory_allocation);
+ }
+ mtctx->roundBuff.capacity = capacity;
+ }
+ }
+ DEBUGLOG(4, "roundBuff capacity : %u KB", (U32)(mtctx->roundBuff.capacity>>10));
+ mtctx->roundBuff.pos = 0;
+ mtctx->inBuff.buffer = g_nullBuffer;
+ mtctx->inBuff.filled = 0;
+ mtctx->inBuff.prefix = kNullRange;
+ mtctx->doneJobID = 0;
+ mtctx->nextJobID = 0;
+ mtctx->frameEnded = 0;
+ mtctx->allJobsCompleted = 0;
+ mtctx->consumed = 0;
+ mtctx->produced = 0;
+ if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params))
+ return ERROR(memory_allocation);
return 0;
}
@@ -855,11 +1322,11 @@ size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx,
ZSTD_parameters params,
unsigned long long pledgedSrcSize)
{
- ZSTD_CCtx_params cctxParams = mtctx->params;
- DEBUGLOG(5, "ZSTDMT_initCStream_advanced (pledgedSrcSize=%u)", (U32)pledgedSrcSize);
+ ZSTD_CCtx_params cctxParams = mtctx->params; /* retrieve sticky params */
+ DEBUGLOG(4, "ZSTDMT_initCStream_advanced (pledgedSrcSize=%u)", (U32)pledgedSrcSize);
cctxParams.cParams = params.cParams;
cctxParams.fParams = params.fParams;
- return ZSTDMT_initCStream_internal(mtctx, dict, dictSize, ZSTD_dm_auto, NULL,
+ return ZSTDMT_initCStream_internal(mtctx, dict, dictSize, ZSTD_dct_auto, NULL,
cctxParams, pledgedSrcSize);
}
@@ -869,10 +1336,10 @@ size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx,
unsigned long long pledgedSrcSize)
{
ZSTD_CCtx_params cctxParams = mtctx->params;
+ if (cdict==NULL) return ERROR(dictionary_wrong); /* method incompatible with NULL cdict */
cctxParams.cParams = ZSTD_getCParamsFromCDict(cdict);
cctxParams.fParams = fParams;
- if (cdict==NULL) return ERROR(dictionary_wrong); /* method incompatible with NULL cdict */
- return ZSTDMT_initCStream_internal(mtctx, NULL, 0 /*dictSize*/, ZSTD_dm_auto, cdict,
+ return ZSTDMT_initCStream_internal(mtctx, NULL, 0 /*dictSize*/, ZSTD_dct_auto, cdict,
cctxParams, pledgedSrcSize);
}
@@ -881,148 +1348,358 @@ size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx,
* pledgedSrcSize can be zero == unknown (for the time being)
* prefer using ZSTD_CONTENTSIZE_UNKNOWN,
* as `0` might mean "empty" in the future */
-size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize)
+size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize)
{
if (!pledgedSrcSize) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
- if (zcs->params.nbThreads==1)
- return ZSTD_resetCStream(zcs->cctxPool->cctx[0], pledgedSrcSize);
- return ZSTDMT_initCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, 0, zcs->params,
+ return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dct_auto, 0, mtctx->params,
pledgedSrcSize);
}
-size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) {
- ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, 0);
- ZSTD_CCtx_params cctxParams = zcs->params;
+size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel) {
+ ZSTD_parameters const params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0);
+ ZSTD_CCtx_params cctxParams = mtctx->params; /* retrieve sticky params */
+ DEBUGLOG(4, "ZSTDMT_initCStream (cLevel=%i)", compressionLevel);
cctxParams.cParams = params.cParams;
cctxParams.fParams = params.fParams;
- return ZSTDMT_initCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN);
-}
-
-
-static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsigned endFrame)
-{
- unsigned const jobID = zcs->nextJobID & zcs->jobIDMask;
-
- DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
- zcs->nextJobID, (U32)srcSize, (U32)zcs->dictSize);
- zcs->jobs[jobID].src = zcs->inBuff.buffer;
- zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start;
- zcs->jobs[jobID].srcSize = srcSize;
- zcs->jobs[jobID].prefixSize = zcs->dictSize;
- assert(zcs->inBuff.filled >= srcSize + zcs->dictSize);
- zcs->jobs[jobID].params = zcs->params;
- /* do not calculate checksum within sections, but write it in header for first section */
- if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0;
- zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL;
- zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize;
- zcs->jobs[jobID].dstBuff = g_nullBuffer;
- zcs->jobs[jobID].cctxPool = zcs->cctxPool;
- zcs->jobs[jobID].bufPool = zcs->bufPool;
- zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0);
- zcs->jobs[jobID].lastChunk = endFrame;
- zcs->jobs[jobID].jobCompleted = 0;
- zcs->jobs[jobID].dstFlushed = 0;
- zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex;
- zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond;
-
- if (zcs->params.fParams.checksumFlag)
- XXH64_update(&zcs->xxhState, (const char*)zcs->inBuff.buffer.start + zcs->dictSize, srcSize);
-
- /* get a new buffer for next input */
- if (!endFrame) {
- size_t const newDictSize = MIN(srcSize + zcs->dictSize, zcs->targetDictSize);
- zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->bufPool);
- if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */
- zcs->jobs[jobID].jobCompleted = 1;
- zcs->nextJobID++;
- ZSTDMT_waitForAllJobsCompleted(zcs);
- ZSTDMT_releaseAllJobResources(zcs);
- return ERROR(memory_allocation);
+ return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dct_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN);
+}
+
+
+/* ZSTDMT_writeLastEmptyBlock()
+ * Write a single empty block with an end-of-frame to finish a frame.
+ * Job must be created from streaming variant.
+ * This function is always successfull if expected conditions are fulfilled.
+ */
+static void ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job)
+{
+ assert(job->lastJob == 1);
+ assert(job->src.size == 0); /* last job is empty -> will be simplified into a last empty block */
+ assert(job->firstJob == 0); /* cannot be first job, as it also needs to create frame header */
+ assert(job->dstBuff.start == NULL); /* invoked from streaming variant only (otherwise, dstBuff might be user's output) */
+ job->dstBuff = ZSTDMT_getBuffer(job->bufPool);
+ if (job->dstBuff.start == NULL) {
+ job->cSize = ERROR(memory_allocation);
+ return;
+ }
+ assert(job->dstBuff.capacity >= ZSTD_blockHeaderSize); /* no buffer should ever be that small */
+ job->src = kNullRange;
+ job->cSize = ZSTD_writeLastEmptyBlock(job->dstBuff.start, job->dstBuff.capacity);
+ assert(!ZSTD_isError(job->cSize));
+ assert(job->consumed == 0);
+}
+
+static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZSTD_EndDirective endOp)
+{
+ unsigned const jobID = mtctx->nextJobID & mtctx->jobIDMask;
+ int const endFrame = (endOp == ZSTD_e_end);
+
+ if (mtctx->nextJobID > mtctx->doneJobID + mtctx->jobIDMask) {
+ DEBUGLOG(5, "ZSTDMT_createCompressionJob: will not create new job : table is full");
+ assert((mtctx->nextJobID & mtctx->jobIDMask) == (mtctx->doneJobID & mtctx->jobIDMask));
+ return 0;
+ }
+
+ if (!mtctx->jobReady) {
+ BYTE const* src = (BYTE const*)mtctx->inBuff.buffer.start;
+ DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
+ mtctx->nextJobID, (U32)srcSize, (U32)mtctx->inBuff.prefix.size);
+ mtctx->jobs[jobID].src.start = src;
+ mtctx->jobs[jobID].src.size = srcSize;
+ assert(mtctx->inBuff.filled >= srcSize);
+ mtctx->jobs[jobID].prefix = mtctx->inBuff.prefix;
+ mtctx->jobs[jobID].consumed = 0;
+ mtctx->jobs[jobID].cSize = 0;
+ mtctx->jobs[jobID].params = mtctx->params;
+ mtctx->jobs[jobID].cdict = mtctx->nextJobID==0 ? mtctx->cdict : NULL;
+ mtctx->jobs[jobID].fullFrameSize = mtctx->frameContentSize;
+ mtctx->jobs[jobID].dstBuff = g_nullBuffer;
+ mtctx->jobs[jobID].cctxPool = mtctx->cctxPool;
+ mtctx->jobs[jobID].bufPool = mtctx->bufPool;
+ mtctx->jobs[jobID].seqPool = mtctx->seqPool;
+ mtctx->jobs[jobID].serial = &mtctx->serial;
+ mtctx->jobs[jobID].jobID = mtctx->nextJobID;
+ mtctx->jobs[jobID].firstJob = (mtctx->nextJobID==0);
+ mtctx->jobs[jobID].lastJob = endFrame;
+ mtctx->jobs[jobID].frameChecksumNeeded = endFrame && (mtctx->nextJobID>0) && mtctx->params.fParams.checksumFlag;
+ mtctx->jobs[jobID].dstFlushed = 0;
+
+ /* Update the round buffer pos and clear the input buffer to be reset */
+ mtctx->roundBuff.pos += srcSize;
+ mtctx->inBuff.buffer = g_nullBuffer;
+ mtctx->inBuff.filled = 0;
+ /* Set the prefix */
+ if (!endFrame) {
+ size_t const newPrefixSize = MIN(srcSize, mtctx->targetPrefixSize);
+ mtctx->inBuff.prefix.start = src + srcSize - newPrefixSize;
+ mtctx->inBuff.prefix.size = newPrefixSize;
+ } else { /* endFrame==1 => no need for another input buffer */
+ mtctx->inBuff.prefix = kNullRange;
+ mtctx->frameEnded = endFrame;
+ if (mtctx->nextJobID == 0) {
+ /* single job exception : checksum is already calculated directly within worker thread */
+ mtctx->params.fParams.checksumFlag = 0;
+ } }
+
+ if ( (srcSize == 0)
+ && (mtctx->nextJobID>0)/*single job must also write frame header*/ ) {
+ DEBUGLOG(5, "ZSTDMT_createCompressionJob: creating a last empty block to end frame");
+ assert(endOp == ZSTD_e_end); /* only possible case : need to end the frame with an empty last block */
+ ZSTDMT_writeLastEmptyBlock(mtctx->jobs + jobID);
+ mtctx->nextJobID++;
+ return 0;
}
- zcs->inBuff.filled -= srcSize + zcs->dictSize - newDictSize;
- memmove(zcs->inBuff.buffer.start,
- (const char*)zcs->jobs[jobID].srcStart + zcs->dictSize + srcSize - newDictSize,
- zcs->inBuff.filled);
- zcs->dictSize = newDictSize;
- } else { /* if (endFrame==1) */
- zcs->inBuff.buffer = g_nullBuffer;
- zcs->inBuff.filled = 0;
- zcs->dictSize = 0;
- zcs->frameEnded = 1;
- if (zcs->nextJobID == 0) {
- /* single chunk exception : checksum is calculated directly within worker thread */
- zcs->params.fParams.checksumFlag = 0;
- } }
+ }
- DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes (end:%u) (note : doneJob = %u=>%u)",
- zcs->nextJobID,
- (U32)zcs->jobs[jobID].srcSize,
- zcs->jobs[jobID].lastChunk,
- zcs->doneJobID,
- zcs->doneJobID & zcs->jobIDMask);
- POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); /* this call is blocking when thread worker pool is exhausted */
- zcs->nextJobID++;
+ DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes (end:%u, jobNb == %u (mod:%u))",
+ mtctx->nextJobID,
+ (U32)mtctx->jobs[jobID].src.size,
+ mtctx->jobs[jobID].lastJob,
+ mtctx->nextJobID,
+ jobID);
+ if (POOL_tryAdd(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[jobID])) {
+ mtctx->nextJobID++;
+ mtctx->jobReady = 0;
+ } else {
+ DEBUGLOG(5, "ZSTDMT_createCompressionJob: no worker available for job %u", mtctx->nextJobID);
+ mtctx->jobReady = 1;
+ }
return 0;
}
-/* ZSTDMT_flushNextJob() :
- * output : will be updated with amount of data flushed .
- * blockToFlush : if >0, the function will block and wait if there is no data available to flush .
- * @return : amount of data remaining within internal buffer, 1 if unknown but > 0, 0 if no more, or an error code */
-static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush)
+/*! ZSTDMT_flushProduced() :
+ * `output` : `pos` will be updated with amount of data flushed .
+ * `blockToFlush` : if >0, the function will block and wait if there is no data available to flush .
+ * @return : amount of data remaining within internal buffer, 0 if no more, 1 if unknown but > 0, or an error code */
+static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, unsigned blockToFlush, ZSTD_EndDirective end)
{
- unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask;
- DEBUGLOG(5, "ZSTDMT_flushNextJob");
- if (zcs->doneJobID == zcs->nextJobID) return 0; /* all flushed ! */
- ZSTD_PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex);
- while (zcs->jobs[wJobID].jobCompleted==0) {
- DEBUGLOG(5, "waiting for jobCompleted signal from job %u", zcs->doneJobID);
- if (!blockToFlush) { ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex); return 0; } /* nothing ready to be flushed => skip */
- ZSTD_pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); /* block when nothing available to flush */
+ unsigned const wJobID = mtctx->doneJobID & mtctx->jobIDMask;
+ DEBUGLOG(5, "ZSTDMT_flushProduced (blocking:%u , job %u <= %u)",
+ blockToFlush, mtctx->doneJobID, mtctx->nextJobID);
+ assert(output->size >= output->pos);
+
+ ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[wJobID].job_mutex);
+ if ( blockToFlush
+ && (mtctx->doneJobID < mtctx->nextJobID) ) {
+ assert(mtctx->jobs[wJobID].dstFlushed <= mtctx->jobs[wJobID].cSize);
+ while (mtctx->jobs[wJobID].dstFlushed == mtctx->jobs[wJobID].cSize) { /* nothing to flush */
+ if (mtctx->jobs[wJobID].consumed == mtctx->jobs[wJobID].src.size) {
+ DEBUGLOG(5, "job %u is completely consumed (%u == %u) => don't wait for cond, there will be none",
+ mtctx->doneJobID, (U32)mtctx->jobs[wJobID].consumed, (U32)mtctx->jobs[wJobID].src.size);
+ break;
+ }
+ DEBUGLOG(5, "waiting for something to flush from job %u (currently flushed: %u bytes)",
+ mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed);
+ ZSTD_pthread_cond_wait(&mtctx->jobs[wJobID].job_cond, &mtctx->jobs[wJobID].job_mutex); /* block when nothing to flush but some to come */
+ } }
+
+ /* try to flush something */
+ { size_t cSize = mtctx->jobs[wJobID].cSize; /* shared */
+ size_t const srcConsumed = mtctx->jobs[wJobID].consumed; /* shared */
+ size_t const srcSize = mtctx->jobs[wJobID].src.size; /* read-only, could be done after mutex lock, but no-declaration-after-statement */
+ ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
+ if (ZSTD_isError(cSize)) {
+ DEBUGLOG(5, "ZSTDMT_flushProduced: job %u : compression error detected : %s",
+ mtctx->doneJobID, ZSTD_getErrorName(cSize));
+ ZSTDMT_waitForAllJobsCompleted(mtctx);
+ ZSTDMT_releaseAllJobResources(mtctx);
+ return cSize;
+ }
+ /* add frame checksum if necessary (can only happen once) */
+ assert(srcConsumed <= srcSize);
+ if ( (srcConsumed == srcSize) /* job completed -> worker no longer active */
+ && mtctx->jobs[wJobID].frameChecksumNeeded ) {
+ U32 const checksum = (U32)XXH64_digest(&mtctx->serial.xxhState);
+ DEBUGLOG(4, "ZSTDMT_flushProduced: writing checksum : %08X \n", checksum);
+ MEM_writeLE32((char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].cSize, checksum);
+ cSize += 4;
+ mtctx->jobs[wJobID].cSize += 4; /* can write this shared value, as worker is no longer active */
+ mtctx->jobs[wJobID].frameChecksumNeeded = 0;
+ }
+ if (cSize > 0) { /* compression is ongoing or completed */
+ size_t const toFlush = MIN(cSize - mtctx->jobs[wJobID].dstFlushed, output->size - output->pos);
+ DEBUGLOG(5, "ZSTDMT_flushProduced: Flushing %u bytes from job %u (completion:%u/%u, generated:%u)",
+ (U32)toFlush, mtctx->doneJobID, (U32)srcConsumed, (U32)srcSize, (U32)cSize);
+ assert(mtctx->doneJobID < mtctx->nextJobID);
+ assert(cSize >= mtctx->jobs[wJobID].dstFlushed);
+ assert(mtctx->jobs[wJobID].dstBuff.start != NULL);
+ memcpy((char*)output->dst + output->pos,
+ (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed,
+ toFlush);
+ output->pos += toFlush;
+ mtctx->jobs[wJobID].dstFlushed += toFlush; /* can write : this value is only used by mtctx */
+
+ if ( (srcConsumed == srcSize) /* job completed */
+ && (mtctx->jobs[wJobID].dstFlushed == cSize) ) { /* output buffer fully flushed => free this job position */
+ DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one",
+ mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed);
+ ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[wJobID].dstBuff);
+ mtctx->jobs[wJobID].dstBuff = g_nullBuffer;
+ mtctx->jobs[wJobID].cSize = 0; /* ensure this job slot is considered "not started" in future check */
+ mtctx->consumed += srcSize;
+ mtctx->produced += cSize;
+ mtctx->doneJobID++;
+ } }
+
+ /* return value : how many bytes left in buffer ; fake it to 1 when unknown but >0 */
+ if (cSize > mtctx->jobs[wJobID].dstFlushed) return (cSize - mtctx->jobs[wJobID].dstFlushed);
+ if (srcSize > srcConsumed) return 1; /* current job not completely compressed */
}
- ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex);
- /* compression job completed : output can be flushed */
- { ZSTDMT_jobDescription job = zcs->jobs[wJobID];
- if (!job.jobScanned) {
- if (ZSTD_isError(job.cSize)) {
- DEBUGLOG(5, "job %u : compression error detected : %s",
- zcs->doneJobID, ZSTD_getErrorName(job.cSize));
- ZSTDMT_waitForAllJobsCompleted(zcs);
- ZSTDMT_releaseAllJobResources(zcs);
- return job.cSize;
+ if (mtctx->doneJobID < mtctx->nextJobID) return 1; /* some more jobs ongoing */
+ if (mtctx->jobReady) return 1; /* one job is ready to push, just not yet in the list */
+ if (mtctx->inBuff.filled > 0) return 1; /* input is not empty, and still needs to be converted into a job */
+ mtctx->allJobsCompleted = mtctx->frameEnded; /* all jobs are entirely flushed => if this one is last one, frame is completed */
+ if (end == ZSTD_e_end) return !mtctx->frameEnded; /* for ZSTD_e_end, question becomes : is frame completed ? instead of : are internal buffers fully flushed ? */
+ return 0; /* internal buffers fully flushed */
+}
+
+/**
+ * Returns the range of data used by the earliest job that is not yet complete.
+ * If the data of the first job is broken up into two segments, we cover both
+ * sections.
+ */
+static range_t ZSTDMT_getInputDataInUse(ZSTDMT_CCtx* mtctx)
+{
+ unsigned const firstJobID = mtctx->doneJobID;
+ unsigned const lastJobID = mtctx->nextJobID;
+ unsigned jobID;
+
+ for (jobID = firstJobID; jobID < lastJobID; ++jobID) {
+ unsigned const wJobID = jobID & mtctx->jobIDMask;
+ size_t consumed;
+
+ ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[wJobID].job_mutex);
+ consumed = mtctx->jobs[wJobID].consumed;
+ ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
+
+ if (consumed < mtctx->jobs[wJobID].src.size) {
+ range_t range = mtctx->jobs[wJobID].prefix;
+ if (range.size == 0) {
+ /* Empty prefix */
+ range = mtctx->jobs[wJobID].src;
}
- DEBUGLOG(5, "zcs->params.fParams.checksumFlag : %u ", zcs->params.fParams.checksumFlag);
- if (zcs->params.fParams.checksumFlag) {
- if (zcs->frameEnded && (zcs->doneJobID+1 == zcs->nextJobID)) { /* write checksum at end of last section */
- U32 const checksum = (U32)XXH64_digest(&zcs->xxhState);
- DEBUGLOG(5, "writing checksum : %08X \n", checksum);
- MEM_writeLE32((char*)job.dstBuff.start + job.cSize, checksum);
- job.cSize += 4;
- zcs->jobs[wJobID].cSize += 4;
- } }
- zcs->jobs[wJobID].jobScanned = 1;
+ /* Job source in multiple segments not supported yet */
+ assert(range.start <= mtctx->jobs[wJobID].src.start);
+ return range;
}
- { size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos);
- DEBUGLOG(5, "Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID);
- memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite);
- output->pos += toWrite;
- job.dstFlushed += toWrite;
+ }
+ return kNullRange;
+}
+
+/**
+ * Returns non-zero iff buffer and range overlap.
+ */
+static int ZSTDMT_isOverlapped(buffer_t buffer, range_t range)
+{
+ BYTE const* const bufferStart = (BYTE const*)buffer.start;
+ BYTE const* const bufferEnd = bufferStart + buffer.capacity;
+ BYTE const* const rangeStart = (BYTE const*)range.start;
+ BYTE const* const rangeEnd = rangeStart + range.size;
+
+ if (rangeStart == NULL || bufferStart == NULL)
+ return 0;
+ /* Empty ranges cannot overlap */
+ if (bufferStart == bufferEnd || rangeStart == rangeEnd)
+ return 0;
+
+ return bufferStart < rangeEnd && rangeStart < bufferEnd;
+}
+
+static int ZSTDMT_doesOverlapWindow(buffer_t buffer, ZSTD_window_t window)
+{
+ range_t extDict;
+ range_t prefix;
+
+ extDict.start = window.dictBase + window.lowLimit;
+ extDict.size = window.dictLimit - window.lowLimit;
+
+ prefix.start = window.base + window.dictLimit;
+ prefix.size = window.nextSrc - (window.base + window.dictLimit);
+ DEBUGLOG(5, "extDict [0x%zx, 0x%zx)",
+ (size_t)extDict.start,
+ (size_t)extDict.start + extDict.size);
+ DEBUGLOG(5, "prefix [0x%zx, 0x%zx)",
+ (size_t)prefix.start,
+ (size_t)prefix.start + prefix.size);
+
+ return ZSTDMT_isOverlapped(buffer, extDict)
+ || ZSTDMT_isOverlapped(buffer, prefix);
+}
+
+static void ZSTDMT_waitForLdmComplete(ZSTDMT_CCtx* mtctx, buffer_t buffer)
+{
+ if (mtctx->params.ldmParams.enableLdm) {
+ ZSTD_pthread_mutex_t* mutex = &mtctx->serial.ldmWindowMutex;
+ DEBUGLOG(5, "source [0x%zx, 0x%zx)",
+ (size_t)buffer.start,
+ (size_t)buffer.start + buffer.capacity);
+ ZSTD_PTHREAD_MUTEX_LOCK(mutex);
+ while (ZSTDMT_doesOverlapWindow(buffer, mtctx->serial.ldmWindow)) {
+ DEBUGLOG(6, "Waiting for LDM to finish...");
+ ZSTD_pthread_cond_wait(&mtctx->serial.ldmWindowCond, mutex);
}
- if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => move to next one */
- ZSTDMT_releaseBuffer(zcs->bufPool, job.dstBuff);
- zcs->jobs[wJobID].dstBuff = g_nullBuffer;
- zcs->jobs[wJobID].jobCompleted = 0;
- zcs->doneJobID++;
- } else {
- zcs->jobs[wJobID].dstFlushed = job.dstFlushed;
+ DEBUGLOG(6, "Done waiting for LDM to finish");
+ ZSTD_pthread_mutex_unlock(mutex);
+ }
+}
+
+/**
+ * Attempts to set the inBuff to the next section to fill.
+ * If any part of the new section is still in use we give up.
+ * Returns non-zero if the buffer is filled.
+ */
+static int ZSTDMT_tryGetInputRange(ZSTDMT_CCtx* mtctx)
+{
+ range_t const inUse = ZSTDMT_getInputDataInUse(mtctx);
+ size_t const spaceLeft = mtctx->roundBuff.capacity - mtctx->roundBuff.pos;
+ size_t const target = mtctx->targetSectionSize;
+ buffer_t buffer;
+
+ assert(mtctx->inBuff.buffer.start == NULL);
+ assert(mtctx->roundBuff.capacity >= target);
+
+ if (spaceLeft < target) {
+ /* ZSTD_invalidateRepCodes() doesn't work for extDict variants.
+ * Simply copy the prefix to the beginning in that case.
+ */
+ BYTE* const start = (BYTE*)mtctx->roundBuff.buffer;
+ size_t const prefixSize = mtctx->inBuff.prefix.size;
+
+ buffer.start = start;
+ buffer.capacity = prefixSize;
+ if (ZSTDMT_isOverlapped(buffer, inUse)) {
+ DEBUGLOG(6, "Waiting for buffer...");
+ return 0;
}
- /* return value : how many bytes left in buffer ; fake it to 1 if unknown but >0 */
- if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed);
- if (zcs->doneJobID < zcs->nextJobID) return 1; /* still some buffer to flush */
- zcs->allJobsCompleted = zcs->frameEnded; /* frame completed and entirely flushed */
- return 0; /* everything flushed */
-} }
+ ZSTDMT_waitForLdmComplete(mtctx, buffer);
+ memmove(start, mtctx->inBuff.prefix.start, prefixSize);
+ mtctx->inBuff.prefix.start = start;
+ mtctx->roundBuff.pos = prefixSize;
+ }
+ buffer.start = mtctx->roundBuff.buffer + mtctx->roundBuff.pos;
+ buffer.capacity = target;
+
+ if (ZSTDMT_isOverlapped(buffer, inUse)) {
+ DEBUGLOG(6, "Waiting for buffer...");
+ return 0;
+ }
+ assert(!ZSTDMT_isOverlapped(buffer, mtctx->inBuff.prefix));
+
+ ZSTDMT_waitForLdmComplete(mtctx, buffer);
+
+ DEBUGLOG(5, "Using prefix range [%zx, %zx)",
+ (size_t)mtctx->inBuff.prefix.start,
+ (size_t)mtctx->inBuff.prefix.start + mtctx->inBuff.prefix.size);
+ DEBUGLOG(5, "Using source range [%zx, %zx)",
+ (size_t)buffer.start,
+ (size_t)buffer.start + buffer.capacity);
+
+
+ mtctx->inBuff.buffer = buffer;
+ mtctx->inBuff.filled = 0;
+ assert(mtctx->roundBuff.pos + buffer.capacity <= mtctx->roundBuff.capacity);
+ return 1;
+}
/** ZSTDMT_compressStream_generic() :
@@ -1034,13 +1711,13 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
ZSTD_inBuffer* input,
ZSTD_EndDirective endOp)
{
- size_t const newJobThreshold = mtctx->dictSize + mtctx->targetSectionSize;
unsigned forwardInputProgress = 0;
- DEBUGLOG(5, "ZSTDMT_compressStream_generic ");
+ DEBUGLOG(5, "ZSTDMT_compressStream_generic (endOp=%u, srcSize=%u)",
+ (U32)endOp, (U32)(input->size - input->pos));
assert(output->pos <= output->size);
assert(input->pos <= input->size);
- if (mtctx->singleThreaded) { /* delegate to single-thread (synchronous) */
+ if (mtctx->singleBlockingThread) { /* delegate to single-thread (synchronous) */
return ZSTD_compressStream_generic(mtctx->cctxPool->cctx[0], output, input, endOp);
}
@@ -1050,10 +1727,11 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
}
/* single-pass shortcut (note : synchronous-mode) */
- if ( (mtctx->nextJobID == 0) /* just started */
- && (mtctx->inBuff.filled == 0) /* nothing buffered */
- && (endOp == ZSTD_e_end) /* end order */
- && (output->size - output->pos >= ZSTD_compressBound(input->size - input->pos)) ) { /* enough room */
+ if ( (mtctx->nextJobID == 0) /* just started */
+ && (mtctx->inBuff.filled == 0) /* nothing buffered */
+ && (!mtctx->jobReady) /* no job already created */
+ && (endOp == ZSTD_e_end) /* end order */
+ && (output->size - output->pos >= ZSTD_compressBound(input->size - input->pos)) ) { /* enough space in dst */
size_t const cSize = ZSTDMT_compress_advanced_internal(mtctx,
(char*)output->dst + output->pos, output->size - output->pos,
(const char*)input->src + input->pos, input->size - input->pos,
@@ -1061,89 +1739,93 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
if (ZSTD_isError(cSize)) return cSize;
input->pos = input->size;
output->pos += cSize;
- ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->inBuff.buffer); /* was allocated in initStream */
mtctx->allJobsCompleted = 1;
mtctx->frameEnded = 1;
return 0;
}
/* fill input buffer */
- if (input->size > input->pos) { /* support NULL input */
+ if ( (!mtctx->jobReady)
+ && (input->size > input->pos) ) { /* support NULL input */
if (mtctx->inBuff.buffer.start == NULL) {
- mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool); /* note : may fail, in which case, no forward input progress */
- mtctx->inBuff.filled = 0;
+ assert(mtctx->inBuff.filled == 0); /* Can't fill an empty buffer */
+ if (!ZSTDMT_tryGetInputRange(mtctx)) {
+ /* It is only possible for this operation to fail if there are
+ * still compression jobs ongoing.
+ */
+ assert(mtctx->doneJobID != mtctx->nextJobID);
+ }
}
- if (mtctx->inBuff.buffer.start) {
- size_t const toLoad = MIN(input->size - input->pos, mtctx->inBuffSize - mtctx->inBuff.filled);
- DEBUGLOG(5, "inBuff:%08X; inBuffSize=%u; ToCopy=%u", (U32)(size_t)mtctx->inBuff.buffer.start, (U32)mtctx->inBuffSize, (U32)toLoad);
+ if (mtctx->inBuff.buffer.start != NULL) {
+ size_t const toLoad = MIN(input->size - input->pos, mtctx->targetSectionSize - mtctx->inBuff.filled);
+ assert(mtctx->inBuff.buffer.capacity >= mtctx->targetSectionSize);
+ DEBUGLOG(5, "ZSTDMT_compressStream_generic: adding %u bytes on top of %u to buffer of size %u",
+ (U32)toLoad, (U32)mtctx->inBuff.filled, (U32)mtctx->targetSectionSize);
memcpy((char*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, (const char*)input->src + input->pos, toLoad);
input->pos += toLoad;
mtctx->inBuff.filled += toLoad;
forwardInputProgress = toLoad>0;
- } }
+ }
+ if ((input->pos < input->size) && (endOp == ZSTD_e_end))
+ endOp = ZSTD_e_flush; /* can't end now : not all input consumed */
+ }
- if ( (mtctx->inBuff.filled >= newJobThreshold) /* filled enough : let's compress */
- && (mtctx->nextJobID <= mtctx->doneJobID + mtctx->jobIDMask) ) { /* avoid overwriting job round buffer */
- CHECK_F( ZSTDMT_createCompressionJob(mtctx, mtctx->targetSectionSize, 0 /* endFrame */) );
+ if ( (mtctx->jobReady)
+ || (mtctx->inBuff.filled >= mtctx->targetSectionSize) /* filled enough : let's compress */
+ || ((endOp != ZSTD_e_continue) && (mtctx->inBuff.filled > 0)) /* something to flush : let's go */
+ || ((endOp == ZSTD_e_end) && (!mtctx->frameEnded)) ) { /* must finish the frame with a zero-size block */
+ size_t const jobSize = mtctx->inBuff.filled;
+ assert(mtctx->inBuff.filled <= mtctx->targetSectionSize);
+ CHECK_F( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp) );
}
/* check for potential compressed data ready to be flushed */
- CHECK_F( ZSTDMT_flushNextJob(mtctx, output, !forwardInputProgress /* blockToFlush */) ); /* block if there was no forward input progress */
-
- if (input->pos < input->size) /* input not consumed : do not flush yet */
- endOp = ZSTD_e_continue;
-
- switch(endOp)
- {
- case ZSTD_e_flush:
- return ZSTDMT_flushStream(mtctx, output);
- case ZSTD_e_end:
- return ZSTDMT_endStream(mtctx, output);
- case ZSTD_e_continue:
- return 1;
- default:
- return ERROR(GENERIC); /* invalid endDirective */
+ { size_t const remainingToFlush = ZSTDMT_flushProduced(mtctx, output, !forwardInputProgress, endOp); /* block if there was no forward input progress */
+ if (input->pos < input->size) return MAX(remainingToFlush, 1); /* input not consumed : do not end flush yet */
+ return remainingToFlush;
}
}
-size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input)
+size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input)
{
- CHECK_F( ZSTDMT_compressStream_generic(zcs, output, input, ZSTD_e_continue) );
+ CHECK_F( ZSTDMT_compressStream_generic(mtctx, output, input, ZSTD_e_continue) );
/* recommended next input size : fill current input buffer */
- return zcs->inBuffSize - zcs->inBuff.filled; /* note : could be zero when input buffer is fully filled and no more availability to create new job */
+ return mtctx->targetSectionSize - mtctx->inBuff.filled; /* note : could be zero when input buffer is fully filled and no more availability to create new job */
}
-static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, unsigned endFrame)
+static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_EndDirective endFrame)
{
- size_t const srcSize = mtctx->inBuff.filled - mtctx->dictSize;
+ size_t const srcSize = mtctx->inBuff.filled;
DEBUGLOG(5, "ZSTDMT_flushStream_internal");
- if ( ((srcSize > 0) || (endFrame && !mtctx->frameEnded))
- && (mtctx->nextJobID <= mtctx->doneJobID + mtctx->jobIDMask) ) {
- DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job");
+ if ( mtctx->jobReady /* one job ready for a worker to pick up */
+ || (srcSize > 0) /* still some data within input buffer */
+ || ((endFrame==ZSTD_e_end) && !mtctx->frameEnded)) { /* need a last 0-size block to end frame */
+ DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job (%u bytes, end:%u)",
+ (U32)srcSize, (U32)endFrame);
CHECK_F( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) );
}
/* check if there is any data available to flush */
- return ZSTDMT_flushNextJob(mtctx, output, 1 /* blockToFlush */);
+ return ZSTDMT_flushProduced(mtctx, output, 1 /* blockToFlush */, endFrame);
}
size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output)
{
DEBUGLOG(5, "ZSTDMT_flushStream");
- if (mtctx->singleThreaded)
+ if (mtctx->singleBlockingThread)
return ZSTD_flushStream(mtctx->cctxPool->cctx[0], output);
- return ZSTDMT_flushStream_internal(mtctx, output, 0 /* endFrame */);
+ return ZSTDMT_flushStream_internal(mtctx, output, ZSTD_e_flush);
}
size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output)
{
DEBUGLOG(4, "ZSTDMT_endStream");
- if (mtctx->singleThreaded)
+ if (mtctx->singleBlockingThread)
return ZSTD_endStream(mtctx->cctxPool->cctx[0], output);
- return ZSTDMT_flushStream_internal(mtctx, output, 1 /* endFrame */);
+ return ZSTDMT_flushStream_internal(mtctx, output, ZSTD_e_end);
}
diff --git a/thirdparty/zstd/compress/zstdmt_compress.h b/thirdparty/zstd/compress/zstdmt_compress.h
index d12f0adb8d..f79e3b4418 100644
--- a/thirdparty/zstd/compress/zstdmt_compress.h
+++ b/thirdparty/zstd/compress/zstdmt_compress.h
@@ -30,15 +30,15 @@
/* === Memory management === */
typedef struct ZSTDMT_CCtx_s ZSTDMT_CCtx;
-ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbThreads);
-ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads,
+ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers);
+ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers,
ZSTD_customMem cMem);
ZSTDLIB_API size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx);
ZSTDLIB_API size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx);
-/* === Simple buffer-to-butter one-pass function === */
+/* === Simple one-pass compression function === */
ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx,
void* dst, size_t dstCapacity,
@@ -50,7 +50,7 @@ ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx,
/* === Streaming functions === */
ZSTDLIB_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel);
-ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize); /**< if srcSize is not known at reset time, use ZSTD_CONTENTSIZE_UNKNOWN. Note: for compatibility with older programs, 0 means the same as ZSTD_CONTENTSIZE_UNKNOWN, but it may change in the future, to mean "empty" */
+ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize); /**< if srcSize is not known at reset time, use ZSTD_CONTENTSIZE_UNKNOWN. Note: for compatibility with older programs, 0 means the same as ZSTD_CONTENTSIZE_UNKNOWN, but it will change in the future to mean "empty" */
ZSTDLIB_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
@@ -68,7 +68,7 @@ ZSTDLIB_API size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
const ZSTD_CDict* cdict,
- ZSTD_parameters const params,
+ ZSTD_parameters params,
unsigned overlapLog);
ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx,
@@ -85,7 +85,7 @@ ZSTDLIB_API size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx,
* List of parameters that can be set using ZSTDMT_setMTCtxParameter() */
typedef enum {
ZSTDMT_p_jobSize, /* Each job is compressed in parallel. By default, this value is dynamically determined depending on compression parameters. Can be set explicitly here. */
- ZSTDMT_p_overlapSectionLog /* Each job may reload a part of previous job to enhance compressionr ratio; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window */
+ ZSTDMT_p_overlapSectionLog /* Each job may reload a part of previous job to enhance compressionr ratio; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window. This is a "sticky" parameter : its value will be re-used on next compression job */
} ZSTDMT_parameter;
/* ZSTDMT_setMTCtxParameter() :
@@ -97,30 +97,46 @@ ZSTDLIB_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter
/*! ZSTDMT_compressStream_generic() :
- * Combines ZSTDMT_compressStream() with ZSTDMT_flushStream() or ZSTDMT_endStream()
+ * Combines ZSTDMT_compressStream() with optional ZSTDMT_flushStream() or ZSTDMT_endStream()
* depending on flush directive.
* @return : minimum amount of data still to be flushed
* 0 if fully flushed
- * or an error code */
+ * or an error code
+ * note : needs to be init using any ZSTD_initCStream*() variant */
ZSTDLIB_API size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
ZSTD_outBuffer* output,
ZSTD_inBuffer* input,
ZSTD_EndDirective endOp);
-/* === Private definitions; never ever use directly === */
+/* ========================================================
+ * === Private interface, for use by ZSTD_compress.c ===
+ * === Not exposed in libzstd. Never invoke directly ===
+ * ======================================================== */
size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_parameter parameter, unsigned value);
-/* ZSTDMT_CCtxParam_setNbThreads()
- * Set nbThreads, and clamp it correctly,
- * also reset jobSize and overlapLog */
-size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThreads);
+/* ZSTDMT_CCtxParam_setNbWorkers()
+ * Set nbWorkers, and clamp it.
+ * Also reset jobSize and overlapLog */
+size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers);
-/* ZSTDMT_getNbThreads():
+/*! ZSTDMT_updateCParams_whileCompressing() :
+ * Updates only a selected set of compression parameters, to remain compatible with current frame.
+ * New parameters will be applied to next compression job. */
+void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* cctxParams);
+
+/* ZSTDMT_getNbWorkers():
* @return nb threads currently active in mtctx.
* mtctx must be valid */
-size_t ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx);
+unsigned ZSTDMT_getNbWorkers(const ZSTDMT_CCtx* mtctx);
+
+/* ZSTDMT_getFrameProgression():
+ * tells how much data has been consumed (input) and produced (output) for current frame.
+ * able to count progression inside worker threads.
+ */
+ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx);
+
/*! ZSTDMT_initCStream_internal() :
* Private use only. Init streaming operation.
@@ -128,7 +144,7 @@ size_t ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx);
* must receive dict, or cdict, or none, but not both.
* @return : 0, or an error code */
size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs,
- const void* dict, size_t dictSize, ZSTD_dictMode_e dictMode,
+ const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType,
const ZSTD_CDict* cdict,
ZSTD_CCtx_params params, unsigned long long pledgedSrcSize);
diff --git a/thirdparty/zstd/decompress/huf_decompress.c b/thirdparty/zstd/decompress/huf_decompress.c
index 79ded96bf6..73f5c46c06 100644
--- a/thirdparty/zstd/decompress/huf_decompress.c
+++ b/thirdparty/zstd/decompress/huf_decompress.c
@@ -49,18 +49,19 @@
****************************************************************/
#define HUF_isError ERR_isError
#define HUF_STATIC_ASSERT(c) { enum { HUF_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
+#define CHECK_F(f) { size_t const err_ = (f); if (HUF_isError(err_)) return err_; }
/* **************************************************************
* Byte alignment for workSpace management
****************************************************************/
-#define HUF_ALIGN(x, a) HUF_ALIGN_MASK((x), (a) - 1)
+#define HUF_ALIGN(x, a) HUF_ALIGN_MASK((x), (a) - 1)
#define HUF_ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask))
+
/*-***************************/
/* generic DTableDesc */
/*-***************************/
-
typedef struct { BYTE maxTableLog; BYTE tableType; BYTE tableLog; BYTE reserved; } DTableDesc;
static DTableDesc HUF_getDTableDesc(const HUF_DTable* table)
@@ -74,7 +75,6 @@ static DTableDesc HUF_getDTableDesc(const HUF_DTable* table)
/*-***************************/
/* single-symbol decoding */
/*-***************************/
-
typedef struct { BYTE byte; BYTE nbBits; } HUF_DEltX2; /* single-symbol decoding */
size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize)
@@ -94,10 +94,7 @@ size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize
huffWeight = (BYTE *)((U32 *)workSpace + spaceUsed32);
spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2;
- if ((spaceUsed32 << 2) > wkspSize)
- return ERROR(tableLog_tooLarge);
- workSpace = (U32 *)workSpace + spaceUsed32;
- wkspSize -= (spaceUsed32 << 2);
+ if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge);
HUF_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable));
/* memset(huffWeight, 0, sizeof(huffWeight)); */ /* is not necessary, even though some analyzer complain ... */
@@ -144,8 +141,10 @@ size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize)
workSpace, sizeof(workSpace));
}
+typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX4; /* double-symbols decoding */
-static BYTE HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, const U32 dtLog)
+FORCE_INLINE_TEMPLATE BYTE
+HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, const U32 dtLog)
{
size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */
BYTE const c = dt[val].byte;
@@ -156,7 +155,7 @@ static BYTE HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, con
#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) \
*ptr++ = HUF_decodeSymbolX2(DStreamPtr, dt, dtLog)
-#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \
+#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \
if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \
HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr)
@@ -164,30 +163,33 @@ static BYTE HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, con
if (MEM_64bits()) \
HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr)
-HINT_INLINE size_t HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog)
+HINT_INLINE size_t
+HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog)
{
BYTE* const pStart = p;
/* up to 4 symbols at a time */
- while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd-4)) {
+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-3)) {
HUF_DECODE_SYMBOLX2_2(p, bitDPtr);
HUF_DECODE_SYMBOLX2_1(p, bitDPtr);
HUF_DECODE_SYMBOLX2_2(p, bitDPtr);
HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
}
- /* closer to the end */
- while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd))
- HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
+ /* [0-3] symbols remaining */
+ if (MEM_32bits())
+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd))
+ HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
- /* no more data to retrieve from bitstream, hence no need to reload */
+ /* no more data to retrieve from bitstream, no need to reload */
while (p < pEnd)
HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
return pEnd-pStart;
}
-static size_t HUF_decompress1X2_usingDTable_internal(
+FORCE_INLINE_TEMPLATE size_t
+HUF_decompress1X2_usingDTable_internal_body(
void* dst, size_t dstSize,
const void* cSrc, size_t cSrcSize,
const HUF_DTable* DTable)
@@ -200,58 +202,17 @@ static size_t HUF_decompress1X2_usingDTable_internal(
DTableDesc const dtd = HUF_getDTableDesc(DTable);
U32 const dtLog = dtd.tableLog;
- { size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize);
- if (HUF_isError(errorCode)) return errorCode; }
+ CHECK_F( BIT_initDStream(&bitD, cSrc, cSrcSize) );
HUF_decodeStreamX2(op, &bitD, oend, dt, dtLog);
- /* check */
if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected);
return dstSize;
}
-size_t HUF_decompress1X2_usingDTable(
- void* dst, size_t dstSize,
- const void* cSrc, size_t cSrcSize,
- const HUF_DTable* DTable)
-{
- DTableDesc dtd = HUF_getDTableDesc(DTable);
- if (dtd.tableType != 0) return ERROR(GENERIC);
- return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable);
-}
-
-size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize,
- const void* cSrc, size_t cSrcSize,
- void* workSpace, size_t wkspSize)
-{
- const BYTE* ip = (const BYTE*) cSrc;
-
- size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize);
- if (HUF_isError(hSize)) return hSize;
- if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
- ip += hSize; cSrcSize -= hSize;
-
- return HUF_decompress1X2_usingDTable_internal (dst, dstSize, ip, cSrcSize, DCtx);
-}
-
-
-size_t HUF_decompress1X2_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize,
- const void* cSrc, size_t cSrcSize)
-{
- U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
- return HUF_decompress1X2_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize,
- workSpace, sizeof(workSpace));
-}
-
-size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
- HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX);
- return HUF_decompress1X2_DCtx (DTable, dst, dstSize, cSrc, cSrcSize);
-}
-
-
-static size_t HUF_decompress4X2_usingDTable_internal(
+FORCE_INLINE_TEMPLATE size_t
+HUF_decompress4X2_usingDTable_internal_body(
void* dst, size_t dstSize,
const void* cSrc, size_t cSrcSize,
const HUF_DTable* DTable)
@@ -286,23 +247,19 @@ static size_t HUF_decompress4X2_usingDTable_internal(
BYTE* op2 = opStart2;
BYTE* op3 = opStart3;
BYTE* op4 = opStart4;
- U32 endSignal;
+ U32 endSignal = BIT_DStream_unfinished;
DTableDesc const dtd = HUF_getDTableDesc(DTable);
U32 const dtLog = dtd.tableLog;
if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */
- { size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1);
- if (HUF_isError(errorCode)) return errorCode; }
- { size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2);
- if (HUF_isError(errorCode)) return errorCode; }
- { size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3);
- if (HUF_isError(errorCode)) return errorCode; }
- { size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4);
- if (HUF_isError(errorCode)) return errorCode; }
+ CHECK_F( BIT_initDStream(&bitD1, istart1, length1) );
+ CHECK_F( BIT_initDStream(&bitD2, istart2, length2) );
+ CHECK_F( BIT_initDStream(&bitD3, istart3, length3) );
+ CHECK_F( BIT_initDStream(&bitD4, istart4, length4) );
- /* 16-32 symbols per loop (4-8 symbols per stream) */
+ /* up to 16 symbols per loop (4 symbols per stream) in 64-bit mode */
endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
- for ( ; (endSignal==BIT_DStream_unfinished) && (op4<(oend-7)) ; ) {
+ while ( (endSignal==BIT_DStream_unfinished) && (op4<(oend-3)) ) {
HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
@@ -319,10 +276,15 @@ static size_t HUF_decompress4X2_usingDTable_internal(
HUF_DECODE_SYMBOLX2_0(op2, &bitD2);
HUF_DECODE_SYMBOLX2_0(op3, &bitD3);
HUF_DECODE_SYMBOLX2_0(op4, &bitD4);
- endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
+ BIT_reloadDStream(&bitD1);
+ BIT_reloadDStream(&bitD2);
+ BIT_reloadDStream(&bitD3);
+ BIT_reloadDStream(&bitD4);
}
/* check corruption */
+ /* note : should not be necessary : op# advance in lock step, and we control op4.
+ * but curiously, binary generated by gcc 7.2 & 7.3 with -mbmi2 runs faster when >=1 test is present */
if (op1 > opStart2) return ERROR(corruption_detected);
if (op2 > opStart3) return ERROR(corruption_detected);
if (op3 > opStart4) return ERROR(corruption_detected);
@@ -335,8 +297,8 @@ static size_t HUF_decompress4X2_usingDTable_internal(
HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog);
/* check */
- endSignal = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4);
- if (!endSignal) return ERROR(corruption_detected);
+ { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4);
+ if (!endCheck) return ERROR(corruption_detected); }
/* decoded size */
return dstSize;
@@ -344,30 +306,309 @@ static size_t HUF_decompress4X2_usingDTable_internal(
}
-size_t HUF_decompress4X2_usingDTable(
+FORCE_INLINE_TEMPLATE U32
+HUF_decodeSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog)
+{
+ size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */
+ memcpy(op, dt+val, 2);
+ BIT_skipBits(DStream, dt[val].nbBits);
+ return dt[val].length;
+}
+
+FORCE_INLINE_TEMPLATE U32
+HUF_decodeLastSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog)
+{
+ size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */
+ memcpy(op, dt+val, 1);
+ if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits);
+ else {
+ if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) {
+ BIT_skipBits(DStream, dt[val].nbBits);
+ if (DStream->bitsConsumed > (sizeof(DStream->bitContainer)*8))
+ /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */
+ DStream->bitsConsumed = (sizeof(DStream->bitContainer)*8);
+ } }
+ return 1;
+}
+
+#define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) \
+ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog)
+
+#define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \
+ if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \
+ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog)
+
+#define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \
+ if (MEM_64bits()) \
+ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog)
+
+HINT_INLINE size_t
+HUF_decodeStreamX4(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd,
+ const HUF_DEltX4* const dt, const U32 dtLog)
+{
+ BYTE* const pStart = p;
+
+ /* up to 8 symbols at a time */
+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) {
+ HUF_DECODE_SYMBOLX4_2(p, bitDPtr);
+ HUF_DECODE_SYMBOLX4_1(p, bitDPtr);
+ HUF_DECODE_SYMBOLX4_2(p, bitDPtr);
+ HUF_DECODE_SYMBOLX4_0(p, bitDPtr);
+ }
+
+ /* closer to end : up to 2 symbols at a time */
+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2))
+ HUF_DECODE_SYMBOLX4_0(p, bitDPtr);
+
+ while (p <= pEnd-2)
+ HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */
+
+ if (p < pEnd)
+ p += HUF_decodeLastSymbolX4(p, bitDPtr, dt, dtLog);
+
+ return p-pStart;
+}
+
+FORCE_INLINE_TEMPLATE size_t
+HUF_decompress1X4_usingDTable_internal_body(
+ void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ BIT_DStream_t bitD;
+
+ /* Init */
+ CHECK_F( BIT_initDStream(&bitD, cSrc, cSrcSize) );
+
+ /* decode */
+ { BYTE* const ostart = (BYTE*) dst;
+ BYTE* const oend = ostart + dstSize;
+ const void* const dtPtr = DTable+1; /* force compiler to not use strict-aliasing */
+ const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr;
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+ HUF_decodeStreamX4(ostart, &bitD, oend, dt, dtd.tableLog);
+ }
+
+ /* check */
+ if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected);
+
+ /* decoded size */
+ return dstSize;
+}
+
+
+FORCE_INLINE_TEMPLATE size_t
+HUF_decompress4X4_usingDTable_internal_body(
+ void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */
+
+ { const BYTE* const istart = (const BYTE*) cSrc;
+ BYTE* const ostart = (BYTE*) dst;
+ BYTE* const oend = ostart + dstSize;
+ const void* const dtPtr = DTable+1;
+ const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr;
+
+ /* Init */
+ BIT_DStream_t bitD1;
+ BIT_DStream_t bitD2;
+ BIT_DStream_t bitD3;
+ BIT_DStream_t bitD4;
+ size_t const length1 = MEM_readLE16(istart);
+ size_t const length2 = MEM_readLE16(istart+2);
+ size_t const length3 = MEM_readLE16(istart+4);
+ size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6);
+ const BYTE* const istart1 = istart + 6; /* jumpTable */
+ const BYTE* const istart2 = istart1 + length1;
+ const BYTE* const istart3 = istart2 + length2;
+ const BYTE* const istart4 = istart3 + length3;
+ size_t const segmentSize = (dstSize+3) / 4;
+ BYTE* const opStart2 = ostart + segmentSize;
+ BYTE* const opStart3 = opStart2 + segmentSize;
+ BYTE* const opStart4 = opStart3 + segmentSize;
+ BYTE* op1 = ostart;
+ BYTE* op2 = opStart2;
+ BYTE* op3 = opStart3;
+ BYTE* op4 = opStart4;
+ U32 endSignal;
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+ U32 const dtLog = dtd.tableLog;
+
+ if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */
+ CHECK_F( BIT_initDStream(&bitD1, istart1, length1) );
+ CHECK_F( BIT_initDStream(&bitD2, istart2, length2) );
+ CHECK_F( BIT_initDStream(&bitD3, istart3, length3) );
+ CHECK_F( BIT_initDStream(&bitD4, istart4, length4) );
+
+ /* 16-32 symbols per loop (4-8 symbols per stream) */
+ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
+ for ( ; (endSignal==BIT_DStream_unfinished) & (op4<(oend-(sizeof(bitD4.bitContainer)-1))) ; ) {
+ HUF_DECODE_SYMBOLX4_2(op1, &bitD1);
+ HUF_DECODE_SYMBOLX4_2(op2, &bitD2);
+ HUF_DECODE_SYMBOLX4_2(op3, &bitD3);
+ HUF_DECODE_SYMBOLX4_2(op4, &bitD4);
+ HUF_DECODE_SYMBOLX4_1(op1, &bitD1);
+ HUF_DECODE_SYMBOLX4_1(op2, &bitD2);
+ HUF_DECODE_SYMBOLX4_1(op3, &bitD3);
+ HUF_DECODE_SYMBOLX4_1(op4, &bitD4);
+ HUF_DECODE_SYMBOLX4_2(op1, &bitD1);
+ HUF_DECODE_SYMBOLX4_2(op2, &bitD2);
+ HUF_DECODE_SYMBOLX4_2(op3, &bitD3);
+ HUF_DECODE_SYMBOLX4_2(op4, &bitD4);
+ HUF_DECODE_SYMBOLX4_0(op1, &bitD1);
+ HUF_DECODE_SYMBOLX4_0(op2, &bitD2);
+ HUF_DECODE_SYMBOLX4_0(op3, &bitD3);
+ HUF_DECODE_SYMBOLX4_0(op4, &bitD4);
+
+ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
+ }
+
+ /* check corruption */
+ if (op1 > opStart2) return ERROR(corruption_detected);
+ if (op2 > opStart3) return ERROR(corruption_detected);
+ if (op3 > opStart4) return ERROR(corruption_detected);
+ /* note : op4 already verified within main loop */
+
+ /* finish bitStreams one by one */
+ HUF_decodeStreamX4(op1, &bitD1, opStart2, dt, dtLog);
+ HUF_decodeStreamX4(op2, &bitD2, opStart3, dt, dtLog);
+ HUF_decodeStreamX4(op3, &bitD3, opStart4, dt, dtLog);
+ HUF_decodeStreamX4(op4, &bitD4, oend, dt, dtLog);
+
+ /* check */
+ { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4);
+ if (!endCheck) return ERROR(corruption_detected); }
+
+ /* decoded size */
+ return dstSize;
+ }
+}
+
+
+typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize,
+ const void *cSrc,
+ size_t cSrcSize,
+ const HUF_DTable *DTable);
+#if DYNAMIC_BMI2
+
+#define X(fn) \
+ \
+ static size_t fn##_default( \
+ void* dst, size_t dstSize, \
+ const void* cSrc, size_t cSrcSize, \
+ const HUF_DTable* DTable) \
+ { \
+ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \
+ } \
+ \
+ static TARGET_ATTRIBUTE("bmi2") size_t fn##_bmi2( \
+ void* dst, size_t dstSize, \
+ const void* cSrc, size_t cSrcSize, \
+ const HUF_DTable* DTable) \
+ { \
+ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \
+ } \
+ \
+ static size_t fn(void* dst, size_t dstSize, void const* cSrc, \
+ size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \
+ { \
+ if (bmi2) { \
+ return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \
+ } \
+ return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \
+ }
+
+#else
+
+#define X(fn) \
+ static size_t fn(void* dst, size_t dstSize, void const* cSrc, \
+ size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \
+ { \
+ (void)bmi2; \
+ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \
+ }
+
+#endif
+
+X(HUF_decompress1X2_usingDTable_internal)
+X(HUF_decompress4X2_usingDTable_internal)
+X(HUF_decompress1X4_usingDTable_internal)
+X(HUF_decompress4X4_usingDTable_internal)
+
+#undef X
+
+
+size_t HUF_decompress1X2_usingDTable(
void* dst, size_t dstSize,
const void* cSrc, size_t cSrcSize,
const HUF_DTable* DTable)
{
DTableDesc dtd = HUF_getDTableDesc(DTable);
if (dtd.tableType != 0) return ERROR(GENERIC);
- return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable);
+ return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
}
-
-size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
+size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize,
const void* cSrc, size_t cSrcSize,
void* workSpace, size_t wkspSize)
{
const BYTE* ip = (const BYTE*) cSrc;
+ size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize);
+ if (HUF_isError(hSize)) return hSize;
+ if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
+ ip += hSize; cSrcSize -= hSize;
+
+ return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0);
+}
+
+
+size_t HUF_decompress1X2_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize)
+{
+ U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+ return HUF_decompress1X2_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize,
+ workSpace, sizeof(workSpace));
+}
+
+size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+ HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX);
+ return HUF_decompress1X2_DCtx (DTable, dst, dstSize, cSrc, cSrcSize);
+}
+
+size_t HUF_decompress4X2_usingDTable(
+ void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ DTableDesc dtd = HUF_getDTableDesc(DTable);
+ if (dtd.tableType != 0) return ERROR(GENERIC);
+ return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+}
+
+static size_t HUF_decompress4X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ void* workSpace, size_t wkspSize, int bmi2)
+{
+ const BYTE* ip = (const BYTE*) cSrc;
+
size_t const hSize = HUF_readDTableX2_wksp (dctx, cSrc, cSrcSize,
workSpace, wkspSize);
if (HUF_isError(hSize)) return hSize;
if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
ip += hSize; cSrcSize -= hSize;
- return HUF_decompress4X2_usingDTable_internal (dst, dstSize, ip, cSrcSize, dctx);
+ return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2);
+}
+
+size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ void* workSpace, size_t wkspSize)
+{
+ return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, 0);
}
@@ -387,8 +628,6 @@ size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cS
/* *************************/
/* double-symbols decoding */
/* *************************/
-typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX4; /* double-symbols decoding */
-
typedef struct { BYTE symbol; BYTE weight; } sortedSymbol_t;
/* HUF_fillDTableX4Level2() :
@@ -508,10 +747,7 @@ size_t HUF_readDTableX4_wksp(HUF_DTable* DTable, const void* src,
weightList = (BYTE *)((U32 *)workSpace + spaceUsed32);
spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2;
- if ((spaceUsed32 << 2) > wkspSize)
- return ERROR(tableLog_tooLarge);
- workSpace = (U32 *)workSpace + spaceUsed32;
- wkspSize -= (spaceUsed32 << 2);
+ if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge);
rankStart = rankStart0 + 1;
memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1));
@@ -588,95 +824,6 @@ size_t HUF_readDTableX4(HUF_DTable* DTable, const void* src, size_t srcSize)
workSpace, sizeof(workSpace));
}
-static U32 HUF_decodeSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog)
-{
- size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */
- memcpy(op, dt+val, 2);
- BIT_skipBits(DStream, dt[val].nbBits);
- return dt[val].length;
-}
-
-static U32 HUF_decodeLastSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog)
-{
- size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */
- memcpy(op, dt+val, 1);
- if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits);
- else {
- if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) {
- BIT_skipBits(DStream, dt[val].nbBits);
- if (DStream->bitsConsumed > (sizeof(DStream->bitContainer)*8))
- /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */
- DStream->bitsConsumed = (sizeof(DStream->bitContainer)*8);
- } }
- return 1;
-}
-
-
-#define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) \
- ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog)
-
-#define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \
- if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \
- ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog)
-
-#define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \
- if (MEM_64bits()) \
- ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog)
-
-HINT_INLINE size_t HUF_decodeStreamX4(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, const HUF_DEltX4* const dt, const U32 dtLog)
-{
- BYTE* const pStart = p;
-
- /* up to 8 symbols at a time */
- while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) {
- HUF_DECODE_SYMBOLX4_2(p, bitDPtr);
- HUF_DECODE_SYMBOLX4_1(p, bitDPtr);
- HUF_DECODE_SYMBOLX4_2(p, bitDPtr);
- HUF_DECODE_SYMBOLX4_0(p, bitDPtr);
- }
-
- /* closer to end : up to 2 symbols at a time */
- while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2))
- HUF_DECODE_SYMBOLX4_0(p, bitDPtr);
-
- while (p <= pEnd-2)
- HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */
-
- if (p < pEnd)
- p += HUF_decodeLastSymbolX4(p, bitDPtr, dt, dtLog);
-
- return p-pStart;
-}
-
-
-static size_t HUF_decompress1X4_usingDTable_internal(
- void* dst, size_t dstSize,
- const void* cSrc, size_t cSrcSize,
- const HUF_DTable* DTable)
-{
- BIT_DStream_t bitD;
-
- /* Init */
- { size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize);
- if (HUF_isError(errorCode)) return errorCode;
- }
-
- /* decode */
- { BYTE* const ostart = (BYTE*) dst;
- BYTE* const oend = ostart + dstSize;
- const void* const dtPtr = DTable+1; /* force compiler to not use strict-aliasing */
- const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr;
- DTableDesc const dtd = HUF_getDTableDesc(DTable);
- HUF_decodeStreamX4(ostart, &bitD, oend, dt, dtd.tableLog);
- }
-
- /* check */
- if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected);
-
- /* decoded size */
- return dstSize;
-}
-
size_t HUF_decompress1X4_usingDTable(
void* dst, size_t dstSize,
const void* cSrc, size_t cSrcSize,
@@ -684,7 +831,7 @@ size_t HUF_decompress1X4_usingDTable(
{
DTableDesc dtd = HUF_getDTableDesc(DTable);
if (dtd.tableType != 1) return ERROR(GENERIC);
- return HUF_decompress1X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable);
+ return HUF_decompress1X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
}
size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize,
@@ -699,7 +846,7 @@ size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize,
if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
ip += hSize; cSrcSize -= hSize;
- return HUF_decompress1X4_usingDTable_internal (dst, dstSize, ip, cSrcSize, DCtx);
+ return HUF_decompress1X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0);
}
@@ -717,99 +864,6 @@ size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cS
return HUF_decompress1X4_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
}
-static size_t HUF_decompress4X4_usingDTable_internal(
- void* dst, size_t dstSize,
- const void* cSrc, size_t cSrcSize,
- const HUF_DTable* DTable)
-{
- if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */
-
- { const BYTE* const istart = (const BYTE*) cSrc;
- BYTE* const ostart = (BYTE*) dst;
- BYTE* const oend = ostart + dstSize;
- const void* const dtPtr = DTable+1;
- const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr;
-
- /* Init */
- BIT_DStream_t bitD1;
- BIT_DStream_t bitD2;
- BIT_DStream_t bitD3;
- BIT_DStream_t bitD4;
- size_t const length1 = MEM_readLE16(istart);
- size_t const length2 = MEM_readLE16(istart+2);
- size_t const length3 = MEM_readLE16(istart+4);
- size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6);
- const BYTE* const istart1 = istart + 6; /* jumpTable */
- const BYTE* const istart2 = istart1 + length1;
- const BYTE* const istart3 = istart2 + length2;
- const BYTE* const istart4 = istart3 + length3;
- size_t const segmentSize = (dstSize+3) / 4;
- BYTE* const opStart2 = ostart + segmentSize;
- BYTE* const opStart3 = opStart2 + segmentSize;
- BYTE* const opStart4 = opStart3 + segmentSize;
- BYTE* op1 = ostart;
- BYTE* op2 = opStart2;
- BYTE* op3 = opStart3;
- BYTE* op4 = opStart4;
- U32 endSignal;
- DTableDesc const dtd = HUF_getDTableDesc(DTable);
- U32 const dtLog = dtd.tableLog;
-
- if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */
- { size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1);
- if (HUF_isError(errorCode)) return errorCode; }
- { size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2);
- if (HUF_isError(errorCode)) return errorCode; }
- { size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3);
- if (HUF_isError(errorCode)) return errorCode; }
- { size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4);
- if (HUF_isError(errorCode)) return errorCode; }
-
- /* 16-32 symbols per loop (4-8 symbols per stream) */
- endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
- for ( ; (endSignal==BIT_DStream_unfinished) & (op4<(oend-(sizeof(bitD4.bitContainer)-1))) ; ) {
- HUF_DECODE_SYMBOLX4_2(op1, &bitD1);
- HUF_DECODE_SYMBOLX4_2(op2, &bitD2);
- HUF_DECODE_SYMBOLX4_2(op3, &bitD3);
- HUF_DECODE_SYMBOLX4_2(op4, &bitD4);
- HUF_DECODE_SYMBOLX4_1(op1, &bitD1);
- HUF_DECODE_SYMBOLX4_1(op2, &bitD2);
- HUF_DECODE_SYMBOLX4_1(op3, &bitD3);
- HUF_DECODE_SYMBOLX4_1(op4, &bitD4);
- HUF_DECODE_SYMBOLX4_2(op1, &bitD1);
- HUF_DECODE_SYMBOLX4_2(op2, &bitD2);
- HUF_DECODE_SYMBOLX4_2(op3, &bitD3);
- HUF_DECODE_SYMBOLX4_2(op4, &bitD4);
- HUF_DECODE_SYMBOLX4_0(op1, &bitD1);
- HUF_DECODE_SYMBOLX4_0(op2, &bitD2);
- HUF_DECODE_SYMBOLX4_0(op3, &bitD3);
- HUF_DECODE_SYMBOLX4_0(op4, &bitD4);
-
- endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
- }
-
- /* check corruption */
- if (op1 > opStart2) return ERROR(corruption_detected);
- if (op2 > opStart3) return ERROR(corruption_detected);
- if (op3 > opStart4) return ERROR(corruption_detected);
- /* note : op4 already verified within main loop */
-
- /* finish bitStreams one by one */
- HUF_decodeStreamX4(op1, &bitD1, opStart2, dt, dtLog);
- HUF_decodeStreamX4(op2, &bitD2, opStart3, dt, dtLog);
- HUF_decodeStreamX4(op3, &bitD3, opStart4, dt, dtLog);
- HUF_decodeStreamX4(op4, &bitD4, oend, dt, dtLog);
-
- /* check */
- { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4);
- if (!endCheck) return ERROR(corruption_detected); }
-
- /* decoded size */
- return dstSize;
- }
-}
-
-
size_t HUF_decompress4X4_usingDTable(
void* dst, size_t dstSize,
const void* cSrc, size_t cSrcSize,
@@ -817,13 +871,12 @@ size_t HUF_decompress4X4_usingDTable(
{
DTableDesc dtd = HUF_getDTableDesc(DTable);
if (dtd.tableType != 1) return ERROR(GENERIC);
- return HUF_decompress4X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable);
+ return HUF_decompress4X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
}
-
-size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
+static size_t HUF_decompress4X4_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize,
const void* cSrc, size_t cSrcSize,
- void* workSpace, size_t wkspSize)
+ void* workSpace, size_t wkspSize, int bmi2)
{
const BYTE* ip = (const BYTE*) cSrc;
@@ -833,7 +886,14 @@ size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
ip += hSize; cSrcSize -= hSize;
- return HUF_decompress4X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx);
+ return HUF_decompress4X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2);
+}
+
+size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ void* workSpace, size_t wkspSize)
+{
+ return HUF_decompress4X4_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, /* bmi2 */ 0);
}
@@ -861,8 +921,8 @@ size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize,
const HUF_DTable* DTable)
{
DTableDesc const dtd = HUF_getDTableDesc(DTable);
- return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable) :
- HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable);
+ return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) :
+ HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
}
size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize,
@@ -870,8 +930,8 @@ size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize,
const HUF_DTable* DTable)
{
DTableDesc const dtd = HUF_getDTableDesc(DTable);
- return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable) :
- HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable);
+ return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) :
+ HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
}
@@ -898,21 +958,22 @@ static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, qu
};
/** HUF_selectDecoder() :
-* Tells which decoder is likely to decode faster,
-* based on a set of pre-determined metrics.
-* @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 .
-* Assumption : 0 < cSrcSize, dstSize <= 128 KB */
+ * Tells which decoder is likely to decode faster,
+ * based on a set of pre-computed metrics.
+ * @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 .
+ * Assumption : 0 < dstSize <= 128 KB */
U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize)
{
+ assert(dstSize > 0);
+ assert(dstSize <= 128 KB);
/* decoder timing evaluation */
- U32 const Q = cSrcSize >= dstSize ? 15 : (U32)(cSrcSize * 16 / dstSize); /* Q < 16 */
- U32 const D256 = (U32)(dstSize >> 8);
- U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256);
- U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256);
- DTime1 += DTime1 >> 3; /* advantage to algorithm using less memory, for cache eviction */
-
- return DTime1 < DTime0;
-}
+ { U32 const Q = (cSrcSize >= dstSize) ? 15 : (U32)(cSrcSize * 16 / dstSize); /* Q < 16 */
+ U32 const D256 = (U32)(dstSize >> 8);
+ U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256);
+ U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256);
+ DTime1 += DTime1 >> 3; /* advantage to algorithm using less memory, to reduce cache eviction */
+ return DTime1 < DTime0;
+} }
typedef size_t (*decompressionAlgo)(void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);
@@ -994,3 +1055,42 @@ size_t HUF_decompress1X_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize,
return HUF_decompress1X_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
workSpace, sizeof(workSpace));
}
+
+
+size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2)
+{
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+ return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) :
+ HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+}
+
+size_t HUF_decompress1X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2)
+{
+ const BYTE* ip = (const BYTE*) cSrc;
+
+ size_t const hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize);
+ if (HUF_isError(hSize)) return hSize;
+ if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
+ ip += hSize; cSrcSize -= hSize;
+
+ return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2);
+}
+
+size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2)
+{
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+ return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) :
+ HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+}
+
+size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2)
+{
+ /* validation checks */
+ if (dstSize == 0) return ERROR(dstSize_tooSmall);
+ if (cSrcSize == 0) return ERROR(corruption_detected);
+
+ { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
+ return algoNb ? HUF_decompress4X4_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2) :
+ HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2);
+ }
+}
diff --git a/thirdparty/zstd/decompress/zstd_decompress.c b/thirdparty/zstd/decompress/zstd_decompress.c
index a59d944112..3ec6a1cb32 100644
--- a/thirdparty/zstd/decompress/zstd_decompress.c
+++ b/thirdparty/zstd/decompress/zstd_decompress.c
@@ -14,8 +14,9 @@
*****************************************************************/
/*!
* HEAPMODE :
- * Select how default decompression function ZSTD_decompress() will allocate memory,
- * in memory stack (0), or in memory heap (1, requires malloc())
+ * Select how default decompression function ZSTD_decompress() allocates its context,
+ * on stack (0), or into heap (1, default; requires malloc()).
+ * Note that functions with explicit context such as ZSTD_decompressDCtx() are unaffected.
*/
#ifndef ZSTD_HEAPMODE
# define ZSTD_HEAPMODE 1
@@ -23,17 +24,18 @@
/*!
* LEGACY_SUPPORT :
-* if set to 1, ZSTD_decompress() can decode older formats (v0.1+)
+* if set to 1+, ZSTD_decompress() can decode older formats (v0.1+)
*/
#ifndef ZSTD_LEGACY_SUPPORT
# define ZSTD_LEGACY_SUPPORT 0
#endif
/*!
-* MAXWINDOWSIZE_DEFAULT :
-* maximum window size accepted by DStream, by default.
-* Frames requiring more memory will be rejected.
-*/
+ * MAXWINDOWSIZE_DEFAULT :
+ * maximum window size accepted by DStream __by default__.
+ * Frames requiring more memory will be rejected.
+ * It's possible to set a different limit using ZSTD_DCtx_setMaxWindowSize().
+ */
#ifndef ZSTD_MAXWINDOWSIZE_DEFAULT
# define ZSTD_MAXWINDOWSIZE_DEFAULT (((U32)1 << ZSTD_WINDOWLOG_DEFAULTMAX) + 1)
#endif
@@ -43,6 +45,7 @@
* Dependencies
*********************************************************/
#include <string.h> /* memcpy, memmove, memset */
+#include "cpu.h"
#include "mem.h" /* low level memory routines */
#define FSE_STATIC_LINKING_ONLY
#include "fse.h"
@@ -80,10 +83,25 @@ typedef enum { ZSTDds_getFrameHeaderSize, ZSTDds_decodeFrameHeader,
typedef enum { zdss_init=0, zdss_loadHeader,
zdss_read, zdss_load, zdss_flush } ZSTD_dStreamStage;
+
+typedef struct {
+ U32 fastMode;
+ U32 tableLog;
+} ZSTD_seqSymbol_header;
+
+typedef struct {
+ U16 nextState;
+ BYTE nbAdditionalBits;
+ BYTE nbBits;
+ U32 baseValue;
+} ZSTD_seqSymbol;
+
+#define SEQSYMBOL_TABLE_SIZE(log) (1 + (1 << (log)))
+
typedef struct {
- FSE_DTable LLTable[FSE_DTABLE_SIZE_U32(LLFSELog)];
- FSE_DTable OFTable[FSE_DTABLE_SIZE_U32(OffFSELog)];
- FSE_DTable MLTable[FSE_DTABLE_SIZE_U32(MLFSELog)];
+ ZSTD_seqSymbol LLTable[SEQSYMBOL_TABLE_SIZE(LLFSELog)];
+ ZSTD_seqSymbol OFTable[SEQSYMBOL_TABLE_SIZE(OffFSELog)];
+ ZSTD_seqSymbol MLTable[SEQSYMBOL_TABLE_SIZE(MLFSELog)];
HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)]; /* can accommodate HUF_decompress4X */
U32 workspace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
U32 rep[ZSTD_REP_NUM];
@@ -91,9 +109,9 @@ typedef struct {
struct ZSTD_DCtx_s
{
- const FSE_DTable* LLTptr;
- const FSE_DTable* MLTptr;
- const FSE_DTable* OFTptr;
+ const ZSTD_seqSymbol* LLTptr;
+ const ZSTD_seqSymbol* MLTptr;
+ const ZSTD_seqSymbol* OFTptr;
const HUF_DTable* HUFptr;
ZSTD_entropyDTables_t entropy;
const void* previousDstEnd; /* detect continuity */
@@ -116,6 +134,7 @@ struct ZSTD_DCtx_s
size_t litSize;
size_t rleSize;
size_t staticSize;
+ int bmi2; /* == 1 if the CPU supports BMI2 and 0 otherwise. CPU support is determined dynamically once per context lifetime. */
/* streaming */
ZSTD_DDict* ddictLocal;
@@ -173,6 +192,7 @@ static void ZSTD_initDCtx_internal(ZSTD_DCtx* dctx)
dctx->inBuffSize = 0;
dctx->outBuffSize = 0;
dctx->streamStage = zdss_init;
+ dctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());
}
ZSTD_DCtx* ZSTD_initStaticDCtx(void *workspace, size_t workspaceSize)
@@ -204,6 +224,7 @@ ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem)
ZSTD_DCtx* ZSTD_createDCtx(void)
{
+ DEBUGLOG(3, "ZSTD_createDCtx");
return ZSTD_createDCtx_advanced(ZSTD_defaultCMem);
}
@@ -234,8 +255,8 @@ void ZSTD_copyDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx)
/*-*************************************************************
-* Decompression section
-***************************************************************/
+ * Frame header decoding
+ ***************************************************************/
/*! ZSTD_isFrame() :
* Tells if the content of `buffer` starts with a valid Frame Identifier.
@@ -257,7 +278,7 @@ unsigned ZSTD_isFrame(const void* buffer, size_t size)
/** ZSTD_frameHeaderSize_internal() :
* srcSize must be large enough to reach header size fields.
- * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless
+ * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless.
* @return : size of the Frame Header
* or an error code, which can be tested with ZSTD_isError() */
static size_t ZSTD_frameHeaderSize_internal(const void* src, size_t srcSize, ZSTD_format_e format)
@@ -480,6 +501,10 @@ static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t he
}
+/*-*************************************************************
+ * Block decoding
+ ***************************************************************/
+
/*! ZSTD_getcBlockSize() :
* Provides the size of compressed block from block header `src` */
size_t ZSTD_getcBlockSize(const void* src, size_t srcSize,
@@ -566,13 +591,13 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
if (HUF_isError((litEncType==set_repeat) ?
( singleStream ?
- HUF_decompress1X_usingDTable(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr) :
- HUF_decompress4X_usingDTable(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr) ) :
+ HUF_decompress1X_usingDTable_bmi2(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr, dctx->bmi2) :
+ HUF_decompress4X_usingDTable_bmi2(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr, dctx->bmi2) ) :
( singleStream ?
- HUF_decompress1X2_DCtx_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize,
- dctx->entropy.workspace, sizeof(dctx->entropy.workspace)) :
- HUF_decompress4X_hufOnly_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize,
- dctx->entropy.workspace, sizeof(dctx->entropy.workspace)))))
+ HUF_decompress1X2_DCtx_wksp_bmi2(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize,
+ dctx->entropy.workspace, sizeof(dctx->entropy.workspace), dctx->bmi2) :
+ HUF_decompress4X_hufOnly_wksp_bmi2(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize,
+ dctx->entropy.workspace, sizeof(dctx->entropy.workspace), dctx->bmi2))))
return ERROR(corruption_detected);
dctx->litPtr = dctx->litBuffer;
@@ -647,115 +672,268 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
}
}
-
-typedef union {
- FSE_decode_t realData;
- U32 alignedBy4;
-} FSE_decode_t4;
+/* Default FSE distribution tables.
+ * These are pre-calculated FSE decoding tables using default distributions as defined in specification :
+ * https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#default-distributions
+ * They were generated programmatically with following method :
+ * - start from default distributions, present in /lib/common/zstd_internal.h
+ * - generate tables normally, using ZSTD_buildFSETable()
+ * - printout the content of tables
+ * - pretify output, report below, test with fuzzer to ensure it's correct */
/* Default FSE distribution table for Literal Lengths */
-static const FSE_decode_t4 LL_defaultDTable[(1<<LL_DEFAULTNORMLOG)+1] = {
- { { LL_DEFAULTNORMLOG, 1, 1 } }, /* header : tableLog, fastMode, fastMode */
- /* base, symbol, bits */
- { { 0, 0, 4 } }, { { 16, 0, 4 } }, { { 32, 1, 5 } }, { { 0, 3, 5 } },
- { { 0, 4, 5 } }, { { 0, 6, 5 } }, { { 0, 7, 5 } }, { { 0, 9, 5 } },
- { { 0, 10, 5 } }, { { 0, 12, 5 } }, { { 0, 14, 6 } }, { { 0, 16, 5 } },
- { { 0, 18, 5 } }, { { 0, 19, 5 } }, { { 0, 21, 5 } }, { { 0, 22, 5 } },
- { { 0, 24, 5 } }, { { 32, 25, 5 } }, { { 0, 26, 5 } }, { { 0, 27, 6 } },
- { { 0, 29, 6 } }, { { 0, 31, 6 } }, { { 32, 0, 4 } }, { { 0, 1, 4 } },
- { { 0, 2, 5 } }, { { 32, 4, 5 } }, { { 0, 5, 5 } }, { { 32, 7, 5 } },
- { { 0, 8, 5 } }, { { 32, 10, 5 } }, { { 0, 11, 5 } }, { { 0, 13, 6 } },
- { { 32, 16, 5 } }, { { 0, 17, 5 } }, { { 32, 19, 5 } }, { { 0, 20, 5 } },
- { { 32, 22, 5 } }, { { 0, 23, 5 } }, { { 0, 25, 4 } }, { { 16, 25, 4 } },
- { { 32, 26, 5 } }, { { 0, 28, 6 } }, { { 0, 30, 6 } }, { { 48, 0, 4 } },
- { { 16, 1, 4 } }, { { 32, 2, 5 } }, { { 32, 3, 5 } }, { { 32, 5, 5 } },
- { { 32, 6, 5 } }, { { 32, 8, 5 } }, { { 32, 9, 5 } }, { { 32, 11, 5 } },
- { { 32, 12, 5 } }, { { 0, 15, 6 } }, { { 32, 17, 5 } }, { { 32, 18, 5 } },
- { { 32, 20, 5 } }, { { 32, 21, 5 } }, { { 32, 23, 5 } }, { { 32, 24, 5 } },
- { { 0, 35, 6 } }, { { 0, 34, 6 } }, { { 0, 33, 6 } }, { { 0, 32, 6 } },
+static const ZSTD_seqSymbol LL_defaultDTable[(1<<LL_DEFAULTNORMLOG)+1] = {
+ { 1, 1, 1, LL_DEFAULTNORMLOG}, /* header : fastMode, tableLog */
+ /* nextState, nbAddBits, nbBits, baseVal */
+ { 0, 0, 4, 0}, { 16, 0, 4, 0},
+ { 32, 0, 5, 1}, { 0, 0, 5, 3},
+ { 0, 0, 5, 4}, { 0, 0, 5, 6},
+ { 0, 0, 5, 7}, { 0, 0, 5, 9},
+ { 0, 0, 5, 10}, { 0, 0, 5, 12},
+ { 0, 0, 6, 14}, { 0, 1, 5, 16},
+ { 0, 1, 5, 20}, { 0, 1, 5, 22},
+ { 0, 2, 5, 28}, { 0, 3, 5, 32},
+ { 0, 4, 5, 48}, { 32, 6, 5, 64},
+ { 0, 7, 5, 128}, { 0, 8, 6, 256},
+ { 0, 10, 6, 1024}, { 0, 12, 6, 4096},
+ { 32, 0, 4, 0}, { 0, 0, 4, 1},
+ { 0, 0, 5, 2}, { 32, 0, 5, 4},
+ { 0, 0, 5, 5}, { 32, 0, 5, 7},
+ { 0, 0, 5, 8}, { 32, 0, 5, 10},
+ { 0, 0, 5, 11}, { 0, 0, 6, 13},
+ { 32, 1, 5, 16}, { 0, 1, 5, 18},
+ { 32, 1, 5, 22}, { 0, 2, 5, 24},
+ { 32, 3, 5, 32}, { 0, 3, 5, 40},
+ { 0, 6, 4, 64}, { 16, 6, 4, 64},
+ { 32, 7, 5, 128}, { 0, 9, 6, 512},
+ { 0, 11, 6, 2048}, { 48, 0, 4, 0},
+ { 16, 0, 4, 1}, { 32, 0, 5, 2},
+ { 32, 0, 5, 3}, { 32, 0, 5, 5},
+ { 32, 0, 5, 6}, { 32, 0, 5, 8},
+ { 32, 0, 5, 9}, { 32, 0, 5, 11},
+ { 32, 0, 5, 12}, { 0, 0, 6, 15},
+ { 32, 1, 5, 18}, { 32, 1, 5, 20},
+ { 32, 2, 5, 24}, { 32, 2, 5, 28},
+ { 32, 3, 5, 40}, { 32, 4, 5, 48},
+ { 0, 16, 6,65536}, { 0, 15, 6,32768},
+ { 0, 14, 6,16384}, { 0, 13, 6, 8192},
}; /* LL_defaultDTable */
+/* Default FSE distribution table for Offset Codes */
+static const ZSTD_seqSymbol OF_defaultDTable[(1<<OF_DEFAULTNORMLOG)+1] = {
+ { 1, 1, 1, OF_DEFAULTNORMLOG}, /* header : fastMode, tableLog */
+ /* nextState, nbAddBits, nbBits, baseVal */
+ { 0, 0, 5, 0}, { 0, 6, 4, 61},
+ { 0, 9, 5, 509}, { 0, 15, 5,32765},
+ { 0, 21, 5,2097149}, { 0, 3, 5, 5},
+ { 0, 7, 4, 125}, { 0, 12, 5, 4093},
+ { 0, 18, 5,262141}, { 0, 23, 5,8388605},
+ { 0, 5, 5, 29}, { 0, 8, 4, 253},
+ { 0, 14, 5,16381}, { 0, 20, 5,1048573},
+ { 0, 2, 5, 1}, { 16, 7, 4, 125},
+ { 0, 11, 5, 2045}, { 0, 17, 5,131069},
+ { 0, 22, 5,4194301}, { 0, 4, 5, 13},
+ { 16, 8, 4, 253}, { 0, 13, 5, 8189},
+ { 0, 19, 5,524285}, { 0, 1, 5, 1},
+ { 16, 6, 4, 61}, { 0, 10, 5, 1021},
+ { 0, 16, 5,65533}, { 0, 28, 5,268435453},
+ { 0, 27, 5,134217725}, { 0, 26, 5,67108861},
+ { 0, 25, 5,33554429}, { 0, 24, 5,16777213},
+}; /* OF_defaultDTable */
+
+
/* Default FSE distribution table for Match Lengths */
-static const FSE_decode_t4 ML_defaultDTable[(1<<ML_DEFAULTNORMLOG)+1] = {
- { { ML_DEFAULTNORMLOG, 1, 1 } }, /* header : tableLog, fastMode, fastMode */
- /* base, symbol, bits */
- { { 0, 0, 6 } }, { { 0, 1, 4 } }, { { 32, 2, 5 } }, { { 0, 3, 5 } },
- { { 0, 5, 5 } }, { { 0, 6, 5 } }, { { 0, 8, 5 } }, { { 0, 10, 6 } },
- { { 0, 13, 6 } }, { { 0, 16, 6 } }, { { 0, 19, 6 } }, { { 0, 22, 6 } },
- { { 0, 25, 6 } }, { { 0, 28, 6 } }, { { 0, 31, 6 } }, { { 0, 33, 6 } },
- { { 0, 35, 6 } }, { { 0, 37, 6 } }, { { 0, 39, 6 } }, { { 0, 41, 6 } },
- { { 0, 43, 6 } }, { { 0, 45, 6 } }, { { 16, 1, 4 } }, { { 0, 2, 4 } },
- { { 32, 3, 5 } }, { { 0, 4, 5 } }, { { 32, 6, 5 } }, { { 0, 7, 5 } },
- { { 0, 9, 6 } }, { { 0, 12, 6 } }, { { 0, 15, 6 } }, { { 0, 18, 6 } },
- { { 0, 21, 6 } }, { { 0, 24, 6 } }, { { 0, 27, 6 } }, { { 0, 30, 6 } },
- { { 0, 32, 6 } }, { { 0, 34, 6 } }, { { 0, 36, 6 } }, { { 0, 38, 6 } },
- { { 0, 40, 6 } }, { { 0, 42, 6 } }, { { 0, 44, 6 } }, { { 32, 1, 4 } },
- { { 48, 1, 4 } }, { { 16, 2, 4 } }, { { 32, 4, 5 } }, { { 32, 5, 5 } },
- { { 32, 7, 5 } }, { { 32, 8, 5 } }, { { 0, 11, 6 } }, { { 0, 14, 6 } },
- { { 0, 17, 6 } }, { { 0, 20, 6 } }, { { 0, 23, 6 } }, { { 0, 26, 6 } },
- { { 0, 29, 6 } }, { { 0, 52, 6 } }, { { 0, 51, 6 } }, { { 0, 50, 6 } },
- { { 0, 49, 6 } }, { { 0, 48, 6 } }, { { 0, 47, 6 } }, { { 0, 46, 6 } },
+static const ZSTD_seqSymbol ML_defaultDTable[(1<<ML_DEFAULTNORMLOG)+1] = {
+ { 1, 1, 1, ML_DEFAULTNORMLOG}, /* header : fastMode, tableLog */
+ /* nextState, nbAddBits, nbBits, baseVal */
+ { 0, 0, 6, 3}, { 0, 0, 4, 4},
+ { 32, 0, 5, 5}, { 0, 0, 5, 6},
+ { 0, 0, 5, 8}, { 0, 0, 5, 9},
+ { 0, 0, 5, 11}, { 0, 0, 6, 13},
+ { 0, 0, 6, 16}, { 0, 0, 6, 19},
+ { 0, 0, 6, 22}, { 0, 0, 6, 25},
+ { 0, 0, 6, 28}, { 0, 0, 6, 31},
+ { 0, 0, 6, 34}, { 0, 1, 6, 37},
+ { 0, 1, 6, 41}, { 0, 2, 6, 47},
+ { 0, 3, 6, 59}, { 0, 4, 6, 83},
+ { 0, 7, 6, 131}, { 0, 9, 6, 515},
+ { 16, 0, 4, 4}, { 0, 0, 4, 5},
+ { 32, 0, 5, 6}, { 0, 0, 5, 7},
+ { 32, 0, 5, 9}, { 0, 0, 5, 10},
+ { 0, 0, 6, 12}, { 0, 0, 6, 15},
+ { 0, 0, 6, 18}, { 0, 0, 6, 21},
+ { 0, 0, 6, 24}, { 0, 0, 6, 27},
+ { 0, 0, 6, 30}, { 0, 0, 6, 33},
+ { 0, 1, 6, 35}, { 0, 1, 6, 39},
+ { 0, 2, 6, 43}, { 0, 3, 6, 51},
+ { 0, 4, 6, 67}, { 0, 5, 6, 99},
+ { 0, 8, 6, 259}, { 32, 0, 4, 4},
+ { 48, 0, 4, 4}, { 16, 0, 4, 5},
+ { 32, 0, 5, 7}, { 32, 0, 5, 8},
+ { 32, 0, 5, 10}, { 32, 0, 5, 11},
+ { 0, 0, 6, 14}, { 0, 0, 6, 17},
+ { 0, 0, 6, 20}, { 0, 0, 6, 23},
+ { 0, 0, 6, 26}, { 0, 0, 6, 29},
+ { 0, 0, 6, 32}, { 0, 16, 6,65539},
+ { 0, 15, 6,32771}, { 0, 14, 6,16387},
+ { 0, 13, 6, 8195}, { 0, 12, 6, 4099},
+ { 0, 11, 6, 2051}, { 0, 10, 6, 1027},
}; /* ML_defaultDTable */
-/* Default FSE distribution table for Offset Codes */
-static const FSE_decode_t4 OF_defaultDTable[(1<<OF_DEFAULTNORMLOG)+1] = {
- { { OF_DEFAULTNORMLOG, 1, 1 } }, /* header : tableLog, fastMode, fastMode */
- /* base, symbol, bits */
- { { 0, 0, 5 } }, { { 0, 6, 4 } },
- { { 0, 9, 5 } }, { { 0, 15, 5 } },
- { { 0, 21, 5 } }, { { 0, 3, 5 } },
- { { 0, 7, 4 } }, { { 0, 12, 5 } },
- { { 0, 18, 5 } }, { { 0, 23, 5 } },
- { { 0, 5, 5 } }, { { 0, 8, 4 } },
- { { 0, 14, 5 } }, { { 0, 20, 5 } },
- { { 0, 2, 5 } }, { { 16, 7, 4 } },
- { { 0, 11, 5 } }, { { 0, 17, 5 } },
- { { 0, 22, 5 } }, { { 0, 4, 5 } },
- { { 16, 8, 4 } }, { { 0, 13, 5 } },
- { { 0, 19, 5 } }, { { 0, 1, 5 } },
- { { 16, 6, 4 } }, { { 0, 10, 5 } },
- { { 0, 16, 5 } }, { { 0, 28, 5 } },
- { { 0, 27, 5 } }, { { 0, 26, 5 } },
- { { 0, 25, 5 } }, { { 0, 24, 5 } },
-}; /* OF_defaultDTable */
+
+static void ZSTD_buildSeqTable_rle(ZSTD_seqSymbol* dt, U32 baseValue, U32 nbAddBits)
+{
+ void* ptr = dt;
+ ZSTD_seqSymbol_header* const DTableH = (ZSTD_seqSymbol_header*)ptr;
+ ZSTD_seqSymbol* const cell = dt + 1;
+
+ DTableH->tableLog = 0;
+ DTableH->fastMode = 0;
+
+ cell->nbBits = 0;
+ cell->nextState = 0;
+ assert(nbAddBits < 255);
+ cell->nbAdditionalBits = (BYTE)nbAddBits;
+ cell->baseValue = baseValue;
+}
+
+
+/* ZSTD_buildFSETable() :
+ * generate FSE decoding table for one symbol (ll, ml or off) */
+static void
+ZSTD_buildFSETable(ZSTD_seqSymbol* dt,
+ const short* normalizedCounter, unsigned maxSymbolValue,
+ const U32* baseValue, const U32* nbAdditionalBits,
+ unsigned tableLog)
+{
+ ZSTD_seqSymbol* const tableDecode = dt+1;
+ U16 symbolNext[MaxSeq+1];
+
+ U32 const maxSV1 = maxSymbolValue + 1;
+ U32 const tableSize = 1 << tableLog;
+ U32 highThreshold = tableSize-1;
+
+ /* Sanity Checks */
+ assert(maxSymbolValue <= MaxSeq);
+ assert(tableLog <= MaxFSELog);
+
+ /* Init, lay down lowprob symbols */
+ { ZSTD_seqSymbol_header DTableH;
+ DTableH.tableLog = tableLog;
+ DTableH.fastMode = 1;
+ { S16 const largeLimit= (S16)(1 << (tableLog-1));
+ U32 s;
+ for (s=0; s<maxSV1; s++) {
+ if (normalizedCounter[s]==-1) {
+ tableDecode[highThreshold--].baseValue = s;
+ symbolNext[s] = 1;
+ } else {
+ if (normalizedCounter[s] >= largeLimit) DTableH.fastMode=0;
+ symbolNext[s] = normalizedCounter[s];
+ } } }
+ memcpy(dt, &DTableH, sizeof(DTableH));
+ }
+
+ /* Spread symbols */
+ { U32 const tableMask = tableSize-1;
+ U32 const step = FSE_TABLESTEP(tableSize);
+ U32 s, position = 0;
+ for (s=0; s<maxSV1; s++) {
+ int i;
+ for (i=0; i<normalizedCounter[s]; i++) {
+ tableDecode[position].baseValue = s;
+ position = (position + step) & tableMask;
+ while (position > highThreshold) position = (position + step) & tableMask; /* lowprob area */
+ } }
+ assert(position == 0); /* position must reach all cells once, otherwise normalizedCounter is incorrect */
+ }
+
+ /* Build Decoding table */
+ { U32 u;
+ for (u=0; u<tableSize; u++) {
+ U32 const symbol = tableDecode[u].baseValue;
+ U32 const nextState = symbolNext[symbol]++;
+ tableDecode[u].nbBits = (BYTE) (tableLog - BIT_highbit32(nextState) );
+ tableDecode[u].nextState = (U16) ( (nextState << tableDecode[u].nbBits) - tableSize);
+ assert(nbAdditionalBits[symbol] < 255);
+ tableDecode[u].nbAdditionalBits = (BYTE)nbAdditionalBits[symbol];
+ tableDecode[u].baseValue = baseValue[symbol];
+ } }
+}
+
/*! ZSTD_buildSeqTable() :
* @return : nb bytes read from src,
- * or an error code if it fails, testable with ZSTD_isError()
- */
-static size_t ZSTD_buildSeqTable(FSE_DTable* DTableSpace, const FSE_DTable** DTablePtr,
+ * or an error code if it fails */
+static size_t ZSTD_buildSeqTable(ZSTD_seqSymbol* DTableSpace, const ZSTD_seqSymbol** DTablePtr,
symbolEncodingType_e type, U32 max, U32 maxLog,
const void* src, size_t srcSize,
- const FSE_decode_t4* defaultTable, U32 flagRepeatTable)
+ const U32* baseValue, const U32* nbAdditionalBits,
+ const ZSTD_seqSymbol* defaultTable, U32 flagRepeatTable)
{
- const void* const tmpPtr = defaultTable; /* bypass strict aliasing */
switch(type)
{
case set_rle :
if (!srcSize) return ERROR(srcSize_wrong);
if ( (*(const BYTE*)src) > max) return ERROR(corruption_detected);
- FSE_buildDTable_rle(DTableSpace, *(const BYTE*)src);
+ { U32 const symbol = *(const BYTE*)src;
+ U32 const baseline = baseValue[symbol];
+ U32 const nbBits = nbAdditionalBits[symbol];
+ ZSTD_buildSeqTable_rle(DTableSpace, baseline, nbBits);
+ }
*DTablePtr = DTableSpace;
return 1;
case set_basic :
- *DTablePtr = (const FSE_DTable*)tmpPtr;
+ *DTablePtr = defaultTable;
return 0;
case set_repeat:
if (!flagRepeatTable) return ERROR(corruption_detected);
return 0;
- default : /* impossible */
case set_compressed :
{ U32 tableLog;
S16 norm[MaxSeq+1];
size_t const headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize);
if (FSE_isError(headerSize)) return ERROR(corruption_detected);
if (tableLog > maxLog) return ERROR(corruption_detected);
- FSE_buildDTable(DTableSpace, norm, max, tableLog);
+ ZSTD_buildFSETable(DTableSpace, norm, max, baseValue, nbAdditionalBits, tableLog);
*DTablePtr = DTableSpace;
return headerSize;
- } }
+ }
+ default : /* impossible */
+ assert(0);
+ return ERROR(GENERIC);
+ }
}
+static const U32 LL_base[MaxLL+1] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 18, 20, 22, 24, 28, 32, 40,
+ 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000,
+ 0x2000, 0x4000, 0x8000, 0x10000 };
+
+static const U32 OF_base[MaxOff+1] = {
+ 0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D,
+ 0xFD, 0x1FD, 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD,
+ 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD,
+ 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD };
+
+static const U32 OF_bits[MaxOff+1] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31 };
+
+static const U32 ML_base[MaxML+1] = {
+ 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 37, 39, 41, 43, 47, 51, 59,
+ 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803,
+ 0x1003, 0x2003, 0x4003, 0x8003, 0x10003 };
+
+
size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr,
const void* src, size_t srcSize)
{
@@ -792,19 +970,27 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr,
/* Build DTables */
{ size_t const llhSize = ZSTD_buildSeqTable(dctx->entropy.LLTable, &dctx->LLTptr,
LLtype, MaxLL, LLFSELog,
- ip, iend-ip, LL_defaultDTable, dctx->fseEntropy);
+ ip, iend-ip,
+ LL_base, LL_bits,
+ LL_defaultDTable, dctx->fseEntropy);
if (ZSTD_isError(llhSize)) return ERROR(corruption_detected);
ip += llhSize;
}
+
{ size_t const ofhSize = ZSTD_buildSeqTable(dctx->entropy.OFTable, &dctx->OFTptr,
OFtype, MaxOff, OffFSELog,
- ip, iend-ip, OF_defaultDTable, dctx->fseEntropy);
+ ip, iend-ip,
+ OF_base, OF_bits,
+ OF_defaultDTable, dctx->fseEntropy);
if (ZSTD_isError(ofhSize)) return ERROR(corruption_detected);
ip += ofhSize;
}
+
{ size_t const mlhSize = ZSTD_buildSeqTable(dctx->entropy.MLTable, &dctx->MLTptr,
MLtype, MaxML, MLFSELog,
- ip, iend-ip, ML_defaultDTable, dctx->fseEntropy);
+ ip, iend-ip,
+ ML_base, ML_bits,
+ ML_defaultDTable, dctx->fseEntropy);
if (ZSTD_isError(mlhSize)) return ERROR(corruption_detected);
ip += mlhSize;
}
@@ -822,10 +1008,15 @@ typedef struct {
} seq_t;
typedef struct {
+ size_t state;
+ const ZSTD_seqSymbol* table;
+} ZSTD_fseState;
+
+typedef struct {
BIT_DStream_t DStream;
- FSE_DState_t stateLL;
- FSE_DState_t stateOffb;
- FSE_DState_t stateML;
+ ZSTD_fseState stateLL;
+ ZSTD_fseState stateOffb;
+ ZSTD_fseState stateML;
size_t prevOffset[ZSTD_REP_NUM];
const BYTE* prefixStart;
const BYTE* dictEnd;
@@ -880,118 +1071,6 @@ size_t ZSTD_execSequenceLast7(BYTE* op,
}
-typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset_e;
-
-/* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum
- * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1)
- * bits before reloading. This value is the maximum number of bytes we read
- * after reloading when we are decoding long offets.
- */
-#define LONG_OFFSETS_MAX_EXTRA_BITS_32 \
- (ZSTD_WINDOWLOG_MAX_32 > STREAM_ACCUMULATOR_MIN_32 \
- ? ZSTD_WINDOWLOG_MAX_32 - STREAM_ACCUMULATOR_MIN_32 \
- : 0)
-
-static seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets)
-{
- seq_t seq;
-
- U32 const llCode = FSE_peekSymbol(&seqState->stateLL);
- U32 const mlCode = FSE_peekSymbol(&seqState->stateML);
- U32 const ofCode = FSE_peekSymbol(&seqState->stateOffb); /* <= MaxOff, by table construction */
-
- U32 const llBits = LL_bits[llCode];
- U32 const mlBits = ML_bits[mlCode];
- U32 const ofBits = ofCode;
- U32 const totalBits = llBits+mlBits+ofBits;
-
- static const U32 LL_base[MaxLL+1] = {
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 18, 20, 22, 24, 28, 32, 40,
- 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000,
- 0x2000, 0x4000, 0x8000, 0x10000 };
-
- static const U32 ML_base[MaxML+1] = {
- 3, 4, 5, 6, 7, 8, 9, 10,
- 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 26,
- 27, 28, 29, 30, 31, 32, 33, 34,
- 35, 37, 39, 41, 43, 47, 51, 59,
- 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803,
- 0x1003, 0x2003, 0x4003, 0x8003, 0x10003 };
-
- static const U32 OF_base[MaxOff+1] = {
- 0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D,
- 0xFD, 0x1FD, 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD,
- 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD,
- 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD };
-
- /* sequence */
- { size_t offset;
- if (!ofCode)
- offset = 0;
- else {
- ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1);
- ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5);
- assert(ofBits <= MaxOff);
- if (MEM_32bits() && longOffsets) {
- U32 const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN_32-1);
- offset = OF_base[ofCode] + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits);
- if (MEM_32bits() || extraBits) BIT_reloadDStream(&seqState->DStream);
- if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits);
- } else {
- offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */
- if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream);
- }
- }
-
- if (ofCode <= 1) {
- offset += (llCode==0);
- if (offset) {
- size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset];
- temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */
- if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1];
- seqState->prevOffset[1] = seqState->prevOffset[0];
- seqState->prevOffset[0] = offset = temp;
- } else {
- offset = seqState->prevOffset[0];
- }
- } else {
- seqState->prevOffset[2] = seqState->prevOffset[1];
- seqState->prevOffset[1] = seqState->prevOffset[0];
- seqState->prevOffset[0] = offset;
- }
- seq.offset = offset;
- }
-
- seq.matchLength = ML_base[mlCode]
- + ((mlCode>31) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */
- if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32))
- BIT_reloadDStream(&seqState->DStream);
- if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog)))
- BIT_reloadDStream(&seqState->DStream);
- /* Verify that there is enough bits to read the rest of the data in 64-bit mode. */
- ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64);
-
- seq.litLength = LL_base[llCode]
- + ((llCode>15) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */
- if (MEM_32bits())
- BIT_reloadDStream(&seqState->DStream);
-
- DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u",
- (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset);
-
- /* ANS state update */
- FSE_updateState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */
- FSE_updateState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */
- if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */
- FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */
-
- return seq;
-}
-
-
HINT_INLINE
size_t ZSTD_execSequence(BYTE* op,
BYTE* const oend, seq_t sequence,
@@ -1073,10 +1152,199 @@ size_t ZSTD_execSequence(BYTE* op,
}
-static size_t ZSTD_decompressSequences(
- ZSTD_DCtx* dctx,
+HINT_INLINE
+size_t ZSTD_execSequenceLong(BYTE* op,
+ BYTE* const oend, seq_t sequence,
+ const BYTE** litPtr, const BYTE* const litLimit,
+ const BYTE* const prefixStart, const BYTE* const dictStart, const BYTE* const dictEnd)
+{
+ BYTE* const oLitEnd = op + sequence.litLength;
+ size_t const sequenceLength = sequence.litLength + sequence.matchLength;
+ BYTE* const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */
+ BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH;
+ const BYTE* const iLitEnd = *litPtr + sequence.litLength;
+ const BYTE* match = sequence.match;
+
+ /* check */
+ if (oMatchEnd > oend) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */
+ if (iLitEnd > litLimit) return ERROR(corruption_detected); /* over-read beyond lit buffer */
+ if (oLitEnd > oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, prefixStart, dictStart, dictEnd);
+
+ /* copy Literals */
+ ZSTD_copy8(op, *litPtr); /* note : op <= oLitEnd <= oend_w == oend - 8 */
+ if (sequence.litLength > 8)
+ ZSTD_wildcopy(op+8, (*litPtr)+8, sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */
+ op = oLitEnd;
+ *litPtr = iLitEnd; /* update for next sequence */
+
+ /* copy Match */
+ if (sequence.offset > (size_t)(oLitEnd - prefixStart)) {
+ /* offset beyond prefix */
+ if (sequence.offset > (size_t)(oLitEnd - dictStart)) return ERROR(corruption_detected);
+ if (match + sequence.matchLength <= dictEnd) {
+ memmove(oLitEnd, match, sequence.matchLength);
+ return sequenceLength;
+ }
+ /* span extDict & currentPrefixSegment */
+ { size_t const length1 = dictEnd - match;
+ memmove(oLitEnd, match, length1);
+ op = oLitEnd + length1;
+ sequence.matchLength -= length1;
+ match = prefixStart;
+ if (op > oend_w || sequence.matchLength < MINMATCH) {
+ U32 i;
+ for (i = 0; i < sequence.matchLength; ++i) op[i] = match[i];
+ return sequenceLength;
+ }
+ } }
+ assert(op <= oend_w);
+ assert(sequence.matchLength >= MINMATCH);
+
+ /* match within prefix */
+ if (sequence.offset < 8) {
+ /* close range match, overlap */
+ static const U32 dec32table[] = { 0, 1, 2, 1, 4, 4, 4, 4 }; /* added */
+ static const int dec64table[] = { 8, 8, 8, 7, 8, 9,10,11 }; /* subtracted */
+ int const sub2 = dec64table[sequence.offset];
+ op[0] = match[0];
+ op[1] = match[1];
+ op[2] = match[2];
+ op[3] = match[3];
+ match += dec32table[sequence.offset];
+ ZSTD_copy4(op+4, match);
+ match -= sub2;
+ } else {
+ ZSTD_copy8(op, match);
+ }
+ op += 8; match += 8;
+
+ if (oMatchEnd > oend-(16-MINMATCH)) {
+ if (op < oend_w) {
+ ZSTD_wildcopy(op, match, oend_w - op);
+ match += oend_w - op;
+ op = oend_w;
+ }
+ while (op < oMatchEnd) *op++ = *match++;
+ } else {
+ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8); /* works even if matchLength < 8 */
+ }
+ return sequenceLength;
+}
+
+static void
+ZSTD_initFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, const ZSTD_seqSymbol* dt)
+{
+ const void* ptr = dt;
+ const ZSTD_seqSymbol_header* const DTableH = (const ZSTD_seqSymbol_header*)ptr;
+ DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog);
+ DEBUGLOG(6, "ZSTD_initFseState : val=%u using %u bits",
+ (U32)DStatePtr->state, DTableH->tableLog);
+ BIT_reloadDStream(bitD);
+ DStatePtr->table = dt + 1;
+}
+
+FORCE_INLINE_TEMPLATE void
+ZSTD_updateFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD)
+{
+ ZSTD_seqSymbol const DInfo = DStatePtr->table[DStatePtr->state];
+ U32 const nbBits = DInfo.nbBits;
+ size_t const lowBits = BIT_readBits(bitD, nbBits);
+ DStatePtr->state = DInfo.nextState + lowBits;
+}
+
+/* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum
+ * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1)
+ * bits before reloading. This value is the maximum number of bytes we read
+ * after reloading when we are decoding long offets.
+ */
+#define LONG_OFFSETS_MAX_EXTRA_BITS_32 \
+ (ZSTD_WINDOWLOG_MAX_32 > STREAM_ACCUMULATOR_MIN_32 \
+ ? ZSTD_WINDOWLOG_MAX_32 - STREAM_ACCUMULATOR_MIN_32 \
+ : 0)
+
+typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset_e;
+
+FORCE_INLINE_TEMPLATE seq_t
+ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets)
+{
+ seq_t seq;
+ U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits;
+ U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits;
+ U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits;
+ U32 const totalBits = llBits+mlBits+ofBits;
+ U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue;
+ U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue;
+ U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue;
+
+ /* sequence */
+ { size_t offset;
+ if (!ofBits)
+ offset = 0;
+ else {
+ ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1);
+ ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5);
+ assert(ofBits <= MaxOff);
+ if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) {
+ U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed);
+ offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits);
+ BIT_reloadDStream(&seqState->DStream);
+ if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits);
+ assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32); /* to avoid another reload */
+ } else {
+ offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */
+ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream);
+ }
+ }
+
+ if (ofBits <= 1) {
+ offset += (llBase==0);
+ if (offset) {
+ size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset];
+ temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */
+ if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1];
+ seqState->prevOffset[1] = seqState->prevOffset[0];
+ seqState->prevOffset[0] = offset = temp;
+ } else { /* offset == 0 */
+ offset = seqState->prevOffset[0];
+ }
+ } else {
+ seqState->prevOffset[2] = seqState->prevOffset[1];
+ seqState->prevOffset[1] = seqState->prevOffset[0];
+ seqState->prevOffset[0] = offset;
+ }
+ seq.offset = offset;
+ }
+
+ seq.matchLength = mlBase
+ + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/) : 0); /* <= 16 bits */
+ if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32))
+ BIT_reloadDStream(&seqState->DStream);
+ if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog)))
+ BIT_reloadDStream(&seqState->DStream);
+ /* Ensure there are enough bits to read the rest of data in 64-bit mode. */
+ ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64);
+
+ seq.litLength = llBase
+ + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits/*>0*/) : 0); /* <= 16 bits */
+ if (MEM_32bits())
+ BIT_reloadDStream(&seqState->DStream);
+
+ DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u",
+ (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset);
+
+ /* ANS state update */
+ ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */
+ ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */
+ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */
+ ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */
+
+ return seq;
+}
+
+FORCE_INLINE_TEMPLATE size_t
+ZSTD_decompressSequences_body( ZSTD_DCtx* dctx,
void* dst, size_t maxDstSize,
- const void* seqStart, size_t seqSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
const ZSTD_longOffset_e isLongOffset)
{
const BYTE* ip = (const BYTE*)seqStart;
@@ -1089,26 +1357,17 @@ static size_t ZSTD_decompressSequences(
const BYTE* const base = (const BYTE*) (dctx->base);
const BYTE* const vBase = (const BYTE*) (dctx->vBase);
const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd);
- int nbSeq;
DEBUGLOG(5, "ZSTD_decompressSequences");
- /* Build Decoding Tables */
- { size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize);
- DEBUGLOG(5, "ZSTD_decodeSeqHeaders: size=%u, nbSeq=%i",
- (U32)seqHSize, nbSeq);
- if (ZSTD_isError(seqHSize)) return seqHSize;
- ip += seqHSize;
- }
-
/* Regen sequences */
if (nbSeq) {
seqState_t seqState;
dctx->fseEntropy = 1;
{ U32 i; for (i=0; i<ZSTD_REP_NUM; i++) seqState.prevOffset[i] = dctx->entropy.rep[i]; }
CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected);
- FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr);
- FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
- FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
+ ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr);
+ ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
+ ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) {
nbSeq--;
@@ -1120,7 +1379,7 @@ static size_t ZSTD_decompressSequences(
} }
/* check if reached exact end */
- DEBUGLOG(5, "after decode loop, remaining nbSeq : %i", nbSeq);
+ DEBUGLOG(5, "ZSTD_decompressSequences: after decode loop, remaining nbSeq : %i", nbSeq);
if (nbSeq) return ERROR(corruption_detected);
/* save reps for next block */
{ U32 i; for (i=0; i<ZSTD_REP_NUM; i++) dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); }
@@ -1136,46 +1395,32 @@ static size_t ZSTD_decompressSequences(
return op-ostart;
}
-
-HINT_INLINE
-seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const longOffsets)
+static size_t
+ZSTD_decompressSequences_default(ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
{
- seq_t seq;
+ return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
- U32 const llCode = FSE_peekSymbol(&seqState->stateLL);
- U32 const mlCode = FSE_peekSymbol(&seqState->stateML);
- U32 const ofCode = FSE_peekSymbol(&seqState->stateOffb); /* <= MaxOff, by table construction */
- U32 const llBits = LL_bits[llCode];
- U32 const mlBits = ML_bits[mlCode];
- U32 const ofBits = ofCode;
- U32 const totalBits = llBits+mlBits+ofBits;
- static const U32 LL_base[MaxLL+1] = {
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 18, 20, 22, 24, 28, 32, 40,
- 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000,
- 0x2000, 0x4000, 0x8000, 0x10000 };
-
- static const U32 ML_base[MaxML+1] = {
- 3, 4, 5, 6, 7, 8, 9, 10,
- 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 26,
- 27, 28, 29, 30, 31, 32, 33, 34,
- 35, 37, 39, 41, 43, 47, 51, 59,
- 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803,
- 0x1003, 0x2003, 0x4003, 0x8003, 0x10003 };
-
- static const U32 OF_base[MaxOff+1] = {
- 0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D,
- 0xFD, 0x1FD, 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD,
- 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD,
- 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD };
+FORCE_INLINE_TEMPLATE seq_t
+ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const longOffsets)
+{
+ seq_t seq;
+ U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits;
+ U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits;
+ U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits;
+ U32 const totalBits = llBits+mlBits+ofBits;
+ U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue;
+ U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue;
+ U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue;
/* sequence */
{ size_t offset;
- if (!ofCode)
+ if (!ofBits)
offset = 0;
else {
ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1);
@@ -1183,17 +1428,17 @@ seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const long
assert(ofBits <= MaxOff);
if (MEM_32bits() && longOffsets) {
U32 const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN_32-1);
- offset = OF_base[ofCode] + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits);
+ offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits);
if (MEM_32bits() || extraBits) BIT_reloadDStream(&seqState->DStream);
if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits);
} else {
- offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */
+ offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */
if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream);
}
}
- if (ofCode <= 1) {
- offset += (llCode==0);
+ if (ofBits <= 1) {
+ offset += (llBase==0);
if (offset) {
size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset];
temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */
@@ -1211,7 +1456,7 @@ seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const long
seq.offset = offset;
}
- seq.matchLength = ML_base[mlCode] + ((mlCode>31) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */
+ seq.matchLength = mlBase + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */
if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32))
BIT_reloadDStream(&seqState->DStream);
if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog)))
@@ -1219,7 +1464,7 @@ seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const long
/* Verify that there is enough bits to read the rest of the data in 64-bit mode. */
ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64);
- seq.litLength = LL_base[llCode] + ((llCode>15) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */
+ seq.litLength = llBase + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */
if (MEM_32bits())
BIT_reloadDStream(&seqState->DStream);
@@ -1231,98 +1476,19 @@ seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const long
}
/* ANS state update */
- FSE_updateState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */
- FSE_updateState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */
+ ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */
+ ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */
if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */
- FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */
+ ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */
return seq;
}
-
-HINT_INLINE
-size_t ZSTD_execSequenceLong(BYTE* op,
- BYTE* const oend, seq_t sequence,
- const BYTE** litPtr, const BYTE* const litLimit,
- const BYTE* const prefixStart, const BYTE* const dictStart, const BYTE* const dictEnd)
-{
- BYTE* const oLitEnd = op + sequence.litLength;
- size_t const sequenceLength = sequence.litLength + sequence.matchLength;
- BYTE* const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */
- BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH;
- const BYTE* const iLitEnd = *litPtr + sequence.litLength;
- const BYTE* match = sequence.match;
-
- /* check */
- if (oMatchEnd > oend) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */
- if (iLitEnd > litLimit) return ERROR(corruption_detected); /* over-read beyond lit buffer */
- if (oLitEnd > oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, prefixStart, dictStart, dictEnd);
-
- /* copy Literals */
- ZSTD_copy8(op, *litPtr); /* note : op <= oLitEnd <= oend_w == oend - 8 */
- if (sequence.litLength > 8)
- ZSTD_wildcopy(op+8, (*litPtr)+8, sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */
- op = oLitEnd;
- *litPtr = iLitEnd; /* update for next sequence */
-
- /* copy Match */
- if (sequence.offset > (size_t)(oLitEnd - prefixStart)) {
- /* offset beyond prefix */
- if (sequence.offset > (size_t)(oLitEnd - dictStart)) return ERROR(corruption_detected);
- if (match + sequence.matchLength <= dictEnd) {
- memmove(oLitEnd, match, sequence.matchLength);
- return sequenceLength;
- }
- /* span extDict & currentPrefixSegment */
- { size_t const length1 = dictEnd - match;
- memmove(oLitEnd, match, length1);
- op = oLitEnd + length1;
- sequence.matchLength -= length1;
- match = prefixStart;
- if (op > oend_w || sequence.matchLength < MINMATCH) {
- U32 i;
- for (i = 0; i < sequence.matchLength; ++i) op[i] = match[i];
- return sequenceLength;
- }
- } }
- assert(op <= oend_w);
- assert(sequence.matchLength >= MINMATCH);
-
- /* match within prefix */
- if (sequence.offset < 8) {
- /* close range match, overlap */
- static const U32 dec32table[] = { 0, 1, 2, 1, 4, 4, 4, 4 }; /* added */
- static const int dec64table[] = { 8, 8, 8, 7, 8, 9,10,11 }; /* subtracted */
- int const sub2 = dec64table[sequence.offset];
- op[0] = match[0];
- op[1] = match[1];
- op[2] = match[2];
- op[3] = match[3];
- match += dec32table[sequence.offset];
- ZSTD_copy4(op+4, match);
- match -= sub2;
- } else {
- ZSTD_copy8(op, match);
- }
- op += 8; match += 8;
-
- if (oMatchEnd > oend-(16-MINMATCH)) {
- if (op < oend_w) {
- ZSTD_wildcopy(op, match, oend_w - op);
- match += oend_w - op;
- op = oend_w;
- }
- while (op < oMatchEnd) *op++ = *match++;
- } else {
- ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8); /* works even if matchLength < 8 */
- }
- return sequenceLength;
-}
-
-static size_t ZSTD_decompressSequencesLong(
+FORCE_INLINE_TEMPLATE size_t
+ZSTD_decompressSequencesLong_body(
ZSTD_DCtx* dctx,
void* dst, size_t maxDstSize,
- const void* seqStart, size_t seqSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
const ZSTD_longOffset_e isLongOffset)
{
const BYTE* ip = (const BYTE*)seqStart;
@@ -1335,13 +1501,6 @@ static size_t ZSTD_decompressSequencesLong(
const BYTE* const prefixStart = (const BYTE*) (dctx->base);
const BYTE* const dictStart = (const BYTE*) (dctx->vBase);
const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd);
- int nbSeq;
-
- /* Build Decoding Tables */
- { size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize);
- if (ZSTD_isError(seqHSize)) return seqHSize;
- ip += seqHSize;
- }
/* Regen sequences */
if (nbSeq) {
@@ -1358,18 +1517,18 @@ static size_t ZSTD_decompressSequencesLong(
seqState.pos = (size_t)(op-prefixStart);
seqState.dictEnd = dictEnd;
CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected);
- FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr);
- FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
- FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
+ ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr);
+ ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
+ ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
/* prepare in advance */
- for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && seqNb<seqAdvance; seqNb++) {
+ for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && (seqNb<seqAdvance); seqNb++) {
sequences[seqNb] = ZSTD_decodeSequenceLong(&seqState, isLongOffset);
}
if (seqNb<seqAdvance) return ERROR(corruption_detected);
/* decode and decompress */
- for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && seqNb<nbSeq ; seqNb++) {
+ for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && (seqNb<nbSeq) ; seqNb++) {
seq_t const sequence = ZSTD_decodeSequenceLong(&seqState, isLongOffset);
size_t const oneSeqSize = ZSTD_execSequenceLong(op, oend, sequences[(seqNb-ADVANCED_SEQS) & STOSEQ_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd);
if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
@@ -1389,6 +1548,9 @@ static size_t ZSTD_decompressSequencesLong(
/* save reps for next block */
{ U32 i; for (i=0; i<ZSTD_REP_NUM; i++) dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); }
+#undef STORED_SEQS
+#undef STOSEQ_MASK
+#undef ADVANCED_SEQS
}
/* last literal segment */
@@ -1401,6 +1563,96 @@ static size_t ZSTD_decompressSequencesLong(
return op-ostart;
}
+static size_t
+ZSTD_decompressSequencesLong_default(ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
+
+
+
+#if DYNAMIC_BMI2
+
+static TARGET_ATTRIBUTE("bmi2") size_t
+ZSTD_decompressSequences_bmi2(ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
+
+static TARGET_ATTRIBUTE("bmi2") size_t
+ZSTD_decompressSequencesLong_bmi2(ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
+
+#endif
+
+typedef size_t (*ZSTD_decompressSequences_t)(
+ ZSTD_DCtx *dctx, void *dst, size_t maxDstSize,
+ const void *seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset);
+
+static size_t ZSTD_decompressSequences(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ DEBUGLOG(5, "ZSTD_decompressSequences");
+#if DYNAMIC_BMI2
+ if (dctx->bmi2) {
+ return ZSTD_decompressSequences_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+ }
+#endif
+ return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
+
+static size_t ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ DEBUGLOG(5, "ZSTD_decompressSequencesLong");
+#if DYNAMIC_BMI2
+ if (dctx->bmi2) {
+ return ZSTD_decompressSequencesLong_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+ }
+#endif
+ return ZSTD_decompressSequencesLong_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
+
+/* ZSTD_getLongOffsetsShare() :
+ * condition : offTable must be valid
+ * @return : "share" of long offsets (arbitrarily defined as > (1<<23))
+ * compared to maximum possible of (1<<OffFSELog) */
+static unsigned
+ZSTD_getLongOffsetsShare(const ZSTD_seqSymbol* offTable)
+{
+ const void* ptr = offTable;
+ U32 const tableLog = ((const ZSTD_seqSymbol_header*)ptr)[0].tableLog;
+ const ZSTD_seqSymbol* table = offTable + 1;
+ U32 const max = 1 << tableLog;
+ U32 u, total = 0;
+ DEBUGLOG(5, "ZSTD_getLongOffsetsShare: (tableLog=%u)", tableLog);
+
+ assert(max <= (1 << OffFSELog)); /* max not too large */
+ for (u=0; u<max; u++) {
+ if (table[u].nbAdditionalBits > 22) total += 1;
+ }
+
+ assert(tableLog <= OffFSELog);
+ total <<= (OffFSELog - tableLog); /* scale to OffFSELog */
+
+ return total;
+}
+
static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
void* dst, size_t dstCapacity,
@@ -1410,13 +1662,9 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
/* isLongOffset must be true if there are long offsets.
* Offsets are long if they are larger than 2^STREAM_ACCUMULATOR_MIN.
* We don't expect that to be the case in 64-bit mode.
- * If we are in block mode we don't know the window size, so we have to be
- * conservative.
+ * In block mode, window size is not known, so we have to be conservative. (note: but it could be evaluated from current-lowLimit)
*/
ZSTD_longOffset_e const isLongOffset = (ZSTD_longOffset_e)(MEM_32bits() && (!frame || dctx->fParams.windowSize > (1ULL << STREAM_ACCUMULATOR_MIN)));
- /* windowSize could be any value at this point, since it is only validated
- * in the streaming API.
- */
DEBUGLOG(5, "ZSTD_decompressBlock_internal (size : %u)", (U32)srcSize);
if (srcSize >= ZSTD_BLOCKSIZE_MAX) return ERROR(srcSize_wrong);
@@ -1428,9 +1676,24 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
ip += litCSize;
srcSize -= litCSize;
}
- if (frame && dctx->fParams.windowSize > (1<<23))
- return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, isLongOffset);
- return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, isLongOffset);
+
+ /* Build Decoding Tables */
+ { int nbSeq;
+ size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, srcSize);
+ if (ZSTD_isError(seqHSize)) return seqHSize;
+ ip += seqHSize;
+ srcSize -= seqHSize;
+
+ if ( (!frame || dctx->fParams.windowSize > (1<<24))
+ && (nbSeq>0) ) { /* could probably use a larger nbSeq limit */
+ U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr);
+ U32 const minShare = MEM_64bits() ? 7 : 20; /* heuristic values, correspond to 2.73% and 7.81% */
+ if (shareLongOffsets >= minShare)
+ return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset);
+ }
+
+ return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset);
+ }
}
@@ -1758,7 +2021,7 @@ static int ZSTD_isSkipFrame(ZSTD_DCtx* dctx) { return dctx->stage == ZSTDds_skip
* or an error code, which can be tested using ZSTD_isError() */
size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize)
{
- DEBUGLOG(5, "ZSTD_decompressContinue");
+ DEBUGLOG(5, "ZSTD_decompressContinue (srcSize:%u)", (U32)srcSize);
/* Sanity check */
if (srcSize != dctx->expected) return ERROR(srcSize_wrong); /* not allowed */
if (dstCapacity) ZSTD_checkContinuity(dctx, dst);
@@ -1819,12 +2082,12 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c
case ZSTDds_decompressLastBlock:
case ZSTDds_decompressBlock:
- DEBUGLOG(5, "case ZSTDds_decompressBlock");
+ DEBUGLOG(5, "ZSTD_decompressContinue: case ZSTDds_decompressBlock");
{ size_t rSize;
switch(dctx->bType)
{
case bt_compressed:
- DEBUGLOG(5, "case bt_compressed");
+ DEBUGLOG(5, "ZSTD_decompressContinue: case bt_compressed");
rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 1);
break;
case bt_raw :
@@ -1838,12 +2101,12 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c
return ERROR(corruption_detected);
}
if (ZSTD_isError(rSize)) return rSize;
- DEBUGLOG(5, "decoded size from block : %u", (U32)rSize);
+ DEBUGLOG(5, "ZSTD_decompressContinue: decoded size from block : %u", (U32)rSize);
dctx->decodedSize += rSize;
if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, dst, rSize);
if (dctx->stage == ZSTDds_decompressLastBlock) { /* end of frame */
- DEBUGLOG(4, "decoded size from frame : %u", (U32)dctx->decodedSize);
+ DEBUGLOG(4, "ZSTD_decompressContinue: decoded size from frame : %u", (U32)dctx->decodedSize);
if (dctx->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) {
if (dctx->decodedSize != dctx->fParams.frameContentSize) {
return ERROR(corruption_detected);
@@ -1867,7 +2130,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c
assert(srcSize == 4); /* guaranteed by dctx->expected */
{ U32 const h32 = (U32)XXH64_digest(&dctx->xxhState);
U32 const check32 = MEM_readLE32(src);
- DEBUGLOG(4, "checksum : calculated %08X :: %08X read", h32, check32);
+ DEBUGLOG(4, "ZSTD_decompressContinue: checksum : calculated %08X :: %08X read", h32, check32);
if (check32 != h32) return ERROR(checksum_wrong);
dctx->expected = 0;
dctx->stage = ZSTDds_getFrameHeaderSize;
@@ -1925,8 +2188,12 @@ static size_t ZSTD_loadEntropy(ZSTD_entropyDTables_t* entropy, const void* const
U32 offcodeMaxValue = MaxOff, offcodeLog;
size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr);
if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted);
+ if (offcodeMaxValue > MaxOff) return ERROR(dictionary_corrupted);
if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted);
- CHECK_E(FSE_buildDTable(entropy->OFTable, offcodeNCount, offcodeMaxValue, offcodeLog), dictionary_corrupted);
+ ZSTD_buildFSETable(entropy->OFTable,
+ offcodeNCount, offcodeMaxValue,
+ OF_base, OF_bits,
+ offcodeLog);
dictPtr += offcodeHeaderSize;
}
@@ -1934,8 +2201,12 @@ static size_t ZSTD_loadEntropy(ZSTD_entropyDTables_t* entropy, const void* const
unsigned matchlengthMaxValue = MaxML, matchlengthLog;
size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr);
if (FSE_isError(matchlengthHeaderSize)) return ERROR(dictionary_corrupted);
+ if (matchlengthMaxValue > MaxML) return ERROR(dictionary_corrupted);
if (matchlengthLog > MLFSELog) return ERROR(dictionary_corrupted);
- CHECK_E(FSE_buildDTable(entropy->MLTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog), dictionary_corrupted);
+ ZSTD_buildFSETable(entropy->MLTable,
+ matchlengthNCount, matchlengthMaxValue,
+ ML_base, ML_bits,
+ matchlengthLog);
dictPtr += matchlengthHeaderSize;
}
@@ -1943,8 +2214,12 @@ static size_t ZSTD_loadEntropy(ZSTD_entropyDTables_t* entropy, const void* const
unsigned litlengthMaxValue = MaxLL, litlengthLog;
size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr);
if (FSE_isError(litlengthHeaderSize)) return ERROR(dictionary_corrupted);
+ if (litlengthMaxValue > MaxLL) return ERROR(dictionary_corrupted);
if (litlengthLog > LLFSELog) return ERROR(dictionary_corrupted);
- CHECK_E(FSE_buildDTable(entropy->LLTable, litlengthNCount, litlengthMaxValue, litlengthLog), dictionary_corrupted);
+ ZSTD_buildFSETable(entropy->LLTable,
+ litlengthNCount, litlengthMaxValue,
+ LL_base, LL_bits,
+ litlengthLog);
dictPtr += litlengthHeaderSize;
}
@@ -2062,13 +2337,23 @@ size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dstDCtx, const ZSTD_DDict* ddi
return 0;
}
-static size_t ZSTD_loadEntropy_inDDict(ZSTD_DDict* ddict)
+static size_t ZSTD_loadEntropy_inDDict(ZSTD_DDict* ddict, ZSTD_dictContentType_e dictContentType)
{
ddict->dictID = 0;
ddict->entropyPresent = 0;
- if (ddict->dictSize < 8) return 0;
+ if (dictContentType == ZSTD_dct_rawContent) return 0;
+
+ if (ddict->dictSize < 8) {
+ if (dictContentType == ZSTD_dct_fullDict)
+ return ERROR(dictionary_corrupted); /* only accept specified dictionaries */
+ return 0; /* pure content mode */
+ }
{ U32 const magic = MEM_readLE32(ddict->dictContent);
- if (magic != ZSTD_MAGIC_DICTIONARY) return 0; /* pure content mode */
+ if (magic != ZSTD_MAGIC_DICTIONARY) {
+ if (dictContentType == ZSTD_dct_fullDict)
+ return ERROR(dictionary_corrupted); /* only accept specified dictionaries */
+ return 0; /* pure content mode */
+ }
}
ddict->dictID = MEM_readLE32((const char*)ddict->dictContent + ZSTD_frameIdSize);
@@ -2079,7 +2364,10 @@ static size_t ZSTD_loadEntropy_inDDict(ZSTD_DDict* ddict)
}
-static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod)
+static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict,
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType)
{
if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dict) || (!dictSize)) {
ddict->dictBuffer = NULL;
@@ -2095,12 +2383,15 @@ static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict, const void* dict, size_
ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */
/* parse dictionary content */
- CHECK_F( ZSTD_loadEntropy_inDDict(ddict) );
+ CHECK_F( ZSTD_loadEntropy_inDDict(ddict, dictContentType) );
return 0;
}
-ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_customMem customMem)
+ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_customMem customMem)
{
if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
@@ -2108,7 +2399,7 @@ ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_di
if (!ddict) return NULL;
ddict->cMem = customMem;
- if (ZSTD_isError( ZSTD_initDDict_internal(ddict, dict, dictSize, dictLoadMethod) )) {
+ if (ZSTD_isError( ZSTD_initDDict_internal(ddict, dict, dictSize, dictLoadMethod, dictContentType) )) {
ZSTD_freeDDict(ddict);
return NULL;
}
@@ -2124,7 +2415,7 @@ ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_di
ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize)
{
ZSTD_customMem const allocator = { NULL, NULL, NULL };
- return ZSTD_createDDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, allocator);
+ return ZSTD_createDDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto, allocator);
}
/*! ZSTD_createDDict_byReference() :
@@ -2134,13 +2425,15 @@ ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize)
ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize)
{
ZSTD_customMem const allocator = { NULL, NULL, NULL };
- return ZSTD_createDDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, allocator);
+ return ZSTD_createDDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto, allocator);
}
-ZSTD_DDict* ZSTD_initStaticDDict(void* workspace, size_t workspaceSize,
- const void* dict, size_t dictSize,
- ZSTD_dictLoadMethod_e dictLoadMethod)
+const ZSTD_DDict* ZSTD_initStaticDDict(
+ void* workspace, size_t workspaceSize,
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType)
{
size_t const neededSpace =
sizeof(ZSTD_DDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize);
@@ -2153,7 +2446,7 @@ ZSTD_DDict* ZSTD_initStaticDDict(void* workspace, size_t workspaceSize,
memcpy(ddict+1, dict, dictSize); /* local copy */
dict = ddict+1;
}
- if (ZSTD_isError( ZSTD_initDDict_internal(ddict, dict, dictSize, ZSTD_dlm_byRef) ))
+ if (ZSTD_isError( ZSTD_initDDict_internal(ddict, dict, dictSize, ZSTD_dlm_byRef, dictContentType) ))
return NULL;
return ddict;
}
@@ -2247,6 +2540,7 @@ size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx,
ZSTD_DStream* ZSTD_createDStream(void)
{
+ DEBUGLOG(3, "ZSTD_createDStream");
return ZSTD_createDStream_advanced(ZSTD_defaultCMem);
}
@@ -2271,58 +2565,99 @@ size_t ZSTD_freeDStream(ZSTD_DStream* zds)
size_t ZSTD_DStreamInSize(void) { return ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize; }
size_t ZSTD_DStreamOutSize(void) { return ZSTD_BLOCKSIZE_MAX; }
-size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize)
+size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType)
{
- zds->streamStage = zdss_loadHeader;
- zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0;
- ZSTD_freeDDict(zds->ddictLocal);
+ if (dctx->streamStage != zdss_init) return ERROR(stage_wrong);
+ ZSTD_freeDDict(dctx->ddictLocal);
if (dict && dictSize >= 8) {
- zds->ddictLocal = ZSTD_createDDict(dict, dictSize);
- if (zds->ddictLocal == NULL) return ERROR(memory_allocation);
- } else zds->ddictLocal = NULL;
- zds->ddict = zds->ddictLocal;
- zds->legacyVersion = 0;
- zds->hostageByte = 0;
+ dctx->ddictLocal = ZSTD_createDDict_advanced(dict, dictSize, dictLoadMethod, dictContentType, dctx->customMem);
+ if (dctx->ddictLocal == NULL) return ERROR(memory_allocation);
+ } else {
+ dctx->ddictLocal = NULL;
+ }
+ dctx->ddict = dctx->ddictLocal;
+ return 0;
+}
+
+size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize)
+{
+ return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto);
+}
+
+size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize)
+{
+ return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto);
+}
+
+size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType)
+{
+ return ZSTD_DCtx_loadDictionary_advanced(dctx, prefix, prefixSize, ZSTD_dlm_byRef, dictContentType);
+}
+
+size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize)
+{
+ return ZSTD_DCtx_refPrefix_advanced(dctx, prefix, prefixSize, ZSTD_dct_rawContent);
+}
+
+
+/* ZSTD_initDStream_usingDict() :
+ * return : expected size, aka ZSTD_frameHeaderSize_prefix.
+ * this function cannot fail */
+size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize)
+{
+ DEBUGLOG(4, "ZSTD_initDStream_usingDict");
+ zds->streamStage = zdss_init;
+ CHECK_F( ZSTD_DCtx_loadDictionary(zds, dict, dictSize) );
return ZSTD_frameHeaderSize_prefix;
}
/* note : this variant can't fail */
size_t ZSTD_initDStream(ZSTD_DStream* zds)
{
+ DEBUGLOG(4, "ZSTD_initDStream");
return ZSTD_initDStream_usingDict(zds, NULL, 0);
}
+size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict)
+{
+ if (dctx->streamStage != zdss_init) return ERROR(stage_wrong);
+ dctx->ddict = ddict;
+ return 0;
+}
+
/* ZSTD_initDStream_usingDDict() :
* ddict will just be referenced, and must outlive decompression session
* this function cannot fail */
-size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict)
+size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict)
{
- size_t const initResult = ZSTD_initDStream(zds);
- zds->ddict = ddict;
+ size_t const initResult = ZSTD_initDStream(dctx);
+ dctx->ddict = ddict;
return initResult;
}
-size_t ZSTD_resetDStream(ZSTD_DStream* zds)
+/* ZSTD_resetDStream() :
+ * return : expected size, aka ZSTD_frameHeaderSize_prefix.
+ * this function cannot fail */
+size_t ZSTD_resetDStream(ZSTD_DStream* dctx)
{
- zds->streamStage = zdss_loadHeader;
- zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0;
- zds->legacyVersion = 0;
- zds->hostageByte = 0;
+ DEBUGLOG(4, "ZSTD_resetDStream");
+ dctx->streamStage = zdss_loadHeader;
+ dctx->lhSize = dctx->inPos = dctx->outStart = dctx->outEnd = 0;
+ dctx->legacyVersion = 0;
+ dctx->hostageByte = 0;
return ZSTD_frameHeaderSize_prefix;
}
-size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds,
+size_t ZSTD_setDStreamParameter(ZSTD_DStream* dctx,
ZSTD_DStreamParameter_e paramType, unsigned paramValue)
{
- ZSTD_STATIC_ASSERT((unsigned)zdss_loadHeader >= (unsigned)zdss_init);
- if ((unsigned)zds->streamStage > (unsigned)zdss_loadHeader)
- return ERROR(stage_wrong);
+ if (dctx->streamStage != zdss_init) return ERROR(stage_wrong);
switch(paramType)
{
default : return ERROR(parameter_unsupported);
case DStream_p_maxWindowSize :
DEBUGLOG(4, "setting maxWindowSize = %u KB", paramValue >> 10);
- zds->maxWindowSize = paramValue ? paramValue : (U32)(-1);
+ dctx->maxWindowSize = paramValue ? paramValue : (U32)(-1);
break;
}
return 0;
@@ -2330,9 +2665,7 @@ size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds,
size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize)
{
- ZSTD_STATIC_ASSERT((unsigned)zdss_loadHeader >= (unsigned)zdss_init);
- if ((unsigned)dctx->streamStage > (unsigned)zdss_loadHeader)
- return ERROR(stage_wrong);
+ if (dctx->streamStage != zdss_init) return ERROR(stage_wrong);
dctx->maxWindowSize = maxWindowSize;
return 0;
}
@@ -2340,17 +2673,15 @@ size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize)
size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format)
{
DEBUGLOG(4, "ZSTD_DCtx_setFormat : %u", (unsigned)format);
- ZSTD_STATIC_ASSERT((unsigned)zdss_loadHeader >= (unsigned)zdss_init);
- if ((unsigned)dctx->streamStage > (unsigned)zdss_loadHeader)
- return ERROR(stage_wrong);
+ if (dctx->streamStage != zdss_init) return ERROR(stage_wrong);
dctx->format = format;
return 0;
}
-size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds)
+size_t ZSTD_sizeof_DStream(const ZSTD_DStream* dctx)
{
- return ZSTD_sizeof_DCtx(zds);
+ return ZSTD_sizeof_DCtx(dctx);
}
size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize)
@@ -2417,23 +2748,25 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
}
DEBUGLOG(5, "input size : %u", (U32)(input->size - input->pos));
-#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
- if (zds->legacyVersion) {
- /* legacy support is incompatible with static dctx */
- if (zds->staticSize) return ERROR(memory_allocation);
- return ZSTD_decompressLegacyStream(zds->legacyContext, zds->legacyVersion, output, input);
- }
-#endif
-
while (someMoreWork) {
switch(zds->streamStage)
{
case zdss_init :
+ DEBUGLOG(5, "stage zdss_init => transparent reset ");
ZSTD_resetDStream(zds); /* transparent reset on starting decoding a new frame */
/* fall-through */
case zdss_loadHeader :
DEBUGLOG(5, "stage zdss_loadHeader (srcSize : %u)", (U32)(iend - ip));
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
+ if (zds->legacyVersion) {
+ /* legacy support is incompatible with static dctx */
+ if (zds->staticSize) return ERROR(memory_allocation);
+ { size_t const hint = ZSTD_decompressLegacyStream(zds->legacyContext, zds->legacyVersion, output, input);
+ if (hint==0) zds->streamStage = zdss_init;
+ return hint;
+ } }
+#endif
{ size_t const hSize = ZSTD_getFrameHeader_internal(&zds->fParams, zds->headerBuffer, zds->lhSize, zds->format);
DEBUGLOG(5, "header size : %u", (U32)hSize);
if (ZSTD_isError(hSize)) {
@@ -2442,14 +2775,17 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
if (legacyVersion) {
const void* const dict = zds->ddict ? zds->ddict->dictContent : NULL;
size_t const dictSize = zds->ddict ? zds->ddict->dictSize : 0;
+ DEBUGLOG(5, "ZSTD_decompressStream: detected legacy version v0.%u", legacyVersion);
/* legacy support is incompatible with static dctx */
if (zds->staticSize) return ERROR(memory_allocation);
CHECK_F(ZSTD_initLegacyStream(&zds->legacyContext,
zds->previousLegacyVersion, legacyVersion,
dict, dictSize));
zds->legacyVersion = zds->previousLegacyVersion = legacyVersion;
- return ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input);
- }
+ { size_t const hint = ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input);
+ if (hint==0) zds->streamStage = zdss_init; /* or stay in stage zdss_loadHeader */
+ return hint;
+ } }
#endif
return hSize; /* error */
}
@@ -2559,6 +2895,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
if (ip==iend) { someMoreWork = 0; break; } /* no more input */
zds->streamStage = zdss_load;
/* fall-through */
+
case zdss_load:
{ size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds);
size_t const toLoad = neededInSize - zds->inPos;
@@ -2585,6 +2922,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
} }
zds->streamStage = zdss_flush;
/* fall-through */
+
case zdss_flush:
{ size_t const toFlushSize = zds->outEnd - zds->outStart;
size_t const flushedSize = ZSTD_limitCopy(op, oend-op, zds->outBuff + zds->outStart, toFlushSize);
@@ -2631,8 +2969,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
return 1;
} /* nextSrcSizeHint==0 */
nextSrcSizeHint += ZSTD_blockHeaderSize * (ZSTD_nextInputType(zds) == ZSTDnit_block); /* preload header of next block */
- if (zds->inPos > nextSrcSizeHint) return ERROR(GENERIC); /* should never happen */
- nextSrcSizeHint -= zds->inPos; /* already loaded*/
+ assert(zds->inPos <= nextSrcSizeHint);
+ nextSrcSizeHint -= zds->inPos; /* part already loaded*/
return nextSrcSizeHint;
}
}
diff --git a/thirdparty/zstd/zstd.h b/thirdparty/zstd/zstd.h
index 9ac0a73dce..6405da602e 100644
--- a/thirdparty/zstd/zstd.h
+++ b/thirdparty/zstd/zstd.h
@@ -45,11 +45,11 @@ extern "C" {
Levels >= 20, labeled `--ultra`, should be used with caution, as they require more memory.
Compression can be done in:
- a single step (described as Simple API)
- - a single step, reusing a context (described as Explicit memory management)
+ - a single step, reusing a context (described as Explicit context)
- unbounded multiple steps (described as Streaming compression)
The compression ratio achievable on small data can be highly improved using a dictionary in:
- a single step (described as Simple dictionary API)
- - a single step, reusing a dictionary (described as Fast dictionary API)
+ - a single step, reusing a dictionary (described as Bulk-processing dictionary API)
Advanced experimental functions can be accessed using #define ZSTD_STATIC_LINKING_ONLY before including zstd.h.
Advanced experimental APIs shall never be used with a dynamic library.
@@ -59,7 +59,7 @@ extern "C" {
/*------ Version ------*/
#define ZSTD_VERSION_MAJOR 1
#define ZSTD_VERSION_MINOR 3
-#define ZSTD_VERSION_RELEASE 3
+#define ZSTD_VERSION_RELEASE 4
#define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE)
ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< useful to check dll version */
@@ -68,7 +68,7 @@ ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< useful to check dll versio
#define ZSTD_QUOTE(str) #str
#define ZSTD_EXPAND_AND_QUOTE(str) ZSTD_QUOTE(str)
#define ZSTD_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LIB_VERSION)
-ZSTDLIB_API const char* ZSTD_versionString(void); /* v1.3.0 */
+ZSTDLIB_API const char* ZSTD_versionString(void); /* added in v1.3.0 */
/***************************************
@@ -92,7 +92,7 @@ ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity,
ZSTDLIB_API size_t ZSTD_decompress( void* dst, size_t dstCapacity,
const void* src, size_t compressedSize);
-/*! ZSTD_getFrameContentSize() : v1.3.0
+/*! ZSTD_getFrameContentSize() : added in v1.3.0
* `src` should point to the start of a ZSTD encoded frame.
* `srcSize` must be at least as large as the frame header.
* hint : any size >= `ZSTD_frameHeaderSize_max` is large enough.
@@ -120,26 +120,24 @@ ZSTDLIB_API unsigned long long ZSTD_getFrameContentSize(const void *src, size_t
/*! ZSTD_getDecompressedSize() :
* NOTE: This function is now obsolete, in favor of ZSTD_getFrameContentSize().
- * Both functions work the same way,
- * but ZSTD_getDecompressedSize() blends
- * "empty", "unknown" and "error" results in the same return value (0),
- * while ZSTD_getFrameContentSize() distinguishes them.
- *
- * 'src' is the start of a zstd compressed frame.
- * @return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise. */
+ * Both functions work the same way, but ZSTD_getDecompressedSize() blends
+ * "empty", "unknown" and "error" results to the same return value (0),
+ * while ZSTD_getFrameContentSize() gives them separate return values.
+ * `src` is the start of a zstd compressed frame.
+ * @return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise. */
ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
/*====== Helper functions ======*/
#define ZSTD_COMPRESSBOUND(srcSize) ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */
-ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case scenario */
+ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */
ZSTDLIB_API unsigned ZSTD_isError(size_t code); /*!< tells if a `size_t` function result is an error code */
ZSTDLIB_API const char* ZSTD_getErrorName(size_t code); /*!< provides readable string from an error code */
ZSTDLIB_API int ZSTD_maxCLevel(void); /*!< maximum compression level available */
/***************************************
-* Explicit memory management
+* Explicit context
***************************************/
/*= Compression context
* When compressing many times,
@@ -345,7 +343,7 @@ ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output
* *******************************************************************************/
typedef ZSTD_DCtx ZSTD_DStream; /**< DCtx and DStream are now effectively same object (>= v1.3.0) */
- /* Continue to distinguish them for compatibility with versions <= v1.2.0 */
+ /* For compatibility with versions <= v1.2.0, continue to consider them separated. */
/*===== ZSTD_DStream management functions =====*/
ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream(void);
ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds);
@@ -375,23 +373,24 @@ ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output
/* --- Constants ---*/
#define ZSTD_MAGICNUMBER 0xFD2FB528 /* >= v0.8.0 */
#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50U
-#define ZSTD_MAGIC_DICTIONARY 0xEC30A437 /* v0.7+ */
+#define ZSTD_MAGIC_DICTIONARY 0xEC30A437 /* >= v0.7.0 */
#define ZSTD_WINDOWLOG_MAX_32 30
#define ZSTD_WINDOWLOG_MAX_64 31
#define ZSTD_WINDOWLOG_MAX ((unsigned)(sizeof(size_t) == 4 ? ZSTD_WINDOWLOG_MAX_32 : ZSTD_WINDOWLOG_MAX_64))
#define ZSTD_WINDOWLOG_MIN 10
-#define ZSTD_HASHLOG_MAX MIN(ZSTD_WINDOWLOG_MAX, 30)
+#define ZSTD_HASHLOG_MAX ((ZSTD_WINDOWLOG_MAX < 30) ? ZSTD_WINDOWLOG_MAX : 30)
#define ZSTD_HASHLOG_MIN 6
-#define ZSTD_CHAINLOG_MAX MIN(ZSTD_WINDOWLOG_MAX+1, 30)
+#define ZSTD_CHAINLOG_MAX_32 29
+#define ZSTD_CHAINLOG_MAX_64 30
+#define ZSTD_CHAINLOG_MAX ((unsigned)(sizeof(size_t) == 4 ? ZSTD_CHAINLOG_MAX_32 : ZSTD_CHAINLOG_MAX_64))
#define ZSTD_CHAINLOG_MIN ZSTD_HASHLOG_MIN
#define ZSTD_HASHLOG3_MAX 17
#define ZSTD_SEARCHLOG_MAX (ZSTD_WINDOWLOG_MAX-1)
#define ZSTD_SEARCHLOG_MIN 1
#define ZSTD_SEARCHLENGTH_MAX 7 /* only for ZSTD_fast, other strategies are limited to 6 */
#define ZSTD_SEARCHLENGTH_MIN 3 /* only for ZSTD_btopt, other strategies are limited to 4 */
-#define ZSTD_TARGETLENGTH_MIN 4 /* only useful for btopt */
-#define ZSTD_TARGETLENGTH_MAX 999 /* only useful for btopt */
+#define ZSTD_TARGETLENGTH_MIN 1 /* only used by btopt, btultra and btfast */
#define ZSTD_LDM_MINMATCH_MIN 4
#define ZSTD_LDM_MINMATCH_MAX 4096
#define ZSTD_LDM_BUCKETSIZELOG_MAX 8
@@ -432,12 +431,17 @@ typedef struct {
typedef struct ZSTD_CCtx_params_s ZSTD_CCtx_params;
-/*--- Custom memory allocation functions ---*/
-typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
-typedef void (*ZSTD_freeFunction) (void* opaque, void* address);
-typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
-/* use this constant to defer to stdlib's functions */
-static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL };
+typedef enum {
+ ZSTD_dct_auto=0, /* dictionary is "full" when starting with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */
+ ZSTD_dct_rawContent, /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */
+ ZSTD_dct_fullDict /* refuses to load a dictionary if it does not respect Zstandard's specification */
+} ZSTD_dictContentType_e;
+
+typedef enum {
+ ZSTD_dlm_byCopy = 0, /**< Copy dictionary content internally */
+ ZSTD_dlm_byRef, /**< Reference dictionary content -- the dictionary buffer must outlive its users. */
+} ZSTD_dictLoadMethod_e;
+
/***************************************
@@ -483,12 +487,12 @@ ZSTDLIB_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize);
/***************************************
-* Context memory usage
+* Memory management
***************************************/
/*! ZSTD_sizeof_*() :
* These functions give the current memory usage of selected object.
- * Object memory usage can evolve when re-used multiple times. */
+ * Object memory usage can evolve when re-used. */
ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx);
ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx);
ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs);
@@ -503,8 +507,8 @@ ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
* It will also consider src size to be arbitrarily "large", which is worst case.
* If srcSize is known to always be small, ZSTD_estimateCCtxSize_usingCParams() can provide a tighter estimation.
* ZSTD_estimateCCtxSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.
- * ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is > 1.
- * Note : CCtx estimation is only correct for single-threaded compression */
+ * ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbWorkers is >= 1.
+ * Note : CCtx size estimation is only correct for single-threaded compression. */
ZSTDLIB_API size_t ZSTD_estimateCCtxSize(int compressionLevel);
ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams);
ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params);
@@ -515,8 +519,8 @@ ZSTDLIB_API size_t ZSTD_estimateDCtxSize(void);
* It will also consider src size to be arbitrarily "large", which is worst case.
* If srcSize is known to always be small, ZSTD_estimateCStreamSize_usingCParams() can provide a tighter estimation.
* ZSTD_estimateCStreamSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.
- * ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is set to a value > 1.
- * Note : CStream estimation is only correct for single-threaded compression.
+ * ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbWorkers is >= 1.
+ * Note : CStream size estimation is only correct for single-threaded compression.
* ZSTD_DStream memory budget depends on window Size.
* This information can be passed manually, using ZSTD_estimateDStreamSize,
* or deducted from a valid frame Header, using ZSTD_estimateDStreamSize_fromFrame();
@@ -529,84 +533,92 @@ ZSTDLIB_API size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_para
ZSTDLIB_API size_t ZSTD_estimateDStreamSize(size_t windowSize);
ZSTDLIB_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize);
-typedef enum {
- ZSTD_dlm_byCopy = 0, /**< Copy dictionary content internally */
- ZSTD_dlm_byRef, /**< Reference dictionary content -- the dictionary buffer must outlive its users. */
-} ZSTD_dictLoadMethod_e;
-
/*! ZSTD_estimate?DictSize() :
* ZSTD_estimateCDictSize() will bet that src size is relatively "small", and content is copied, like ZSTD_createCDict().
- * ZSTD_estimateCStreamSize_advanced_usingCParams() makes it possible to control precisely compression parameters, like ZSTD_createCDict_advanced().
- * Note : dictionary created by reference using ZSTD_dlm_byRef are smaller
+ * ZSTD_estimateCDictSize_advanced() makes it possible to control compression parameters precisely, like ZSTD_createCDict_advanced().
+ * Note : dictionaries created by reference (`ZSTD_dlm_byRef`) are logically smaller.
*/
ZSTDLIB_API size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel);
ZSTDLIB_API size_t ZSTD_estimateCDictSize_advanced(size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod);
ZSTDLIB_API size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod);
-
-/***************************************
-* Advanced compression functions
-***************************************/
-/*! ZSTD_createCCtx_advanced() :
- * Create a ZSTD compression context using external alloc and free functions */
-ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
-
-/*! ZSTD_initStaticCCtx() : initialize a fixed-size zstd compression context
- * workspace: The memory area to emplace the context into.
- * Provided pointer must 8-bytes aligned.
- * It must outlive context usage.
- * workspaceSize: Use ZSTD_estimateCCtxSize() or ZSTD_estimateCStreamSize()
- * to determine how large workspace must be to support scenario.
- * @return : pointer to ZSTD_CCtx* (same address as workspace, but different type),
- * or NULL if error (typically size too small)
- * Note : zstd will never resize nor malloc() when using a static cctx.
- * If it needs more memory than available, it will simply error out.
+/*! ZSTD_initStatic*() :
+ * Initialize an object using a pre-allocated fixed-size buffer.
+ * workspace: The memory area to emplace the object into.
+ * Provided pointer *must be 8-bytes aligned*.
+ * Buffer must outlive object.
+ * workspaceSize: Use ZSTD_estimate*Size() to determine
+ * how large workspace must be to support target scenario.
+ * @return : pointer to object (same address as workspace, just different type),
+ * or NULL if error (size too small, incorrect alignment, etc.)
+ * Note : zstd will never resize nor malloc() when using a static buffer.
+ * If the object requires more memory than available,
+ * zstd will just error out (typically ZSTD_error_memory_allocation).
* Note 2 : there is no corresponding "free" function.
- * Since workspace was allocated externally, it must be freed externally too.
- * Limitation 1 : currently not compatible with internal CDict creation, such as
- * ZSTD_CCtx_loadDictionary() or ZSTD_initCStream_usingDict().
- * Limitation 2 : currently not compatible with multi-threading
+ * Since workspace is allocated externally, it must be freed externally too.
+ * Note 3 : cParams : use ZSTD_getCParams() to convert a compression level
+ * into its associated cParams.
+ * Limitation 1 : currently not compatible with internal dictionary creation, triggered by
+ * ZSTD_CCtx_loadDictionary(), ZSTD_initCStream_usingDict() or ZSTD_initDStream_usingDict().
+ * Limitation 2 : static cctx currently not compatible with multi-threading.
+ * Limitation 3 : static dctx is incompatible with legacy support.
*/
-ZSTDLIB_API ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
+ZSTDLIB_API ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
+ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticCCtx() */
+ZSTDLIB_API ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize);
+ZSTDLIB_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticDCtx() */
-/*! ZSTD_createCDict_byReference() :
- * Create a digested dictionary for compression
- * Dictionary content is simply referenced, and therefore stays in dictBuffer.
- * It is important that dictBuffer outlives CDict, it must remain read accessible throughout the lifetime of CDict */
-ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);
+ZSTDLIB_API const ZSTD_CDict* ZSTD_initStaticCDict(
+ void* workspace, size_t workspaceSize,
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_compressionParameters cParams);
+
+ZSTDLIB_API const ZSTD_DDict* ZSTD_initStaticDDict(
+ void* workspace, size_t workspaceSize,
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType);
+
+/*! Custom memory allocation :
+ * These prototypes make it possible to pass your own allocation/free functions.
+ * ZSTD_customMem is provided at creation time, using ZSTD_create*_advanced() variants listed below.
+ * All allocation/free operations will be completed using these custom variants instead of regular <stdlib.h> ones.
+ */
+typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
+typedef void (*ZSTD_freeFunction) (void* opaque, void* address);
+typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
+static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< this constant defers to stdlib's functions */
+
+ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
+ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
+ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem);
+ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
-typedef enum { ZSTD_dm_auto=0, /* dictionary is "full" if it starts with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */
- ZSTD_dm_rawContent, /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */
- ZSTD_dm_fullDict /* refuses to load a dictionary if it does not respect Zstandard's specification */
-} ZSTD_dictMode_e;
-/*! ZSTD_createCDict_advanced() :
- * Create a ZSTD_CDict using external alloc and free, and customized compression parameters */
ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize,
ZSTD_dictLoadMethod_e dictLoadMethod,
- ZSTD_dictMode_e dictMode,
+ ZSTD_dictContentType_e dictContentType,
ZSTD_compressionParameters cParams,
ZSTD_customMem customMem);
-/*! ZSTD_initStaticCDict() :
- * Generate a digested dictionary in provided memory area.
- * workspace: The memory area to emplace the dictionary into.
- * Provided pointer must 8-bytes aligned.
- * It must outlive dictionary usage.
- * workspaceSize: Use ZSTD_estimateCDictSize()
- * to determine how large workspace must be.
- * cParams : use ZSTD_getCParams() to transform a compression level
- * into its relevants cParams.
- * @return : pointer to ZSTD_CDict* (same address as workspace, but different type),
- * or NULL if error (typically, size too small).
- * Note : there is no corresponding "free" function.
- * Since workspace was allocated externally, it must be freed externally.
- */
-ZSTDLIB_API ZSTD_CDict* ZSTD_initStaticCDict(
- void* workspace, size_t workspaceSize,
- const void* dict, size_t dictSize,
- ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode,
- ZSTD_compressionParameters cParams);
+ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_customMem customMem);
+
+
+
+/***************************************
+* Advanced compression functions
+***************************************/
+
+/*! ZSTD_createCDict_byReference() :
+ * Create a digested dictionary for compression
+ * Dictionary content is simply referenced, and therefore stays in dictBuffer.
+ * It is important that dictBuffer outlives CDict, it must remain read accessible throughout the lifetime of CDict */
+ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);
/*! ZSTD_getCParams() :
* @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize.
@@ -652,28 +664,6 @@ ZSTDLIB_API size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
* Note 3 : Skippable Frame Identifiers are considered valid. */
ZSTDLIB_API unsigned ZSTD_isFrame(const void* buffer, size_t size);
-/*! ZSTD_createDCtx_advanced() :
- * Create a ZSTD decompression context using external alloc and free functions */
-ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem);
-
-/*! ZSTD_initStaticDCtx() : initialize a fixed-size zstd decompression context
- * workspace: The memory area to emplace the context into.
- * Provided pointer must 8-bytes aligned.
- * It must outlive context usage.
- * workspaceSize: Use ZSTD_estimateDCtxSize() or ZSTD_estimateDStreamSize()
- * to determine how large workspace must be to support scenario.
- * @return : pointer to ZSTD_DCtx* (same address as workspace, but different type),
- * or NULL if error (typically size too small)
- * Note : zstd will never resize nor malloc() when using a static dctx.
- * If it needs more memory than available, it will simply error out.
- * Note 2 : static dctx is incompatible with legacy support
- * Note 3 : there is no corresponding "free" function.
- * Since workspace was allocated externally, it must be freed externally.
- * Limitation : currently not compatible with internal DDict creation,
- * such as ZSTD_initDStream_usingDict().
- */
-ZSTDLIB_API ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize);
-
/*! ZSTD_createDDict_byReference() :
* Create a digested dictionary, ready to start decompression operation without startup delay.
* Dictionary content is referenced, and therefore stays in dictBuffer.
@@ -681,26 +671,6 @@ ZSTDLIB_API ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize
* it must remain read accessible throughout the lifetime of DDict */
ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize);
-/*! ZSTD_createDDict_advanced() :
- * Create a ZSTD_DDict using external alloc and free, optionally by reference */
-ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize,
- ZSTD_dictLoadMethod_e dictLoadMethod,
- ZSTD_customMem customMem);
-
-/*! ZSTD_initStaticDDict() :
- * Generate a digested dictionary in provided memory area.
- * workspace: The memory area to emplace the dictionary into.
- * Provided pointer must 8-bytes aligned.
- * It must outlive dictionary usage.
- * workspaceSize: Use ZSTD_estimateDDictSize()
- * to determine how large workspace must be.
- * @return : pointer to ZSTD_DDict*, or NULL if error (size too small)
- * Note : there is no corresponding "free" function.
- * Since workspace was allocated externally, it must be freed externally.
- */
-ZSTDLIB_API ZSTD_DDict* ZSTD_initStaticDDict(void* workspace, size_t workspaceSize,
- const void* dict, size_t dictSize,
- ZSTD_dictLoadMethod_e dictLoadMethod);
/*! ZSTD_getDictID_fromDict() :
* Provides the dictID stored within dictionary.
@@ -732,8 +702,6 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize);
********************************************************************/
/*===== Advanced Streaming compression functions =====*/
-ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
-ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticCCtx() */
ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */
ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/
ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize,
@@ -748,14 +716,28 @@ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const
* If pledgedSrcSize is not known at reset time, use macro ZSTD_CONTENTSIZE_UNKNOWN.
* If pledgedSrcSize > 0, its value must be correct, as it will be written in header, and controlled at the end.
* For the time being, pledgedSrcSize==0 is interpreted as "srcSize unknown" for compatibility with older programs,
- * but it may change to mean "empty" in some future version, so prefer using macro ZSTD_CONTENTSIZE_UNKNOWN.
+ * but it will change to mean "empty" in future version, so use macro ZSTD_CONTENTSIZE_UNKNOWN instead.
* @return : 0, or an error code (which can be tested using ZSTD_isError()) */
ZSTDLIB_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);
+typedef struct {
+ unsigned long long ingested;
+ unsigned long long consumed;
+ unsigned long long produced;
+} ZSTD_frameProgression;
+
+/* ZSTD_getFrameProgression():
+ * tells how much data has been ingested (read from input)
+ * consumed (input actually compressed) and produced (output) for current frame.
+ * Therefore, (ingested - consumed) is amount of input data buffered internally, not yet compressed.
+ * Can report progression inside worker threads (multi-threading and non-blocking mode).
+ */
+ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx);
+
+
+
/*===== Advanced Streaming decompression functions =====*/
-ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
-ZSTDLIB_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticDCtx() */
typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e;
ZSTDLIB_API size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue); /* obsolete : this API will be removed in a future version */
ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: no dictionary will be used if dict == NULL or dictSize < 8 */
@@ -924,10 +906,8 @@ ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
* and then applied on all subsequent compression jobs.
* When no parameter is ever provided, CCtx is created with compression level ZSTD_CLEVEL_DEFAULT.
*
- * This API is intended to replace all others experimental API.
- * It can basically do all other use cases, and even new ones.
- * In constrast with _advanced() variants, it stands a reasonable chance to become "stable",
- * after a good testing period.
+ * This API is intended to replace all others advanced / experimental API entry points.
+ * But it stands a reasonable chance to become "stable", after a reasonable testing period.
*/
/* note on naming convention :
@@ -944,12 +924,12 @@ ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
* All enum will be pinned to explicit values before reaching "stable API" status */
typedef enum {
- /* Question : should we have a format ZSTD_f_auto ?
- * For the time being, it would mean exactly the same as ZSTD_f_zstd1.
- * But, in the future, should several formats be supported,
+ /* Opened question : should we have a format ZSTD_f_auto ?
+ * Today, it would mean exactly the same as ZSTD_f_zstd1.
+ * But, in the future, should several formats become supported,
* on the compression side, it would mean "default format".
- * On the decompression side, it would mean "multi format",
- * and ZSTD_f_zstd1 could be reserved to mean "accept *only* zstd frames".
+ * On the decompression side, it would mean "automatic format detection",
+ * so that ZSTD_f_zstd1 would mean "accept *only* zstd frames".
* Since meaning is a little different, another option could be to define different enums for compression and decompression.
* This question could be kept for later, when there are actually multiple formats to support,
* but there is also the question of pinning enum values, and pinning value `0` is especially important */
@@ -967,42 +947,76 @@ typedef enum {
/* compression parameters */
ZSTD_p_compressionLevel=100, /* Update all compression parameters according to pre-defined cLevel table
* Default level is ZSTD_CLEVEL_DEFAULT==3.
- * Special: value 0 means "do not change cLevel". */
+ * Special: value 0 means "do not change cLevel".
+ * Note 1 : it's possible to pass a negative compression level by casting it to unsigned type.
+ * Note 2 : setting a level sets all default values of other compression parameters.
+ * Note 3 : setting compressionLevel automatically updates ZSTD_p_compressLiterals. */
ZSTD_p_windowLog, /* Maximum allowed back-reference distance, expressed as power of 2.
* Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX.
- * Special: value 0 means "do not change windowLog".
+ * Special: value 0 means "use default windowLog".
* Note: Using a window size greater than ZSTD_MAXWINDOWSIZE_DEFAULT (default: 2^27)
- * requires setting the maximum window size at least as large during decompression. */
+ * requires explicitly allowing such window size during decompression stage. */
ZSTD_p_hashLog, /* Size of the probe table, as a power of 2.
* Resulting table size is (1 << (hashLog+2)).
* Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX.
* Larger tables improve compression ratio of strategies <= dFast,
* and improve speed of strategies > dFast.
- * Special: value 0 means "do not change hashLog". */
+ * Special: value 0 means "use default hashLog". */
ZSTD_p_chainLog, /* Size of the full-search table, as a power of 2.
* Resulting table size is (1 << (chainLog+2)).
* Larger tables result in better and slower compression.
* This parameter is useless when using "fast" strategy.
- * Special: value 0 means "do not change chainLog". */
+ * Special: value 0 means "use default chainLog". */
ZSTD_p_searchLog, /* Number of search attempts, as a power of 2.
* More attempts result in better and slower compression.
* This parameter is useless when using "fast" and "dFast" strategies.
- * Special: value 0 means "do not change searchLog". */
+ * Special: value 0 means "use default searchLog". */
ZSTD_p_minMatch, /* Minimum size of searched matches (note : repCode matches can be smaller).
* Larger values make faster compression and decompression, but decrease ratio.
* Must be clamped between ZSTD_SEARCHLENGTH_MIN and ZSTD_SEARCHLENGTH_MAX.
* Note that currently, for all strategies < btopt, effective minimum is 4.
- * Note that currently, for all strategies > fast, effective maximum is 6.
- * Special: value 0 means "do not change minMatchLength". */
- ZSTD_p_targetLength, /* Only useful for strategies >= btopt.
- * Length of Match considered "good enough" to stop search.
- * Larger values make compression stronger and slower.
- * Special: value 0 means "do not change targetLength". */
+ * , for all strategies > fast, effective maximum is 6.
+ * Special: value 0 means "use default minMatchLength". */
+ ZSTD_p_targetLength, /* Impact of this field depends on strategy.
+ * For strategies btopt & btultra:
+ * Length of Match considered "good enough" to stop search.
+ * Larger values make compression stronger, and slower.
+ * For strategy fast:
+ * Distance between match sampling.
+ * Larger values make compression faster, and weaker.
+ * Special: value 0 means "use default targetLength". */
ZSTD_p_compressionStrategy, /* See ZSTD_strategy enum definition.
* Cast selected strategy as unsigned for ZSTD_CCtx_setParameter() compatibility.
* The higher the value of selected strategy, the more complex it is,
* resulting in stronger and slower compression.
- * Special: value 0 means "do not change strategy". */
+ * Special: value 0 means "use default strategy". */
+
+ ZSTD_p_enableLongDistanceMatching=160, /* Enable long distance matching.
+ * This parameter is designed to improve compression ratio
+ * for large inputs, by finding large matches at long distance.
+ * It increases memory usage and window size.
+ * Note: enabling this parameter increases ZSTD_p_windowLog to 128 MB
+ * except when expressly set to a different value. */
+ ZSTD_p_ldmHashLog, /* Size of the table for long distance matching, as a power of 2.
+ * Larger values increase memory usage and compression ratio,
+ * but decrease compression speed.
+ * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
+ * default: windowlog - 7.
+ * Special: value 0 means "automatically determine hashlog". */
+ ZSTD_p_ldmMinMatch, /* Minimum match size for long distance matcher.
+ * Larger/too small values usually decrease compression ratio.
+ * Must be clamped between ZSTD_LDM_MINMATCH_MIN and ZSTD_LDM_MINMATCH_MAX.
+ * Special: value 0 means "use default value" (default: 64). */
+ ZSTD_p_ldmBucketSizeLog, /* Log size of each bucket in the LDM hash table for collision resolution.
+ * Larger values improve collision resolution but decrease compression speed.
+ * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX .
+ * Special: value 0 means "use default value" (default: 3). */
+ ZSTD_p_ldmHashEveryLog, /* Frequency of inserting/looking up entries in the LDM hash table.
+ * Must be clamped between 0 and (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN).
+ * Default is MAX(0, (windowLog - ldmHashLog)), optimizing hash table usage.
+ * Larger values improve compression speed.
+ * Deviating far from default value will likely result in a compression ratio decrease.
+ * Special: value 0 means "automatically determine hashEveryLog". */
/* frame parameters */
ZSTD_p_contentSizeFlag=200, /* Content size will be written into frame header _whenever known_ (default:1)
@@ -1012,58 +1026,45 @@ typedef enum {
ZSTD_p_dictIDFlag, /* When applicable, dictionary's ID is written into frame header (default:1) */
/* multi-threading parameters */
- ZSTD_p_nbThreads=400, /* Select how many threads a compression job can spawn (default:1)
- * More threads improve speed, but also increase memory usage.
- * Can only receive a value > 1 if ZSTD_MULTITHREAD is enabled.
- * Special: value 0 means "do not change nbThreads" */
- ZSTD_p_jobSize, /* Size of a compression job. This value is only enforced in streaming (non-blocking) mode.
- * Each compression job is completed in parallel, so indirectly controls the nb of active threads.
+ /* These parameters are only useful if multi-threading is enabled (ZSTD_MULTITHREAD).
+ * They return an error otherwise. */
+ ZSTD_p_nbWorkers=400, /* Select how many threads will be spawned to compress in parallel.
+ * When nbWorkers >= 1, triggers asynchronous mode :
+ * ZSTD_compress_generic() consumes some input, flush some output if possible, and immediately gives back control to caller,
+ * while compression work is performed in parallel, within worker threads.
+ * (note : a strong exception to this rule is when first invocation sets ZSTD_e_end : it becomes a blocking call).
+ * More workers improve speed, but also increase memory usage.
+ * Default value is `0`, aka "single-threaded mode" : no worker is spawned, compression is performed inside Caller's thread, all invocations are blocking */
+ ZSTD_p_jobSize, /* Size of a compression job. This value is enforced only in non-blocking mode.
+ * Each compression job is completed in parallel, so this value indirectly controls the nb of active threads.
* 0 means default, which is dynamically determined based on compression parameters.
- * Job size must be a minimum of overlapSize, or 1 KB, whichever is largest
+ * Job size must be a minimum of overlapSize, or 1 MB, whichever is largest.
* The minimum size is automatically and transparently enforced */
ZSTD_p_overlapSizeLog, /* Size of previous input reloaded at the beginning of each job.
* 0 => no overlap, 6(default) => use 1/8th of windowSize, >=9 => use full windowSize */
- /* advanced parameters - may not remain available after API update */
+ /* =================================================================== */
+ /* experimental parameters - no stability guaranteed */
+ /* =================================================================== */
+
+ ZSTD_p_compressLiterals=1000, /* control huffman compression of literals (enabled) by default.
+ * disabling it improves speed and decreases compression ratio by a large amount.
+ * note : this setting is automatically updated when changing compression level.
+ * positive compression levels set ZSTD_p_compressLiterals to 1.
+ * negative compression levels set ZSTD_p_compressLiterals to 0. */
+
ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize,
* even when referencing into Dictionary content (default:0) */
- ZSTD_p_enableLongDistanceMatching=1200, /* Enable long distance matching.
- * This parameter is designed to improve the compression
- * ratio for large inputs with long distance matches.
- * This increases the memory usage as well as window size.
- * Note: setting this parameter sets all the LDM parameters
- * as well as ZSTD_p_windowLog. It should be set after
- * ZSTD_p_compressionLevel and before ZSTD_p_windowLog and
- * other LDM parameters. Setting the compression level
- * after this parameter overrides the window log, though LDM
- * will remain enabled until explicitly disabled. */
- ZSTD_p_ldmHashLog, /* Size of the table for long distance matching, as a power of 2.
- * Larger values increase memory usage and compression ratio, but decrease
- * compression speed.
- * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
- * (default: windowlog - 7). */
- ZSTD_p_ldmMinMatch, /* Minimum size of searched matches for long distance matcher.
- * Larger/too small values usually decrease compression ratio.
- * Must be clamped between ZSTD_LDM_MINMATCH_MIN
- * and ZSTD_LDM_MINMATCH_MAX (default: 64). */
- ZSTD_p_ldmBucketSizeLog, /* Log size of each bucket in the LDM hash table for collision resolution.
- * Larger values usually improve collision resolution but may decrease
- * compression speed.
- * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX (default: 3). */
- ZSTD_p_ldmHashEveryLog, /* Frequency of inserting/looking up entries in the LDM hash table.
- * The default is MAX(0, (windowLog - ldmHashLog)) to
- * optimize hash table usage.
- * Larger values improve compression speed. Deviating far from the
- * default value will likely result in a decrease in compression ratio.
- * Must be clamped between 0 and ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN. */
} ZSTD_cParameter;
/*! ZSTD_CCtx_setParameter() :
* Set one compression parameter, selected by enum ZSTD_cParameter.
+ * Setting a parameter is generally only possible during frame initialization (before starting compression),
+ * except for a few exceptions which can be updated during compression: compressionLevel, hashLog, chainLog, searchLog, minMatch, targetLength and strategy.
* Note : when `value` is an enum, cast it to unsigned for proper type checking.
- * @result : informational value (typically, the one being set, possibly corrected),
+ * @result : informational value (typically, value being set clamped correctly),
* or an error code (which can be tested with ZSTD_isError()). */
ZSTDLIB_API size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned value);
@@ -1079,26 +1080,24 @@ ZSTDLIB_API size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param
ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize);
/*! ZSTD_CCtx_loadDictionary() :
- * Create an internal CDict from dict buffer.
- * Decompression will have to use same buffer.
+ * Create an internal CDict from `dict` buffer.
+ * Decompression will have to use same dictionary.
* @result : 0, or an error code (which can be tested with ZSTD_isError()).
- * Special : Adding a NULL (or 0-size) dictionary invalidates any previous dictionary,
- * meaning "return to no-dictionary mode".
- * Note 1 : `dict` content will be copied internally. Use
- * ZSTD_CCtx_loadDictionary_byReference() to reference dictionary
- * content instead. The dictionary buffer must then outlive its
- * users.
+ * Special: Adding a NULL (or 0-size) dictionary invalidates previous dictionary,
+ * meaning "return to no-dictionary mode".
+ * Note 1 : Dictionary will be used for all future compression jobs.
+ * To return to "no-dictionary" situation, load a NULL dictionary
* Note 2 : Loading a dictionary involves building tables, which are dependent on compression parameters.
* For this reason, compression parameters cannot be changed anymore after loading a dictionary.
- * It's also a CPU-heavy operation, with non-negligible impact on latency.
- * Note 3 : Dictionary will be used for all future compression jobs.
- * To return to "no-dictionary" situation, load a NULL dictionary
- * Note 5 : Use ZSTD_CCtx_loadDictionary_advanced() to select how dictionary
- * content will be interpreted.
- */
+ * It's also a CPU consuming operation, with non-negligible impact on latency.
+ * Note 3 :`dict` content will be copied internally.
+ * Use ZSTD_CCtx_loadDictionary_byReference() to reference dictionary content instead.
+ * In such a case, dictionary buffer must outlive its users.
+ * Note 4 : Use ZSTD_CCtx_loadDictionary_advanced()
+ * to precisely select how dictionary content must be interpreted. */
ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
-ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode);
+ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
/*! ZSTD_CCtx_refCDict() :
@@ -1110,8 +1109,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void
* Special : adding a NULL CDict means "return to no-dictionary mode".
* Note 1 : Currently, only one dictionary can be managed.
* Adding a new dictionary effectively "discards" any previous one.
- * Note 2 : CDict is just referenced, its lifetime must outlive CCtx.
- */
+ * Note 2 : CDict is just referenced, its lifetime must outlive CCtx. */
ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
/*! ZSTD_CCtx_refPrefix() :
@@ -1121,20 +1119,29 @@ ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
* Subsequent compression jobs will be done without prefix (if none is explicitly referenced).
* If there is a need to use same prefix multiple times, consider embedding it into a ZSTD_CDict instead.
* @result : 0, or an error code (which can be tested with ZSTD_isError()).
- * Special : Adding any prefix (including NULL) invalidates any previous prefix or dictionary
+ * Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary
* Note 1 : Prefix buffer is referenced. It must outlive compression job.
* Note 2 : Referencing a prefix involves building tables, which are dependent on compression parameters.
- * It's a CPU-heavy operation, with non-negligible impact on latency.
- * Note 3 : By default, the prefix is treated as raw content
- * (ZSTD_dm_rawContent). Use ZSTD_CCtx_refPrefix_advanced() to alter
- * dictMode. */
+ * It's a CPU consuming operation, with non-negligible impact on latency.
+ * Note 3 : By default, the prefix is treated as raw content (ZSTD_dm_rawContent).
+ * Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode. */
ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize);
-ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictMode_e dictMode);
+ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
+
+/*! ZSTD_CCtx_reset() :
+ * Return a CCtx to clean state.
+ * Useful after an error, or to interrupt an ongoing compression job and start a new one.
+ * Any internal data not yet flushed is cancelled.
+ * Dictionary (if any) is dropped.
+ * All parameters are back to default values.
+ * It's possible to modify compression parameters after a reset.
+ */
+ZSTDLIB_API void ZSTD_CCtx_reset(ZSTD_CCtx* cctx);
typedef enum {
- ZSTD_e_continue=0, /* collect more data, encoder transparently decides when to output result, for optimal conditions */
+ ZSTD_e_continue=0, /* collect more data, encoder decides when to output compressed result, for optimal conditions */
ZSTD_e_flush, /* flush any data provided so far - frame will continue, future data can still reference previous data for better compression */
ZSTD_e_end /* flush any remaining data and close current frame. Any additional data starts a new frame. */
} ZSTD_EndDirective;
@@ -1150,10 +1157,11 @@ typedef enum {
* and then immediately returns, just indicating that there is some data remaining to be flushed.
* The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte.
* - Exception : in multi-threading mode, if the first call requests a ZSTD_e_end directive, it is blocking : it will complete compression before giving back control to caller.
- * - @return provides the minimum amount of data remaining to be flushed from internal buffers
+ * - @return provides a minimum amount of data remaining to be flushed from internal buffers
* or an error code, which can be tested using ZSTD_isError().
* if @return != 0, flush is not fully completed, there is still some data left within internal buffers.
- * This is useful to determine if a ZSTD_e_flush or ZSTD_e_end directive is completed.
+ * This is useful for ZSTD_e_flush, since in this case more flushes are necessary to empty all buffers.
+ * For ZSTD_e_end, @return == 0 when internal buffers are fully flushed and frame is completed.
* - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0),
* only ZSTD_e_end or ZSTD_e_flush operations are allowed.
* Before starting a new compression job, or changing compression parameters,
@@ -1164,16 +1172,6 @@ ZSTDLIB_API size_t ZSTD_compress_generic (ZSTD_CCtx* cctx,
ZSTD_inBuffer* input,
ZSTD_EndDirective endOp);
-/*! ZSTD_CCtx_reset() :
- * Return a CCtx to clean state.
- * Useful after an error, or to interrupt an ongoing compression job and start a new one.
- * Any internal data not yet flushed is cancelled.
- * Dictionary (if any) is dropped.
- * All parameters are back to default values.
- * It's possible to modify compression parameters after a reset.
- */
-ZSTDLIB_API void ZSTD_CCtx_reset(ZSTD_CCtx* cctx); /* Not ready yet ! */
-
/*! ZSTD_compress_generic_simpleArgs() :
* Same as ZSTD_compress_generic(),
@@ -1207,25 +1205,26 @@ ZSTDLIB_API size_t ZSTD_compress_generic_simpleArgs (
* for static allocation for single-threaded compression.
*/
ZSTDLIB_API ZSTD_CCtx_params* ZSTD_createCCtxParams(void);
+ZSTDLIB_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params);
-/*! ZSTD_resetCCtxParams() :
- * Reset params to default, with the default compression level.
+
+/*! ZSTD_CCtxParams_reset() :
+ * Reset params to default values.
*/
-ZSTDLIB_API size_t ZSTD_resetCCtxParams(ZSTD_CCtx_params* params);
+ZSTDLIB_API size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params);
-/*! ZSTD_initCCtxParams() :
+/*! ZSTD_CCtxParams_init() :
* Initializes the compression parameters of cctxParams according to
* compression level. All other parameters are reset to their default values.
*/
-ZSTDLIB_API size_t ZSTD_initCCtxParams(ZSTD_CCtx_params* cctxParams, int compressionLevel);
+ZSTDLIB_API size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel);
-/*! ZSTD_initCCtxParams_advanced() :
+/*! ZSTD_CCtxParams_init_advanced() :
* Initializes the compression and frame parameters of cctxParams according to
* params. All other parameters are reset to their default values.
*/
-ZSTDLIB_API size_t ZSTD_initCCtxParams_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params);
+ZSTDLIB_API size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params);
-ZSTDLIB_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params);
/*! ZSTD_CCtxParam_setParameter() :
* Similar to ZSTD_CCtx_setParameter.
@@ -1238,9 +1237,10 @@ ZSTDLIB_API size_t ZSTD_CCtxParam_setParameter(ZSTD_CCtx_params* params, ZSTD_cP
/*! ZSTD_CCtx_setParametersUsingCCtxParams() :
* Apply a set of ZSTD_CCtx_params to the compression context.
- * This must be done before the dictionary is loaded.
- * The pledgedSrcSize is treated as unknown.
- * Multithreading parameters are applied only if nbThreads > 1.
+ * This can be done even after compression is started,
+ * if nbWorkers==0, this will have no impact until a new compression is started.
+ * if nbWorkers>=1, new parameters will be picked up at next job,
+ * with a few restrictions (windowLog, pledgedSrcSize, nbWorkers, jobSize, and overlapLog are not updated).
*/
ZSTDLIB_API size_t ZSTD_CCtx_setParametersUsingCCtxParams(
ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params);
@@ -1267,9 +1267,9 @@ ZSTDLIB_API size_t ZSTD_CCtx_setParametersUsingCCtxParams(
* Note 3 : Use ZSTD_DCtx_loadDictionary_advanced() to select
* how dictionary content will be interpreted and loaded.
*/
-ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); /* not implemented */
-ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); /* not implemented */
-ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode); /* not implemented */
+ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
+ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
+ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
/*! ZSTD_DCtx_refDDict() :
@@ -1281,7 +1281,7 @@ ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void
* Special : adding a NULL DDict means "return to no-dictionary mode".
* Note 2 : DDict is just referenced, its lifetime must outlive its usage from DCtx.
*/
-ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); /* not implemented */
+ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
/*! ZSTD_DCtx_refPrefix() :
@@ -1295,8 +1295,8 @@ ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
* Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode.
* Note 4 : Referencing a raw content prefix has almost no cpu nor memory cost.
*/
-ZSTDLIB_API size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize); /* not implemented */
-ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictMode_e dictMode); /* not implemented */
+ZSTDLIB_API size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize);
+ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
/*! ZSTD_DCtx_setMaxWindowSize() :
@@ -1389,7 +1389,7 @@ ZSTDLIB_API void ZSTD_DCtx_reset(ZSTD_DCtx* dctx);
ZSTDLIB_API size_t ZSTD_getBlockSize (const ZSTD_CCtx* cctx);
ZSTDLIB_API size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
ZSTDLIB_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
-ZSTDLIB_API size_t ZSTD_insertBlock(ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression */
+ZSTDLIB_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */
#endif /* ZSTD_H_ZSTD_STATIC_LINKING_ONLY */